Related
I am actually new to openmp, I have a working aes-128-cbc toy code that decrypts an hardcoded ciphertext to 12345, this book was recommended for me by one of the community user, I also came across this openmp reference guide and lastly I was heavily guided by one of the community user. From those books and the guide, I was trying to parallelize the serial code below
SERIAL WORKING CODE:
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
int success = 0;
void handleOpenSSLErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}
unsigned char* decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ){
EVP_CIPHER_CTX *ctx;
unsigned char *plaintexts;
int len;
int plaintext_len;
unsigned char* plaintext = malloc(ciphertext_len);
bzero(plaintext,ciphertext_len);
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();
/* Initialise the decryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
handleOpenSSLErrors();
EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleOpenSSLErrors();
plaintext_len = len;
/* Finalise the decryption. Further plaintext bytes may be written at
* this stage.
*/
// return 1 if decryption successful, otherwise 0
if(1 == EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
success = 1;
plaintext_len += len;
/* Add the null terminator */
plaintext[plaintext_len] = 0;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
//delete [] plaintext;
return plaintext;
}
size_t calcDecodeLength(char* b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
padding = 2;
else if (b64input[len-1] == '=') //last char is =
padding = 1;
return (len*3)/4 - padding;
}
void Base64Decode( char* b64message, unsigned char** buffer, size_t* length) {
BIO *bio, *b64; // A BIO is an I/O strean abstraction
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
//BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
*length = BIO_read(bio, *buffer, strlen(b64message));
BIO_free_all(bio);
}
void initAES(const unsigned char *pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
{
//initialisatio of key and iv with 0
bzero(key,sizeof(key));
bzero(iv,sizeof(iv));
EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, pass, strlen(pass), 1, key, iv);
}
int checkPlaintext(char* plaintext, char* result){
int length = 10; // we just check the first then characters
return strncmp(plaintext, result, length);
}
int main (void)
{
// password 12345
// it took 9 seconds to work out
char* ciphertext_base64 = (char*) "U2FsdGVkX19q3SzS6GhhzAKgK/YhFVTkM3RLVxxZ+nM6yXdfLZtvhyRR4oGohDotiifnR1iKyitSpiBM3hng+eoFfGbtgCu3Zh9DwIhgfS5A+OTl5a4L7pRFG4yL432HsMGRC1hy1RNPSzA0U5YyWA==\n";
char* plaintext = "This is the top seret message in parallel computing! Please keep it in a safe place.";
char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // changed
int decryptedtext_len, ciphertext_len, dict_len;
// cipher (binary) pointer and length
size_t cipher_len; // size_t is sizeof(type)
unsigned char* ciphertext;
unsigned char salt[8];
ERR_load_crypto_strings();
Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);
unsigned char key[16];
unsigned char iv[16];
unsigned char plainpassword[] = "00000";
unsigned char* password = &plainpassword[0];
// retrive the slater from ciphertext (binary)
if (strncmp((const char*)ciphertext,"Salted__",8) == 0) { // find the keyword "Salted__"
memcpy(salt,&ciphertext[8],8);
ciphertext += 16;
cipher_len -= 16;
}
dict_len = strlen(dict);
time_t begin = time(NULL);
for(int i=0; i<dict_len; i++)
for(int j=0; j<dict_len; j++)
for(int k=0; k<dict_len; k++)
for(int l=0; l<dict_len; l++)
for(int m=0; m<dict_len; m++){
*password = dict[i];
*(password+1) = dict[j];
*(password+2) = dict[k];
*(password+3) = dict[l];
*(password+4) = dict[m];
initAES(password, salt, key, iv);
unsigned char* result = decrypt(ciphertext, cipher_len, key, iv);
if (success == 1){
if(checkPlaintext(plaintext, result)==0){
printf("Password is %s\n", password);
time_t end = time(NULL);
printf("Time elpased is %ld seconds", (end - begin));
return 0;
}
}
free(result);
}
// Clean up
EVP_cleanup();
ERR_free_strings();
return 0;
}
THIS IS THE PARALLEL VERSION:
int main (void)
{
// password 12345
// it took 9 seconds to work out
char* ciphertext_base64 = (char*) "U2FsdGVkX19q3SzS6GhhzAKgK/YhFVTkM3RLVxxZ+nM6yXdfLZtvhyRR4oGohDotiifnR1iKyitSpiBM3hng+eoFfGbtgCu3Zh9DwIhgfS5A+OTl5a4L7pRFG4yL432HsMGRC1hy1RNPSzA0U5YyWA==\n";
char* plaintext = "This is the top seret message in parallel computing! Please keep it in a safe place.";
char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // changed
int decryptedtext_len, ciphertext_len, dict_len;
// cipher (binary) pointer and length
size_t cipher_len; // size_t is sizeof(type)
unsigned char* ciphertext;
unsigned char salt[8];
ERR_load_crypto_strings();
Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);
unsigned char key[16];
unsigned char iv[16];
omp_set_num_threads(NUM_THREADS);
double begin = omp_get_wtime();
// retrive the slater from ciphertext (binary)
if (strncmp((const char*)ciphertext,"Salted__",8) == 0)
{ // find the keyword "Salted__"
memcpy(salt,&ciphertext[8],8);
ciphertext += 16;
cipher_len -= 16;
}
dict_len = strlen(dict);
#pragma omp parallel for shared (key, iv)
for (int i=0; i<dict_len; i++)
{
unsigned char password[5] = {i};
for(int j=0; j<dict_len; j++)
{
password[1] = dict[j];
for(int k=0; k<dict_len; k++)
{
password[2] = dict[k];
for(int l=0; l<dict_len; l++)
{
password[3] = dict[l];
for(int m=0; m<dict_len; m++)
{
password[4] = dict[m];
{
*password = dict[i];
*(password+1) = dict[j];
*(password+2) = dict[k];
*(password+3) = dict[l];
*(password+4) = dict[m];
initAES(password, salt, key, iv);
unsigned char* result = decrypt(ciphertext, cipher_len, key, iv);
#pragma omp if(checkPlaintext(plaintext, result)==0)
{
printf("\nPassword is %s\n\n", password);
success == 1;
strcpy(result,password); // Copy thread-private copy to shared variable
time_t end = time(NULL);
printf("\nTime elpased is %ld seconds\n", (end - begin));
exit(0);
}
free(result);
}
}
}
}
#pragma omp cancellation point for
}
}
// Clean up
EVP_cleanup();
ERR_free_strings();
return 0;
}
Should bring my expected password, but when I run the code it does not output the expected . Kindly help.
You have only one buffer for the trial password (plainpassword), and all of the threads in your parallel for are trying to use it at the same time (via pointer password). There are massive data races here, and the resulting behavior is undefined.
One solution would be to declare the buffer inside the parallel loop instead of outside, as variables local to a parallel region are automatically private -- each thread will have its own.
#pragma omp parallel for shared (key, iv)
for (int i=0; i<dict_len; i++) {
unsigned char password[5] = { i };
for (int j=0; j<dict_len; j++) {
password[1] = dict[j];
for (int k=0; k<dict_len; k++) {
password[2] = dict[k];
for (int l=0; l<dict_len; l++) {
password[3] = dict[l];
for (int m=0; m<dict_len; m++) {
password[4] = dict[m];
// ...
}
}
}
}
}
Note also that
there is no apparent benefit in your original code to having both plainpassword and password. The above uses just an array, not a separate pointer as well, and it chooses the name "password" for that.
*(x + y), where x and y are primaries, means exactly the same thing as x[y] (and y[x]). The subscript form is easier to read, and that almost always makes it stylistically better.
Update:
I observe also that this code from the parallel version does not make sense, especially as compared to the serial code:
#pragma omp parallel if (strncmp((const char*)ciphertext,"Salted__",8) == 0) shared(ciphertext, plaintext, ciphertext_base64) private(salt)
{ // find the keyword "Salted__"
memcpy(salt,&ciphertext[8],8);
ciphertext += 16;
cipher_len -= 16;
}
The original code executes the if statement once per run, and its body at most once per run, so if the parallel version executes the body multiple times, and it has side effects (which it does), then the result of the program will be different. The original code should be restored for this part.
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(¶ms);
}
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
I want to use AES-128-cbc encryption/decryption algorithm to encrypt/decrypt some data,and use nodejs to encrypt the data and use c to decrypt them.But found that using the same key and IV,the two languages have different encryption results.See follows:
nodes js code:
var crypto = require('crypto');
var encrypt = function (key, iv, data) {
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
var crypted = cipher.update(data, 'utf8', 'binary');
var cbase64 = new Buffer(crypted, 'binary').toString('base64');
//console.log(crypted);
//console.log(cbase64);
crypted += cipher.final('binary');
//console.log("hahahaaaaaa:"+crypted.toString('hex'));
crypted = new Buffer(crypted, 'binary').toString('base64');
//var c16 = new Buffer(crypted, 'binary').toString(16);
//console.log(crypted);
//console.log(c16);
return crypted;
};
var decrypt = function (key, iv, crypted) {
crypted = new Buffer(crypted, 'base64').toString('binary');
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
var decoded = decipher.update(crypted, 'binary', 'utf8');
//console.log(decoded);
decoded += decipher.final('utf8');
//console.log(decoded);
return decoded;
};
var key='ABCDEFGHIJKLMNOP';
//var iv = new Buffer(crypto.randomBytes(16));
//var iv16 = iv.toString('hex').slice(0,16);
var iv16='0000000000000000';
var fs = require('fs');
fs.readFile('file.txt','utf8',function(err,data){
console.log(data);
var encrypted = encrypt(key,iv16,data);
console.log(encrypted);
var decrypted = decrypt(key,iv16,encrypted);
console.log(decrypted);
fs.writeFile('encrypted.txt',encrypted,function(err){});
});
c codes:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#define AES_BITS 128
#define MSG_LEN 128
int base64_encode(char *in_str, int in_len, char *out_str)
{
BIO *b64, *bio;
BUF_MEM *bptr = NULL;
size_t size = 0;
if (in_str == NULL || out_str == NULL)
return -1;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_write(bio, in_str, in_len);
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bptr);
memcpy(out_str, bptr->data, bptr->length);
out_str[bptr->length] = '\0';
size = bptr->length;
BIO_free_all(bio);
return size;
}
int aes_encrypt(char* in, char* key, char* out)//, int olen)
{
if(!in || !key || !out) return 0;
unsigned char iv[16];
for(int i=0; i<16; ++i)
iv[i]='0';
//printf("size:%d",AES_BLOCK_SIZE);
//unsigned char *iv = (unsigned char *)"0123456789ABCDEF";
printf("iv: %s\n",iv);
AES_KEY aes;
if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
{
return 0;
}
int len=strlen(in);
AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
return 1;
}
int aes_decrypt(char* in, char* key, char* out)
{
if(!in || !key || !out) return 0;
unsigned char iv[16];
for(int i=0; i<16; ++i)
iv[i]='0';
//unsigned char *iv = (unsigned char *)"0123456789ABCDEF";
AES_KEY aes;
if(AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
{
return 0;
}
int len=strlen(in);
AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
return 1;
}
int main(int argc,char *argv[])
{
char sourceStringTemp[MSG_LEN];
char dstStringTemp[MSG_LEN];
char dstStringTemp_base64[MSG_LEN];
memset((char*)sourceStringTemp, 0 ,MSG_LEN);
memset((char*)dstStringTemp, 0 ,MSG_LEN);
strcpy((char*)sourceStringTemp, "My name is Harlan Chen!");
//strcpy((char*)sourceStringTemp, argv[1]);
char key[AES_BLOCK_SIZE]={0};
int i;
for(i = 0; i < 16; i++)
{
key[i] = 'A' + i;
}
printf("keys:%s\n",key);
if(!aes_encrypt(sourceStringTemp,key,dstStringTemp))
{
printf("encrypt error\n");
return -1;
}
/*To Base64 encrypted data */
base64_encode(dstStringTemp, strlen(dstStringTemp),dstStringTemp_base64);
printf("Base64 Encrypted data: %s\n",dstStringTemp_base64);
printf("encrypted:%s\n",dstStringTemp);
printf("enc %lu:",strlen((char*)dstStringTemp));
for(i= 0;dstStringTemp[i];i+=1){
printf("%x",(unsigned char)dstStringTemp[i]);
}
memset((char*)sourceStringTemp, 0 ,MSG_LEN);
if(!aes_decrypt(dstStringTemp,key,sourceStringTemp))
{
printf("decrypt error\n");
return -1;
}
printf("\n");
printf("dec %lu:",strlen((char*)sourceStringTemp));
printf("%s\n",sourceStringTemp);
//for(i= 0;sourceStringTemp[i];i+=1){
// printf("%x",(unsigned char)sourceStringTemp[i]);
//}
printf("\n");
return 0;
}
nodejs results:
bogon:AES_128_encryption zexu$ node encrypt.js
My name is Harlan Chen!
jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=
My name is Harlan Chen!
and the c language results:
bogon:AES_128_encryption zexu$ ./a.out
keys:ABCDEFGHIJKLMNOP
iv: 0000000000000000
Base64 Encrypted data: jERcWr8ZMzSJcKPGB7RYAf6kEOmjJgUksDtrttx4r3k=
encrypted:?D\Z?34?p???X???&$?;k??x?y
enc 32:8c445c5abf1933348970a3c67b4581fea410e9a326524b03b6bb6dc78af79
dec 23:My name is Harlan Chen!
Compare the two base64 strings :
jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=
jERcWr8ZMzSJcKPGB7RYAf6kEOmjJgUksDtrttx4r3k=
The first 21 characters are the same,the following ones are different.I don't know why.
AES is a block cipher, which means, that you always encrypt full blocks of 128 bit (i.e. 16 byte).
Now if you look at your plaintext "My name is Harlan Chen!", you will notice, that these are 23 bytes. This means, that 9 bytes are still left, which is filled with PKCS#7-Padding (9 bytes of value 09) in the nodejs-case, but with zero-bytes in the C case. Furthermore, I suspect there is a trailing newline in the file.txt with your string, which isn't present in the C example, too.
So, you should check with a hex editor, that your string "My name is Harlan Chen!" without a newline and padded with zero bytes up to 32 bytes is inside your file.txt.
This will still not yield exactly the same result, since one full block of padding will be added by nodejs (see PKCS#7-Padding) because one byte is always added as padding. But you can disable the automatic padding in your nodejs-script wih
cipher.setAutoPadding(0);
and
decipher.setAutoPadding(0);
Then you should get the same results. As mentioned above, make sure your file.txt does not contain a newline and is padded to 32 bytes with zero-bytes.
Alternatively, you can change your C program to implement PKCS#7 padding; then you should also get the same results. The string to encrypt would be
"My name is Harlan Chen!\x09\x09\x09\x09\x09\x09\x09\x09\x09"
then
I am trying to first encrypt one file and send it to server, both client and server are all written in C openssl. I try to encrypt one file and decrypt it in server, but when decrypting I got an error:
error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02
I checked this error for a long time and fail to modify it.
Here is part of my code:
RSA:I think this part is fine, becaue in the client I decrypt it immediately after encryption, and it success.
int padding = RSA_PKCS1_PADDING;
RSA * createRSA(unsigned char * key,int public)
{
RSA *rsa= NULL;
BIO *keybio ;
keybio = BIO_new_mem_buf(key, -1);
if (keybio==NULL)
{
printf( "Failed to create key BIO");
return 0;
}
if(public)
{
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,NULL, NULL);
}
else
{
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
}
if(rsa == NULL)
{
printf( "Failed to create RSA");
}
return rsa;
}
char publicKey[]="-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJY\n"\
"ozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+\n"\
"vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQAp\n"\
"fc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68\n"\
"i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoV\n"\
"PpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUy\n"\
"wQIDAQAB\n"\
"-----END PUBLIC KEY-----\n";
char privateKey[]="-----BEGIN RSA PRIVATE KEY-----\n"
/* ----8<---------------------*/
"-----END RSA PRIVATE KEY-----\n";
int public_encrypt(unsigned char * data,int data_len,unsigned char * key, unsigned char *encrypted)
{
RSA * rsa = createRSA(key,1);
int result = RSA_public_encrypt(data_len,data,encrypted,rsa,padding);
return result;
}
int private_decrypt(unsigned char * enc_data,int data_len,unsigned char * key, unsigned char *decrypted)
{
RSA * rsa = createRSA(key,0);
int result = RSA_private_decrypt(data_len,enc_data,decrypted,rsa,padding);
if(result==-1){
printf("in\n");
unsigned int errCode = ERR_get_error();
printf("\nError: %s\n", ERR_error_string(errCode, NULL));
}
return result;
}
Socket:
int readData(int s, void *buf, int buflen)
{
int total = 0;
char *pbuf = (char*) buf;
while (buflen > 0) {
int numread = recv(s, pbuf, buflen, 0);
if (numread <= 0) return numread;
pbuf += numread;
buflen -= numread;
total += numread;
}
return total;
}
int sendData(int s, void *buf, int buflen)
{
int total = 0;
char *pbuf = (char*) buf;
while (buflen > 0) {
int numsent = send(s, pbuf, buflen, 0);
if (numsent <= 0) return numsent;
pbuf += numsent;
buflen -= numsent;
total += numsent;
}
return total;
}
client:(this is not the whole code I just pick some important code, the decrypt here just to test, and it success to decrypt)
unsigned char encrypted[8192]={};
unsigned char decrypted[8192]={};
char buffer[8192];
bzero(buffer,8192);
stream = fopen(save_name,"w+t");
struct stat st ;
stat( put_name, &st );
fread(buffer,1,st.st_size,stream))
int encrypted_length=public_encrypt(buffer,st.st_size,publicKey,encrypted);
printf("after encrypted%s\n", encrypted);
int decrypted_length= private_decrypt(encrypted,256,privateKey,decrypted);
printf("afterdecrypted: %s\n",decrypted);
sendData(fd,encrypted,strlen(encrypted))
server:
readData(connfd,buf,intsize);
("recieve data buf:%s\n", buf);
unsigned char decrypted[8192]={};
bzero(decrypted,8192);
int decrypted_length = private_decrypt(buf,256,privateKey,decrypted);
I used md5 to check the sent and received data, they are the same. Could you find the reason?
Here is another question, with same error, but I can't get any idea from here.
Encryption and decryption error 0x0407106B using OpenSSL
I use
printf("in server: \ndata:%s\nlength:%d\nkeylen:%d\n",buf,strlen(buf),strlen(privateKey) );
to print all perimeters and they are same.
in server:
data:k???
U??uE????^??%?^{?N?-?pg???5?|??
???$???ěQ????zܯ?(T?n>f&??J?C??x?
D
length:82
keylen:1675
in client:
data:k???
U??uE????^??%?^{?N?-?pg???5?|??
???$???ěQ????zܯ?(T?n>f&??J?C??x?
D
length:82
keylen:1675
When I use the valgrind, it shows a lot of problem, the first is:
==21631== 8 bytes in 1 blocks are indirectly lost in loss record 1 of 35
==21631== at 0x4A06A2E: malloc (vg_replace_malloc.c:270)
==21631== by 0x31CF06AC2D: CRYPTO_malloc (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0A72C1: ??? (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0A742C: bn_expand2 (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0A7674: BN_copy (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0A7899: BN_dup (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0ABE2C: BN_BLINDING_create_param (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0CC500: RSA_setup_blinding (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0C4483: ??? (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x31CF0C49C7: ??? (in /usr/lib64/libcrypto.so.1.0.1e)
==21631== by 0x402263: put_file (client.c:442)
==21631== by 0x402675: main (client.c:628)
the line 442 is my decrypt function. So, any problem with my decryption function.
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.