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;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm trying to encrypt and decrypt txt file using AES in C.
but I got problem.
I watched binary of plain.txt and decrypted.txt.
so There's not same binary.
I want to make same binary.
How to fix this problem ?
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
void encrypt(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
int fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outLen1 = 0; int outLen2 = 0;
unsigned char *indata = malloc(fsize);
unsigned char *outdata;
unsigned blocksize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, sizeof(unsigned char), fsize, ifp);
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
blocksize = EVP_CIPHER_CTX_block_size(&ctx);
outdata = malloc(fsize * 2 + blocksize + 15);
EVP_EncryptUpdate(&ctx, outdata, &outLen1, indata, fsize);
EVP_EncryptFinal(&ctx, outdata, &outLen2);
fwrite(outdata, sizeof(unsigned char), fsize, ofp);
free(indata);
free(outdata);
}
void decrypt(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
int fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outLen1 = 0; int outLen2 = 0;
unsigned char *indata = malloc(fsize);
unsigned char *outdata;
unsigned blocksize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, sizeof(unsigned char), fsize, ifp);
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
blocksize = EVP_CIPHER_CTX_block_size(&ctx);
outdata = malloc(fsize * 2 + blocksize + 15);
EVP_DecryptUpdate(&ctx, outdata, &outLen1, indata, fsize);
EVP_DecryptFinal(&ctx, outdata, &outLen2);
fwrite(outdata, sizeof(unsigned char), fsize, ofp);
free(indata);
free(outdata);
}
int main(int argc, char *argv[])
{
FILE *fIN;
FILE *fOUT;
errno_t error;
char buf[255 + 1];
if ((error =fopen_s(&fIN,"C://Projects/test_curl/Debug/plain.txt","rb"))!=0 {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
if ((error = fopen_s(&fOUT, "C://Projects/test_curl/Debug/cyphertext.txt", "wb")) != 0) {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
encrypt(fIN, fOUT);
fclose(fIN);
fclose(fOUT);
if ((error = fopen_s(&fIN, "C://Projects/test_curl/Debug/cyphertext.txt", "rb")) != 0) {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
if ((error = fopen_s(&fOUT, "C://Projects/test_curl/Debug/decrypted.txt", "wb")) != 0) {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
decrypt(fIN, fOUT);
fclose(fIN);
fclose(fOUT);
return 0;
}
You're basically not following the letter of the instructions on how to invoke the API's you're calling. Among the things wrong:
The EVP_EncryptUpdate function encrypts the given data, storing the size of the encryption output in its size out param, which you're basically ignoring. The same is true on the decryption side.
You blast over the initial encrypted/decrypted data block with your invoke of EVP_EncryptFinal (and EVP_DecryptFinal), in contradiction to precisely what the libcrypto documentation specifically warns you NOT to do.
Your output size in both functions is wrong. You're writing fsize bytes, which was the input size, not the output size. the latter is provided in the output-size params which you're ignoring (see the first item in this list).
The following functions do what you want. Compare them to what you had (wishful thinking on my part). I took the liberty of not using encrypt as the name, as that is a commonly provided DES function in unistd.h.
void encrypt_data(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
long fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outlen=0;
unsigned char *indata = malloc(fsize);
unsigned char *outdata;
unsigned blocksize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, sizeof(unsigned char), fsize, ifp);
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
// round up to nearest block size, this will add a complete
// block of additional padding if the input size is perfectly
// a factor of the block size
blocksize = EVP_CIPHER_CTX_block_size(&ctx);
outdata = malloc(((fsize + blocksize)/blocksize) * blocksize);
EVP_EncryptUpdate(&ctx, outdata, &outlen, indata, (int)fsize);
fwrite(outdata, 1, outlen, ofp);
EVP_EncryptFinal(&ctx, outdata, &outlen);
fwrite(outdata, 1, outlen, ofp);
free(indata);
free(outdata);
}
void decrypt_data(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
long fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outlen = 0;
unsigned char *indata = malloc(2 * fsize);
unsigned char *outdata = indata + fsize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, 1, fsize, ifp);
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
EVP_DecryptUpdate(&ctx, outdata, &outlen, indata, (int)fsize);
fwrite(outdata, 1, outlen, ofp);
EVP_DecryptFinal(&ctx, outdata, &outlen);
fwrite(outdata, 1, outlen, ofp);
free(indata);
}
Those will work.
Best of luck.
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;
}