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;
}
Related
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.
I am trying to encrypt and decrypt a large file (100MB) using aes ctr with a 256-bit key. I downloaded this code from this http://www.gurutechnologies.net/blog/aes-ctr-encryption-in-c/. I am using Xcode to run it, but there is a problem, it give me "Read file is null" as output. I would appreciate if somebody could help me or give me a hint.
Note: I add the text file as shown in the image below:
struct ctr_state{
unsigned char ivec[AES_BLOCK_SIZE];
unsigned int num;
unsigned char ecount[AES_BLOCK_SIZE];};
FILE *readFile;
FILE *writeFile;
AES_KEY key;
int bytes_read, bytes_written;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
int init_ctr(struct ctr_state *state, const unsigned char iv[16])
{
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);
return 0;
}
void fencrypt(char* read, char* write, const unsigned char* enc_key)
{
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
readFile = fopen(read,"rb"); // The b is required in windows.
writeFile = fopen(write,"wb");
if(readFile==NULL)
{
fprintf(stderr, "Read file is null.");
exit(1);
}
if(writeFile==NULL)
{
fprintf(stderr, "Write file is null.");
exit(1);
}
fwrite(iv, 1, 8, writeFile); // IV bytes 1 - 8
fwrite("\0\0\0\0\0\0\0\0", 1, 8, writeFile); // Fill the last 4 with null bytes 9 - 16
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set encryption key.");
exit(1);
}
init_ctr(&state, iv); //Counter call
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
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);
bytes_written = 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)
{
readFile=fopen(read,"rb"); // The b is required in windows.
writeFile=fopen(write,"wb");
if(readFile==NULL)
{
fprintf(stderr,"Read file is null.");
exit(1);
}
if(writeFile==NULL)
{
fprintf(stderr, "Write file is null.");
exit(1);
}
fread(iv, 1, AES_BLOCK_SIZE, readFile);
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set decryption key.");
exit(1);
}
init_ctr(&state, iv);//Counter call
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
//printf("%i\n", state.num);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
bytes_written = fwrite(outdata, 1, bytes_read, writeFile);
if (bytes_read < AES_BLOCK_SIZE)
{
break;
}
}
fclose(writeFile);
fclose(readFile);
}
int main(int argc, char *argv[])
{
fencrypt("encme.txt", "enced.enc", (unsigned const char*)"1234567812345678");
fdecrypt("enced.enc", "unenced.txt", (unsigned const char*)"1234567812345678");
getc(stdin);
return 0;
}
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;
}
I have the following openssl decryption code:
int decrypt(unsigned char *plain, unsigned char *key, unsigned char *iv, unsigned char *cipher, int len)
{
int i;
unsigned char outbuf[2000];
int outlen, tmplen;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if(!EVP_DecryptUpdate(&ctx, outbuf, &outlen, cipher, len))
{
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
printf("\nBytes decrypted (outlen) : %d\n",outlen);
if(!EVP_DecryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
printf("\nBytes decrypted (tmplen) : %d\n",tmplen);
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
printf("\nLength decrypted :%d\n",outlen);
printf("\nOutbuf: ");
for(i=0; i<outlen; i++){
plain[i] = outbuf[i];
printf(" %02x ",outbuf[i]);
}
printf("\n");
return outlen;
}
The ctx is initialized as follows:
SSL_library_init();
SSL_load_error_strings();
meth = SSLv23_server_method();
ctx = SSL_CTX_new (meth);
if (!ctx) {
ERR_print_errors_fp(stderr);
exit(1);
}
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_load_verify_locations(ctx,CACERT,NULL);
if (SSL_CTX_use_certificate_file(ctx, "./server.pem", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(1);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "./server.key", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(1);
}
The server.key file was generated using:
openssl genrsa -des3 -out server.key 1024
I pass the decrypt function a cipher of type unsigned char* and with cipher length of 16 bytes (the plain text was 2 bytes), key of length 16 bytes (unsigned char) and IV of length 8 bytes (unsigned char).
I get the following output from the decrypt function:
Bytes decrypted (outlen) : 0
19637:error:06065064:digital envelope routines: EVP_DecryptFinal_ex: bad decrypt: evp_enc.c: 337:
The following is the source of evp_enc.c:
int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
{
int i,n;
unsigned int b;
*outl=0;
b=ctx->cipher->block_size;
if (ctx->flags & EVP_CIPH_NO_PADDING)
{
if(ctx->buf_len)
{
EVPerr(EVP_F_EVP_DECRYPTFINAL_EX,EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);
return 0;
}
*outl = 0;
return 1;
}
if (b > 1)
{
if (ctx->buf_len || !ctx->final_used)
{
EVPerr(EVP_F_EVP_DECRYPTFINAL_EX,EVP_R_WRONG_FINAL_BLOCK_LENGTH);
return(0);
}
OPENSSL_assert(b <= sizeof ctx->final);
n=ctx->final[b-1];
if (n == 0 || n > (int)b)
{
EVPerr(EVP_F_EVP_DECRYPTFINAL_EX,EVP_R_BAD_DECRYPT);
return(0);
}
for (i=0; i<n; i++)
{
if (ctx->final[--b] != n)
{
EVPerr(EVP_F_EVP_DECRYPTFINAL_EX,EVP_R_BAD_DECRYPT); // 337
return(0);
}
}
n=ctx->cipher->block_size-n;
for (i=0; i<n; i++)
out[i]=ctx->final[i];
*outl=n;
}
else
*outl=0;
return(1);
}
Can anyone suggest a solution / point out the problem ?
Thanks.
I'm using the following working code to decrypt a file:
#include <openssl/aes.h>
#include <stdio.h>
#include <string.h>
struct ctr_state {
unsigned char ivec[16];
unsigned int num;
unsigned char ecount[16];
};
FILE *fp;
FILE *rp;
FILE *op;
size_t count;
char * buffer;
AES_KEY key;
int bytes_read, bytes_written;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char ckey[] = "1234567890123456";
unsigned char iv[8] = {0};
struct ctr_state state;
void init_ctr(struct ctr_state *state, const unsigned char iv[8]){
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
}
void decrypt(){
//Opening files where text cipher text is read and the plaintext recovered
rp=fopen("c:\\temp\\decrypted.mp3","wb");
op=fopen("c:\\temp\\encrypted.mp3","rb");
if (rp==NULL) {fputs ("File error",stderr); return;}
if (op==NULL) {fputs ("File error",stderr); return;}
//Initializing the encryption KEY
AES_set_encrypt_key(ckey, 128, &key);
init_ctr(&state, iv);//Counter call
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while (1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, op);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
bytes_written = fwrite(outdata, 1, bytes_read, rp);
if (bytes_read < AES_BLOCK_SIZE)
break;
}
fclose (rp);
fclose (op);
}
int main(int argc, char *argv[]){
decrypt();
return 0;
}
Is it possible to do the same thing using the openssl command line?
AFAIK, there's no -aes-128-ctr cipher but is there any equivalent in openssl?
No version of the openssl(1) command line tool that I've checked appears to support counter mode for any cipher.