Libgcrypt - extract RSA encrypted data from a gcry_sexp_t object - c

I'm trying to extract the encrypted RSA data from a gcry_sexp_t object (which I assume is an MPI), so I could store the encrypted data as an encrypted file (without the other gcrypt metadata in the sexp object).
I've tried using gcry_sexp_sprint, which serialized the whole sexp object and which kind of works, but I assume that is not the intended way for storing RSA encrypted bytes on the disk. I've also tried extracting data using gcry_sexp_nth_buffer and gcry_sexp_nth_mpi, both of which just return the string enc-val.
This code is the relevant part:
gcry_sexp_t ciph;
err = gcry_pk_encrypt(&ciph, msgSexp, this->publicKey);
if(err){
throw std::runtime_error("gcry_pk_encrypt error: " + std::string(gcry_strerror(err)));
}
After this, I have a gcry_sexp_t object holding all of the relevant data, of which I would like to extract the encrypted data.
Thank you!
EDIT: I ended up calling gcry_sexp_nth two times to traverse the nested S expression object and then using gcry_sexp_nth_data to get the raw encrypted data. I'm sure there's a better way, though.

Libgcrypt API to deal with SExpression is very powerful. You can get data from the expression easily. Here is a sample code which:
Generate key pair
Encrypt a random data
Extract cipher text value
#include <stdio.h>
#include <gcrypt.h>
#include <string.h>
static void die(const char *format, ...) {
va_list arg_ptr;
va_start(arg_ptr, format);
vfprintf(stderr, format, arg_ptr);
va_end(arg_ptr);
if (*format && format[strlen(format) - 1] != '\n') {
putc('\n', stderr);
}
exit(1);
}
static void show_sexp(const char *prefix, gcry_sexp_t a) {
char *buf;
size_t size;
if (prefix)
fputs(prefix, stderr);
size = gcry_sexp_sprint(a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
buf = gcry_xmalloc(size);
gcry_sexp_sprint(a, GCRYSEXP_FMT_ADVANCED, buf, size);
fprintf(stderr, "%.*s", (int) size, buf);
gcry_free(buf);
}
int main() {
gcry_sexp_t key_spec, key, pub_key, sec_key;
int rc;
size_t len;
gcry_sexp_t cipher, l;
gcry_sexp_t plain;
gcry_mpi_t x;
const int nbits_data = 512;
//-------------------------------------------------------------------
// Generate Key
//-------------------------------------------------------------------
rc = gcry_sexp_new(&key_spec, "(genkey (rsa (nbits 4:2048)))", 0, 1);
if (rc) {
die("error creating S-expression: %s\n", gcry_strerror(rc));
}
//>> Generate key
rc = gcry_pk_genkey(&key, key_spec);
gcry_sexp_release(key_spec);
if (rc) {
die("error generating RSA key: %s\n", gcry_strerror(rc));
}
show_sexp("generated RSA key:\n", key);
//>> Extract parts
pub_key = gcry_sexp_find_token(key, "public-key", 0);
if (!pub_key) {
die("public part missing in key\n");
}
sec_key = gcry_sexp_find_token(key, "private-key", 0);
if (!sec_key) {
die("private part missing in key\n");
}
//-------------------------------------------------------------------
// Encrypt Data
//-------------------------------------------------------------------
//>> Create plain text.
x = gcry_mpi_new(nbits_data);
gcry_mpi_randomize(x, nbits_data, GCRY_WEAK_RANDOM);
rc = gcry_sexp_build(&plain, NULL, "(data (flags raw) (value %m))", x);
if (rc) {
die("converting data for encryption failed: %s\n", gcry_strerror(rc));
}
//>> Encrypt data.
rc = gcry_pk_encrypt(&cipher, plain, pub_key);
if (rc) {
die("encryption failed: %s\n", gcry_strerror(rc));
}
//-------------------------------------------------------------------
// Extract value
//-------------------------------------------------------------------
show_sexp("Encrypted data:\n", cipher);
l = gcry_sexp_find_token(cipher, "a", 0);
const char *data = gcry_sexp_nth_data(l, 1, &len);
for (int i = 0; i < len; i++) {
printf("%02x", (unsigned char) data[i]);
}
printf("\n");
return 0;
}
And this is the output:
generated RSA key:
(key-data
(public-key
(rsa
(n #00D43DB15B8AB929675C0196F8750451F2814F62FBF36219008051F99C82EB89B138FAFCBA51DB24D1DAADF6706016C268F59C793E91F05630376E58DCFAE728EEA1D26714244334643BB3A285D0860E9800ECBD5B85B11F4962F438A6C6D465851A991426A4AFF819D1B0D2DA28C016B80613B28070EAEC54A8CDF8EB5D04F03E8F925E384B75D3CEF326CF03901AD053925DFA08B0E8A38E8F6D1C4E14299312FB38791D23EDDFB9B57CAD596BFF09C4EEE0AF85CFC5A228BA5330A8C6F17B233FF8EB351B35C2FD26C3C723B9E4E89A3728D33D5D6C4D35931A0463FBF3FA1D056B4A5B1274BBB82B345130C2DE0FBCC4C258ED2EE3AA28A149A20A6BA0B2C7#)
(e #010001#)
)
)
(private-key
(rsa
(n #00D43DB15B8AB929675C0196F8750451F2814F62FBF36219008051F99C82EB89B138FAFCBA51DB24D1DAADF6706016C268F59C793E91F05630376E58DCFAE728EEA1D26714244334643BB3A285D0860E9800ECBD5B85B11F4962F438A6C6D465851A991426A4AFF819D1B0D2DA28C016B80613B28070EAEC54A8CDF8EB5D04F03E8F925E384B75D3CEF326CF03901AD053925DFA08B0E8A38E8F6D1C4E14299312FB38791D23EDDFB9B57CAD596BFF09C4EEE0AF85CFC5A228BA5330A8C6F17B233FF8EB351B35C2FD26C3C723B9E4E89A3728D33D5D6C4D35931A0463FBF3FA1D056B4A5B1274BBB82B345130C2DE0FBCC4C258ED2EE3AA28A149A20A6BA0B2C7#)
(e #010001#)
(d #29943875AD949EB6E00073BA12FA98AF4083F3E95601E6D201518DEC0A7DE848AED804C0DF9E65F532B917E965F9AF766E7F8C052504076E84BD7A6D383E26F7B0FE0E3194F511837D6AB0B1346BBEDA2A7C3690827D739AA5E7205E13DFB24FC4292F186EF5064ED094AA8A10A7A90BBE2D07B03FEED603B3DA3E24A5E17CCAF93E8CF54DE7C690FA08C4D57691FB47F637C283B6B8F2752F1FDDF303E5DA3D32ECCE9EE36F78AE83C74C0F875A11E3F907F7DA2197138BC22709123FAC72FD65937E730B99372BDC5A94958A288B67D448534CBB11CCEDA4D5DDB83040FA373932520A0268EE3CCD9BCD3D1040E7DD177E452DBD0CA91442AA39E29A2614F9#)
(p #00DA039BF562531FA1E2098E0D642D38DB35FCE5CC7D56EE397A2BBAE61DE0777341024D914183C6B9EF83FAE962BC4EC4DA3036B643FD1F6E8EF20E489AF9FA997F860F80DA672B85216DAAEFA91059ED340EB8A7411A2DCDCCE23DB0AB414B8BD2FFB516E23B8AB0D95717DE5A253EC96A7B2FD1BFDA3B001CA590200FEF1F6B#)
(q #00F93893CE49EB488250AA6D1F568CA14D247EBBEA2FC47D70A5BBF211543DF06F3B5BA51F4119481371B6433D6C1150F14EB19C5C44B7335F4FF3BE3C2FBDC8E17920CEC5D194A71E9F6B0715D7CD6867AB6D0F384A02CDECBBE111BB1276DAD40B872268E3381F3032C44D8B01BC80ACDC3C19D04735D1007202085E345C1D15#)
(u #00AFC622BC85AB68634EE4EBE0AAFA9DD612A11E7E09657AC937FFD4642622BD63BF825ACD419F273D86E77D74AAA6FBB8A28C633D58C6DDE1F9C5655B5AF8E412CDDA43DCA008EE084AB45D9C8DB454601D7CBB4739692FA91FD910A8A67D5041E339FBD2920875439F5D97C574E21A9DE8C779ADDF67686A940221E4518B6AE4#)
)
)
)
Encrypted data:
(enc-val
(rsa
(a #4CAC81402948B4E0A0FEFCECF89C389D711AAD158D3B6A927FA7D6D276C6BD7AAACA2CDEBB88A2430A0948AD9CFB2192D6521B90C291EDC122D6E7422915FCB083C987204CADCC6BF956B296C33A1CADBF5972921FE58454A6076E29B368195739FDAD3D02D612147E413E7E01E6185CA1784DA2F31874DE9C3DFB31DEE4764BA066BF208C0F362EB25C780001D5AD285DBD41450C735AFBFEE8CF35A178A007865629DC5A3ED50257E473DF73DC02422924E1541393824B91336D30F3989CA2FE8E4BA463673F1BF83EC0D95E958FD2B757699F805B342D52729232568228E01632607821CC9F116580496C228524FD4E3147CF6F6E9456FBBAD1FFD1889F0D#)
)
)
4cac81402948b4e0a0fefcecf89c389d711aad158d3b6a927fa7d6d276c6bd7aaaca2cdebb88a2430a0948ad9cfb2192d6521b90c291edc122d6e7422915fcb083c987204cadcc6bf956b296c33a1cadbf5972921fe58454a6076e29b368195739fdad3d02d612147e413e7e01e6185ca1784da2f31874de9c3dfb31dee4764ba066bf208c0f362eb25c780001d5ad285dbd41450c735afbfee8cf35a178a007865629dc5a3ed50257e473df73dc02422924e1541393824b91336d30f3989ca2fe8e4ba463673f1bf83ec0d95e958fd2b757699f805b342d52729232568228e01632607821cc9f116580496c228524fd4e3147cf6f6e9456fbbad1ffd1889f0d

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 ?

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 Variable Length Result for RSA Encryption [C programming]

I am trying to encrypt some text using OpenSSL's RSA encryption functions. My main issue is that the length of the encrypted RSA text varies between 0 and 256.
My RSA encryption function is:
/* Encrypt data using RSA */
char* rsa_encrypt(char* pub_key_filename, const unsigned char *data)
{
int padding = RSA_PKCS1_PADDING;
FILE *fp_pub;
fp_pub = fopen(pub_key_filename, "rb");
if (fp_pub == NULL)
{
printf("There was an error opening the public key file. Exiting!\n");
exit(EXIT_FAILURE);
}
RSA *pub_key = PEM_read_RSA_PUBKEY(fp_pub, NULL, NULL, NULL);
char *encrypted = malloc(2048);
int i;
for (i = 0; i < (2048); i++)
{
encrypted[i] = '\0';
}
int result = RSA_public_encrypt(strlen(data), data, encrypted, pub_key, padding);
if (result == -1)
{
printf("There was an error during RSA encryption.\n");
return "ERROR_RSA_ENCRYPTION";
}
fclose(fp_pub);
return encrypted;
}
The following code involves trying to encrypt some text:
const unsigned char *key = (unsigned char *)"abcdefghijklmnopqrstuvwxyzabcdef";
unsigned char *encrypted_aes_key = rsa_encrypt("public.pem", key);
I know that RSA with no padding is primitive RSA encryption and the resulting length is between 0 and n (RSA bit size) as seen here but my code is using RSA_PKCS1_PADDING so I am not sure why I am still getting variable length output.
The length of the encrypted data is returned in result from:
int result = RSA_public_encrypt(strlen(data), data, encrypted, pub_key, padding);
The encrypted data returned in encrypted is binary data. You can't do a strlen on it. The data is not 0 terminated and might contain some random 0 in it.

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.

Encrypting a plain text file in C

I am currently writing a linux application in C that reads from a configuration file. This configuration file contains some data that I would like to encrypt so it is not plain text. I have spent hours researching this and have not found a viable solution. Since the application will need to read from the configuration I will need to be able to encrypt it and decrypt it on the fly. So far from research I really like openSSL crypto library. I know from the command line you can do:
openssl enc -aes-256-cbc -salt -in file.txt -out file.enc
If anyone can provide an example of how I can do this in C, it would be most appreciated.
You should have a look at the O'Reilly-Book. There are a couple of examples on how to encrypt things. Unfortunately the most are for Network-Encryption.
I found an example in the book, but didnt test it:
#include <openssl/evp.h>
int main(int argc, char *argv[])
{
EVP_CIPHER_CTX ctx;
char key[EVP_MAX_KEY_LENGTH];
char iv[EVP_MAX_IV_LENGTH];
char *ct, *out;
char final[EVP_MAX_BLOCK_LENGTH];
char str[] = "123456789abcdef";
int i;
if (!seed_prng())
{
printf("Fatal Error! Unable to seed the PRNG!\n");
abort();
}
select_random_key(key, EVP_MAX_KEY_LENGTH);
select_random_iv(iv, EVP_MAX_IV_LENGTH);
EVP_EncryptInit(&ctx, EVP_bf_cbc(), key, iv);
ct = encrypt_example(&ctx, str, strlen(str), &i);
printf("Ciphertext is %d bytes.\n", i);
EVP_DecryptInit(&ctx, EVP_bf_cbc(), key, iv);
out = decrypt_example(&ctx, ct, 8);
printf("Decrypted: >>%s<<\n", out);
out = decrypt_example(&ctx, ct + 8, 8);
printf("Decrypted: >>%s<<\n", out);
if (!EVP_DecryptFinal(&ctx, final, &i))
{
printf("Padding incorrect.\n");
abort();
}
final[i] = 0;
printf("Decrypted: >>%s<<\n", final);
}
char *encrypt_example(EVP_CIPHER_CTX *ctx, char *data, int inl, int *rb)
{
char *ret;
int i, tmp, ol;
ol = 0;
ret = (char *)malloc(inl + EVP_CIPHER_CTX_block_size(ctx));
for (i = 0; i < inl / 100; i++)
{
EVP_EncryptUpdate(ctx, &ret[ol], &tmp, &data[ol], 100);
ol += tmp;
}
if (inl % 100)
{
EVP_EncryptUpdate(ctx, &ret[ol], &tmp, &data[ol], inl%100);
ol += tmp;
}
EVP_EncryptFinal(ctx, &ret[ol], &tmp);
*rb = ol + tmp;
return ret;
}
char *decrypt_example(EVP_CIPHER_CTX *ctx, char *ct, int inl)
{
/* We're going to null-terminate the plaintext under the assumption it's
* non-null terminated ASCII text. The null can be ignored otherwise.
*/
char *pt = (char *)malloc(inl + EVP_CIPHER_CTX_block_size(ctx) + 1);
int ol;
EVP_DecryptUpdate(ctx, pt, &ol, ct, inl);
if (!ol) /* there's no block to decrypt */
{
free(pt);
return NULL;
}
pt[ol] = 0;
return pt;
}
Hope this will help you.
Check this answer and check this article.
You can replace EVP_bf_cbc() with EVP_aes_128_cbc() EVP_aes_192_cbc() or EVP_aes_256_cbc() depending on what you need.
You need the SSL development packages. (libssl-dev in Ubuntu). Depending on how you implement it, you will also need libcrypto-dev.
I'm not a huge O'Reilly fan, but I found this book to be very helpful when starting this type of thing for the first time.
Why not do your own? Read enough bytes from dev/random (your key) and XOR the configuration file with it. To decrypt, XOR again with the same key. This is simple,fast,and secure. No complicated libraries needed.

Resources