finished implementing decryption routines. Still needs fixing compilation

This commit is contained in:
csoler 2015-10-01 23:37:50 -04:00
parent 6626538cab
commit 1c12178874
5 changed files with 260 additions and 86 deletions

View File

@ -404,42 +404,30 @@ bool GxsSecurity::validateNxsMsg(const RsNxsMsg& msg, const RsTlvKeySignature& s
return false;
}
bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, const RsTlvSecurityKey& key)
bool GxsSecurity::initEncryption(GxsSecurity::MultiEncryptionContext& encryption_context, const std::list<RsTlvSecurityKey>& keys)
{
// encrypting for a single security key. This is a proxy function.
return encrypt(out,outlen,in,inlen,std::vector<RsTlvSecurityKey>(1,key)) ;
}
#ifdef TODO
bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, const std::vector<RsTlvSecurityKey>& keys)
{
#ifdef DISTRIB_DEBUG
std::cerr << "GxsSecurity::encrypt() " << std::endl;
#endif
if(keys.empty())
return false ;
// prepare an array of encrypted keys ek and public keys puk
unsigned char ** ek = new unsigned char *[keys.size()] ;
EVP_PKEY **pubk = new EVP_PKEY *[keys.size()] ;
int * ekl = new int [keys.size()] ;
memset(ek ,0,keys.size()*sizeof(unsigned char *)) ;
memset(pubk,0,keys.size()*sizeof(EVP_PKEY *)) ;
memset(ekl ,0,keys.size()*sizeof(int )) ;
try
{
encryption_context.clear() ;
encryption_context.ek = new unsigned char *[keys.size()] ;
encryption_context.ekl = new int [keys.size()] ;
EVP_PKEY **pubk = new EVP_PKEY *[keys.size()] ;
memset(pubk,0,keys.size()*sizeof(EVP_PKEY *)) ;
memset(encryption_context.ek ,0,keys.size()*sizeof(unsigned char *)) ;
memset(encryption_context.ekl ,0,keys.size()*sizeof(int )) ;
for(uint32_t i=0;i<keys.size();++i)
{
RSA *tmpkey = ::extractPublicKey(keys[i]) ;
RSA *rsa_publish_pub = RSAPublicKey_dup(tmpkey) ;
RSA_free(tmpkey) ;
if(rsa_publish_pub == NULL)
if(rsa_publish_pub == NULL)
throw std::runtime_error("Wrong key in input key table. Cannot extract public key.") ;
pubk[i] = EVP_PKEY_new();
@ -447,30 +435,37 @@ bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, u
int max_evp_key_size = EVP_PKEY_size(pubk[i]);
ek [i] = (unsigned char*)malloc(max_evp_key_size);
ekl[i] = max_evp_key_size ;
total_ekl += max_evp_key_size ;
encryption_context.ek [i] = (unsigned char*)malloc(max_evp_key_size);
encryption_context.ekl[i] = max_evp_key_size ;
}
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
unsigned char iv[EVP_MAX_IV_LENGTH];
EVP_CIPHER_CTX_init(&encryption_context.ctx);
const EVP_CIPHER *cipher = EVP_aes_128_cbc();
// intialize context and send store encrypted cipher key in ek
if(!EVP_SealInit(&ctx, cipher, ek, ekl, iv, pubk, keys.size()))
throw std::runtime_error("Error in EVP_SealInit. Cannot init encryption. Something's wrong.") ;
// now paste all encrypted keys into the output buffer
for(uint32_t i=0;i<keys.size();++i)
{
}
if(!EVP_SealInit(&encryption_context.ctx, cipher, encryption_context.ek, encryption_context.ekl, encryption_context.iv, pubk, keys.size()))
throw std::runtime_error("Error in EVP_SealInit. Cannot init encryption. Something's wrong.") ;
return true ;
}
catch(std::exception& e)
{
std::cerr << "(EE) cannot init encryption context: " << e.what << std::endl;
encryption_context.clear() ;
return false ;
}
}
bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, GxsSecurity::MultiEncryptionContext& encryption_context)
{
// encrypting for a single security key. This is a proxy function.
out = NULL ;
try
{
int eklen, net_ekl;
int out_currOffset = 0;
int out_offset = 0;
@ -478,74 +473,49 @@ bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, u
int size_net_ekl = sizeof(net_ekl);
int cipher_block_size = EVP_CIPHER_block_size(cipher);
int max_outlen = inlen + cipher_block_size + EVP_MAX_IV_LENGTH + max_evp_key_size + size_net_ekl;
int max_outlen = inlen + cipher_block_size ;
// now assign memory to out accounting for data, and cipher block size, key length, and key length val
out = (uint8_t*)malloc(inlen + cipher_block_size + size_net_ekl + eklen + EVP_MAX_IV_LENGTH);
out = (uint8_t*)malloc(max_outlen) ;
if(out == NULL)
{
std::cerr << "Malloc error for size " << inlen + cipher_block_size + size_net_ekl + eklen + EVP_MAX_IV_LENGTH << std::endl;
throw std::runtime_error("GxsSecurity::encrypt(): cannot allocate memory") ;
}
net_ekl = htonl(eklen);
memcpy((unsigned char*)out + out_offset, &net_ekl, size_net_ekl);
out_offset += size_net_ekl;
memcpy((unsigned char*)out + out_offset, ek, eklen);
out_offset += eklen;
memcpy((unsigned char*)out + out_offset, iv, EVP_MAX_IV_LENGTH);
out_offset += EVP_MAX_IV_LENGTH;
// now encrypt actual data
if(!EVP_SealUpdate(&ctx, (unsigned char*) out + out_offset, &out_currOffset, (unsigned char*) in, inlen))
{
free(out) ;
out = NULL ;
return false;
}
if(!EVP_SealUpdate(&encryption_context.ctx, (unsigned char*)out, &out_currOffset, (unsigned char*) in, inlen))
throw std::runtime_error("(EE) EVP_SealUpdate failed. Cannot encrypt.") ;
// move along to partial block space
out_offset += out_currOffset;
out_currOffset = 0 ;
// add padding
if(!EVP_SealFinal(&ctx, (unsigned char*) out + out_offset, &out_currOffset))
{
free(out) ;
out = NULL ;
return false;
}
if(!EVP_SealFinal(&encryption_context.ctx, (unsigned char*)&out[out_offset], &out_currOffset))
throw std::runtime_error("(EE) EVP_SealFinal failed. Cannot encrypt.") ;
// move to end
out_offset += out_currOffset;
// make sure offset has not gone passed valid memory bounds
if(out_offset > max_outlen)
{
free(out) ;
out = NULL ;
return false;
}
// free encrypted key data
free(ek);
throw std::runtime_error("(EE) GxsSecurity::encrypt(): exceeded memory bounds! This is a serious bug.") ;
outlen = out_offset;
return true;
}
catch(std::exception& e)
{
std::cerr << "(EE) GxsSecurity::encrypt(): ERROR: " << e.what() << std::endl;
return false ;
if(out)
free(out) ;
return false ;
}
}
#endif
bool GxsSecurity::encrypt_single(uint8_t *& out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, const RsTlvSecurityKey& key)
bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, const RsTlvSecurityKey& key)
{
#ifdef DISTRIB_DEBUG
std::cerr << "GxsSecurity::encrypt() " << std::endl;
@ -650,6 +620,91 @@ bool GxsSecurity::encrypt_single(uint8_t *& out, uint32_t &outlen, const uint8_t
return true;
}
bool GxsSecurity::initDecryption(GxsSecurity::MultiEncryptionContext& encryption_context, const RsTlvSecurityKey& key,unsigned char *IV,uint32_t IV_size,unsigned char *encrypted_session_key,uint32_t encrypted_session_key_size)
{
// prepare an array of encrypted keys ek and public keys puk
try
{
encryption_context.clear() ;
encryption_context.ek = new unsigned char *[1] ;
encryption_context.ekl = new int [1] ;
RSA *rsa_publish = extractPrivateKey(key) ;
if(rsa_publish == NULL)
{
#ifdef DISTRIB_DEBUG
std::cerr << "GxsSecurity(): Could not generate publish key " << grpId
<< std::endl;
#endif
return false;
}
EVP_PKEY *privateKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(privateKey, rsa_publish);
encryption_context.ek[0] = (unsigned char*)malloc(EVP_PKEY_size(privateKey));
encryption_context.ekl[0] = encrypted_session_key_size ;
memcpy(encryption_context.ek[0],encrypted_session_key,encrypted_session_key_size) ;
EVP_CIPHER_CTX_init(&encryption_context.ctx);
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
if(!EVP_OpenInit(&encryption_context.ctx, cipher, encryption_context.ek[0], encryption_context.ekl[0], IV, privateKey))
{
std::cerr << "(EE) Cannot decrypt data. Most likely reason: private GXS key is missing." << std::endl;
encryption_context.clear() ;
return false;
}
return true ;
}
catch(std::exception& e)
{
std::cerr << "(EE) cannot init decryption context: " << e.what << std::endl;
encryption_context.clear() ;
return false ;
}
}
bool GxsSecurity::decrypt(uint8_t *&out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, MultiEncryptionContext& encryption_context)
{
out = (uint8_t*)malloc(inlen); // this is conservative
if(out == NULL)
{
std::cerr << "gxssecurity::decrypt(): cannot allocate memory of size " << inlen << " to decrypt data." << std::endl;
return false;
}
int out_currOffset = 0 ;
if(!EVP_OpenUpdate(&encryption_context.ctx, (unsigned char*) out, &out_currOffset, (unsigned char*)in, inlen))
{
std::cerr << "(EE) EVP_OpenUpdate failed! Decryption context is probably not inited correctly" << std::endl;
free(out) ;
out = NULL ;
outlen=0 ;
return false;
}
outlen = out_currOffset;
if(!EVP_OpenFinal(&ctx, (unsigned char*)out + out_currOffset, &out_currOffset))
{
free(out) ;
out = NULL ;
outlen=0 ;
return false;
}
outlen += out_currOffset;
return true ;
}
bool GxsSecurity::decrypt(uint8_t *& out, uint32_t & outlen, const uint8_t *in, uint32_t inlen, const RsTlvSecurityKey& key)
{
@ -707,7 +762,11 @@ bool GxsSecurity::decrypt(uint8_t *& out, uint32_t & outlen, const uint8_t *in,
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
if(!EVP_OpenInit(&ctx, cipher, ek, eklen, iv, privateKey)) return false;
if(!EVP_OpenInit(&ctx, cipher, ek, eklen, iv, privateKey))
{
std::cerr << "(EE) Cannot decrypt data. Most likely reason: private GXS key is missing." << std::endl;
return false;
}
if(inlen < in_offset)
{

View File

@ -42,6 +42,36 @@
class GxsSecurity
{
public:
/*!
* \brief The MultiEncryptionContext struct
*
* This structure is used to store encryption keys generated when encrypting for multiple keys at once, so that
* the client doesn't need to know about all the libcrypto variables involved.
* Typically, the client will first ask to init a MultiEncryptionContext by providing several GXS ids,
* and then pass the structure as a parameter to encrypt some data with the same key.
*/
class MultiEncryptionContext
{
public:
MultiEncryptionContext() { ekl = NULL; ek=NULL; nk=0 ; }
~MultiEncryptionContext() { clear() ;}
void clear() ;
// The functions below give access to the encrypted symmetric key to be used.
//
int n_encrypted_keys() const ;
RsGxsId encrypted_key_id (int i) ;
unsigned char *encrypted_key_data(int i) ;
int encrypted_key_size(int i) ;
protected:
int *ekl ; // array of encrypted keys length
unsigned char **ek ; // array of encrypted keys
int nk ; // number of encrypted keys
EVP_CIPHER_CTX ctx; // EVP encryption context
unsigned char iv[EVP_MAX_IV_LENGTH]; // initialization vector of the cipher.
};
/*!
* Extracts a public key from a private key.
*/
@ -65,6 +95,16 @@ class GxsSecurity
*/
static bool encrypt(uint8_t *&out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, const RsTlvSecurityKey& key) ;
/*!
* Encrypts/decrypt data using envelope encryption using the key pre-computed in the encryption context passed as
* parameter.
*/
static bool initEncryption(MultiEncryptionContext& encryption_context, const std::list<RsTlvSecurityKey> &keys) ;
static bool initDecryption(MultiEncryptionContext& encryption_context, const RsTlvSecurityKey& key, unsigned char *IV, uint32_t IV_size, unsigned char *encrypted_session_key, uint32_t encrypted_session_key_size) ;
static bool encrypt(uint8_t *&out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, MultiEncryptionContext& encryption_context) ;
static bool decrypt(uint8_t *&out, uint32_t &outlen, const uint8_t *in, uint32_t inlen, MultiEncryptionContext& encryption_context) ;
/**
* Decrypts data using evelope decryption (taken from open ssl's evp_sealinit )
* only full publish key holders can decrypt data for a group

View File

@ -2663,21 +2663,94 @@ bool RsGxsNetService::encryptTransaction(NxsTransaction *tr)
delete *it ;
tr->mItems = encrypted_items ;
// 5 - make session key item and push it front.
RsNxsSessionKeyItem *session_key_item = new RsNxsSessionKeyItem() ;
memcpy(session_key_item->initialization_vector,muctx.IV,EVP_MAX_IV_LENGTH) ;
for(int i=0;i<muctx->n_encrypted_keys();++i)
{
std::cerr << " addign session key for ID " << muctx.encrypted_key_id(i) << std::endl;
RsTlvBinaryData data ;
data.setBinData(muctx.encrypted_key_data(i), muctx.encrypted_key_size(i)) ;
session_key_item->encrypted_session_keys[muctx.encrypted_key_id(i)] = data ;
}
tr->mItems.push_front(session_key_item) ;
}
bool RsGxsNetService::decryptTransaction(NxsTransaction *tr)
{
std::cerr << "RsGxsNetService::decryptTransaction()" << std::endl;
std::cerr << " Circle Id: " << tr->destination_circle << std::endl;
// 1 - Checks that the transaction is encrypted. It should contain
// one packet with an encrypted session key for the group,
// and as many encrypted data items as necessary.
RsNxsSessionKeyItem *esk = NULL;
for(std::list<RsNxsItem*>::const_iterator it(tr->mItems.begin());it!=tr->mItems.end();++it)
if(NULL != (esk = dynamic_cast<RsNxsSessionKeyItem*>(*it)))
break ;
if(esk == NULL)
{
std::cerr << " (II) nothing to decrypt. No session key packet in this transaction." << std::endl;
return false ;
}
// 2 - Try to decrypt the session key. If not, return false. That probably means
// we don't own that identity.
GxsSecurity::MultiEncryptionContext muctx ;
if(!GxsSecurity::initDecryption(muctx,key,esk.initialization_vector.bin_data,esk.initialization_vector.bin_len,ek.bin_data,ek.bin_len))
{
std::cerr << " (EE) cannot decrypt transaction. initDecryption() failed." << std::endl;
return false ;
}
// 3 - Using session key, decrypt all packets, by calling GXSSecurity.
// 4 - Deserialise packets from the decrypted data and restore the clear transaction.
std::list<RsNxsItem*> decrypted_items ;
RsNxsEncryptedDataItem encrypted_item ;
RsNxsSerialiser serial ;
for(std::list<RsNxsItem*>::const_iterator it(tr->mItems.begin());it!=tr->mItems.end();++it)
if(NULL != (encrypted_item = dynamic_cast<RsNxsEncryptedDataItem*>(*it)))
{
unsigned char *tempmem;
uint32_t tempmemsize ;
if(!GxsSecurity::decrypt(muctx,tempmem,tempmemsize,encrypted_item->aes_encrypted_data.bin_data, encrypted_item.aes_encrypted_data.bin_len))
{
std::cerr << " (EE) Cannot decrypt item. Something went wrong. Skipping this item." << std::endl;
continue ;
}
RsNxsItem *item = serial.deserialise(tempmem,tempmemsize) ;
std::cerr << " Decrypted an item of type " << std::hex << item->getType() << std::dec << std::endl;
decrypted_items.push_back(item) ;
free(tempmem) ;
}
// 4 - put back in transaction.
std::cerr << " replacing items with clear items" << std::endl;
for(std::list<RsNxsItem*>::const_iterator it(tr->mItems.begin());it!=tr->mItems.end();++it)
delete *it ;
tr->mItems = encrypted_items ;
return true ;
}
void RsGxsNetService::cleanTransactionItems(NxsTransaction* tr) const

View File

@ -239,7 +239,8 @@ public:
/// Session key encrypted for the whole group
///
RsTlvBinaryData encrypted_key_data ;
RsTlvBinaryData initialisation_vector ;
std::map<RsGxsId, RsTlvBinaryData> encrypted_session_keys;
};
/*!
* Use to send to peer list of grps

View File

@ -53,6 +53,7 @@ virtual bool SetTlv(void *data, uint32_t size, uint32_t *offset) const;
virtual bool GetTlv(void *data, uint32_t size, uint32_t *offset);
virtual std::ostream &print(std::ostream &out, uint16_t indent) const; /*! Error/Debug util function */
// mallocs the necessary size, and copies data into the allocated buffer in bin_data
bool setBinData(const void *data, uint32_t size);
uint16_t tlvtype; /// set/checked against TLV input