Reputation: 51
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.
Upvotes: 3
Views: 2614
Reputation: 131
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));
}
Upvotes: 1
Reputation: 51
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.
Upvotes: 2
Reputation: 5069
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;
}
Upvotes: 0