Following the OpenSSL docs, I /think/ that what I'm doing is correct.. but apparently it's not. Compiling the file (with gcc -g -Wall -Wextra -lssl sign.c) yields no errors or warnings. EVP_VerifyFinal() always returns 0 (Meaning the check failed). What is causing that?
static const EVP_MD * type;
unsigned char * sha(char * input)
{
EVP_MD_CTX c;
unsigned char *md;
unsigned int md_len;
md = malloc(EVP_MAX_MD_SIZE);
EVP_MD_CTX_init(&c);
EVP_DigestInit_ex(&c, type, NULL);
EVP_DigestUpdate(&c, input, strlen(input));
EVP_DigestFinal_ex(&c, md, &md_len);
EVP_MD_CTX_cleanup(&c);
return md;
}
unsigned char * sign(EVP_PKEY * key, unsigned char * data)
{
EVP_MD_CTX c;
unsigned char *sig;
unsigned int len;
EVP_MD_CTX_init(&c);
sig = malloc(EVP_PKEY_size(key));
EVP_SignInit(&c, type);
EVP_SignUpdate(&c, data, strlen((char *)data));
EVP_SignFinal(&c, sig, &len, key);
EVP_MD_CTX_cleanup(&c);
return sig;
}
int verify(EVP_PKEY * key, unsigned char * data, unsigned char * original)
{
EVP_MD_CTX c;
int ret;
EVP_MD_CTX_init(&c);
EVP_VerifyInit(&c, type);
EVP_VerifyUpdate(&c, data, (unsigned int)sizeof(data));
ret = EVP_VerifyFinal(&c, original, (unsigned int)strlen((char *)original), key);
return ret;
}
int main(void)
{
EVP_PKEY *sk, *pk;
FILE *sfd, *pfd;
unsigned char *hash, *sig;
unsigned int i;
sfd = fopen("secret.pem", "r");
pfd = fopen("public.pem", "r");
sk = PEM_read_PrivateKey(sfd, NULL, NULL, NULL);
pk = PEM_read_PUBKEY(pfd, NULL, NULL, NULL);
fclose(sfd);
fclose(pfd);
OpenSSL_add_all_digests();
type = EVP_get_digestbyname("SHA1");
hash = sha("moo");
for(i = 0; i < sizeof(hash); i++)
printf("%02x", hash[i]);
printf("\n");
sig = sign(sk, hash);
switch( verify(pk, sig, hash) )
{
case 0:
printf("Check failed.\n");
break;
case 1:
printf("Check succeeded!\n");
break;
default:
printf("Oh look, an error: %d", ERR_get_error());
break;
}
return 0;
}
You are not passing the correct size:
EVP_VerifyUpdate(&c, data, (unsigned int)sizeof(data));
Since data is defined as unsigned char *, sizeof(data) is probably 4 or 8 (number of bytes required to hold a pointer).
Try passing the actual number of bytes that you've allocated, i.e. EVP_PKEY_size(key). You will have to pass that to your verify() function.
It's possible that something else is also wrong, but this one caught my eye.
I know this is an old question, but one bug could confuse users.
In the verify function original and data should be swapped, original past to EVP_VerifyUpdate and data to EVP_VerifyFinal. String length should not be used as a byte array can contain 0x00 as a valid value which will be recognized as "\0".
Related
I'm trying to write a C program to brute force AES decryption using openssl. However, when I tried to decrypt the message by passing a key attackKey, I got and error printed from the decrypt() function. The error is as follows
There was an error calling the decrypted final object...: Success
The code is given below, I tried reading the book on Openssl for more information but as it is quite outdated it was not helpful. I also googled and barely found any resources on my issue and yes I am well aware of the fact that brute forcing aes_128 is a pipe dream. I am just testing some things with this library to see if the key is only a few combinations ahead, so can I iterate on the attackKey and try to get the decrypted text.
#include <string.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <stdbool.h>
#include <openssl/err.h>
#define BUFFER_SIZE 64
// void addPadding(unsigned char *buff, );
int encrypt(const unsigned char *plaintext, const int plaintext_len, const unsigned char *key, unsigned char *ciphertext);
int decrypt(const unsigned char *ciphertext, const int ciphertext_len, const unsigned char *key, unsigned char *decryptedtext);
void iterateKey(unsigned char *key, const int length);
bool compareKeys(const unsigned char *key1, const unsigned char *key2, const int length);
void *bruteForceAttack(void *arg);
int main(int argc, char *argv[])
{
OpenSSL_add_all_algorithms(); // load all cipher algorithms
ERR_load_crypto_strings(); // load human readable errors
/* A 256 bit key */
unsigned char key[] = "abcdefghijklmnqp"; // 6 + 1 bytes
unsigned char plaintext[] = "Hello, my name is shaheryar ;)."; // 32 + 1 bytes
/* Buffer to store the ciphertext. The size may be different due to padding. */
unsigned char ciphertext[BUFFER_SIZE];
int ciphertext_len = 0;
/* Encryption. */
ciphertext_len = encrypt(plaintext, strlen((char *)plaintext), key, ciphertext);
printf("Length of text:%d, Ciphertext is:\n", ciphertext_len);
BIO_dump_fp(stdout, (const char *)ciphertext, ciphertext_len);
//----------------MAIN DECRYPTION LOGIC
/* Buffer for the decrypted text for verifying decryption. */
unsigned char decryptedtext[BUFFER_SIZE];
int decryptedtext_len = 0;
unsigned char attackKey[] = "abcdefghijklmnop";
const int attackKey_len = 17;
for (int i = 0; i < 10e2; i++)
{
// printf("Key : %s\n", attackKey);
decryptedtext_len = 0;
decryptedtext_len = decrypt(ciphertext, ciphertext_len, attackKey, decryptedtext);
decryptedtext[decryptedtext_len] = '\0';
if (strcmp((const char *)attackKey, (const char *)key) == 0)
{
printf("\nLength of text: %d, Decryptedtext is: %s\n", decryptedtext_len, decryptedtext);
}
iterateKey(attackKey, attackKey_len);
}
//------------END OF DECRYPTION PHASE
if (strncmp((const char *)plaintext, (const char *)decryptedtext, decryptedtext_len))
{
printf("FAIL: enc/dec failed for \"%s\"\n", decryptedtext);
}
else
{
printf("OK: enc/dec ok for \"%s\"\n", plaintext);
}
return 0;
}
int encrypt(const unsigned char *plaintext, const int plaintext_len, const unsigned char *key, unsigned char *ciphertext)
{
/* The following object is used to keep track of the key and internal states*/
EVP_CIPHER_CTX *ctx = NULL;
int len = 0, ciphertext_len = 0;
/* Create and initialize the context and returns a pointer for success and null for failure*/
if (!(ctx = EVP_CIPHER_CTX_new()))
{
printf("There was an error creating the encryption object...");
}
/* Initialize the encryption operation. */
if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL))
{
printf("There was an error loading the encryption object...");
}
if (plaintext)
{
if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
{
printf("There was an error creating the update the encryption object...");
}
ciphertext_len = len;
}
if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
{
printf("There was an error encrypting the final object...");
}
ciphertext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
int decrypt(const unsigned char *ciphertext, const int ciphertext_len, const unsigned char *key, unsigned char *decryptedtext)
{
/* The following object is used to keep track of the key and internal states*/
EVP_CIPHER_CTX *ctx = NULL;
int len = 0, decryptedtext_len = 0;
/* Create and initialize the context and returns a pointer for success and null for failure*/
if (!(ctx = EVP_CIPHER_CTX_new()))
{
printf("There was an error creating the decryption object...");
}
/* Initialize the encryption operation. */
if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL))
{
printf("There was an error initializing the cipher object...");
}
if (ciphertext)
{
if (!EVP_DecryptUpdate(ctx, decryptedtext, &len, ciphertext, ciphertext_len))
{
printf("There was an error decrypting the cipher text...");
}
decryptedtext_len = len;
}
if (!EVP_DecryptFinal_ex(ctx, decryptedtext + len, &len))
{
printf("There was an error calling the decrypted final object...");
}
decryptedtext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return decryptedtext_len;
}
bool compareKeys(const unsigned char *key1, const unsigned char *key2, const int length)
{
for (int i = 0; i < length; i++)
{
if (key1[i] != key2[i])
{
return false;
}
}
return true;
}
void iterateKey(unsigned char *key, const int length)
{
for (int i = 0; i < (length - 1); i++)
{
if (key[length - 2 - i] == 'z')
{
key[length - 2 - i] = 'a';
}
else
{
key[length - 2 - i]++;
return;
}
}
}
void *bruteForceAttack(void *arg)
{
return NULL;
}
Below are the resources I used for implementing the code:
https://www.cs.ucy.ac.cy/courses/EPL326/labs/lab3/aesimplentation.pdf
https://www.oreilly.com/library/view/network-security-with/059600270X/
In a database there is string encrypted with glibc's xencrypt which is using internally cbc_crypt and the decryption was happening with xdecrypt function which also uses cbc_crypt.
Since glibc 2.32 there is no way to enable again xencrypt and xdecrypt functions.
So I am trying to decrypt the encrypted string with openssl with command:
openssl enc -des-cbc -d -in encrypted.enc -out decrypted.txt -iv 0000000000000000 -K <my key in hex>
But I get:
bad decrypt 140401522647424:error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length:../openssl-1.1.1k/crypto/evp/evp_enc.c:599
What am I doing wrong and is there an openssl command or function call that can provide the same result as xdecrypt (cbc_encrypt)?
***** UPDATE *****
I tried to solve the above question also programmatically.
So I used the following code in order to be also in topic as #dave_thompson_085 mentioned in comment.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/des_crypt.h>
#include <openssl/des.h>
#include <openssl/pkcs7.h>
#define BUFFSIZE 420
int glibc_encrypt(const char *, const char *, const char *, char **, unsigned int *);
int glibc_decrypt(const char *, const char *, const char *, char **);
int openssl_encrypt(const char *, const char *, const char *, unsigned int, char *, unsigned int *);
int openssl_decrypt(const char *, const char *, char *, unsigned int, char **);
int main(void) {
const char *key = "abcdefgh";
const char *ivec = "87654321";
const char *data = "mypass1234test";
char *dst_buf;
unsigned int dst_size;
char *final_buf;
glibc_encrypt(key, ivec, data, &dst_buf, &dst_size);
glibc_decrypt(key, ivec, (const char *) dst_buf, &final_buf);
openssl_encrypt(key, ivec, data, strlen(data), dst_buf, &dst_size);
openssl_decrypt(key, ivec, dst_buf, dst_size, &final_buf);
return 0;
}
int glibc_encrypt(const char *key, const char *ivec, const char *data, char **dst, unsigned int *dst_size) {
char tmpkey[BUFFSIZE], tmpivec[BUFFSIZE];
unsigned int rawsize, i, result, pading;
printf("*** glibc encrytion ***\n");
strncpy(tmpkey, key, strlen(key));
strncpy(tmpivec, ivec, strlen(ivec));
des_setparity(tmpkey);
rawsize = strlen(data);
printf("data to be ecrypted is : %s\n", data);
printf("size of data to be encrypted is : %d\n", rawsize);
/* Add to data to ensure size is divisable by 8. */
pading = 8 - (rawsize % 8);
*dst_size = rawsize + pading;
printf("data size were rounded to : %d\n", *dst_size);
/* Allocate the containing ecryption buffer */
*dst = malloc(*dst_size);
memcpy(*dst, data, *dst_size);
for (i = rawsize ; i < *dst_size; i++)
dst[i] = '\0';
result = cbc_crypt(tmpkey, *dst, *dst_size, DES_ENCRYPT | DES_SW, tmpivec);
if (DES_FAILED(result) || strcmp(*dst, "") == 0) {
if(strcmp(*dst, "") == 0)
printf("*** Null Output ***\n");
else
printf("*** Encryption Error ***\n");
} else {
snprintf(tmpkey, *dst_size, "%s", *dst);
printf("glibc encrypted data : %s\n", tmpkey);
}
return 0;
}
int glibc_decrypt(const char *key, const char *ivec, const char *data, char **dst) {
char tmpkey[BUFFSIZE], tmpivec[BUFFSIZE];
unsigned int dstsize;
int result;
printf("\n*** glibc decryption ***\n");
strncpy(tmpkey, key, strlen(key));
strncpy(tmpivec, ivec, strlen(ivec));
des_setparity(tmpkey);
dstsize = strlen(data);
printf("data to be decrypted is : %s\n", data);
printf("size of data to be decrypted is : %d\n", dstsize);
/* Allocate the containing ecryption buffer */
*dst = malloc(dstsize);
memcpy(*dst, data, dstsize);
result = cbc_crypt(tmpkey, *dst, dstsize, DES_DECRYPT | DES_SW, tmpivec);
if(DES_FAILED(result) || strcmp(*dst, "") == 0) {
if(strcmp(*dst, "") == 0)
printf("*** Null Output ***\n");
else
printf("*** Decryption Error ***\n");
} else {
snprintf(tmpkey, dstsize, "%s", *dst);
printf("glibc decrypted data : %s\n", tmpkey);
}
return 0;
}
int openssl_encrypt(const char *key, const char *ivec, const char *_raw_ptr, unsigned int _raw_size, char *_dst_buf, unsigned int *_dst_size) {
DES_key_schedule schedule;
char tmpkey[BUFFSIZE];
DES_cblock *iv3;
int pading;
size_t i, vt_size;
char *mid_buf;
printf("\n*** openssl ecryption ***\n");
strncpy(tmpkey, key, strlen(key));
printf("data to be ecrypted is : %s\n", _raw_ptr);
printf("size of data to be encrypted is : %d\n", _raw_size);
DES_set_key_unchecked((const_DES_cblock*) &tmpkey, &schedule);
vt_size = strlen(ivec);
iv3 = (DES_cblock *) malloc(vt_size * sizeof(unsigned char));
memcpy(iv3, ivec, vt_size);
pading = 8 - (_raw_size % 8);
*_dst_size = _raw_size + pading;
printf("data size were rounded to : %d\n", *_dst_size);
mid_buf = malloc(*_dst_size);
memcpy(mid_buf, _raw_ptr, _raw_size );
/* Add to data to ensure size is divisable by 8. */
for (i = _raw_size ; i < *_dst_size; i++ )
mid_buf[i] = '\0';
DES_ncbc_encrypt((const unsigned char*) mid_buf, (unsigned char *) _dst_buf, *_dst_size, &schedule, iv3, DES_ENCRYPT);
printf("openssl ecrypted data : %s\n", _dst_buf);
printf("openssl ecrypted data size : %d\n", *_dst_size);
free(iv3);
free(mid_buf);
return 0;
}
int openssl_decrypt(const char *key, const char *ivec, char *_raw_ptr, unsigned int _raw_size, char **_dst_buf) {
DES_key_schedule schedule;
char tmpkey[BUFFSIZE];
DES_cblock *iv3;
size_t vt_size;
printf("\n*** openssl decryption ***\n");
strncpy(tmpkey, key, strlen(key));
printf("data to be decrypted is : %s\n", _raw_ptr);
printf("size of data to be dencrypted is: %d\n", _raw_size);
DES_set_key_unchecked((const_DES_cblock*) &tmpkey, &schedule);
vt_size = strlen(ivec);
iv3 = (DES_cblock *) malloc(vt_size * sizeof(unsigned char));
memcpy(iv3, ivec, vt_size);
*_dst_buf = (char*) malloc(_raw_size);
DES_ncbc_encrypt((const unsigned char*) _raw_ptr, (unsigned char *) *_dst_buf, _raw_size, &schedule, iv3, DES_DECRYPT);
printf("openssl decrypted data : %s\n", *_dst_buf);
free(iv3);
return 0;
}
And here is the output:
*** glibc encrytion ***
data to be ecrypted is : mypass1234test
size of data to be encrypted is : 14
data size were rounded to : 16
glibc encrypted data :▒!▒e͛▒4▒ k
▒▒
*** glibc decryption ***
data to be decrypted is :▒!▒e͛▒4▒ k
▒▒d
size of data to be decrypted is : 16
glibc decrypted data : mypass1234test
*** openssl ecryption ***
data to be ecrypted is : mypass1234test
size of data to be encrypted is : 14
data size were rounded to : 16
openssl ecrypted data : ▒▒▒▒˛▒▒▒uc▒
openssl ecrypted data size : 16
*** openssl decryption ***
data to be decrypted is : ▒▒▒▒˛▒▒▒uc▒
size of data to be dencrypted is: 16
openssl decrypted data : mypass1234test
Both openssl and glibc implementations encrypt and decrypt the data.
But if used interchangeably the outcome if not correct.
I would expect the encrypted data from both of them to be exactly the same if the DES implementation is the same, as it should be I guess since the algorithm should produce the same output.
So why openssl and glibc produce seemingly different encrypted data when the same algorithm is used?
DISCLAIMER
This is not the correct answer but rather a work around I found and used in case some else wants to use the same solution.
The main problem is that recent glibc version do not provide any more the function cbc_crypt and hence xencrypt and xdecrypt.
But libtirpc (http://sourceforge.net/projects/libtirpc) includes the missing cbc_crypt function.
So instead of using openssl to decrypt the cbc_crypted staff I used the libtirp library.
I am creating an HMAC digest in java application and want to verify it into the C program. I have a hardcoded secret key in hex format.
I'm getting Segmentation fault while trying to calculate HmacSHA256 in C. I couldn't figure out what I am messing up.
The java program
byte[] decodedKey = hexStringToByteArray("d44d4435c5eea8791456f2e20d7e176a");
SecretKey key = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
try {
Mac mac = Mac.getInstance("HmacSHA256"); //Creating a Mac object
mac.init(key); //Initializing the Mac object
byte[] bytes = challenge.getBytes();
byte[] macResult = mac.doFinal(bytes);
return macResult;
} catch (NoSuchAlgorithmException e) {
System.out.println("Not valid algorithm"+ e);
} catch (InvalidKeyException e) {
System.out.println("Invalid key"+ e);
}
C program
const char* key = hexstr_to_char("d44d4435c5eea8791456f2e20d7e176a");
unsigned char *result;
unsigned int* resultlen;
hmac_sha256(key, strlen(key),
challenge, strlen("d44d4435c5eea8791456f2e20d7e176a"),
result, resultlen);
unsigned char* hmac_sha256(const void *key, int keylen,
const unsigned char *data, int datalen,
unsigned char *result, unsigned int* resultlen)
{
return HMAC(EVP_sha256(), key, keylen, data, datalen, result, resultlen);
}
unsigned char* hexstr_to_char(const char* hexstr)
{
size_t len = strlen(hexstr);
if (len % 2 != 0)
return NULL;
size_t final_len = len / 2;
unsigned char* chrs = (unsigned char*)malloc((final_len+1) * sizeof(*chrs));
for (size_t i=0, j=0; j<final_len; i+=2, j++)
chrs[j] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i+1] % 32 + 9) % 25;
chrs[final_len] = '\0';
return chrs;
}
If you read the documentation for HMAC() more carefully you'll see that there are a few ways to call the method to determine size of the output buffer, or to let it return a static array buffer.
You can pass NULL for the result and get the length with a first call, then allocate the result buffer and call again.
Or you can pass NULL for the result buffer, and it will return a static array buffer (which you don't own), however that is documented as not thread-safe.
The easiest way may be to rely on EVP_MAX_MD_SIZE to statically declare your buffer. You should also declare the length variable as int not int*, and pass its address with the & operation - this is very basic C.
Try this:
unsigned int resultlen = 0;
unsigned char resultbuf[EVP_MAX_MD_SIZE];
hmac_sha256(key, strlen(key), challenge, strlen("d44d4435c5eea8791456f2e20d7e176a"),
resultbuf, &resultlen);
Documentation:
https://www.openssl.org/docs/man1.1.1/man3/HMAC.html
There are many C language tutorials out there.
I'm trying to create a certain modular code using OpenSSL's EVP API for MD5 by passing the EVP_MD object within functions as shown below.
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
EVP_MD* md5_digest_init() {
OpenSSL_add_all_digests();
EVP_MD *md = EVP_get_digestbyname("MD5");
if(!md) {
printf("Unable to init MD5 digest\n");
exit(1);
}
return md;
}
unsigned char *md5_digest_process(EVP_MD* md, unsigned char *input_text) {
EVP_MD_CTX mdctx;
unsigned char hash[EVP_MAX_MD_SIZE];
int hash_len;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, input_text, strlen(input_text)+1);
EVP_DigestFinal_ex(&mdctx, hash, &hash_len);
EVP_MD_CTX_cleanup(&mdctx);
return hash;
}
int main() {
EVP_MD *md;
md = md5_digest_init();
unsigned char* res;
res = md5_digest_process(md, "foobar");
printf("%02x", res);
return 0;
}
The problem is that on executing the code every time, I obtain a different hash value for the same text.
Such as
585c64a0
554454a0
5f75a4a0, etc
MD5 is deterministic and such an issue should not exist. Any reason why such as error exists? Also, the passing of the EVP_MD object within functions is important to me.
EDIT:
Replacing the final printf with the following code
for(int i = 0; i < 16; ++i)
printf("%02x", res[i]);
I get the following output.
b4000000000000000100000000000000
However, this stays the same for all executions. But I'm guessing that this hash isn't right.
As stated in comments, md5_digest_process() is returning a pointer to a local variable that goes out of scope when the function exits, leaving the pointer dangling as it is left pointing to invalid memory.
But that doesn't matter to your issue, because you are printing the memory address that the pointer is pointing at, not the data that it is pointing at. So your printed output is displaying whatever random memory address the local hash variable existed at when the function was called. That is why you are seeing inconsistent values in your output. As stated in comments, you need to dereference the pointer in order to print the data that is being pointed at.
If you want to return a pointer to memory that outlives the function, you have to allocate the memory dynamically:
unsigned char* md5_digest_process(EVP_MD* md, unsigned char *input_text, int *hash_len) {
unsigned char *hash = (unsigned char *) malloc(EVP_MAX_MD_SIZE);
if (!hash) return NULL;
EVP_MD_CTX mdctx;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, input_text, strlen(input_text)+1);
EVP_DigestFinal_ex(&mdctx, hash, hash_len);
EVP_MD_CTX_cleanup(&mdctx);
return hash;
}
int main() {
EVP_MD *md = md5_digest_init();
int hash_len = 0;
unsigned char* res = md5_digest_process(md, "foobar", &hash_len);
if (res) {
for(int i = 0; i < hash_len; ++i) {
printf("%02x", res[i]);
}
free(res)
}
return 0;
}
Otherwise, the caller will have to allocate the memory and pass it into the function to fill in:
int md5_digest_process(EVP_MD* md, unsigned char *input_text, unsigned char* hash) {
EVP_MD_CTX mdctx;
int hash_len = 0;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, input_text, strlen(input_text)+1);
EVP_DigestFinal_ex(&mdctx, hash, &hash_len);
EVP_MD_CTX_cleanup(&mdctx);
return hash_len;
}
int main() {
EVP_MD *md = md5_digest_init();
unsigned char hash[EVP_MAX_MD_SIZE];
int hash_len = md5_digest_process(md, "foobar", hash);
for(int i = 0; i < hash_len; ++i) {
printf("%02x", res[i]);
}
return 0;
}
I think you are close. I would probably change md5_digest_process to:
/* md_digest is declared as unsigned char md_digest[EVP_MAX_MD_SIZE] */
unsigned int md5_digest_process(EVP_MD* md, unsigned char *input_text, unsigned int input_len, unsigned char* md_digest)
{
int hash_len;
EVP_MD_CTX mdctx;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, input_text, input_len);
EVP_DigestFinal_ex(&mdctx, md_digest, &hash_len);
EVP_MD_CTX_cleanup(&mdctx);
return hash_len;
}
Then, print md_digest with:
int main()
{
EVP_MD *md;
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int hash_len;
md = md5_digest_init();
hash_len = md5_digest_process(md, "foobar", 6, hash);
for(unsigned int i=0; i<hash_len; i++)
printf("%02x", hash[i]);
printf("\n");
return 0;
}
You should also add some error checking.
This is a continuation of my previous question: Intermittent decryption failures in EVP_DecryptFinal_ex when using AES-128/CBC.
I am trying to encrypt and decrypt using the C OpenSSL EVP library. After I received an answer to my question above, I updated my code accordingly:
This variable:
int len = outlen1 + outlen2;
Stores the number of bytes encrypted in the encrypt function. I then pass that variable to the decrypt function (the passing is not shown in the code below) which then uses what is supposedly the actual number of bytes.
However on some input strings, I get segmentation faults at the EVP_DecryptFinal_ex() function.
Something is obviously wrong with the number of bytes encrypted/decrypted or padding. I just don't know what that is.
char* encrypt(char *key, char *s) {
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);
len = outlen1 + outlen2;
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
char* decrypt(char *key, char *s, int len) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
printf("len: %d\n", len);
printf("strlen(s): %d\n", strlen(s));
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, len) == 1) {
printf("After update\n");
if (EVP_DecryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
printf("After final\n");
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
NOTE:
I was able to fix the problems I previously had where decrypt final would fail to decrypt certain strings. Those strings can be decrypted fine now. However, some other strings are facing the same problem but this time I am getting seg faults.
You cannot use string functions on binary data. This is especially the case if that binary data is indistinguishable from random. Random binary data may contain null characters anywhere, or not at all. strdup uses strcpy internally, which relies on the null character to be present.