user3752324 user3752324 - 4 months ago 30
C++ Question

Not getting same session key after decoding payload under RSA

I am not getting same session key after encoding and decoding it using below functions which uses crypto++ library:

CryptoPP::RSA::PrivateKey RSA_master_privKey;
CryptoPP::RSA::PublicKey RSA_master_pubKey;

std::string generate_Master_Keys()
{
std::string rsaParams;
try {
CryptoPP::InvertibleRSAFunction parameters;
RSA_master_privKey = CryptoPP::RSA::PrivateKey(parameters);
RSA_master_pubKey = CryptoPP::RSA::PublicKey(parameters);
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return rsaParams;
}

PAES_KEY_WITH_IV create_session_key(void)
{
CryptoPP::AutoSeededX917RNG<CryptoPP::AES> rng;
PAES_KEY_WITH_IV aes_info = new AES_KEY_WITH_IV;
try {
aes_info->key.resize(CryptoPP::AES::DEFAULT_KEYLENGTH);
rng.GenerateBlock(aes_info->key, aes_info->key.size());
aes_info->iv.resize(CryptoPP::AES::BLOCKSIZE);
rng.GenerateBlock(&aes_info->iv[0], aes_info->iv.size());
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return (aes_info);
}

std::string encrypt_session_key(PAES_KEY_WITH_IV pKey)
{
std::string ciphered;
CryptoPP::SecByteBlock block(pKey->key.size());
try {
CryptoPP::RSAES< CryptoPP::OAEP<CryptoPP::SHA> >::Encryptor enc(RSA_master_pubKey);
enc.Encrypt(rng, pKey->key, pKey->key.size(), block);
ciphered.assign((char *)block.BytePtr(), 192);
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return ciphered;
}

PAES_KEY_WITH_IV decrypt_session_key(std::string & ciphered)
{
CryptoPP::SecByteBlock rec(ciphered.size());
CryptoPP::SecByteBlock block((const byte *)ciphered.data(), ciphered.size());
PAES_KEY_WITH_IV pKey = new AES_KEY_WITH_IV;
try {
CryptoPP::RSAES< CryptoPP::OAEP<CryptoPP::SHA> >::Decryptor dec(RSA_master_privKey);
dec.Decrypt(rng, block, block.size(), rec);

pKey->key = rec;
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return pKey;
}


Tailing of 192 bytes are not getting matched with original session key's bytes.

Can some one help me on this ?

Thanks in advance.

jww jww
Answer

I am not getting same session key after encoding and decoding it using below functions

I think you are close to what you need. There's also an opportunity for improvement in the way you are doing it. I'll show you the improved way, and you can apply it to the existing method as well.

The improved way simply uses FixedMaxPlaintextLength, CiphertextLength and some friends to determine sizes. It also uses a technique from Integrated Encryption Schemes (IES).

First, transport the raw seed bytes, and not the {key, iv} pair. Then, when you need the {key, iv} pair, you derive the bytes you need from the seed bytes. Your derivation should include a usage label and a version number.

Second, the open question: how many bytes do you transport as seed bytes. That answer is FixedMaxPlaintextLength() or MaxPreimage() (I don't recall which). That's the size of the plaintext that can be encrypted under the scheme, and it depends on things like the modulus size and the padding scheme.

A lot of the code below is discussed at RSA Encryption Schemes and other places on the Crypto++ wiki. But its not readily apparent you need to visit them because you are still learning some of the techniques.


The following generates a random seed and encrypts it under the public key.

RSA_master_pubKey = RSA::PublicKey(parameters);
SecByteBlock seed(RSA_master_pubKey.FixedMaxPlaintextLength());
// You may need RSA_master_pubKey.MaxPreimage()

AutoSeededX917RNG<AES> rng;
rng.GenerateBlock(seed, seed.size());

RSAES< OAEP<SHA> >::Encryptor enc(RSA_master_pubKey);
SecByteBlock block(enc.CiphertextLength(seed.size())));

size_t req = enc.Encrypt(rng, seed, seed.size(), block);
block.resize(req);

// Transport block to peer as session seed

When the peer receives the encrypted seed block, they must decrypt it. Here's how to do it.

// Received from peer
SecByteBlock block(...);

RSAES< OAEP<SHA> >::Decryptor dec(RSA_master_privKey);
size_t req = dec.MaxPlaintextLength(block.size());

SecByteBlock seed(req);
DecodingResult result = dec.Decrypt(rng, block, block.size(), seed);
seed.resize(result.isValidCoding ? result.messageLength : 0);

You could even thrown an exception if result.isValidCoding returns false:

DecodingResult result = dec.Decrypt(rng, block, block.size(), seed);
if (!result.isValidCoding)
    throw Exception(OTHER_ERROR, "Failed to decrypt seed bytes");

seed.resize(result.messageLength);

When you want to encrypt or decrypt with AES, you need to derive a key, iv and possibly an hmac key (are you authenticating the data?).

// Random seed from above
SecByteBlock seed;

HKDF<SHA256> kdf;
SecByteBlock aesKey(AES::DEFAULT_KEYLENGTH);
SecByteBlock aesIV(AES::BLOCKSIZE);   

const byte aesLabel[] = "AES encryption key, version 1";
kdf.Derive(aesKey, aesKey.size(), seed, seed.size(), NULL, 0, aesLabel, COUNTOF(aesLabel));

const byte ivLabel[] = "AES initialization vector, version 1";
kdf.Derive(aesIV, aesIV.size(), seed, seed.size(), NULL, 0, ivLabel, COUNTOF(ivLabel));

IF you authenticate your data, then you can derive an HMAC key with the following. But generally speaking, you should probably use an Authenticated Encryption mode of operation:

const byte hmacLabel[] = "HMAC authentication key, version 1";
kdf.Derive(hmacKey, hmacKey.size(), seed, seed.size(), NULL, 0, hmacLabel, COUNTOF(hmacLabel));

HKDF was added at 5.6.3 or 5.6.4. If you don't have it, then grab hkdf.h from Wei Dai's GitHub (its header-only). By deriving from a base seed with unique labels, you are using a technique called independent derivation.

You add the labels and the version information to avoid gaps like discussed in Attacking and Repairing the WinZip Encryption Scheme. Also, using the entire FixedMaxPlaintextLength side steps some cryptographic attacks related to message length.


You might also want to look at Integrated Encryption Schemes (IES). We basically lifted the Key Encapsulation Mechanism (KEM) from IES. There's a Data Encapsulation Mechanism (DEM) that could be lifted, too.

If you are going to borrow the KEM and the DEM, then you may as well use the scheme. For that, see the following on the Crypto++ wiki:

If you use one of the Integrated Encryption Schemes, then you are changing the underlying mathematical problem. RSA is Integer Factorization (IF), while IES is Diffie-Hellman and Discrete Logs (FF).

Using an Integrated Encryption Scheme is a good choice. Its IND-CCA2, which is a very strong notion of security. I believe it has better security properties than your original scheme.

Comments