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.