mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-02 14:16:16 -04:00
Merged branch v0.5-OpenPGP into trunk:
User-level changes: ================== - libgpgme is not used anymore; it is replaced by a built-in piece of code called OpenPGP-SDK (http://openpgp.nominet.org.uk/cgi-bin/trac.cgi) that was improved to be used by RetroShare for handling PGP keys. - the gnupg keyring is not used anymore. Now, RetroShare has it's own gpg keyring, shared by all instances. On linux it's located in ~/.retroshare/pgp/. A lock system prevents multiple locations to read/write keyrings simultaneously. - the trust database from gnupg is not documented, so RetroShare cannot import it. This comes from the fact that the GPG standard (RFC4880) asks explicitly not to export trust information. So RetroShare has it's own trust DB shared by locations. This means you need to re-trust people. Sorry for that! - at start, if no keyring is found, RS will propose to copy the gnupg keyring to use your existing keys. Clicking on "OK" will do the copy, and you should find back all existing locations, except for DSA keys. - locations for which the suitable keypair is not in the keyring will not be displayed in the login window - locations for which the suitable keypair is not a RSA/RSA key will not be displayed. RetroShare does not support DSA/Elgamal keypairs yet. - a key import/export exchange function has been added in the certificate creation window (you go there from the login window by clicking on "manage keys/locations". This allows to easily create a new location with the same pgp key on another computer. To obtain a suitable keypair using gnupg, you need to concatenate the encrypted private key and the public key into an ascii file. This can be done using: gpg -a --export-secret-keys [your ID] > mykey.asc gpg -a --export [your ID] >> mykey.asc - importing a key with subkeys in not yet possible. Please remove subkeys before importing. - The code has been tested for a reasonnable amount of time, but it's not possible to prevent some new bugs to appear. Please report them asap supplying: call-stacks if possible, and terminal output. In particular, openpgp has some assert()'s that should not be triggered unless RetroShare is calling it in an improper way. Internal changes ================ - a specific component, PGPHandler, takes care of the interface between openpgp-sdk and RetroShare openpgp-sdk is c-code, with it's own memory management, which has been kept well separated from RetroShare. - GPG Ids are now a specific class (not a std::string anymore) for code consistency reasons. As strings are still used in many places, this requires a few conversions. In particular, AuthGPG takes strings as function params and calls GPGHandler with the proper PGPIdType class. In the future, RetroShare should only use PGPIdType. The same will be done for SSL ids. - signature cleaning is still handled by the Retroshare built-in function, not by openpgp, but we will do this later. Still to do =========== - DSA needs subkey handling, since the encryption is performed by a Elgamal subkey. Not sure this will be done. - GPGIds/SSLIds cleaning (meaning replace strings by appropriate types). Lots of confusion throughout the code in retroshare-gui in particular. - key removal from keyring. This is a challenge to keep locations synchronised. git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@5293 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
commit
fc8dfcf65b
109 changed files with 26549 additions and 2997 deletions
|
@ -1,242 +0,0 @@
|
|||
#include <stdint.h>
|
||||
#include <util/radix64.h>
|
||||
#include "pgpkey.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
/****************************/
|
||||
/* #define DEBUG_PGPUTIL 1 */
|
||||
/****************************/
|
||||
|
||||
#define PGP_PACKET_TAG_PUBLIC_KEY 6
|
||||
#define PGP_PACKET_TAG_USER_ID 13
|
||||
#define PGP_PACKET_TAG_SIGNATURE 2
|
||||
|
||||
#define PGP_CRC24_INIT 0xB704CEL
|
||||
#define PGP_CRC24_POLY 0x1864CFBL
|
||||
|
||||
#define PGP_CERTIFICATE_START_STRING "-----BEGIN PGP PUBLIC KEY BLOCK-----"
|
||||
#define PGP_CERTIFICATE_END_STRING "-----END PGP PUBLIC KEY BLOCK-----"
|
||||
//
|
||||
// All size are big endian
|
||||
// MPI: 2 bytes size (length in bits) + string of octets
|
||||
//
|
||||
bool PGPKeyManagement::createMinimalKey(const std::string& pgp_certificate,std::string& cleaned_certificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 0 - Extract Radix64 portion of the certificate
|
||||
//
|
||||
int n = pgp_certificate.length() ;
|
||||
int i=0 ;
|
||||
std::string version_string = "" ;
|
||||
|
||||
while(i < n && pgp_certificate[i] != '\n') ++i ; // remove first part -----BEGIN PGP CERTIFICATE-----
|
||||
++i ;
|
||||
while(i < n && pgp_certificate[i] != '\n') version_string += pgp_certificate[i++] ; // remove first part Version: [fdfdfdf]
|
||||
++i ;
|
||||
while(i < n && pgp_certificate[i] != '\n') ++i ; // remove blank line
|
||||
|
||||
++i ;
|
||||
|
||||
int j=n-1 ;
|
||||
|
||||
while(j>0 && pgp_certificate[j] != '=' && j>=i) --j ;
|
||||
|
||||
std::string radix_cert = pgp_certificate.substr(i,j-i) ;
|
||||
|
||||
#ifdef DEBUG_PGPUTIL
|
||||
std::cerr << "extracted radix cert: " << std::endl;
|
||||
std::cerr << radix_cert ;
|
||||
#endif
|
||||
|
||||
// 1 - Convert armored key into binary key
|
||||
//
|
||||
|
||||
char *keydata = NULL ;
|
||||
size_t len = 0 ;
|
||||
|
||||
Radix64::decode(radix_cert,keydata,len) ;
|
||||
|
||||
unsigned char *data = (unsigned char *)keydata ;
|
||||
|
||||
#ifdef DEBUG_PGPUTIL
|
||||
std::cerr << "Total size: " << len << std::endl;
|
||||
#endif
|
||||
|
||||
uint8_t packet_tag;
|
||||
uint32_t packet_length ;
|
||||
|
||||
// 2 - parse key data, only keep public key data, user id and self-signature.
|
||||
|
||||
bool public_key=false ;
|
||||
bool own_signature=false ;
|
||||
bool user_id=false ;
|
||||
|
||||
while(true)
|
||||
{
|
||||
PGPKeyParser::read_packetHeader(data,packet_tag,packet_length) ;
|
||||
#ifdef DEBUG_PGPUTIL
|
||||
std::cerr << "Header:" << std::endl;
|
||||
std::cerr << " Packet tag: " << (int)packet_tag << std::endl;
|
||||
std::cerr << " Packet length: " << packet_length << std::endl;
|
||||
#endif
|
||||
|
||||
data += packet_length ;
|
||||
|
||||
if(packet_tag == PGP_PACKET_TAG_PUBLIC_KEY)
|
||||
public_key = true ;
|
||||
if(packet_tag == PGP_PACKET_TAG_USER_ID)
|
||||
user_id = true ;
|
||||
if(packet_tag == PGP_PACKET_TAG_SIGNATURE)
|
||||
own_signature = true ;
|
||||
|
||||
if(public_key && own_signature && user_id)
|
||||
break ;
|
||||
|
||||
if( (uint64_t)data - (uint64_t)keydata >= len )
|
||||
break ;
|
||||
}
|
||||
|
||||
std::string outstring ;
|
||||
Radix64::encode(keydata,(uint64_t)data - (uint64_t)keydata,outstring) ;
|
||||
|
||||
uint32_t crc = compute24bitsCRC((unsigned char *)keydata,(uint64_t)data - (uint64_t)keydata) ;
|
||||
|
||||
unsigned char tmp[3] = { (crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff } ;
|
||||
std::string crc_string ;
|
||||
Radix64::encode((const char *)tmp,3,crc_string) ;
|
||||
|
||||
#ifdef DEBUG_PGPUTIL
|
||||
std::cerr << "After signature pruning: " << std::endl;
|
||||
std::cerr << outstring << std::endl;
|
||||
#endif
|
||||
|
||||
cleaned_certificate = std::string(PGP_CERTIFICATE_START_STRING) + "\n" + version_string + "\n\n" ;
|
||||
|
||||
for(uint32_t i=0;i<outstring.length();i+=64)
|
||||
cleaned_certificate += outstring.substr(i,64) + "\n" ;
|
||||
|
||||
cleaned_certificate += "=" + crc_string + "\n" ;
|
||||
cleaned_certificate += std::string(PGP_CERTIFICATE_END_STRING) + "\n" ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
cleaned_certificate = "" ;
|
||||
std::cerr << "Certificate cleaning failed: " << e.what() << std::endl;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t PGPKeyManagement::compute24bitsCRC(unsigned char *octets, size_t len)
|
||||
{
|
||||
long crc = PGP_CRC24_INIT;
|
||||
int i;
|
||||
while (len--) {
|
||||
crc ^= (*octets++) << 16;
|
||||
for (i = 0; i < 8; i++) {
|
||||
crc <<= 1;
|
||||
if (crc & 0x1000000)
|
||||
crc ^= PGP_CRC24_POLY;
|
||||
}
|
||||
}
|
||||
return crc & 0xFFFFFFL;
|
||||
}
|
||||
|
||||
uint64_t PGPKeyParser::read_KeyID(unsigned char *& data)
|
||||
{
|
||||
uint64_t val = 0 ;
|
||||
|
||||
val |= uint64_t( *data ) << 56 ; ++data ;
|
||||
val |= uint64_t( *data ) << 48 ; ++data ;
|
||||
val |= uint64_t( *data ) << 40 ; ++data ;
|
||||
val |= uint64_t( *data ) << 32 ; ++data ;
|
||||
val |= uint64_t( *data ) << 24 ; ++data ;
|
||||
val |= uint64_t( *data ) << 16 ; ++data ;
|
||||
val |= uint64_t( *data ) << 8 ; ++data ;
|
||||
val |= uint64_t( *data ) << 0 ; ++data ;
|
||||
|
||||
return val ;
|
||||
}
|
||||
|
||||
uint32_t PGPKeyParser::read_125Size(unsigned char *& data)
|
||||
{
|
||||
uint8_t b1 = *data ;
|
||||
++data ;
|
||||
|
||||
if(b1 < 192)
|
||||
return b1 ;
|
||||
|
||||
uint8_t b2 = *data ;
|
||||
|
||||
if(b1 < 224)
|
||||
return ((b1-192) << 8) + b2 + 192 ;
|
||||
|
||||
if(b1 != 0xff)
|
||||
throw std::runtime_error("GPG parsing error") ;
|
||||
|
||||
uint8_t b3 = *data ; ++data ;
|
||||
uint8_t b4 = *data ; ++data ;
|
||||
uint8_t b5 = *data ; ++data ;
|
||||
|
||||
return (b2 << 24) | (b3 << 16) | (b4 << 8) | b5 ;
|
||||
}
|
||||
|
||||
uint32_t PGPKeyParser::read_partialBodyLength(unsigned char *& data)
|
||||
{
|
||||
uint8_t b1 =*data ;
|
||||
++data ;
|
||||
|
||||
return 1 << (b1 & 0x1F) ;
|
||||
}
|
||||
|
||||
|
||||
void PGPKeyParser::read_packetHeader(unsigned char *& data,uint8_t& packet_tag,uint32_t& packet_length)
|
||||
{
|
||||
uint8_t b1 = *data ;
|
||||
++data ;
|
||||
|
||||
bool new_format = b1 & 0x40 ;
|
||||
|
||||
if(new_format)
|
||||
{
|
||||
#ifdef DEBUG_PGPUTIL
|
||||
std::cerr << "Packet is in new format" << std::endl;
|
||||
#endif
|
||||
packet_tag = b1 & 0x3f ;
|
||||
packet_length = read_125Size(data) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_PGPUTIL
|
||||
std::cerr << "Packet is in old format" << std::endl;
|
||||
#endif
|
||||
uint8_t length_type = b1 & 0x03 ;
|
||||
packet_tag = (b1 & 0x3c) >> 2 ;
|
||||
|
||||
int length_size ;
|
||||
switch(length_type)
|
||||
{
|
||||
case 0: length_size = 1 ;
|
||||
break ;
|
||||
case 1: length_size = 2 ;
|
||||
break ;
|
||||
case 2: length_size = 4 ;
|
||||
break ;
|
||||
default:
|
||||
throw std::runtime_error("Unhandled length type!") ;
|
||||
}
|
||||
|
||||
packet_length = 0 ;
|
||||
for(int k=0;k<length_size;++k)
|
||||
{
|
||||
packet_length <<= 8 ;
|
||||
packet_length |= *data ;
|
||||
++data ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/****************************************************************
|
||||
* RetroShare is distributed under the following license:
|
||||
*
|
||||
* Copyright (C) 2012 Cyril Soler <csoler@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
****************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// refer to RFC4880 specif document for loading GPG public keys:
|
||||
//
|
||||
// 11.1: transferable public keys
|
||||
// Global structure of transferable public keys
|
||||
//
|
||||
// - one public key packet (see 12.2)
|
||||
// - zero or more revocation signatures (See signature type 5.2.1 for key signature types)
|
||||
//
|
||||
// - user certification signatures (0x10 or 0x13)
|
||||
//
|
||||
// - 5.2.2: Signature format packet
|
||||
// - 5.2.3.1: signature subpacket specification
|
||||
//
|
||||
// - 4.3: packet tags (1 byte)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
// This class handles GPG keys. For now we only clean them from signatures, but
|
||||
// in the future, we might cache them to avoid unnecessary calls to gpgme.
|
||||
//
|
||||
class PGPKeyManagement
|
||||
{
|
||||
public:
|
||||
// Create a minimal key, removing all signatures and third party info.
|
||||
// Input: a clean PGP certificate (starts with "----BEGIN",
|
||||
// ends with "-----END PGP PUBLIC KEY BLOCK-----"
|
||||
// Output: the same certificate without signatures.
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// true if the certificate cleaning succeeded
|
||||
// false otherwise.
|
||||
//
|
||||
static bool createMinimalKey(const std::string& pgp_certificate,std::string& cleaned_certificate) ;
|
||||
|
||||
private:
|
||||
// Computes the 24 bits CRC checksum necessary to all PGP data.
|
||||
//
|
||||
static uint32_t compute24bitsCRC(unsigned char *data,size_t len) ;
|
||||
};
|
||||
|
||||
// This class handles the parsing of PGP packet headers under various (old and new) formats.
|
||||
//
|
||||
class PGPKeyParser
|
||||
{
|
||||
public:
|
||||
static uint64_t read_KeyID(unsigned char *& data) ;
|
||||
static uint32_t read_125Size(unsigned char *& data) ;
|
||||
static uint32_t read_partialBodyLength(unsigned char *& data) ;
|
||||
static void read_packetHeader(unsigned char *& data,uint8_t& packet_tag,uint32_t& packet_length) ;
|
||||
};
|
||||
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
// Includes for directory creation.
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/rsdir.h"
|
||||
|
@ -848,6 +849,118 @@ std::string RsDirUtil::makePath(const std::string &path1, const std::string &pat
|
|||
return path;
|
||||
}
|
||||
|
||||
int RsDirUtil::createLockFile(const std::string& lock_file_path, rs_lock_handle_t &lock_handle)
|
||||
{
|
||||
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
|
||||
#ifndef WINDOWS_SYS
|
||||
// Suspended. The user should make sure he's not already using the file descriptor.
|
||||
// if(lock_handle != -1)
|
||||
// close(lock_handle);
|
||||
|
||||
// open the file in write mode, create it if necessary, truncate it (it should be empty)
|
||||
lock_handle = open(lock_file_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
if(lock_handle == -1)
|
||||
{
|
||||
std::cerr << "Could not open lock file " << lock_file_path.c_str() << std::flush;
|
||||
perror(NULL);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// see "man fcntl" for the details, in short: non blocking lock creation on the whole file contents
|
||||
struct flock lockDetails;
|
||||
lockDetails.l_type = F_WRLCK;
|
||||
lockDetails.l_whence = SEEK_SET;
|
||||
lockDetails.l_start = 0;
|
||||
lockDetails.l_len = 0;
|
||||
|
||||
if(fcntl(lock_handle, F_SETLK, &lockDetails) == -1)
|
||||
{
|
||||
int fcntlErr = errno;
|
||||
std::cerr << "Could not request lock on file " << lock_file_path.c_str() << std::flush;
|
||||
perror(NULL);
|
||||
|
||||
// there's no lock so let's release the file handle immediately
|
||||
close(lock_handle);
|
||||
lock_handle = -1;
|
||||
|
||||
if(fcntlErr == EACCES || fcntlErr == EAGAIN)
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
// Suspended. The user should make sure he's not already using the file descriptor.
|
||||
//
|
||||
// if (lock_handle) {
|
||||
// CloseHandle(lock_handle);
|
||||
// }
|
||||
|
||||
std::wstring wlockFile;
|
||||
librs::util::ConvertUtf8ToUtf16(lock_file_path, wlockFile);
|
||||
|
||||
// open the file in write mode, create it if necessary
|
||||
lock_handle = CreateFile(wlockFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
||||
|
||||
if (lock_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD lasterror = GetLastError();
|
||||
|
||||
std::cerr << "Could not open lock file " << lock_file_path.c_str() << std::endl;
|
||||
std::cerr << "Last error: " << lasterror << std::endl << std::flush;
|
||||
perror(NULL);
|
||||
|
||||
if (lasterror == ERROR_SHARING_VIOLATION || lasterror == ERROR_ACCESS_DENIED)
|
||||
return 1;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
|
||||
}
|
||||
|
||||
void RsDirUtil::releaseLockFile(rs_lock_handle_t lockHandle)
|
||||
{
|
||||
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
|
||||
#ifndef WINDOWS_SYS
|
||||
if(lockHandle != -1)
|
||||
{
|
||||
close(lockHandle);
|
||||
lockHandle = -1;
|
||||
}
|
||||
#else
|
||||
if(lockHandle)
|
||||
{
|
||||
CloseHandle(lockHandle);
|
||||
lockHandle = 0;
|
||||
}
|
||||
#endif
|
||||
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
|
||||
}
|
||||
|
||||
RsStackFileLock::RsStackFileLock(const std::string& file_path)
|
||||
{
|
||||
while(RsDirUtil::createLockFile(file_path,_file_handle))
|
||||
{
|
||||
std::cerr << "Cannot acquire file lock " << file_path << ", waiting 1 sec." << std::endl;
|
||||
#ifdef WINDOWS_SYS
|
||||
Sleep(1000) ;
|
||||
#else
|
||||
sleep(1) ;
|
||||
#endif
|
||||
}
|
||||
std::cerr << "Acquired file handle " << _file_handle << ", lock file:" << file_path << std::endl;
|
||||
}
|
||||
RsStackFileLock::~RsStackFileLock()
|
||||
{
|
||||
RsDirUtil::releaseLockFile(_file_handle) ;
|
||||
std::cerr << "Released file lock with handle " << _file_handle << std::endl;
|
||||
}
|
||||
|
||||
#if 0 // NOT ENABLED YET!
|
||||
/************************* WIDE STRING ***************************/
|
||||
/************************* WIDE STRING ***************************/
|
||||
|
|
|
@ -36,6 +36,27 @@ class RsThread;
|
|||
|
||||
#include <retroshare/rstypes.h>
|
||||
|
||||
#ifndef WINDOWS_SYS
|
||||
typedef int rs_lock_handle_t;
|
||||
#else
|
||||
typedef /*HANDLE*/ void *rs_lock_handle_t;
|
||||
#endif
|
||||
|
||||
// This is a scope guard on a given file. Works like a mutex. Is blocking.
|
||||
// We could do that in another way: derive RsMutex into RsLockFileMutex, and
|
||||
// use RsStackMutex on it transparently. Only issue: this will cost little more
|
||||
// because of the multiple inheritance.
|
||||
//
|
||||
class RsStackFileLock
|
||||
{
|
||||
public:
|
||||
RsStackFileLock(const std::string& file_path) ;
|
||||
~RsStackFileLock() ;
|
||||
|
||||
private:
|
||||
rs_lock_handle_t _file_handle ;
|
||||
};
|
||||
|
||||
namespace RsDirUtil {
|
||||
|
||||
std::string getTopDir(const std::string&);
|
||||
|
@ -70,6 +91,15 @@ bool getFileHash(const std::string& filepath,std::string &hash, uint64_t &size
|
|||
|
||||
Sha1CheckSum sha1sum(uint8_t *data,uint32_t size) ;
|
||||
|
||||
// Creates a lock file with given path, and returns the lock handle
|
||||
// returns:
|
||||
// 0: Success
|
||||
// 1: Another instance already has the lock
|
||||
// 2 : Unexpected error
|
||||
int createLockFile(const std::string& lock_file_path, rs_lock_handle_t& lock_handle) ;
|
||||
|
||||
// Removes the lock file with specified handle.
|
||||
void releaseLockFile(rs_lock_handle_t lockHandle) ;
|
||||
|
||||
std::wstring getWideTopDir(std::wstring);
|
||||
std::wstring getWideRootDir(std::wstring);
|
||||
|
|
112
libretroshare/src/util/rsid.h
Normal file
112
libretroshare/src/util/rsid.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
// 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. Examples include:
|
||||
//
|
||||
// class PGPIdType: public t_RsGenericIdType<8>
|
||||
// {
|
||||
// [..]
|
||||
// };
|
||||
//
|
||||
// class PGPFingerprintType: public t_RsGenericIdType<20>
|
||||
// {
|
||||
// [..]
|
||||
// };
|
||||
//
|
||||
// With this, there is no implicit conversion between subtypes, and therefore ID mixup
|
||||
// is impossible.
|
||||
//
|
||||
// A simpler way to make ID types is to
|
||||
// typedef t_RsGenericIdType<MySize> MyType ;
|
||||
//
|
||||
// ID Types with different lengths will be incompatible on compilation.
|
||||
//
|
||||
// Warning: never store references to a t_RsGenericIdType accross threads, since the
|
||||
// cached string convertion is not thread safe.
|
||||
//
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
template<uint32_t ID_SIZE_IN_BYTES> class t_RsGenericIdType
|
||||
{
|
||||
public:
|
||||
t_RsGenericIdType() {}
|
||||
virtual ~t_RsGenericIdType() {}
|
||||
|
||||
// Explicit constructor from a hexadecimal string
|
||||
//
|
||||
explicit t_RsGenericIdType(const std::string& hex_string) ;
|
||||
|
||||
// Explicit constructor from a byte array. The array should have size at least ID_SIZE_IN_BYTES
|
||||
//
|
||||
explicit t_RsGenericIdType(const unsigned char bytes[]) ;
|
||||
|
||||
// Converts to a std::string using cached value.
|
||||
//
|
||||
std::string toStdString() const ;
|
||||
const unsigned char *toByteArray() const { return &bytes[0] ; }
|
||||
static const uint32_t SIZE_IN_BYTES = ID_SIZE_IN_BYTES ;
|
||||
|
||||
bool operator==(const t_RsGenericIdType<ID_SIZE_IN_BYTES>& fp) const
|
||||
{
|
||||
for(uint32_t i=0;i<ID_SIZE_IN_BYTES;++i)
|
||||
if(fp.bytes[i] != bytes[i])
|
||||
return false ;
|
||||
return true ;
|
||||
}
|
||||
bool operator!=(const t_RsGenericIdType<ID_SIZE_IN_BYTES>& fp) const
|
||||
{
|
||||
return !operator==(fp) ;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char bytes[ID_SIZE_IN_BYTES] ;
|
||||
};
|
||||
|
||||
template<uint32_t ID_SIZE_IN_BYTES> std::string t_RsGenericIdType<ID_SIZE_IN_BYTES>::toStdString() const
|
||||
{
|
||||
static const char out[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++)
|
||||
{
|
||||
res[2*j ] = out[ (bytes[j]>>4) ] ;
|
||||
res[2*j+1] = out[ bytes[j] & 0xf ] ;
|
||||
}
|
||||
|
||||
return res ;
|
||||
}
|
||||
|
||||
template<uint32_t ID_SIZE_IN_BYTES> t_RsGenericIdType<ID_SIZE_IN_BYTES>::t_RsGenericIdType(const std::string& s)
|
||||
{
|
||||
int n=0;
|
||||
if(s.length() != ID_SIZE_IN_BYTES*2)
|
||||
throw std::runtime_error("t_RsGenericIdType<>::t_RsGenericIdType(std::string&): supplied string in constructor has wrong size.") ;
|
||||
|
||||
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
|
||||
throw std::runtime_error("t_RsGenericIdType<>::t_RsGenericIdType(std::string&): supplied string is not purely hexadecimal") ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<uint32_t ID_SIZE_IN_BYTES> t_RsGenericIdType<ID_SIZE_IN_BYTES>::t_RsGenericIdType(const unsigned char *mem)
|
||||
{
|
||||
memcpy(bytes,mem,ID_SIZE_IN_BYTES) ;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue