EVP_DecryptUpdate is giving segmentation fault - c

In c while using the openssl/evp.h library.
If I am doing
EVP_CIPHER_CTX_new();
EVP_DecryptInit(ctx, EVP_aes_256_wrap_pad(), NULL, key, iv);
EVP_DecryptUpdate(ctx, buf, &cipher_len, 32);
I am getting a seg fault in the last line but when I change the EVP_aes_256_wrap_pad() to EVP_aes_128_ecb() and keeping all other parameters same seg fault is gone.
Here is an example code
Working
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
void dump_head(unsigned char *buf, size_t len)
{
unsigned end, i;
for (end = len; end > 0; end--)
if (buf[end-1] != 0)
break;
printf("buf = {");
for (i = 0; i < end; i++)
printf(" %02hhx,", buf[i]);
printf(" }\n");
}
int main(void)
{
unsigned char key[] = "0123456789abcdef";
unsigned char iv[] = "1234567887654321";
unsigned char indata[32] = "0123456789abcdeffedcba9876543210";
unsigned char buf[4096];
unsigned pos;
int cipher_len;
EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
EVP_DecryptUpdate(ctx, buf, &cipher_len, indata, 32);
printf("Got %d\n", cipher_len);
dump_head(buf, sizeof(buf));
printf("Final!\n");
memset(buf, 0, sizeof(buf));
EVP_DecryptFinal_ex(ctx, buf, &cipher_len);
printf("Got %d\n", cipher_len);
dump_head(buf, sizeof(buf));
return 0;
}
Not working
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
void dump_head(unsigned char *buf, size_t len)
{
unsigned end, i;
for (end = len; end > 0; end--)
if (buf[end-1] != 0)
break;
printf("buf = {");
for (i = 0; i < end; i++)
printf(" %02hhx,", buf[i]);
printf(" }\n");
}
int main(void)
{
unsigned char key[] = "0123456789abcdef";
unsigned char iv[] = "1234567887654321";
unsigned char indata[32] = "0123456789abcdeffedcba9876543210";
unsigned char buf[4096];
unsigned pos;
int cipher_len;
EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_128_wrap_pad(), NULL, key, iv);
EVP_DecryptUpdate(ctx, buf, &cipher_len, indata, 32);
printf("Got %d\n", cipher_len);
dump_head(buf, sizeof(buf));
printf("Final!\n");
memset(buf, 0, sizeof(buf));
EVP_DecryptFinal_ex(ctx, buf, &cipher_len);
printf("Got %d\n", cipher_len);
dump_head(buf, sizeof(buf));
return 0;
}
Can you please suggest is there anything more that I have to add for EVP_aes_256_wrap_pad().

It's not documented that I can find, and may not actually be supported, but to use a wrap-mode cipher in EVP_{Cipher,Encrypt,Decrypt}* you must first set a flag on the context:
EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
and then use Init_ex as you did.
Since you didn't set this, your call to DecryptInit_ex failed, but you ignored the return code, and then called DecryptUpdate on an uninitialized context, which crashes. You should always check the return code from any OpenSSL routine that returns one.
However even when I set this flag, DecryptUpdate returns 0, I suspect because your data is not valid ciphertext for this algorithm (the keywrap algorithms include an integrity check), although I would expect it to put something in the errorstack and it doesn't. I will look more deeply at that if I have time.

Related

OpenSSL EVP decryption fails for ECB and CBC but works for OFB

For AES-128 with OFB mode my code decrypts and works perfectly but for ECB and CBC it just gives random output even though I use the same key and same IV.
Here is my code:
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>
#include <stdio.h>
#define BUFFER_SIZE 4096
#define KEY_SIZE 16
int failed = 0;
void print_hex(char *s){
while(*s){
printf("%02x" , (unsigned int) *s++);
}
}
int min(int a , int b){
return a < b ? a : b;
}
void set_padding(unsigned char* array){
for(int i= 0 ; i < KEY_SIZE; ++i){
array[i] = ' ';
}
}
int handleErrors(void)
{
// ERR_print_errors_fp(stderr);
// abort();
failed = 1;
return -1;
}
int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
unsigned char *iv, unsigned char *ciphertext , char* mode)
{
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len;
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
if(strcmp(mode , "OFB") == 0){
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ofb(), NULL, key, iv))
handleErrors();
}
else{
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL))
handleErrors();
}
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv, unsigned char *plaintext , char* mode)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
if(strcmp(mode , "OFB") == 0){
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ofb(), NULL, key, iv))
handleErrors();
}
else{
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL))
handleErrors();
}
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
return plaintext_len;
}
void write_to_file(FILE* fd , unsigned char * hash , int size){
for(int i = 0; i < size; ++i){
fprintf(fd , "%02x" , hash[i]);
}
}
void encrypt_and_write(unsigned char *plaintext, int plaintext_len, unsigned char *key,
unsigned char *iv, char* mode , FILE *fd){
unsigned char buffer[2 * BUFFER_SIZE];
int size = 0;
size = encrypt(plaintext , plaintext_len , key , iv , buffer , mode);
write_to_file(fd , buffer , size);
}
int main(int argc , char** argv){
FILE *plain_text , *crypto_text , *dictionary;
unsigned char plain_text_buffer[BUFFER_SIZE];
unsigned char crypto_text_buffer[2 * BUFFER_SIZE];
unsigned int plain_text_size = 0;
unsigned int crypto_text_size = 0;
unsigned char iv[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
unsigned char key[] = "\x73\x65\x63\x69\x6E\x66\x6F\x20\x20\x20\x20\x20\x20\x20\x20\x20";
memset(plain_text_buffer, 0, sizeof(plain_text_buffer));
memset(crypto_text_buffer, 0, sizeof(crypto_text_buffer));
if(argc < 4){
printf("Wrong number of arguments!\n");
return 0;
}
if(strcmp(argv[3] , "OFB") != 0 && strcmp(argv[3] , "ECB") != 0){
printf("Use OFB or ECB mode only!\n");
return 0;
}
plain_text = fopen(argv[1] , "r");
crypto_text = fopen(argv[2] , "r");
dictionary = fopen("word_dict.txt" , "r");
char c;
while((c = fgetc(plain_text)) != EOF){
plain_text_buffer[plain_text_size] = c;
++plain_text_size;
}
// encrypt_and_write(plain_text_buffer , plain_text_size , key , iv , argv[3] , crypto_text);
unsigned int hexa = 0;
while(fscanf(crypto_text , "%02x" , &hexa) != EOF){
crypto_text_buffer[crypto_text_size] = hexa;
++crypto_text_size;
}
char * line = NULL;
int attempts = 0;
int len = 0;
unsigned char decrypt_buffer[BUFFER_SIZE];
size_t size = 0;
while(getline(&line , &size , dictionary) != -1){
set_padding(key);
for(int i = 0; i < strlen(line); ++i){
if(line[i] != '\n' && line[i] != '\0'){
key[i] = line[i];
}
}
key[KEY_SIZE] = '\0';
decrypt(crypto_text_buffer , crypto_text_size , key , iv , decrypt_buffer , argv[3]);
if(failed == 0){
if(strcmp(plain_text_buffer , decrypt_buffer) == 0){
printf("%i " , attempts);
print_hex(key);
printf(" %s" , line);
break;
}
else
++attempts;
}
else
++attempts;
}
return 0;
}
I've tried to figure out what the problem is but no luck. It works even for DES in OFB mode, but with ECB and CBC it fails again.
The word_dict.txt is just a file used to brute force the key.
You're using EVP_EncryptInit_ex for your decryption routines; you should be using EVP_DecryptInit_ex. OFB is a stream cipher, which means that the encryption / decryption routines are very similar. This is not the case for ECB / CBC because they require padding / unpadding routines.
Note that you should not pad / unpad a key. If you are using a password you should be using a password hash, also called a Password Based Key Derivation Function. Support for PBKDF2 is build into OpenSSL.

How to convert AES_encrypt in counter mode to EVP interfaces?

I'm in the process of coding some stuff around common-encryption which require AES Ctr 128. So I'm digging a bit with crypto stuff.
Currently I test a code (find here) which work (encrypt/decrypt a file):
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
struct ctr_state {
unsigned char ivec[AES_BLOCK_SIZE];
unsigned int num;
unsigned char ecount[AES_BLOCK_SIZE];
};
void print_hex(unsigned char *c) {
for(int i = 0; i < 16; i++) {
printf("%02X.", c[i]);
}
printf("\n");
}
void init_ctr(struct ctr_state *state, const unsigned char iv[16]) {
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
}
void fencrypt(char* read, char* write, const unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
AES_KEY key;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
RAND_bytes(iv, AES_BLOCK_SIZE);
print_hex(iv);
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
AES_set_encrypt_key(enc_key, 128, &key);
init_ctr(&state, iv);
fwrite(state.ivec, 1, AES_BLOCK_SIZE, writeFile);
print_hex(state.ivec);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
print_hex(state.ivec);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
void fdecrypt(char* read, char* write, const unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
AES_KEY key;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
readFile=fopen(read,"rb");
writeFile=fopen(write,"wb");
fread(iv, 1, AES_BLOCK_SIZE, readFile);
AES_set_encrypt_key(enc_key, 128, &key);
init_ctr(&state, iv);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
print_hex(state.ivec);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
int main(int argc, char *argv[]) {
char* secret = "supersecret";
fencrypt("encme.txt", "enced.enc", (const unsigned char*)secret);
fdecrypt("enced.enc", "unenced.txt", (const unsigned char*)secret);
}
This work well. But It seems that the standard is to to use EVP functions now with openssl. So I try to adapt my code but something is clearly wrong with my implementations. I don't understand on how properly update/increment the IV vector.
Here my new code with EVP (work but not increment/counter):
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct ctr_state {
EVP_CIPHER_CTX* cipher;
int num;
};
void print_hex(unsigned char *c) {
for(int i = 0; i < 16; i++) {
printf("%02X.", c[i]);
}
printf("\n");
}
void init_ctr(struct ctr_state *state, unsigned char iv[16], unsigned char* key) {
state->num = 0;
state->cipher = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ctr(), NULL, key, iv);
}
void fencrypt(char* read, char* write, unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
RAND_bytes(iv, AES_BLOCK_SIZE);
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
fwrite(iv, 1, AES_BLOCK_SIZE, writeFile);
init_ctr(&state, iv, enc_key);
print_hex(iv);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
EVP_EncryptUpdate(state.cipher, outdata, &state.num, indata, bytes_read);
EVP_EncryptUpdate(state.cipher, outdata, &state.num, indata, bytes_read);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
void fdecrypt(char* read, char* write, unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
fread(iv, 1, AES_BLOCK_SIZE, readFile);
init_ctr(&state, iv, enc_key);
print_hex(iv);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
EVP_EncryptUpdate(state.cipher, outdata, &state.num, indata, bytes_read);
printf("Pass %d ",state.num);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
int main(int argc, char *argv[]) {
char* secret = "supersecret";
fencrypt("encme.txt", "enced.enc", (unsigned char*)secret);
fdecrypt("enced.enc", "unenced.txt", (unsigned char*)secret);
}
Any help appreciated. Thank you.
OK I think I got it.
I will copy here my two program example:
AES_CTR_128 (without EVP) :
#include <openssl/aes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct ctr_state {
unsigned int num;
unsigned char ivec[AES_BLOCK_SIZE];
unsigned char ecount[AES_BLOCK_SIZE];
};
void init_ctr(struct ctr_state *state, const unsigned char iv[16]) {
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
}
void fencrypt(char* read, char* write, const unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
AES_KEY key;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
struct ctr_state state;
unsigned char *iv = (unsigned char *)"0123456789012345";
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
fwrite(iv, 1, AES_BLOCK_SIZE, writeFile);
AES_set_encrypt_key(enc_key, 128, &key);
init_ctr(&state, iv);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
void fdecrypt(char* read, char* write, const unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
AES_KEY key;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
readFile=fopen(read,"rb");
writeFile=fopen(write,"wb");
fread(iv, 1, AES_BLOCK_SIZE, readFile);
AES_set_encrypt_key(enc_key, 128, &key);
init_ctr(&state, iv);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
int main(int argc, char *argv[]) {
unsigned char *secret = (unsigned char *)"0123456789012345";
fencrypt("encme.txt", "enced.enc", secret);
fdecrypt("enced.enc", "unenced.txt", secret);
}
Everything classic as in another example.
The IV(or nonce) is constant to make the debugging easier (don't do that).
And below my code with EVP:
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct ctr_state {
EVP_CIPHER_CTX* cipher;
unsigned int num;
unsigned char ivec[AES_BLOCK_SIZE];
unsigned char ecount[AES_BLOCK_SIZE];
};
static void AES_ctr128_inc(unsigned char *counter) {
unsigned char* cur_pos;
for (cur_pos = counter + 15; cur_pos >= counter; cur_pos--) {
(*cur_pos)++;
if (*cur_pos != 0) {
break;
}
}
}
void AES_ctr128_EVPencrypt(EVP_CIPHER_CTX* cipher, const unsigned char *in, unsigned char *out,
const unsigned long length,
unsigned char counter[AES_BLOCK_SIZE],
unsigned char ecount_buf[AES_BLOCK_SIZE],
unsigned int *num) {
int nb;
unsigned int n;
unsigned long l=length;
n = *num;
while (l--) {
if (n == 0) {
EVP_EncryptUpdate(cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE);
AES_ctr128_inc(counter);
}
*(out++) = *(in++) ^ ecount_buf[n];
n = (n+1) % AES_BLOCK_SIZE;
}
*num=n;
}
void init_ctr(struct ctr_state *state, unsigned char iv[16], unsigned char* key) {
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
state->cipher = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ecb(), NULL, key, NULL);
}
void fencrypt(char* read, char* write, unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
struct ctr_state state;
unsigned char *iv = (unsigned char *)"0123456789012345";
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
fwrite(iv, 1, AES_BLOCK_SIZE, writeFile);
init_ctr(&state, iv, enc_key);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
void fdecrypt(char* read, char* write, unsigned char* enc_key) {
FILE *readFile;
FILE *writeFile;
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
readFile = fopen(read,"rb");
writeFile = fopen(write,"wb");
fread(iv, 1, AES_BLOCK_SIZE, readFile);
init_ctr(&state, iv, enc_key);
while(1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num);
fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE) {
break;
}
}
fclose(writeFile);
fclose(readFile);
}
int main(int argc, char *argv[]) {
unsigned char *secret = (unsigned char *)"0123456789012345";
fencrypt("encme.txt", "enced.enc", (unsigned char*)secret);
fdecrypt("enced.enc", "unenced.txt", secret);
}
So I basically copy the AES_ctr_encrypt function to use EVP, and adapt it.
It work for me, as I can use both implementation to encrypt/decrypt the same program.
Comments are welcome.
Questions remain that what do the aes_ctr_128 in EVP ? and how to use it?
I think I have reinvented the wheel.
In {EVP_aes_N_ctr()} the counter state is kept in {ctx->iv}, treated as an 128 big-endian integer, counting the crypto blocks, starting from the IV given to the Init function. This counter tells the number of the next crypto block to be encrypted/decrypted. This works with random access, i.e. ctx->iv can be computed manually before an *Update call, if on crypto block boundary. As soon as at least one byte is returned from a cipher op, the counter is updated to hold the next crypto block to be processed. If byte-level random access is wanted, there is also {ctx->num} that holds the byte offset into the current crypto block. For this to work, the block must have been started on, i.e. a cipher op where {ctx->num} was equal to zero must have been issued, in order to correctly setup the internal state.
Below is a stupid, but still working example of how CTR mode can be used for random access decryption.
#include <openssl/evp.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
static void add_be_128(uint8_t *ctr, ptrdiff_t delta) {
for (int n = 15; delta != 0 && n >= 0; n--) {
// The risk of overflow can safely be neglected.
ptrdiff_t nval = ctr[n] + delta;
ctr[n] = nval & 0xff;
delta = nval >> 8;
}
}
int main() {
uint8_t in[] =
"0123456789abcdeffedcba9876543210fedcba98765432100123456789abcdef";
uint8_t out[64];
int sz;
uint8_t cmp[33];
uint8_t key[] = {
0xde, 0xad, 0xbe, 0xef, 0xc0, 0x01, 0xd0, 0x0d,
0xde, 0xad, 0xbe, 0xef, 0xc0, 0x01, 0xd0, 0x0d
};
uint8_t iv[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv);
EVP_CIPHER_CTX_set_padding(ctx, 0);
EVP_EncryptUpdate(ctx, out, &sz, in, sizeof in);
EVP_EncryptFinal_ex(ctx, out + sz, &sz);
EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv);
EVP_CIPHER_CTX_set_padding(ctx, 0);
add_be_128(ctx->iv, 1);
EVP_DecryptUpdate(ctx, cmp, &sz, out + 16, 32);
EVP_DecryptFinal_ex(ctx, cmp + sz, &sz);
cmp[32] = 0;
printf("%s %d\n", cmp, strncmp(in + 16, cmp, 32));
EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv);
EVP_CIPHER_CTX_set_padding(ctx, 0);
int new = ctx->num + 8;
add_be_128(ctx->iv, (new - 1) / 16);
ctx->num = 0;
EVP_DecryptUpdate(ctx, cmp, &sz, out, 1);
ctx->num = new;
EVP_DecryptUpdate(ctx, cmp, &sz, out + 8, 32);
EVP_DecryptFinal_ex(ctx, cmp + sz, &sz);
cmp[32] = 0;
printf("%s %d\n", cmp, strncmp(in + 8, cmp, 32));
}
Here is my solution when I had to use the EVP openssl functions. Should work with every available cipher
/*! #file s70357.c
* IS_Beleg by Markus Klemm
* */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <openssl/evp.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*!
* #param cipher_text Buffer, at least (plain_len + cipher_block_size - 1) bytes big,
* where the encrypted data will be stored.
* #param cipher_text_len Actual length of encrypted data in cipher_text in bytes
*/
bool mk_evp_encrypt(const unsigned char *plain_text,
const int plain_text_len,
unsigned char *cipher_text,
int *cipher_text_len,
const EVP_CIPHER *cipher,
unsigned char *key,
unsigned char *iv) {
EVP_CIPHER_CTX *context = EVP_CIPHER_CTX_new();
if (!context) {
return false;
}
if (!EVP_EncryptInit_ex(context, cipher, NULL, key, iv)) {
EVP_CIPHER_CTX_free(context);
return false;
}
*cipher_text_len = 0;
if (!EVP_EncryptUpdate(context, cipher_text, cipher_text_len, plain_text, plain_text_len)) {
EVP_CIPHER_CTX_free(context);
return false;
}
int final_block_len = 0;
if (!EVP_EncryptFinal_ex(context, cipher_text + *cipher_text_len, &final_block_len)) {
EVP_CIPHER_CTX_free(context);
return false;
}
*cipher_text_len += final_block_len;
EVP_CIPHER_CTX_free(context);
return true;
}
/*! #param plain_text Buffer that must at least be cipher_text_len + cipher_block_size big
* */
bool mk_evp_decrypt(const unsigned char *cipher_text,
const int cipher_text_len,
unsigned char *plain_text,
int *plain_text_len,
const EVP_CIPHER *cipher,
unsigned char *key,
unsigned char *iv) {
EVP_CIPHER_CTX *context = EVP_CIPHER_CTX_new();
if (!context) {
return false;
}
if (!EVP_DecryptInit_ex(context, cipher, NULL, key, iv)) {
EVP_CIPHER_CTX_free(context);
return false;
}
*plain_text_len = 0;
if (!EVP_DecryptUpdate(context, plain_text, plain_text_len, cipher_text, cipher_text_len)) {
EVP_CIPHER_CTX_free(context);
return false;
}
int final_block_size = 0;
if (!EVP_DecryptFinal_ex(context, plain_text + *plain_text_len, &final_block_size)) {
EVP_CIPHER_CTX_free(context);
return false;
}
*plain_text_len += final_block_size;
EVP_CIPHER_CTX_free(context);
return true;
}
struct file_memory_map_meta {
int file_desc;
struct stat file_info;
};
/*! #param digest Must be big engough to hold at least EVP_MAX_MD_SIZE
* */
bool mk_evp_digest(const unsigned char *text,
const size_t text_len,
unsigned char *digest,
unsigned int *digest_len,
const EVP_MD *digest_type) {
EVP_MD_CTX *context = EVP_MD_CTX_create();
if (!context) {
return false;
}
if (!EVP_DigestInit_ex(context, digest_type, NULL)) {
return false;
}
if (!EVP_DigestUpdate(context, text, text_len)) {
return false;
}
if (!EVP_DigestFinal_ex(context, digest, digest_len)) {
return false;
}
EVP_MD_CTX_destroy(context);
return true;
}
void open_file_memory_mapped_read(char *file_path,
void **file_memory,
struct file_memory_map_meta *meta) {
meta->file_desc = open(file_path, O_RDONLY);
if (meta->file_desc == -1) {
fprintf(stderr, "Can't open read file %s", file_path);
perror(" ");
exit(EXIT_FAILURE);
}
if (stat(file_path, &meta->file_info) != 0) {
perror("Can't get source file infos");
exit(EXIT_FAILURE);
}
void *source_mem = mmap(NULL, meta->file_info.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, meta->file_desc, 0);
if (source_mem == MAP_FAILED) {
perror("Mapping read file failed");
exit(EXIT_FAILURE);
}
*file_memory = source_mem;
}
void open_file_memory_mapped_write(char *file_path,
void **file_memory,
struct file_memory_map_meta *meta,
size_t size) {
meta->file_desc = open(file_path, O_TRUNC | O_CREAT | O_RDWR, 744);
if (meta->file_desc == -1) {
fprintf(stderr, "Can't open write file %s", file_path);
perror(" ");
exit(EXIT_FAILURE);
}
if (stat(file_path, &meta->file_info) != 0) {
perror("Can't get source file infos");
exit(EXIT_FAILURE);
}
void *source_mem = mmap(NULL, size, PROT_WRITE, MAP_FILE | MAP_SHARED, meta->file_desc, 0);
if (source_mem == MAP_FAILED) {
perror("Mapping write file failed");
exit(EXIT_FAILURE);
}
*file_memory = source_mem;
}
void close_file_memory_mapped(void **file_memory, struct file_memory_map_meta *meta) {
munmap(*file_memory, meta->file_info.st_size);
close(meta->file_desc);
}
void create_key_iv_from_file(char *key_iv_path, unsigned char **key, unsigned char **iv, const EVP_CIPHER *cipher) {
FILE *f = fopen(key_iv_path, "rb");
if (!f) {
fprintf(stderr, "Could not open file %s", key_iv_path);
perror(" ");
exit(EXIT_FAILURE);
}
*key = malloc(EVP_CIPHER_key_length(cipher));
if (*key == NULL) {
perror("Could not alloc for key");
exit(EXIT_FAILURE);
}
if (EVP_CIPHER_iv_length(cipher) != 0) {
*iv = malloc(EVP_CIPHER_iv_length(cipher));
if (*iv == NULL) {
perror("Could not alloc for iv");
exit(EXIT_FAILURE);
}
} else {
*iv = NULL;
}
if (fread(*key, 1, EVP_CIPHER_key_length(cipher), f) != EVP_CIPHER_key_length(cipher)) {
fprintf(stderr, "Error while reading key\n");
exit(EXIT_FAILURE);
}
if (*iv != NULL) {
if (fread(*iv, 1, EVP_CIPHER_iv_length(cipher), f) != EVP_CIPHER_iv_length(cipher)) {
fprintf(stderr, "Error while reading iv\n");
exit(EXIT_FAILURE);
}
}
fclose(f);
}
unsigned char *permutate_key(unsigned char *key, unsigned corrupt_byte_pos) {
key[corrupt_byte_pos] = key[corrupt_byte_pos] + 1; //although in DES, we could reduce to half because of parity bit
return key;
}
bool is_pdf(unsigned char *data) {
unsigned char pdf_start[] = {"%PDF"};
unsigned char pdf_end[] = {"%%EOF"};
return !memcmp(pdf_start, data, sizeof(pdf_start) - 1); //TODO check pdf_end, but cutaway the padding
}
void decrypt_mode(char *cipher_text_path,
char *plain_text_path,
char *key_iv,
unsigned corrupt_byte_pos,
char *cipher) {
OpenSSL_add_all_algorithms();//Needed for older versions to use EVP_get_cipherbyname
const EVP_CIPHER *evp_cipher = EVP_get_cipherbyname(cipher);
EVP_cleanup(); //cleanup for OpenSSL_add_all_algorithms
if (evp_cipher == NULL) {
fprintf(stderr, "Cipher %s not found\n", cipher);
exit(EXIT_FAILURE);
}
void *cipher_text_mem;
struct file_memory_map_meta cipher_text_meta;
open_file_memory_mapped_read(cipher_text_path,
&cipher_text_mem, &cipher_text_meta);
void *plain_text_mem;
struct file_memory_map_meta plain_text_meta;
open_file_memory_mapped_write(plain_text_path,
&plain_text_mem, &plain_text_meta, cipher_text_meta.file_info.st_size);
if (chmod(plain_text_path, cipher_text_meta.file_info.st_mode) != 0) {
perror("Can't copy file permissions");
}
if (lseek(plain_text_meta.file_desc, cipher_text_meta.file_info.st_size - 1 + EVP_CIPHER_block_size(evp_cipher),
SEEK_SET) == -1) {
perror("Can't seek to new end of destination file");
}
unsigned char dummy = 0;
if (write(plain_text_meta.file_desc, &dummy, 1) == -1) {
perror("Couldn't write dummy byte");
}
unsigned char *key;
unsigned char *iv;
create_key_iv_from_file(key_iv, &key, &iv, evp_cipher);
//now lets try the keys
const unsigned key_len = EVP_CIPHER_key_length(evp_cipher);
int plain_len = 0;
bool decrypt_return = mk_evp_decrypt(cipher_text_mem,
cipher_text_meta.file_info.st_size, plain_text_mem, &plain_len, evp_cipher,
key, iv);
while (!decrypt_return || !is_pdf(plain_text_mem)) {
fprintf(stderr, "Key 0x");
for (unsigned i = 0; i < key_len; ++i) {
fprintf(stderr, "%02X", key[i]);
}
fprintf(stderr, " didn't catch it trying the next one\n");
plain_len = 0;
decrypt_return = mk_evp_decrypt(cipher_text_mem,
cipher_text_meta.file_info.st_size, plain_text_mem, &plain_len, evp_cipher,
permutate_key(key, corrupt_byte_pos), iv);
}
if (ftruncate(plain_text_meta.file_desc, plain_len) != 0) {
perror("Trimming of final plain text failed");
exit(EXIT_FAILURE);
}
free(key);
free(iv);
close_file_memory_mapped(&plain_text_mem, &plain_text_meta);
close_file_memory_mapped(&cipher_text_mem, &cipher_text_meta);
}
void hash_mode(char *text_path,
char *opt_hash_path,
char *digest_name) {
OpenSSL_add_all_digests();//Needed for older versions to use EVP_get_cipherbyname
const EVP_MD *digest = EVP_get_digestbyname(digest_name);
EVP_cleanup(); //cleanup for OpenSSL_add_all_algorithms
if (digest == NULL) {
fprintf(stderr, "Digest %s not found\n", digest_name);
exit(EXIT_FAILURE);
}
void *text_mem;
struct file_memory_map_meta text_meta;
open_file_memory_mapped_read(text_path,
&text_mem, &text_meta);
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned hash_len = 0;
mk_evp_digest(text_mem, text_meta.file_info.st_size, hash, &hash_len, digest);
if (strlen(opt_hash_path) == 0) {
for (unsigned i = 0; i < hash_len; ++i) {
printf("%02X", hash[i]);
}
printf("\n");
} else {
FILE *out_file = fopen(opt_hash_path, "wb");
if (!out_file) {
perror("Could not open output file");
exit(EXIT_FAILURE);
}
if (fwrite(hash, hash_len, 1, out_file) != 1) {
fprintf(stderr, "Could not write the hash correctly\n");
exit(EXIT_FAILURE);
}
fclose(out_file);
}
close_file_memory_mapped(&text_mem, &text_meta);
}
void encrypt_mode(char *plain_text_path,
char *cipher_text_path,
char *key_iv,
char *cipher) {
OpenSSL_add_all_algorithms();//Needed for older versions to use EVP_get_cipherbyname
const EVP_CIPHER *evp_cipher = EVP_get_cipherbyname(cipher);
EVP_cleanup(); //cleanup for OpenSSL_add_all_algorithms
if (evp_cipher == NULL) {
fprintf(stderr, "Cipher %s not found\n", cipher);
exit(EXIT_FAILURE);
}
void *plain_text_mem;
struct file_memory_map_meta plain_text_meta;
open_file_memory_mapped_read(plain_text_path,
&plain_text_mem, &plain_text_meta);
void *cipher_text_mem;
struct file_memory_map_meta cipher_text_meta;
open_file_memory_mapped_write(cipher_text_path,
&cipher_text_mem, &cipher_text_meta, plain_text_meta.file_info.st_size);
if (chmod(cipher_text_path, plain_text_meta.file_info.st_mode) != 0) {
perror("Can't copy file permissions");
}
if (lseek(cipher_text_meta.file_desc, plain_text_meta.file_info.st_size + EVP_CIPHER_block_size(evp_cipher),
SEEK_SET) == -1) {
perror("Can't seek to new end of destination file");
}
unsigned char dummy = 0;
if (write(cipher_text_meta.file_desc, &dummy, 1) == -1) {
perror("Couldn't write dummy byte");
}
unsigned char *key;
unsigned char *iv;
create_key_iv_from_file(key_iv, &key, &iv, evp_cipher);
int cipher_text_len = 0;
if (!mk_evp_encrypt(plain_text_mem, plain_text_meta.file_info.st_size, cipher_text_mem, &cipher_text_len,
evp_cipher, key, iv)) {
fprintf(stderr, "Encryption went wrong\n");
exit(EXIT_FAILURE);
}
if (ftruncate(cipher_text_meta.file_desc, cipher_text_len) != 0) {
perror("Trimming of final plain text failed");
exit(EXIT_FAILURE);
}
free(key);
free(iv);
close_file_memory_mapped(&plain_text_mem, &plain_text_meta);
close_file_memory_mapped(&cipher_text_mem, &cipher_text_meta);
}
int main(int argc, char *argv[]) {
enum mode {
none, decrypt, encrypt, hash
} mode = none;
char in_path[512];
memset(in_path, '\0', sizeof(in_path));
char out_path[512];
memset(out_path, '\0', sizeof(out_path));
char key_path[512];
memset(key_path, '\0', sizeof(key_path));
char cipher[512];
memset(cipher, '\0', sizeof(cipher));
unsigned corrupt_byte_pos = -1;
int flag;
while ((flag = getopt(argc, argv, "deh i:o:c:k:b:")) != -1) {
switch (flag) {
case 'e':
mode = encrypt;
break;
case 'd':
mode = decrypt;
break;
case 'h':
mode = hash;
break;
case 'i':
strncpy(in_path, optarg, sizeof(in_path) - 1);
break;
case 'o':
strncpy(out_path, optarg, sizeof(out_path) - 1);
break;
case 'k':
strncpy(key_path, optarg, sizeof(key_path) - 1);
break;
case 'c':
strncpy(cipher, optarg, sizeof(cipher) - 1);
break;
case 'b':
errno = 0;
corrupt_byte_pos = strtol(optarg, NULL, 10);
if (errno != 0) {
perror("Could not read byte position, assuming key is ok");
corrupt_byte_pos = -1;
}
break;
default:
return EXIT_FAILURE;
break;
}
}
switch (mode) {
case decrypt:
decrypt_mode(in_path, out_path, key_path, corrupt_byte_pos, cipher);
break;
case encrypt:
encrypt_mode(in_path, out_path, key_path, cipher);
break;
case hash:
hash_mode(in_path, out_path, cipher);
break;
case none:
default:
fprintf(stderr, "No mode was specified\n");
printf("Usage %s -<MODE> -<PARAMETERS>\n", argv[0]);
printf("\t<MODE>:\n");
printf("\t\t e Encrypt aka Aufgabe 3\n");
printf("\t\t d Decrypt aka Aufgabe 1\n");
printf("\t\t h Hash aka Aufgabe 2\n");
printf("\t<PARAMETERS>: \n");
printf("\t\t i Input file path\n");
printf("\t\t o Output file path, optional for hash mode\n");
printf("\t\t k Key/IV file path, optional for hash mode\n");
printf("\t\t c EVP Cipher/Digest to be used\n");
printf("\t\t b Corrupt byte position, counted from 0, optional for hash mode\n");
exit(EXIT_FAILURE);
break;
}
return EXIT_SUCCESS;
}

AES CTR encryption and decryption

So I have this code, which basically, encrypts two plain text messages and then try to decrypt them, and print. The problem is that first message is recovered fine but the second is garbage. I downloaded this code from this tutorial and then modified it to work with strings instead of files as I need it for sending encrypted text over sockets. So length of the plaintext won't be known to other endpoint, is there a way to find the length or will I have to send the length of plaintext along with the cipher somehow?
Now, I think there is a problem in the break condition of decrypt.
Also, is the main() code right conceptually: encrypt messages with updating state and then reset state and decrypt messages with updating state?
And is there a way to find out the actual length of cipher text (not the buffer)?
This is just a dummy program that I was trying out to understand how AES CTR will work.
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
// Code example uses partail code from: http://stackoverflow.com/questions/3141860/aes-ctr-256-encryption-mode-of-operation-on-openssl
// Mostly in the ctr_ state, and init_ctr functions.
struct ctr_state
{
unsigned char ivec[AES_BLOCK_SIZE];
unsigned int num;
unsigned char ecount[AES_BLOCK_SIZE];
};
int init_ctr(struct ctr_state *state, const unsigned char iv[16])
{
/* aes_ctr128_encrypt requires 'num' and 'ecount' set to zero on the
* first call. */
state->num = 0;
memset(state->ecount, 0, AES_BLOCK_SIZE);
/* Initialise counter in 'ivec' to 0 */
memset(state->ivec + 8, 0, 8);
/* Copy IV into 'ivec' */
memcpy(state->ivec, iv, 8);
}
void fencrypt(char* text, char* cipher, const unsigned char* enc_key, struct ctr_state* state)
{
AES_KEY key;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
int offset=0;
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set encryption key.");
exit(1);
}
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
printf("while going\n");
memcpy(indata, text+offset, AES_BLOCK_SIZE);
AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);
memcpy(cipher+offset, outdata, AES_BLOCK_SIZE);
offset=offset+AES_BLOCK_SIZE;
if (offset > strlen(text))
{
break;
}
}
}
void fdecrypt(char* cipher, char* text, const unsigned char* enc_key, struct ctr_state* state)
{
AES_KEY key;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
int offset=0;
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set decryption key.");
exit(1);
}
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
memcpy(indata, cipher+offset, AES_BLOCK_SIZE);
//printf("%i\n", state.num);
AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);
memcpy(text+offset, outdata, AES_BLOCK_SIZE);
offset=offset+AES_BLOCK_SIZE;
if (offset > strlen(cipher))
{
break;
}
}
}
int main(int argc, char *argv[])
{
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
char* plain="quick brown fox jumped over the lazy dog what ";
char* plain2="a dog he is idiot who is the genius ";
char cipher[128];
char cipher2[128];
char recovered[128];
char recovered2[128];
const unsigned char* enc_key="123456789abcdef0";
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
init_ctr(&state, iv); //Counter call
printf("Plain text length:%lu\n",strlen(plain));
// BIO_dump_fp(stdout, plain, strlen(plain));
// printf("Plain text:%s\n",plain);
fencrypt(plain, cipher,enc_key,&state);
fencrypt(plain2, cipher2,enc_key,&state);
// cipher[strlen(plain)]='\0';
// BIO_dump_fp(stdout, cipher, strlen(plain));
init_ctr(&state, iv); //Counter call
fdecrypt(cipher,recovered,enc_key,&state);
fdecrypt(cipher2,recovered2,enc_key,&state);
// printf("Cipher text length:%lu\n",strlen(cipher));
printf("Recovered text:%s\n",recovered);
printf("Recovered text:%s\n",recovered2);
return 0;
}
CTR mode doesn't need separate encrypt and decrypt method. Encryption key can be set once. OpenSSL's AES_ctr128_encrypt takes care of most of the work, so the code can be simplified.
Also we really need vector test. Here we are just testing with random text "quick brown fox...", we get back the same text, but there is NO guarantee that 'cipher' was correct, and wether or not the encryption is AES quality. I'll add a quick vector test later if there is time.
void init_ctr(struct ctr_state *state, const unsigned char iv[16])
{
state->num = 0;
memset(state->ecount, 0, 16);
memcpy(state->ivec, iv, 16);
}
void crypt_message(const u8* src, u8* dst, unsigned int src_len, const AES_KEY* key, const u8* iv)
{
struct ctr_state state;
init_ctr(&state, iv);
AES_ctr128_encrypt(src, dst, src_len, key, state.ivec, state.ecount, &state.num);
}
int main()
{
int len;
char source[128];
char cipher[128];
char recovered[128];
unsigned char iv[AES_BLOCK_SIZE];
const unsigned char* enc_key = (const unsigned char*)"123456789abcdef0";
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
AES_KEY key;
AES_set_encrypt_key(enc_key, 128, &key);
strcpy(source, "quick brown fox jumped over the lazy dog what.");
len = strlen(source);
memset(recovered, 0, sizeof(recovered));
crypt_message((const u8*)source, (u8*)cipher, len, &key, iv);
crypt_message((const u8*)cipher, (u8*)recovered, len, &key, iv);
printf("Recovered text:%s\n", recovered);
strcpy(source, "a dog he is idiot who is the genius.");
len = strlen(source);
memset(recovered, 0, sizeof(recovered));
crypt_message((const u8*)source, (u8*)cipher, len, &key, iv);
crypt_message((const u8*)cipher, (u8*)recovered, len, &key, iv);
printf("Recovered text:%s\n", recovered);
return 0;
}
To encrypt/decrypt files, or send/receive:
void crypt_file(const u8* src_file, const u8* dst_file, const AES_KEY* key, const u8* iv)
{
struct ctr_state state;
init_ctr(&state, iv);
const int buffer_size = 512; //not less than 16
unsigned char buffer_in[buffer_size];
unsigned char buffer_out[buffer_size];
int bytes_read;
//open files and/or socket
//file/message loop
{
//read source, obtain buffer_in and bytes_read
AES_ctr128_encrypt(buffer_in, buffer_out, bytes_read, key, state.ivec, state.ecount, &state.num);
//write buffer_out/bytes_read to destination
}
//close handles
}
In your code, fdecrypt() contains strlen(cipher). However cipher is pure binary data, strlen doesn't work with it. You have to supply the length manually. I added len parameter to fdecrypt. In main I use strlen(plaintext) for simplicity, though it should be the true length of cipher data. Changes are denoted by ##change
void fdecrypt(unsigned int len, char* cipher, char* text, const unsigned char* enc_key, struct ctr_state* state)
{
AES_KEY key;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
int offset=0;
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set decryption key.");
exit(1);
}
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
memcpy(indata, cipher+offset, AES_BLOCK_SIZE);
//printf("%i\n", state.num);
AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);
memcpy(text+offset, outdata, AES_BLOCK_SIZE);
offset=offset+AES_BLOCK_SIZE;
//if (offset > strlen(cipher))##changed
if (offset > len)
{
break;
}
}
}
int main(int argc, char *argv[])
{
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
char* plain="quick brown fox jumped over the lazy dog what ";
char* plain2="a dog he is idiot who is the genius ";
char cipher[128];
char cipher2[128];
char recovered[128];
char recovered2[128];
const unsigned char* enc_key=(const unsigned char*)"123456789abcdef0";
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
init_ctr(&state, iv); //Counter call
printf("Plain text length:%lu\n",strlen(plain));
// BIO_dump_fp(stdout, plain, strlen(plain));
// printf("Plain text:%s\n",plain);
fencrypt(plain, cipher,enc_key,&state);
fencrypt(plain2, cipher2,enc_key,&state);
// cipher[strlen(plain)]='\0';
// BIO_dump_fp(stdout, cipher, strlen(plain));
init_ctr(&state, iv); //Counter call
fdecrypt(strlen(plain), cipher,recovered,enc_key,&state);//##changed
fdecrypt(strlen(plain2), cipher2,recovered2,enc_key,&state);//##changed
// printf("Cipher text length:%lu\n",strlen(cipher));
printf("Recovered text:%s\n",recovered);
printf("Recovered text:%s\n",recovered2);
return 0;
}

Issues with encrypting a file using openssl evp api(aes256cbc)

I am trying to read a file(.txt) in this case and encrypting/decrypting it with AES256CBC using EVP api of openssl.(read(plain.txt)->create(encrypt.txt)->create(decrypt.txt))
# include <stdio.h>
# include <stdlib.h>
# include <openssl/evp.h>
# include <openssl/aes.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <assert.h>
# include <error.h>
# include "debug.h"
# define SIZE 32
char buf[SIZE];
int aes_init(unsigned char* pwd, unsigned int pwd_len, unsigned char * salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
{
int i, rounds =5; /* rounds */
unsigned char key[32], iv[32];
i = EVP_BytesToKey(EVP_aes_256_cbc(),EVP_sha1(),salt,pwd,pwd_len,rounds,key,iv);
if(i != 32)
{
printf("\n Error,Incorrect key size generated:%d:\n",i);
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
return 0;
}
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
*len = ci_len + flen;
return cipher_text;
}
unsigned char* aes_decrypt(EVP_CIPHER_CTX *e, unsigned char * c_text, unsigned int * len)
{
int pi_len = (*len);
int flen = 0;
unsigned char * plain_text = malloc(pi_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plain_text, &pi_len, c_text, *len);
EVP_DecryptFinal_ex(e, plain_text+pi_len, &flen);
(*len) = pi_len + flen;
return plain_text;
}
int main(int argc,char **argv)
{
if(argc != 2)
{
perror("\n Error:\nCorrect Usage: Enter Password to be used");
exit(-1);
}
EVP_CIPHER_CTX en,de; /* The EVP structure which keeps track of all crypt operations see evp.h for details */
int in, out, fd, dec,i =0; /* fd for input and output files and random dev*/
unsigned int pwd_len = strlen((const char *)argv[1]); /* Length of the pwd supplied by the user */
unsigned char *pwd =(unsigned char*) argv[1]; /* Pointer to the pwd supplied by the user */
unsigned int rd= 0;
unsigned char salt[8];
unsigned char * encry = NULL, *decry = NULL;
i =0;
if((in = open("plain.txt",O_RDONLY)) == -1) /* Opening a plain text file for encryption */
{
perror("\n Error,Opening file for reading::");
exit(-1);
}
if((fd = open("/dev/random", O_RDONLY)) == -1)
{
perror("\n Error,Opening /dev/random::");
exit(-1);
}
else
{
if(read(fd,salt,8) == -1)
{
perror("\n Error,reading from /dev/random::");
exit(-1);
}
}
if(aes_init(pwd,pwd_len,(unsigned char*) salt,&en,&de)) /* Generating Key and IV and initializing the EVP struct */
{
perror("\n Error, Cant initialize key and IV:");
return -1;
}
if((out = open("encrypt.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("ENC%d",out);
perror("\n Error,Opening the file to be written::");
exit(-1);
}
rd =0;
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
if((write(out,encry,rd)) != rd)
{
perror("\n Error,Required encrypted bytes not written::");
exit(-1);
}
free(encry);
}
rd =0;
if((dec = open("dec22.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("dec%d",dec);
perror("\n Error,Opening the decrypting o/p file::");
exit(-1);
}
if((lseek(out,0,SEEK_SET)) != 0) perror("\n Error:setting lseek::");
for(i=0;i<SIZE;i++) buf[i] =0;
while((rd = read(out,dbuf,SIZE)) >0)
{
decry = aes_decrypt(&de,(unsigned char*) dbuf, &rd);
if((write(dec,decry,rd)) != rd)
{
perror("\n Error,Required decrypted bytes not written::");
exit(-1);
}
free(decry);
}
close(in);
close(fd);
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return 0;
}
My problem was that my when i decrypt an encrypted file i get a file which is not properly decrypted (e.g. correct stringgarbagecorrect stringgarbage ...)
abhi#ubuntu:~/mpro/EFF$ cat plain.txt
Today is tuesday
tomorrow is wednesday
then thursday and friday and saturday
finally sunday
Decrypted file
cat dec22.txt
Today is tuesdayw)Q������O-%�A�8���R��.�O���and saturday
finally sunday
What can be the reason for this. Is it reading something else also or i am making any foolish error somewhere.
EDIT: If I just encrypt an array (tried with 36char long) it correctly encrypted and decrypted without printing any garbage.
I guess i am missing(not handling) some *nix file structure details ..??
Or is there any better way to do this encryption on a file.?
Many thanks
I think your analysis is wrong. This loop is problematic:
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::\n",buf);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
dbug_p("\n EN::%s::\n",encry);
decry = aes_decrypt(&de,(unsigned char*) encry,&rd);
dbug_p("\n DE::%s::\n",decry);
free(encry);
free(decry);
}
Firstly because you print using %s which expects a zero terminator. However, the encrypted/decrypted data is not zero terminated. Instead, you should print rd characters using a loop like for (i = 0; i < rd; i++) printf("%02x "); - this is why your analysis of the problem is likely flawed.
Secondly, I assume that in your real problem, you are reading SIZE bytes at a time and sending them to aes_decrypt() separately. This will fail because EVP_DecryptFinal_ex() is getting called too early (before all the encrypted blocks were read). You have two options. Either you send the read bytes through EVP_DecryptUpdate() in each loop iteration, and call EVP_DecryptFinal() after completing the loop (and init accordingly before the loop), or you read the whole file into a buffer first, and then send it through aes_decrypt() in one go.
Or in other words, you need to send the whole data block resulting from aes_encrypt() later to aes_decrypt(). You cannot send them in different chunks, unless you split the functions up and use the EVP "update" functions on the separate chunks.
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
and,
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
You are calling EVP_EncryptFinal_ex multiple times. It is supposed to be called only once at the end.
Same is true for how you are doing decryption.
Here is a simple example from the man page on how to do encrypt.
Have a similar function for decryption and it should work.
int do_crypt(char *outfile)
{
unsigned char outbuf[1024];
int outlen, tmplen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
unsigned char iv[] = {1,2,3,4,5,6,7,8};
char intext[] = "Some Crypto Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))
{
/* Error */
return 0;
}
/* Buffer passed to EVP_EncryptFinal() must be after data just
* encrypted to avoid overwriting it.
*/
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* Error */
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
/* Need binary mode for fopen because encrypted data is
* binary data. Also cannot use strlen() on it because
* it wont be null terminated and may contain embedded
* nulls.
*/
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
the following example is reading a file as your case. See how Update (called multiple times) and Final (once at the end) routines are used.
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789";
unsigned char iv[] = "12345678";
/* Don't set key or IV because we will modify the parameters */
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);
EVP_CIPHER_CTX_set_key_length(&ctx, 10);
/* We finished modifying parameters so now we can set key and IV */
EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
for(;;)
{
inlen = fread(inbuf, 1, 1024, in);
if(inlen <= 0) break;
if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}

OpenSSL and AES

i'm working with OpenSSL lib and get very strange effect with AES encrypt/decrypt: if i'll change some byte in encrypted message and decrypt it, i will see the part of original message, that isn't suppose to be. This is source code:
#include <openssl/evp.h>
#include <string.h>
int do_crypt(void)
{
int outlen, inlen;
FILE *in, *out;
in = fopen("in.txt", "r");
out = fopen("out.txt", "w");
unsigned char key[32];
strcpy(key, "10000000000000000000000000000002");
unsigned char iv[8];
unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE];
EVP_CIPHER_CTX ctx;
const EVP_CIPHER * cipher;
EVP_CIPHER_CTX_init(&ctx);
cipher = EVP_aes_256_cfb();
EVP_EncryptInit(&ctx, cipher, key, 0);
while(1) {
inlen = fread(inbuf, 1, BUFSIZE, in);
if(inlen <= 0) break;
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) return 0;
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_EncryptFinal(&ctx, outbuf, &outlen)) return 0;
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
int do_decrypt(char *infile)
{
int outlen, inlen;
FILE *in, *out;
in = fopen("out.txt", "r");
out = fopen("out2.txt", "w");
unsigned char key[32];
strcpy(key, "10000000000000000000000000000002");
unsigned char iv[8];
unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE];
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit(&ctx, EVP_aes_256_cfb(), key, 0);
while(1) {
inlen = fread(inbuf, 1, BUFSIZE, in);
if(inlen <= 0) break;
if(!EVP_DecryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) return 0;
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_DecryptFinal(&ctx, outbuf, &outlen)) return 0;
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
main(int argc, char **argv){
if(atoi(argv[1]) == 1)
do_crypt(0);
if(atoi(argv[1]) == 2)
do_decrypt(0);
}
What could be wrong?
What's wrong is your expectation that the whole message becomes unreadable because a single byte was changed.
Which parts of the message become unreadable depends on the chosen encryption mode. You're using CFB. This means if you change a single byte in the ciphertext, the corresponding byte and the block after that get corrupted, and the cipher recovers from the error afterwards.
PCBC will corrupt all output after the error. But it still does not detect the error.
I recommend adding authentication (either a MAC, or a mode with integrated authentication such as AES-GCM).
This is exactly what is supposed to be.
Greatly oversimplifying, message is encrypted and decrypted from left to right. Until decryption routine hits changed byte, decrypted text MUST match original message. What happens after changed byte is dependent on cipher mode, but decryption of the left part of the text cannot be affected by this change.

Resources