Intermittent decryption failures in EVP_DecryptFinal_ex when using AES-128/CBC - c

I am using the EVP library found here: https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html
Here are my two encryption and decryption functions:
I am trying to encrypt a string using AES 128 CBC.
The string is usually of the format word1 word2 word3
char* encrypt(char *s, char *key) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if (EVP_EncryptUpdate(&ctx, outbuf, &outlen1, s, strlen(s)) == 1) {
if (EVP_EncryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
char* decrypt(char *s, char *key) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if (EVP_DecryptUpdate(&ctx, outbuf, &outlen1, s, strlen(s)) == 1) {
printf("After decrypt update\n");
if (EVP_DecryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
printf("After decrypt final\n");
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
The problem is the decryption final function works on some strings but not on others.
If the string before it is encrypted is something like cat dog cow, the decryption works.
But if it is like bat dog cow, the decryption fails in particular at the EVP_DecryptFinal_ex() function.
For some strings, the decryption always fails at the EVP_DecryptFinal_ex() function. It does not return 1.
Any idea what the problem could be? Padding maybe? I just can't seem to figure it out.

You probably miss that the encrypted string may contain zero-bytes, so the strlen(s) in DecryptUpdate has a too low value. You have to remember from encrypt how long the encrypted data is and use that value for decrypting.

Related

Problem with decrypting using mbedtls on esp32

I am trying to write function to decrypt rsa2048 with mbedtls/pk.h
I am trying to write function to decrypt rsa2048 with mbedtls/pk.h esp32 but on the site they wrote Store data to be decrypted and its length in variables. This tutorial stores the data in to_decrypt, and its length in to_decrypt_len:
I idk what is the format of to_decrypt i tried to put encrypted with code upper buf but it didn't work. Tried co encode on rsa online sites into base64 and put into the program convert to unsigned char* It gaves to me error 0x4080 RSA - Bad input parameters to function
code :
#include <Arduino.h>
#include "mbedtls/pk.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/platform.h"
#include "mbedtls/base64.h"
void rsa2048_encrypt(const char *text)
{
// RNG (Random number generator init)
int ret = 0;
mbedtls_entropy_context entropy;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_context ctr_drbg;
const char *personalization = "mgkegneljgnjlwgnjefdcmeg12313123dsggsd";
mbedtls_ctr_drbg_init(&ctr_drbg);
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)personalization,
strlen(personalization));
if (ret != 0)
{
// ERROR HANDLING CODE FOR YOUR APP
}
// Creating rsa context + Importing pub key
ret = 0;
mbedtls_pk_context pk;
mbedtls_pk_init(&pk);
/*
* Read the RSA public key
*/
const unsigned char *key = (const unsigned char *)"-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmwukduTT4dMD+EXuho4L\n"
"zEg9pH7Bk4y6TPw9pQqiw4b5Qx3+KM+SFi2I4AncfkOcjtuMqtSPdNSgFb1DErQD\n"
"+I+yOS7ztuAVy6hO/oRpVKAJNVl385pC/Ah8aiZts6cY8kjs47Mw4ufFNwIH8hOy\n"
"6f1+e8chBgeKxOVJBNiWr2nsPhvvAERTunw/CTvWsBLakyGs+OJwOcYsr0m5iOJx\n"
"XfBUEYOQ68XDIUTTLKdYsUOFSESwtIsPgqytj+SRcA/STH8eQJigNQNj8Zexpi+3\n"
"ykDWAxbmHQ8UWma1vV//oM6xy3DI/SXxvPusjNbKxDg+q5/e3hWoaBVq3ti9/ZTe\n"
"kQIDAQAB\n"
"-----END PUBLIC KEY-----\n";
if ((ret = mbedtls_pk_parse_public_key(&pk, key, strlen((const char *)key) + 1)) != 0)
{
printf(" failed\n ! mbedtls_pk_parse_public_key returned -0x%04x\n", -ret);
};
// Encrypting data
const unsigned char *to_encrypt = (const unsigned char *)text;
size_t to_encrypt_len = strlen((const char *)to_encrypt);
unsigned char buf[MBEDTLS_MPI_MAX_SIZE];
size_t olen = 0;
/*
* Calculate the RSA encryption of the data.
*/
printf("\n . Generating the encrypted value: \n");
fflush(stdout);
if ((ret = mbedtls_pk_encrypt(&pk, to_encrypt, to_encrypt_len,
buf, &olen, sizeof(buf),
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
{
printf(" failed\n ! mbedtls_pk_encrypt returned -0x%04x\n", -ret);
}
for (size_t i = 0; i < olen; i++)
{
mbedtls_printf("%02X%s", buf[i],
(i + 1) % 16 == 0 ? "\r\n" : " ");
}
mbedtls_pk_free(&pk);
mbedtls_entropy_free(&entropy);
mbedtls_ctr_drbg_free(&ctr_drbg);
}
void decrypt_test()
{
const unsigned char *private_key = (const unsigned char *)"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEogIBAAKCAQEAmwukduTT4dMD+EXuho4LzEg9pH7Bk4y6TPw9pQqiw4b5Qx3+\n"
"KM+SFi2I4AncfkOcjtuMqtSPdNSgFb1DErQD+I+yOS7ztuAVy6hO/oRpVKAJNVl3\n"
"85pC/Ah8aiZts6cY8kjs47Mw4ufFNwIH8hOy6f1+e8chBgeKxOVJBNiWr2nsPhvv\n"
"AERTunw/CTvWsBLakyGs+OJwOcYsr0m5iOJxXfBUEYOQ68XDIUTTLKdYsUOFSESw\n"
"tIsPgqytj+SRcA/STH8eQJigNQNj8Zexpi+3ykDWAxbmHQ8UWma1vV//oM6xy3DI\n"
"/SXxvPusjNbKxDg+q5/e3hWoaBVq3ti9/ZTekQIDAQABAoIBADXzji5FICnDuOzq\n"
"wL6XrSlPtguIhCmo3acuWvEUS2EIlbIyPJ/M4wPOooN7Svuw4Uigw0kqoCTCXFZU\n"
"PoPCmmMi9ZyKZwoq3cq5bYuJXfGxoqKq2F+vPUHgXhK9/ox2R+r/T1dIomlCx1CF\n"
"52foTOi5agr+VtJ3S2WKd6c1CvJMuRRoIX9vI49L+NdA9FUcA4Ge2rJZPu7zd/Xj\n"
"VvqtIH63Y/4z+S5YqnBgYjk7xWf3f9ybrkdi9fiRNt9wq4LOet+OSiQXWyuX+ppL\n"
"im6Sl3O8XkaDWAFo8dUWkZf+6RpABxFnUy45CWZGs7W8MpVwykXdpxcn9iJ7jIaR\n"
"9dcmUgECgYEA3aAWxuiX081mFIdmQEpKkp1JFbZOoZpIzBXNx/M05FGkz8QPL7G3\n"
"9h8A8UCbTFG+cAM1vjMUPzWXbsytE8VC4qjGy+1RBDltBt5/XX4VECVDIwjTdmc0\n"
"RPfJ2vKkAFYPHjyQijQCZxAPM0E/IFGKnTP4Dt+rITxkYvoouFiR2eECgYEAsxfq\n"
"qk2d8K62DQPURHvNmFnct+QAlIF9i/XHGcLNvnzEASiIsZ75TiXUc0xIxY2/ewo4\n"
"CMbNUG98xiW0Q5pdyKFQ3qnhNNTKPCK1T4qqKarDU2pSXFC2afkCSBbfO7qWblRD\n"
"PMJJ7SG8fAeIWbqKVzPgSfwRw4xY+iEe7SvoerECgYAEIQhrmkfB3XDKbx9bkUbE\n"
"ZoPHEMd0QVCb5MgZspFIs7CzYj66L8ByqG83D3IVQOygX57vtTnqV5BDszKCTMmL\n"
"OYPCpuA8iOlcGGcdEc1IqLkQfQibix6xLkCngJ/HldLgSFaVDJUC4Iy38r4/VuWT\n"
"OjWj6Uzh6KMiKPD7RkMpYQKBgH2BSjM0l3U+ilfOkie39tlISDQaNQndQQUfJPr5\n"
"mENgnd8N79VBygYo3pw6HllLP5/TBneoEePHbVJSw+QIPqbF3a1csXTblinUTOlE\n"
"DIGMqLtBLByDd4IGPcIVPTVXSephZIkkwrfKR5NHmBcBcccwlIJkgnJeXVBUe57L\n"
"gWzRAoGAAmLnPNkIT5Nruqy7EdEeb90W0VA/7/CaESvZKUkAHUy4bIqwFSGGJz1Q\n"
"oKUx8cuK4tz79mHsjzlJoCSLUvI5Fpfz+CS9uvA15QBIHU+5G37Ga5WsuBTww+lx\n"
"XC6IQ356/xfs4CmAVD1xjhEuBjANSs8lgHMAQPGngU5EVaE1hPw=\n"
"-----END RSA PRIVATE KEY-----";
// RNG (Random number generator init)
int ret = 0;
mbedtls_entropy_context entropy;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_context ctr_drbg;
const char *personalization = "mgkegneljgnjlwgnjefdcmeg12313123dsggsd";
mbedtls_ctr_drbg_init(&ctr_drbg);
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)personalization,
strlen(personalization));
if (ret != 0)
{
// ERROR HANDLING CODE FOR YOUR APP
}
ret = 0;
mbedtls_pk_context pk;
mbedtls_pk_init(&pk);
/*
* Read the RSA privatekey
*/
if ((ret = mbedtls_pk_parse_key(&pk, private_key, strlen((const char *)private_key) + 1, NULL, 0)) != 0)
{
printf(" failed\n ! mbedtls_pk_parse_keyfile returned -0x%04x\n", -ret);
}
unsigned char result[MBEDTLS_MPI_MAX_SIZE];
size_t olen = 0;
/*
* Calculate the RSA encryption of the data.
*/
printf("\n . Generating the decrypted value");
fflush(stdout);
const char *text = "fnianPxs/09bx75ufVLWPeFF9kbGEIL3+TQqW2+ZoeMpdvVnkifFToAii92ODVBPOL0RzQPfxlJcN/nVY3K5fWNSGHM8TTwTgCqvUc0ia5L5YHI1YSgDKzx2QPZlu7tEd06sjW7txRacnhilRfjFPp0CYeLwxYVBlPmKIE7oqQHrc8sal3X9NSqgwO7+03TBeH3beNanMCqQBRk9t+Z80XApEBMcZQHZ0lb+Z0C6DOuY0elH/fOp1SGlXuzf+tgcv7+TzL5uVVFCBNyMonTwMEp+zbLjX2Ck1IHhp8JXi3ovVi8HNcKCOQx/fxX1qTSt2NulHTwP2urCQSZbGjnYuw==";
const unsigned char *to_decrypt = (unsigned char *)text;
if ((ret = mbedtls_pk_decrypt(&pk, to_decrypt, (strlen(text) * 4) - 1, result, &olen, sizeof(result),
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
{
printf(" failed\n ! mbedtls_pk_decrypt returned -0x%04x\n", -ret);
}
}
So what I do wrong ?

AES cbc 256 decryption failed in C openssl

I'm trying to write AES encryption/decryption program in C using openssl. However, when I tried to decrypt the message, I got error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length.
void aes_encrypt(unsigned char* in, int inl, unsigned char *out, int* len, unsigned char * key){
unsigned char iv[16] = "encryptionIntVec";
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
*len = 0;
int outl = 0;
EVP_EncryptUpdate(&ctx, out+*len, &outl, in+*len, inl);
*len+=outl;
int test = inl>>4;
if(inl != test<<4){
EVP_EncryptFinal_ex(&ctx,out+*len,&outl);
*len+=outl;
}
EVP_CIPHER_CTX_cleanup(&ctx);
}
void aes_decrypt(unsigned char* in, int inl, unsigned char *out, unsigned char *key){
unsigned char iv[16] = "encryptionIntVec";
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int result;
result = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
if(result > 0) {
printf("passed\n");
} else {
printf("failed\n");
}
int len = 0;
int outl = 0;
result = EVP_DecryptUpdate(&ctx, out+len, &outl, in+len, inl);
if(result > 0) {
printf("passed\n");
} else {
printf("failed\n");
}
len += outl;
result = EVP_DecryptFinal_ex(&ctx, out+len, &outl);
if(result > 0) {
printf("passed\n");
} else {
printf("failed\n");
ERR_print_errors_fp(stdout);
}
len+=outl;
out[len]=0;
EVP_CIPHER_CTX_cleanup(&ctx);
}
int main()
{
unsigned char content[400];
unsigned char key[] = "0123456789abcdef0123456789abcdef";
/******************Block 1*****************************/
unsigned char en[400],de[400],base64[400], base64_out[400];
int len;
memset(content, 0,400);
memset(en, 0, 400);
memset(de, 0, 400);
memset(base64, 0,400);
memset(base64_out, 0, 400);
strcpy((char *)content, "loc: 123.2132412, -39.123142");
printf("%d %s\n", strlen((const char*)content), content);
//encrypt content
aes_encrypt(content,strlen((const char*)content), en, &len, (unsigned char*)key);
//base64 encode ciphertext
int encode_str_size = EVP_EncodeBlock(base64, en, len);
printf("%d %s\n", encode_str_size, base64);
//base64 decode
int length = EVP_DecodeBlock(base64_out, base64, strlen((const char*)base64));
while(base64[--encode_str_size] == '=') length--;
//decrypt
aes_decrypt(base64_out, length, de, (unsigned char*)key);
printf("%d %s\n", strlen((const char*)de), de);
/***********************Block 2*******************************/
unsigned char msg_out[400];
unsigned char msg[400] = "6hKe8RGg+4p1N1R6Y9aaTovxLtuH115JoWUO8plrAJE=";
unsigned char result[400];
int l = EVP_DecodeBlock(msg_out, msg, strlen((const char*)msg));
if(strcmp((const char*)msg, (const char*)base64)==0) {
printf("match\n");
}
if(strcmp((const char*)en, (const char*)msg_out)==0) {
printf("match\n");
}
while(msg[--encode_str_size] == '=') l--;
aes_decrypt(msg_out, l, result, (unsigned char*)key);
printf("%d %s\n", strlen((const char*)result), result);
return 0;
}
In block 1 of main function, I encrypted, base64 encoded, base64 decoded, decrypted, then got the exactly the same String as it was, and produced NO error.
However, in the block 2, I directly used the base64 encoded string which was produced from block 1, decoded and decypted, but an error occurred at result = EVP_DecryptFinal_ex(&ctx, out+len, &outl); that was error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length.
If I printed the decrypted string anyways, it is loc: 123.2132412, -39.123142^D^D^D^D, there are four '^D' appended at the end. I compared strings, both match was printed, which means the ciphertexts that was decoded from base64 with the one(en) in block 1 were the same. (NOTE: same key, same IV)
Any ideas why it failed?
I am using version OpenSSL 3.0.0-dev and therefore had to modify the code for the reasons described in David C. Rankin's comment. The problem occurs even after these changes.
Debugging reveals that the length of the Base64 decoded data in block 2 is determined incorrectly with l = 0x21 = 33. The correct value is l = 0x20 = 32.
This is because in block 2, when determining the length of the Base64 decoded data without the 0x00 padding bytes (added by EVP_DecodeBlock()) with
while (msg[--encode_str_size] == '=') l--;
the value for encode_str_size already changed in block 1 is used. The fix is to apply the current value for the length of the Base64 encoded data, e.g.
encode_str_size = strlen((const char*)msg);
Then decryption for block 2 works on my machine.
Note that EVP_DecodeBlock() always returns the Base64 decoded data with a length equal to an integer multiple of 3, padded with 0x00 values if necessary.
The actual length of the data can therefore also be determined with msg_out and l alone. l specifies the length including the 0x00 padding bytes, so that only the number of the 0x00 padding bytes has to be subtracted from l.
Thus, msg and encode_str_size are actually not needed (although both can be used, since the number of = padding bytes is equal to the number of 0x00 padding bytes).
As already mentioned in the comment by Joël Hecht, the EVP_EncryptFinal_ex() call is necessary, which among other things performs the padding (PKCS7).
In the currently posted code, this only occurs if the length of the plaintext does not equal an integer multiple of 16 bytes (the AES block size) which is satisfied for the test data.
However, for a plaintext whose length is a multiple of 16 bytes, EVP_EncryptFinal_ex() is not called, so padding is not performed, resulting in a decryption failure in EVP_DecryptFinal_ex().
Apart from the Base64 encoding, the OpenSSL documentation here provides a complete example for EVP_aes_256_cbc(), which could be used for comparison.

Verify "PKCS5_PBKDF2_HMAC" is generating required byte of key

I am not getting a correct length of key generated by "PKCS5_PBKDF2_HMAC" oppenssl api.
I am expecting a length of key will be 32 (ie 32*8 = 256 bits).
Am i doing something wrong or missing something here?
unsigned char key[32];
bzero(key,sizeof(key));
const char * password1 = "locpasswordkey";
size_t plen = strlen(password1);
const char * salt = "fixedsaltlengthsforenc";
size_t slen = strlen(salt);
if(PKCS5_PBKDF2_HMAC(password1, (int)plen, (const unsigned char *)salt, (int)slen,65536, EVP_sha256(), (int)sizeof(key), key) == 0)
{
cout << "PKCS5_PBKDF2_HMAC_KEY() failed" << endl;
}
cout << "KeyLen: " << strlen(key) << endl
How we can be sure that "PKCS5_PBKDF2_HMAC" is generating required bits key.
Using this key i am performing decryption, but "EVP_CIPHER_CTX_set_key_length" function reporting below error
error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length:evp_enc.c:651:
Below is my decryption function.
std::string decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ) {
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
unsigned char* plaintext = new unsigned char[ciphertext_len];
bzero(plaintext,ciphertext_len);
if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleOpenSSLErrors();
EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleOpenSSLErrors();
plaintext_len = len;
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleOpenSSLErrors();
plaintext_len += len;
plaintext[plaintext_len] = 0;
EVP_CIPHER_CTX_free(ctx);
std::string ret = (char*)plaintext;
delete [] plaintext;
return ret;
}
Any thoughts will be helpful.
Yes, your use of PKCS5_PBKDF2_HMAC is correct, and does not contribute at all to your error.
First of all, to use _set_key_length you need to split your Init so you can set the algorithm, then the key length, THEN the key. See e.g. EVP_CIPHER_CTX_set_key_length accepts bad sizes for blowfish and OPENSSL Blowfish CBC encryption differs from PHP to C++ (which, note, are for Blowfish, which is variable-key; AES in OpenSSL EVP is not).
But if you are using OpenSSL 1.0.1 or higher, which you probably are, EVP_MAX_KEY_LENGTH is 64, and 64 bytes is NOT a valid key size for AES ever much less for EVP_aes_256_cbc as you have here which is a fixed-size instantiation, and moreover 64 also is NOT the size of your actual key, so even if it did somehow accept your call it would use garbage data for the key and produce totally wrong and useless results.
Don't do that.

Why is there a difference in generated cipher text between OpenSSL EVP C libraries and Python?

I'm seeing a difference in the cipher text generated ( and decryption fails as well but that's another story - I need the encrypted output to be correct/ as expected first). I ran the encryption using Python ( Pycryptodome) and saw different results for the tag and encrypted data. I'm not sure where I'm going wrong in assuming what the OpenSSL libraries require.
For clarity, I'm using AES-256 GCM mode.
I've tried using this site as well to generate encrypted data on the fly , although it doesn't allow for addition of aad, but the cipher text matches what I get with the Python script.
C code
int gcm_enc_dec( cipher_params_t * params) {
int out_len, ret;
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher_type;
switch(params->cipher_sel) {
case 0: cipher_type = EVP_aes_128_gcm();
break;
case 1: cipher_type = EVP_aes_256_gcm();
break;
case 2: cipher_type = EVP_aes_192_gcm();
break;
default: cipher_type = EVP_aes_128_gcm();
break;
}
if(!(ctx = EVP_CIPHER_CTX_new()))
handleErrors();
if(1 != (EVP_CipherInit_ex(ctx, cipher_type, NULL, NULL, NULL, params->encryption_mode)))
handleErrors();
if(!EVP_CipherInit_ex(ctx, NULL, NULL, params->key, params->iv, params->encryption_mode))
handleErrors();
if(1 != EVP_CipherUpdate(ctx, NULL, &out_len, params->aad, params->aad_len))
handleErrors();
if(params->encryption_mode) {
if(1 != EVP_CipherUpdate(ctx, params->ct, &out_len, params->pt, params->pt_len))
handleErrors();
params->ct_len = out_len;
} else {
if(1 != EVP_CipherUpdate(ctx, params->pt, &out_len, params->ct, params->ct_len))
handleErrors();
params->pt_len = out_len;
}
Additional C code
char key[32] = { 0xfe, 0xff,0xe9,0x92,0x86,0x65,0x73,0x1c,0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08,0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c,0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08 };
char iv[16] = {0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad,0xde,0xca,0xf8,0x88};
char pt[1024] = { 0xd9,0x31,0x32,0x25,0xf8,0x84,0x16,0xe5,0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a,0x86,0xa7,0xa9,0x53,
0x15,0x34,0xf7,0xda,0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72,0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53,
0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25,0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57,0xba,0x63,0x7b,0x39 };
char aad[20] = { 0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef,0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef,0xab,0xad,0xda,0xd2 };
...
...
void gcm_encrypt( char *pt_in, int pt_len, char *aad, int aad_len, char *key, int cipher_sel,
char *iv, int iv_len, char *ct_out, int *ct_len, char *tag_out){
cipher_params_t *params = (cipher_params_t *)malloc(sizeof(cipher_params_t));
if (!params) {
/* Unable to allocate memory on heap*/
fprintf(stderr, "ERROR: malloc error for cipher_params_t: %s\n", strerror(errno));
abort();
}
params->cipher_sel = cipher_sel;
params->iv = (unsigned char*)iv;
params->iv_len = iv_len;
params->pt = (unsigned char*)pt_in;
params->pt_len = pt_len;
params->ct = (unsigned char*)ct_out;
*ct_len = params->ct_len;
params->aad = (unsigned char*)aad;
params->aad_len = aad_len;
params->key = (unsigned char*)key;
params->tag = tag_out;
params->encryption_mode = 1; // encrypt
gcm_encrypt_data(&params);
}
Python code for testing
key = binascii.unhexlify('feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308')
aad = binascii.unhexlify('feedfacedeadbeeffeedfacedeadbeefabaddad2')
iv = binascii.unhexlify('cafebabefacedbaddecaf888')
pt = binascii.unhexlify('d9313225f88416e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39')
cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
cipher.update(aad)
ciphertext, tag = cipher.encrypt_and_digest(pt)
nonce = cipher.nonce
# Print all the components of the message
print ("\nCOMPONENTS OF TRANSMITTED MESSAGE")
print ("AAD: " + binascii.hexlify(aad).decode())
print ("Ciphertext: " + binascii.hexlify(ciphertext).decode())
print ("Authentication tag: " + binascii.hexlify(tag).decode())
print ("Nonce: " + binascii.hexlify(nonce).decode())
I'm seeing the cipher text output from C as:
3980cab3c0f841eb6fac4872a2757859e1ceaa6efd984628593b4ca1e19c7d773d0c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710
but the one from Python is
522dc1f099566d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662
Your posted C code is incomplete, and also the indentation doesn't match the actual structure. But if I complete it in the way that seems obvious to me, and supply the input you show for the python, I get the ciphertext you want:
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/err.h>
void handleErrors (const char *lab){
puts(lab); ERR_print_errors_fp(stdout); exit(1);
}
void hex2 (const char*in, unsigned char*out){
int x; while(sscanf(in, "%02x",&x)>0){ *out++ = x; in+=2; }
}
void hout (const unsigned char *x, int len){
while(len--) printf("%02x",*x++); putchar('\n');
}
int main(void) {
unsigned char key[32], iv[12], aad[20], pt[60], ct[128], tag[16];
int out_len = 0, out_len2 = 0;
hex2("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",key);
hex2("feedfacedeadbeeffeedfacedeadbeefabaddad2",aad);
hex2("cafebabefacedbaddecaf888",iv);
hex2("d9313225f88416e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",pt);
const EVP_CIPHER *cipher = EVP_aes_256_gcm();
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if(1 != EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 1))
handleErrors("init1");
if(!EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1))
handleErrors("init2");
if(1 != EVP_CipherUpdate(ctx, NULL, &out_len, aad, sizeof aad))
handleErrors("updaad");
if(1 != EVP_CipherUpdate(ctx, ct, &out_len, pt, sizeof pt))
handleErrors("upddat");
if(1 != EVP_CipherFinal(ctx, ct+out_len, &out_len2))
handleErrors("final");
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, sizeof tag, tag))
handleErrors("gettag");
printf ("%d+%d=", out_len, out_len2);
hout(ct, out_len+out_len2);
printf ("tag=");
hout(tag, sizeof tag);
return 0;
}
60+0=522dc1f099566d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662
tag=175227bf3ebf9eb1bfb85b5e89126c10

OpenSSL RSA: Unable to encrypt/decrypt messages longer than 16 bytes

I'm working on a simple program that uses OpenSSL to do basic RSA encryption and decryption. It is working fine for small messages (<16 bytes), but fails for anything over that. I understand that a limitation of public key cryptography is that you cannot encrypt anything longer than the key size. In my case, I'm using a 1024bit key, so I should have 128bytes to work with (maybe slightly less due to padding), correct? If so, that's not what I'm experiencing.
Here's the output from my program with 15 bytes:
Generating RSA keypair...done.
Message to encrypt: 0123456789ABCDE
16 bytes encrypted
Decrypted message: 0123456789ABCDE
And with 16 bytes:
Generating RSA keypair...done.
Message to encrypt: 0123456789ABCDEF
16 bytes encrypted
140153837057696:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:467:
Decrypted message: (null)
It seems that no matter what, only a total of 16 bytes are encrypted.
My encryption function (updated with fix):
unsigned char* rsa_seal(EVP_PKEY *pub_key, unsigned char *msg, size_t **enc_msg_len, unsigned char **sym_key, int *sym_key_len, unsigned char **iv) {
size_t msg_len = strlen((char*)msg);
unsigned char *encrypt = malloc(EVP_PKEY_size(pub_key));
EVP_CIPHER_CTX *ctx = malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
*sym_key = malloc(EVP_PKEY_size(pub_key));
*iv = malloc(EVP_MAX_IV_LENGTH);
**enc_msg_len = 0;
if(!EVP_SealInit(ctx, EVP_aes_128_cbc(), sym_key, sym_key_len, *iv, &pub_key, 1)) {
ERR_print_errors_fp(stderr);
encrypt = NULL;
goto return_free;
}
if(!EVP_SealUpdate(ctx, encrypt, (int*)*enc_msg_len, msg, (int)msg_len)) {
ERR_print_errors_fp(stderr);
encrypt = NULL;
goto return_free;
}
if(!EVP_SealFinal(ctx, encrypt, (int*)*enc_msg_len)) {
ERR_print_errors_fp(stderr);
encrypt = NULL;
goto return_free;
}
return_free:
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
ctx = NULL;
return encrypt;
}
The corresponding decryption function (updated with fix):
char* rsa_open(EVP_PKEY *pri_key, unsigned char *enc_msg, size_t *enc_msg_len, unsigned char *sym_key, int sym_key_len, unsigned char *iv) {
size_t dec_len = 0;
unsigned char *decrypt = malloc((*enc_msg_len) + EVP_MAX_IV_LENGTH);
if(decrypt == NULL) return NULL;
EVP_CIPHER_CTX *ctx = malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
if(!EVP_OpenInit(ctx, EVP_aes_128_cbc(), sym_key, sym_key_len, iv, pri_key)) {
ERR_print_errors_fp(stderr);
decrypt = NULL;
goto return_free;
}
if(!EVP_OpenUpdate(ctx, decrypt, (int*)&dec_len, enc_msg, (int)*enc_msg_len)) {
ERR_print_errors_fp(stderr);
decrypt = NULL;
goto return_free;
}
if(!EVP_OpenFinal(ctx, decrypt, (int*)&dec_len)) {
ERR_print_errors_fp(stderr);
decrypt = NULL;
goto return_free;
}
decrypt[dec_len] = '\0';
return_free:
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
ctx = NULL;
return (char*)decrypt;
}
The key generation function:
int rsa_init(EVP_PKEY **rsa_keypair) {
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if(!EVP_PKEY_keygen_init(ctx)) {
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KEY_LENGTH)) {
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_PKEY_keygen(ctx, rsa_keypair)) {
ERR_print_errors_fp(stderr);
return -1;
}
EVP_PKEY_CTX_free(ctx);
return 0;
}
And finally, my main:
int main() {
EVP_PKEY *rsa_keypair = NULL; // RSA keypair
char msg[BUFFER]; // Message to encrypt
unsigned char *encrypt = NULL; // Encrypted message
char *decrypt = NULL; // Decrypted message
// Generate key pair
printf("Generating RSA keypair...");
if(rsa_init(&rsa_keypair) == -1) {
fprintf(stderr, "\nError generating RSA keypair.\n");
exit(1);
}
printf("done.\n");
// Get the message to encrypt
printf("Message to encrypt: ");
fgets(msg, BUFFER-1, stdin);
msg[strlen(msg)-1] = '\0';
// Load error strings in anticipation of error
ERR_load_crypto_strings();
// Encrypt the message
size_t *encrypt_len = malloc(sizeof(size_t));
unsigned char *sym_key = NULL;
unsigned char *iv = NULL;
int sym_key_len;
encrypt = rsa_seal(rsa_keypair, (unsigned char*)msg, &encrypt_len, &sym_key, &sym_key_len, &iv);
printf("%d bytes encrypted\n", (int)*encrypt_len);
// Decrypt it
decrypt = rsa_open(rsa_keypair, (unsigned char*)encrypt, (size_t*)encrypt_len, sym_key, sym_key_len, iv);
printf("Decrypted message: %s\n", decrypt);
free(encrypt);
free(decrypt);
free(encrypt_len);
free(sym_key);
free(iv);
EVP_PKEY_free(rsa_keypair);
return 0;
}
Any help is greatly appreciated! Thank you.
EDIT: As pointed out by math below, it seems that the answer to my mistake was hiding in the OpenSSL here: https://www.openssl.org/docs/crypto/EVP_EncryptInit.html#
This is because you do not properly handle out and outl parameters in EVP_SealUpdate(), EVP_SealFinal(), EVP_OpenUpdate() and EVP_OpenFinal().
Each EVP_XxxxUpdate() and EVP_XxxxFinal() call will contribute to the output buffer. So, you are required to keep track of the seal/open process by summing each outl returned and providing the expected buffer each time (start of buffer + already handled bytes).
unsigned char* rsa_seal(...)
{
...
**enc_msg_len = 0;
EVP_SealUpdate(ctx, encrypt + **enc_msg_len, &outl, msg, (int)msg_len);
**enc_msg_len += outl;
EVP_SealFinal(ctx, encrypt + **enc_msg_len, &outl);
**enc_msg_len += outl;
...
}
char* rsa_open(...)
{
...
dec_len = 0;
EVP_OpenUpdate(ctx, decrypt + dec_len, &outl, enc_msg, (int)*enc_msg_len);
dec_len += outl;
EVP_OpenFinal(ctx, decrypt + dec_len, &outl);
dec_len += outl;
...
}
The program was working with 15-bytes buffer because in that case, the EVP_XxxxUpdate() call is returning 0 in outl (not enough data to seal/open a block), hiding the problem in your code logic.
Note: The data is not directly encrypted using the RSA key but using a generated symetric key (AES-128 in your case). This is why the block size is 16 bytes.

Resources