/******************************************************************************* * libretroshare/src/retroshare: rsids.h * * * * libretroshare: retroshare core library * * * * Copyright (C) 2013 Cyril Soler * * Copyright (C) 2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * *******************************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include #include #include "util/rsdebug.h" #include "util/rsrandom.h" #include "util/stacktrace.h" /** * RsGenericIdType values might be random, but must be different, in order to * make the various IDs incompatible with each other. */ enum class RsGenericIdType { SSL, PGP_ID, SHA1, PGP_FINGERPRINT, GXS_GROUP, GXS_ID, GXS_MSG, GXS_CIRCLE, GROUTER, GXS_TUNNEL, DISTANT_CHAT, NODE_GROUP, SHA256, BIAS_20_BYTES }; /** * This class aims at defining a generic ID type that is a list of bytes. It * can be converted into a hexadecial string for printing, mainly) or for * compatibility with old methods. * * To use this class, derive your own ID type from it. * @see RsPpgFingerprint as an example. * * Take care to define and use a different @see RsGenericIdType for each ne type * of ID you create, to avoid implicit conversion between subtypes, and * therefore accidental ID mixup is impossible. * * ID Types with different lengths are not convertible even using explicit * constructor and compilation would fail if that is attempted. * * Warning: never store references to a t_RsGenericIdType accross threads, since * the cached string convertion is not thread safe. */ template struct t_RsGenericIdType { using Id_t = t_RsGenericIdType; using std_list = std::list; using std_vector = std::vector; using std_set = std::set; /// by default, ids are set to null() t_RsGenericIdType() { memset(bytes, 0, ID_SIZE_IN_BYTES); } /// Explicit constructor from a hexadecimal string explicit t_RsGenericIdType(const std::string& hex_string); /** * @brief Construct from a buffer of at least the size of SIZE_IN_BYTES * This is dangerous if used without being absolutely sure of buffer size, * nothing prevent a buffer of wrong size being passed at runtime! * @param[in] buff pointer to the buffer * @return empty id on failure, an id initialized from the bytes in the * buffer */ static Id_t fromBufferUnsafe(const uint8_t* buff) { Id_t ret; if(!buff) { RsErr() << __PRETTY_FUNCTION__ << " invalid paramethers buff: " << buff << std::endl; print_stacktrace(); return ret; } memmove(ret.bytes, buff, SIZE_IN_BYTES); return ret; } /** * Explicit constructor from a different type but with same size. * * This is used for conversions such as * GroupId -> CircleId * GroupId -> GxsId */ template explicit t_RsGenericIdType( const t_RsGenericIdType& id ) { memmove(bytes, id.toByteArray(), ID_SIZE_IN_BYTES); } /// Random initialization. Can be useful for testing and to generate new ids. static Id_t random() { Id_t id; RsRandom::random_bytes(id.bytes, ID_SIZE_IN_BYTES); return id; } inline void clear() { memset(bytes, 0, SIZE_IN_BYTES); } /// Converts to a std::string using cached value. const uint8_t* toByteArray() const { return &bytes[0]; } uint8_t* asByteArray() { return &bytes[0]; } static constexpr uint32_t SIZE_IN_BYTES = ID_SIZE_IN_BYTES; inline bool operator==(const Id_t& fp) const { return !memcmp(bytes, fp.bytes, ID_SIZE_IN_BYTES); } inline bool operator!=(const Id_t& fp) const { return !!memcmp(bytes, fp.bytes, ID_SIZE_IN_BYTES); } inline bool operator< (const Id_t& fp) const { return (memcmp(bytes, fp.bytes, ID_SIZE_IN_BYTES) < 0); } inline Id_t operator~ () const { Id_t ret; for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i) ret.bytes[i] = ~bytes[i]; return ret; } inline Id_t operator| (const Id_t& fp) const { Id_t ret; for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i) ret.bytes[i] = bytes[i] | fp.bytes[i]; return ret; } inline Id_t& operator&=(const Id_t& fp) { for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i) bytes[i] &= fp.bytes[i]; return *this; } inline Id_t operator^ (const Id_t& fp) const { Id_t ret; for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i) ret.bytes[i] = bytes[i] ^ fp.bytes[i]; return ret; } inline bool isNull() const { for(uint32_t i=0; i < SIZE_IN_BYTES; ++i) if(bytes[i] != 0) return false; return true; } friend std::ostream& operator<<(std::ostream& out, const Id_t& id) { switch (UNIQUE_IDENTIFIER) { case RsGenericIdType::PGP_FINGERPRINT: { uint8_t index = 0; for(char c : id.toStdString()) { out << c; if(++index % 4 == 0 && index < id.SIZE_IN_BYTES*2) out << ' '; } } break; default: out << id.toStdString(UPPER_CASE); break; } return out; } inline std::string toStdString() const { return toStdString(UPPER_CASE); } inline static uint32_t serial_size() { return SIZE_IN_BYTES; } bool serialise(void* data,uint32_t pktsize,uint32_t& offset) const { if(offset + SIZE_IN_BYTES > pktsize) return false; memmove( &(reinterpret_cast(data))[offset], bytes, SIZE_IN_BYTES ); offset += SIZE_IN_BYTES; return true; } bool deserialise(const void* data, uint32_t pktsize, uint32_t& offset) { if(offset + SIZE_IN_BYTES > pktsize) return false; memmove( bytes, &(reinterpret_cast(data))[offset], SIZE_IN_BYTES ); offset += SIZE_IN_BYTES; return true; } /** Explicit constructor from a byte array. The array must have size at * least ID_SIZE_IN_BYTES * @deprecated This is too dangerous! * Nothing prevent a buffer of wrong size being passed at runtime! */ RS_DEPRECATED_FOR("fromBufferUnsafe(const uint8_t* buff)") explicit t_RsGenericIdType(const uint8_t bytes[]); private: std::string toStdString(bool upper_case) const; uint8_t bytes[ID_SIZE_IN_BYTES]; }; template std::string t_RsGenericIdType ::toStdString(bool upper_case) const { static const char outh[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' } ; static const char outl[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' } ; std::string res(ID_SIZE_IN_BYTES*2,' ') ; for(uint32_t j = 0; j < ID_SIZE_IN_BYTES; j++) if(upper_case) { res[2*j ] = outh[ (bytes[j]>>4) ] ; res[2*j+1] = outh[ bytes[j] & 0xf ] ; } else { res[2*j ] = outl[ (bytes[j]>>4) ] ; res[2*j+1] = outl[ bytes[j] & 0xf ] ; } return res ; } template t_RsGenericIdType ::t_RsGenericIdType(const std::string& s) { std::string::size_type n = 0; if(s.length() != ID_SIZE_IN_BYTES*2) { if(!s.empty()) { RsErr() << __PRETTY_FUNCTION__ << " supplied string in constructor " << "has wrong size. Expected ID size=" << ID_SIZE_IN_BYTES*2 << " String=\"" << s << "\" = " << s.length() << std::endl; print_stacktrace(); } clear(); return; } for(uint32_t i = 0; i < ID_SIZE_IN_BYTES; ++i) { bytes[i] = 0 ; for(int k=0;k<2;++k) { char b = s[n++] ; if(b >= 'A' && b <= 'F') bytes[i] += (b-'A'+10) << 4*(1-k) ; else if(b >= 'a' && b <= 'f') bytes[i] += (b-'a'+10) << 4*(1-k) ; else if(b >= '0' && b <= '9') bytes[i] += (b-'0') << 4*(1-k) ; else { RsErr() << __PRETTY_FUNCTION__ << "supplied string is not " << "purely hexadecimal: s=\"" << s << "\"" << std::endl; clear(); return; } } } } template RS_DEPRECATED_FOR("t_RsGenericIdType::fromBuffer(...)") t_RsGenericIdType:: t_RsGenericIdType(const uint8_t mem[]) /// @deprecated Too dangerous! { if(mem == nullptr) memset(bytes, 0, ID_SIZE_IN_BYTES); else memcpy(bytes, mem, ID_SIZE_IN_BYTES); } /** * This constants are meant to be used only inside this file. * Use @see t_RsGenericIdType::SIZE_IN_BYTES in other places. */ namespace _RsIdSize { constexpr uint32_t SSL_ID = 16; // = CERT_SIGN constexpr uint32_t CERT_SIGN = 16; // = SSL_ID constexpr uint32_t PGP_ID = 8; constexpr uint32_t PGP_FINGERPRINT = 20; constexpr uint32_t SHA1 = 20; constexpr uint32_t SHA256 = 32; } using RsPeerId = t_RsGenericIdType<_RsIdSize::SSL_ID , false, RsGenericIdType::SSL >; using RsPgpId = t_RsGenericIdType<_RsIdSize::PGP_ID , true, RsGenericIdType::PGP_ID >; using Sha1CheckSum = t_RsGenericIdType<_RsIdSize::SHA1 , false, RsGenericIdType::SHA1 >; using Sha256CheckSum = t_RsGenericIdType<_RsIdSize::SHA256 , false, RsGenericIdType::SHA256 >; using RsPgpFingerprint = t_RsGenericIdType<_RsIdSize::PGP_FINGERPRINT, true, RsGenericIdType::PGP_FINGERPRINT>; using Bias20Bytes = t_RsGenericIdType<_RsIdSize::SHA1 , true, RsGenericIdType::BIAS_20_BYTES >; using RsGxsGroupId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::GXS_GROUP >; using RsGxsMessageId = t_RsGenericIdType<_RsIdSize::SHA1 , false, RsGenericIdType::GXS_MSG >; using RsGxsId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::GXS_ID >; using RsGxsCircleId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::GXS_CIRCLE >; using RsGxsTunnelId = t_RsGenericIdType<_RsIdSize::SSL_ID , false, RsGenericIdType::GXS_TUNNEL >; using DistantChatPeerId = t_RsGenericIdType<_RsIdSize::SSL_ID , false, RsGenericIdType::DISTANT_CHAT >; using RsNodeGroupId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::NODE_GROUP >; /// @deprecated Ugly name kept temporarly only because it is used in many places using PGPFingerprintType RS_DEPRECATED_FOR(RsPpgFingerprint) = RsPgpFingerprint;