memcpy: Segfault after second call - c

I am writing a wrapper for Karl Malbrain's AES implementation to work with input files bigger then 16 bytes.
Therefore, I wrote a functions aes_encrypt_block which would split the input buffer (message) into blocks of 16 bytes (chunk / chunk_cipher), calls the encryption/decryption functions and put the encrypted/decrypted 16 bytes back into the result buffer.
This works. However, I need to pad the message before encrypting it. So, the last 16 bytes I encrypt (outside the for-loop) are the padding bytes. When copying these bytes to the output buffer (cipher) I get a segfault. I can't really see what is going wrong.
Do you see the mistake?
Regards
#include "aes.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#define DEBUG 1
void aes_encrypt_block(uint8_t **message, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes);
void aes_decrypt_block(uint8_t **msg_decrypted, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes);
unsigned long readFile(char *fileName, uint8_t **buffer);
//unsigned long readFile1(char *fileName, uint8_t *buffer);
uint8_t secret[16] = {0x44, 0x43, 0x45, 0x33, 0x44, 0x03, 0x34, 0x44, 0x43, 0x45, 0x33, 0x44, 0x03, 0x34, 0x03, 0x34};
uint8_t *chunk = NULL;
uint8_t *chunk_cipher = NULL;
uint8_t expanded[176] = {0x00};
uint8_t *buffer = NULL;
uint8_t *cipher = NULL;
uint8_t *msg_decrypted = NULL;
uint8_t mode = -1;
int main(int argc, char *argv[]) {
uint32_t i = 0;
uint8_t blocks = -1, pad_bytes = -1;
unsigned long fileLen;
char* ch = NULL;
if(argc<3) {
printf("Wrong arguments supplied.\n\n%s {0/1} OUT\n\n\t0 - encrypt\n\t1 - decrypt\n\tOUT - file to read from / to write to.\n\n", argv[0]);
return 0;
} else {
mode = atoi(argv[1]);
if(mode<0 || mode>1) {
printf("Wrong arguments supplied.\n\n%s {0/1} OUT\n\n\t0 - encrypt\n\t1 - decrypt\n\tOUT - file to read from / to write to.\n\n", argv[0]);
return 0;
}
}
puts("");
// Read file
fileLen = readFile(argv[2], &buffer);
// Setting up parameters and memory
if(fileLen%16!=0) {
printf("- ");
blocks = fileLen/16+1;
pad_bytes = (blocks*16)-fileLen;
} else {
printf("+ ");
blocks = fileLen/16+1;
pad_bytes = 16;
}
cipher = malloc((blocks*16)*sizeof(int));
if(cipher==NULL) printf("malloc() error!\n");
aes_expand_key(secret, expanded);
if(DEBUG) printf("size: %u, size/16: %d, blocks: %d, padding: %d\n\n", fileLen, fileLen/16, blocks, pad_bytes);
if(!mode) { // We will encrypt
aes_encrypt_block(&buffer, &cipher, blocks, pad_bytes);
free(buffer);
FILE *file_enc;
file_enc = fopen("bla.enc", "wb");
fileLen = fwrite(&cipher, sizeof(uint8_t), 32, file_enc);
printf("\nWrote %ld bytes to %s\n", fileLen, argv[2]);
fclose(file_enc);
} else { // We will decrypt
msg_decrypted = malloc((16*blocks)*sizeof(uint8_t));
// Decrypting blocks
aes_decrypt_block(msg_decrypted, cipher, blocks, pad_bytes);
puts("\nDecrypted message:");
printf("%s\n", msg_decrypted);
puts("");
free(msg_decrypted);
}
return 0;
}
unsigned long readFile(char *fileName, uint8_t **buffer) {
unsigned long fileLen = 0;
uint8_t i;
char* ch = NULL;
FILE *file;
file = fopen (fileName, "rb"); /* open the file for reading */
if(file==NULL) {
perror(fileName);
return 0;
}
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
*buffer=malloc(fileLen+1);
if (!buffer) {
fprintf(stderr, "Memory error!");
fclose(file);
return;
}
fread(*buffer, 1, fileLen, file);
printf( "Source message in hex(%s, %ld bytes):\n", fileName, fileLen );
for (ch = *buffer ; ch < *buffer + fileLen; ++ch) {
printf( "%02X", *ch );
}
puts("\nASCII:\n---------");
for (ch = *buffer ; ch < *buffer + fileLen; ++ch) {
printf( "%c", *ch );
}
puts("");
fclose(file);
return fileLen;
}
void aes_encrypt_block(uint8_t **message, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes) {
uint8_t i;
chunk = malloc(16*sizeof(uint8_t));
if(chunk==NULL) printf("malloc() error!\n");
chunk_cipher = malloc(16*sizeof(uint8_t));
if(chunk_cipher == NULL) printf("malloc() error!\n");
for(i=0; i<(blocks-1); i++) {
memcpy(chunk, message[i*16], 16*sizeof(uint8_t));
aes_encrypt(chunk, expanded, chunk_cipher);
memcpy(cipher[i*16], chunk_cipher, 16*sizeof(uint8_t));
}
// Padding
memcpy(chunk, message[(blocks-1)*16], (16-pad_bytes)*sizeof(uint8_t));
uint8_t j;
for(j=0; j<=pad_bytes; j++) {
chunk[15-j] = pad_bytes;
}
aes_encrypt(chunk, expanded, chunk_cipher);
memcpy(cipher[i*16], chunk, 16*sizeof(uint8_t));
}
EDIT:
Valgrind output (line 149 in test.c corresponds to: memcpy(cipher[i*16], chunk, 16*sizeof(uint8_t));)
Valgrind outputs:
InvalidWrite: Invalid write of size 4
Call stack:
/usr/lib/valgrind/vgpreload_memcheck-x86-linux.so 0x402E08A: memcpy
/home/dev/aes/test.c|149|0x804A604: aes_encrypt_block
/home/dev/aes/test.c|59|0x8049AFE: main
Address 0x0 is not stack'd, malloc'd or (recently) free'd
Valgrind found 1 errors!

If pad_bytes == 16 then this loop:
for(j=0; j<=pad_bytes; j++) {
chunk[15-j] = pad_bytes;
}
will write into an invalid location before the start of chunk, possibly corrupting your heap.
It should probably be:
for(j=0; j<pad_bytes; j++) {
chunk[15-j] = pad_bytes;
}
Note also that you also appear to have a number of memory leaks where memory allocated via malloc is not beeing freed, e.g. chunk and chunk_cipher in aes_encrypt_block().

I'd say this line
memcpy(cipher[i*16], chunk_cipher, 16*sizeof(uint8_t));
should be
memcpy((*cipher) + i*16, chunk_cipher, 16*sizeof(uint8_t));
(for the 2nd second call of memcpy() apply same to 1st argument)
Or even better change the signature of aes_encrypt_block() from
void aes_encrypt_block(uint8_t **message, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes)
to
void aes_encrypt_block(uint8_t **message, uint8_t (*cipher)[16], uint8_t blocks, uint8_t pad_bytes)
and do
memcpy(cipher + i, chunk_cipher, sizeof(*cipher));
(for the 2nd second call of memcpy() apply same to 1st argument)

Related

Tested File Reads as expected but on Complete Code Not Working

I have tested the following code to read the tester.txt file and it outputted my expectation, however, when I inserted similar code in my complete code, it just stuck at: "Data from the file:" Why it is not outputting my expectation?
#include <stdio.h>
#include <stdlib.h>
char c[1000];
FILE *fptr;
int fileReader(){
if ((fptr = fopen("tester.txt", "r")) == NULL) {
printf("Error! File cannot be opened.");
// Program exits if the file pointer returns NULL.
exit(1);
}
// reads text until newline is encountered
fscanf(fptr, "%[^\n]", c);
fclose(fptr);
}
int main() {
fileReader();
printf("Data from the file:\n%s", c);
char* anotherone = (char*) c;
printf(anotherone);
return 0;
}
Another code to read file:
int fileReader() {
fp = fopen("tester.txt", "r");
fscanf(fp, "%s", c);
fclose(fp);
}
The complete code is:
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int success = 0;
void handleOpenSSLErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}
unsigned char* decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ){
EVP_CIPHER_CTX *ctx;
unsigned char *plaintexts;
int len;
int plaintext_len;
unsigned char* plaintext = malloc(ciphertext_len);
bzero(plaintext,ciphertext_len);
if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
handleOpenSSLErrors();
EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleOpenSSLErrors();
plaintext_len = len;
// return 1 if decryption successful, otherwise 0
if(1 == EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
success = 1;
plaintext_len += len;
/* Add the null terminator */
plaintext[plaintext_len] = 0;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
//delete [] plaintext;
return plaintext;
}
size_t calcDecodeLength(char* b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
padding = 2;
else if (b64input[len-1] == '=') //last char is =
padding = 1;
return (len*3)/4 - padding;
}
void Base64Decode( char* b64message, unsigned char** buffer, size_t* length) {
BIO *bio, *b64; // A BIO is an I/O strean abstraction
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
//BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
*length = BIO_read(bio, *buffer, strlen(b64message));
BIO_free_all(bio);
}
void initAES(const unsigned char *pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
{
//initialisatio of key and iv with 0
bzero(key,sizeof(key));
bzero(iv,sizeof(iv));
EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, pass, strlen(pass), 1, key, iv);
}
int checkPlaintext(char* plaintext, char* result){
int length = 10; // we just check the first then characters
return strncmp(plaintext, result, length);
}
char *cas[1000];
FILE *fptr;
int fileReader(){
if ((fptr = fopen("tester.txt", "r")) == NULL) {
printf("Error! File cannot be opened.");
// Program exits if the file pointer returns NULL.
exit(1);
}
// reads text until newline is encountered
fscanf(fptr, "%[^\n]", cas);
fclose(fptr);
}
int main (void)
{
fileReader();
printf("Data from the file:\n%s", cas);
char* ciphertext_base64 = (char*) cas;
char* plaintext = "to be or not to be is the question";
char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int decryptedtext_len, ciphertext_len, dict_len;
// cipher (binary) pointer and length
size_t cipher_len; // size_t is sizeof(type)
unsigned char* ciphertext;
unsigned char salt[8];
ERR_load_crypto_strings();
Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);
unsigned char key[16];
unsigned char iv[16];
unsigned char plainpassword[] = "00000";
unsigned char* password = &plainpassword[0];
// retrive the slater from ciphertext (binary)
if (strncmp((const char*)ciphertext,"Salted__",8) == 0) { // find the keyword "Salted__"
memcpy(salt,&ciphertext[8],8);
ciphertext += 16;
cipher_len -= 16;
}
dict_len = strlen(dict);
time_t begin = time(NULL);
for(int i=0; i<dict_len; i++)
for(int j=0; j<dict_len; j++)
for(int k=0; k<dict_len; k++)
for(int l=0; l<dict_len; l++)
for(int m=0; m<dict_len; m++){
*password = dict[i];
*(password+1) = dict[j];
*(password+2) = dict[k];
*(password+3) = dict[l];
*(password+4) = dict[m];
initAES(password, salt, key, iv);
unsigned char* result = decrypt(ciphertext, cipher_len, key, iv);
if (success == 1){
if(checkPlaintext(plaintext, result)==0){
printf("Password is %s\n", password);
time_t end = time(NULL);
printf("Time elpased is %ld seconds", (end - begin));
return 0;
}
}
free(result);
}
// Clean up
EVP_cleanup();
ERR_free_strings();
return 0;
}
char *cas[1000]; should be char cas[1000];, I would say this is the main issue, you are using an array of pointers as if it was a character array, this is very likely what's causing your program to fail.
Fix bzero, pass the size of the array as argument. When you use sizeof(key) in initAES(), what you are actually getting is the size of the pointer, not the size of the container it points to. More oh this in How to find the 'sizeof' (a pointer pointing to an array)?
You can add size of the array as a global constant with a macro:
#define SIZE 16 // size of the arrays
//...
void initAES(const unsigned char *pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
{
//initialisatio of key and iv with 0
bzero(key, SIZE);
bzero(iv, SIZE);
EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, pass, strlen(pass), 1, key, iv);
}
You can use it to define the size in main making your code more consistent.
int main (void)
{
//...
unsigned char key[SIZE];
unsigned char iv[SIZE];
//...
}
For good measure make fileReader have void return type if you don't want it to return an int.
I always advise the use of compiler warnings at full blast and preferably to treat warnings as errors.

Convert uint8_t array (Hexadecimal) to string in C

Requirements
I'm a developer but with near-zero experience with C until a week ago. For a project, I need to encrypt/decrypt source-code/text files and need to use C for that.
What I did till now?
I'm using Kokke/tony-AES-c library for it and implementing it with pkcs7 padding as explained in this Gist.
In my code (given below), I have a function my_encrypt, which is encrypting contents and returning uint8_t array (which contains hex). I need to save it and read it later from a file for decryption. I know how to save a string into a file but how can I save uint8_t in a file.
For this, I need to convert uint8_t to string but after trying multiple solution, I am unable to do it.
Question
How can I convert uint8_t array (which contains hex) to string in C.
My code
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <stdlib.h>
#define CBC 1
#define DECRYPT 0;
#define ENCRYPT 1;
#include "aes.h"
#include "pkcs7_padding.h"
const uint8_t * my_encrypt(char *report) {
//Initialization Vector
uint8_t iv[] = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };
uint8_t i;
// char* report = "my super secret thing that needs to remain that way!";
char* key = "thisIstheKey";
int dlen = strlen(report);
int klen = strlen(key);
printf("THE PLAIN TEXT STRING = ");
for (i=0; i<dlen;i++){
printf("%c", report[i]);
}
printf("\n");
//Proper Length of report
int dlenu = dlen;
if (dlen % 16) {
dlenu += 16 - (dlen % 16);
printf("The original length of the STRING = %d and the length of the padded STRING = %d\n", dlen, dlenu);
}
//Proper length of key
int klenu = klen;
if (klen % 16) {
klenu += 16 - (klen % 16);
printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
}
// Make the uint8_t arrays
uint8_t hexarray[dlenu];
uint8_t kexarray[klenu];
// Initialize them with zeros
memset( hexarray, 0, dlenu );
memset( kexarray, 0, klenu );
// Fill the uint8_t arrays
for (int i=0;i<dlen;i++) {
hexarray[i] = (uint8_t)report[i];
}
for (int i=0;i<klen;i++) {
kexarray[i] = (uint8_t)key[i];
}
int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );
printf("The padded STRING in hex is = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
printf("The padded key in hex is = ");
for (i=0; i<klenu;i++){
printf("%02x",kexarray[i]);
}
printf("\n");
// In case you want to check if the padding is valid
int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
printf("Is the pkcs7 padding valid report = %d | key = %d\n", valid, valid2);
//start the encryption
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, kexarray, iv);
// encrypt
AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);
printf("the encrypted STRING = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
return hexarray;
}
int main(int argc, char *argv[]) {
// File pointer
FILE *input_file;
int operation;
char * input_file_buffer = 0;
long input_file_length;
// ////////////////////////////////////////////////////////
// Read command line arguments
// ////////////////////////////////////////////////////////
// Make sure proper command params are supplied
if (argc != 3) {
printf("Usage: %s encrypt/decrypt filename\n", argv[0]);
return -1;
}
// Define operation type
if (strcmp(argv[1], "encrypt") == 0) {
operation = ENCRYPT;
} else if (strcmp(argv[1], "decrypt") == 0) {
operation = DECRYPT;
}
// ////////////////////////////////////////////////////////
// Open File contents
// ////////////////////////////////////////////////////////
// Open input_file to encrypt/decrypt.
input_file = fopen(argv[2], "rb");
if (!input_file) {
fprintf(stderr, "ERROR: '%s' file fopen error: %s\n", argv[2], strerror(errno));
return errno;
}
// Read contents of file in buffer
fseek(input_file, 0, SEEK_END);
input_file_length = ftell (input_file);
fseek(input_file, 0, SEEK_SET);
input_file_buffer = malloc (input_file_length);
if (input_file_buffer)
{
fread(input_file_buffer, 1, input_file_length, input_file);
}
// Close input_file
fclose(input_file);
// We have contents of file in input_file_buffer, let's print them.
printf("File contents:\n-------\n%s\n", input_file_buffer);
// Let's encrypt input_file_buffer
uint8_t result = 0;
result = my_encrypt(input_file_buffer);
// Convery result to string.
printf("main: Encrypted File contents:\n-------\n%d\n", result);
return 0;
}

Program to read binary and determine filetype

I wrote this code which reads a file binary and determines its filetype (for a few test fileendings).It works fine for PDF, MP3 but not for jpg.
What is the problem? For jpg the line printf("%s[%d]: %x\n", "Buffer", j, buffer[j]); shows me multiple bytes (i.e ffffff instead of just one byte)
#include <stdio.h>
const int header[6][8] = {
{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A},
{0xFF,0xD8,0x00,0x00,0x00,0x00,0x00,0x00},
{0xFF,0xFB,0x00,0x00,0x00,0x00,0x00,0x00},
{0x49,0x44,0x33,0x00,0x00,0x00,0x00,0x00},
{0x25,0x50,0x44,0x46,0x2D,0x00,0x00,0x00},
{0x42,0x4C,0x45,0x4E,0x44,0x45,0x52,0x00}
};
const char* filetype[6] = {"PNG","JPG","MP3","MP3v2","PDF","Blender"};
int main()
{
FILE *fd;
char buffer[8];
if ((fd = fopen("C:\\Users\\***\\Desktop\\Unnamed.jpg", "rb")) == NULL) {
return -1;
}
//fread(buffer, sizeof(char), 8, fd);
fread(buffer, sizeof(buffer), 1, fd);
for (int i = 0; i < 6; i++) {
for(int j = 0; j < 8; j++){
printf("%s[%d][%d]: %x\n","Header",i,j,header[i][j]);
printf("%s[%d]: %x\n", "Buffer", j, buffer[j]);
if (header[i][j] == 0x00) {
printf("%s: %s","Found file type",filetype[i]);
return 1;
}
if (header[i][j] != buffer[j]) {
break;
}
}
}
printf("%s", "Couldn't determine filetype - Not in library");
return 0;
}
You forgot to make your buffer unsigned char, so you're getting sign extension on the values.
Clearly the table of built-in magic signatures should be const unsigned char, too, and the comparison should be made with a single memcmp() call.
Use unsigned char for buffer (you can also for header) to avoid problem if your char are signed and give a negative value (you compare with int like 0x89 where the bit 7 is set)
You also have a problem for PNG because the values for PNG are :
{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A},
there are not ended by 0 like in all the other cases, this is needed because your algorithm need to find 0 to indicate you found :
if (header[i][j] == 0x00) {
printf("%s: %s","Found file type",filetype[i]);
return 1;
}
Just add a column to also have 0 also for PNG.
Finally :
const unsigned char header[6][9] = {
{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A, 0x00},
{0xFF,0xD8,0x00,0x00,0x00,0x00,0x00,0x00, 0x00},
{0xFF,0xFB,0x00,0x00,0x00,0x00,0x00,0x00, 0x00},
{0x49,0x44,0x33,0x00,0x00,0x00,0x00,0x00, 0x00},
{0x25,0x50,0x44,0x46,0x2D,0x00,0x00,0x00, 0x00},
{0x42,0x4C,0x45,0x4E,0x44,0x45,0x52,0x00, 0x00}
};
const char* filetype[6] = {"PNG","JPG","MP3","MP3v2","PDF","Blender"};
int main()
{
FILE *fd;
unsigned char buffer[sizeof(header[0])];
if ((fd = fopen("C:\\Users\\***\\Desktop\\Unnamed.jpg", "rb")) == NULL) {
return -1;
}
fread(buffer, sizeof(buffer), 1, fd);
for (int i = 0; i < ; i++) {
for(int j = 0; j < sizeof(header[0]); j++){
printf("%s[%d][%d]: %x\n","Header",i,j,header[i][j]);
printf("%s[%d]: %x\n", "Buffer", j, buffer[j]);
if (header[i][j] == 0x00) {
printf("%s: %s","Found file type",filetype[i]);
return 1;
}
if (header[i][j] != buffer[j]) {
break;
}
}
}
printf("%s", "Couldn't determine filetype - Not in library");
return 0;
}

Can't encrypt long string with XOR C crypter

I wrote a C program to perform an XOR encryption,
my problem is that the program is not able to encrypt files with more than 24 characters.
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define BUF_SIZE 2
char* xor(char*, char*);
char* gen_key(size_t);
int main(int argc, char **argv) {
char *buffer = NULL,* encrypted_buffer = NULL;
size_t file_size;
char *key = gen_key(6);
char tmp_buffer[BUF_SIZE];
FILE *finput = NULL, *foutput = NULL;
finput = fopen("file.txt", "rb");
fseek(finput, 0, SEEK_END);
file_size = ftell(finput);
rewind(finput);
printf("File size : %d\n", (int)file_size);
buffer = (char*)malloc((file_size + 1) * sizeof(char));
memset(buffer, 0, sizeof(buffer));
while (!feof(finput)) {
memset(tmp_buffer, 0, sizeof(tmp_buffer));
fgets(tmp_buffer, sizeof(tmp_buffer), finput);
strcat(buffer, tmp_buffer);
}
printf("%s", buffer);
encrypted_buffer = xor(buffer, key);
free(buffer);
buffer = xor(encrypted_buffer, key);
printf("Encrypted : %s\n", encrypted_buffer);
printf("Decrypted : %s\n", buffer);
printf("EOF\n");
free(encrypted_buffer);
fclose(finput);
return 0;
}
char *gen_key(size_t length) {
srand(time(NULL));
const char charset[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz012345679";
const size_t charset_size = (sizeof(charset) - 1);
unsigned int i;
char *key = NULL;
key = (char*)malloc((length + 1) * sizeof(char));
memset(key, 0, sizeof(key));
for (i = 0; i < length; i++)
key[i] = charset[rand() % charset_size];
return key;
}
char *xor(char *file, char *key) {
unsigned int i;
char *xor = NULL;
xor = (char*)malloc(sizeof(file));
memset(xor, 0, sizeof(xor));
for (i = 0; i < strlen(file); i++)
*(xor + i) = *(file + i) ^ *(key + (i % strlen(key) - 1));
return xor;
}
And the output is :
File size : 55
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklimnopqrstuvwxyz
Encrypted : A2#G8- M >7S$1!
Decrypted : ABCDEFGHIJKLMNOPQRSTUVWX!:!e!
EOF
There are multiple problems in your code:
The buffer size is very small: #define BUF_SIZE 2. You should use a reasonable size for the line buffer, such as 80 or 100.
in memset(buffer, 0, sizeof(buffer));, buffer is a pointer, so sizeof(buffer) is not the size of the array, just the size of the pointer. Use file_size + 1 in this case.
You make the same mistake in other places in your code: pass the size of the buffers instead of relying on the sizeof() operator.
while (!feof(f)) is always wrong: you could jut use fread to read the file in one step or read line by line with:
while (fgets(tmp_buffer, sizeof(tmp_buffer), finput)) {
...
Note that there is a major problem with your approach: the key is composed of letters and digits and the file is assumed to contain text. If the file contains one of the characters in the key at the appropriate position, xoring this character with the key byte will produce a null byte that will stop the output in printf() and that would also stop the decryption if you were to store it in an output file. You rightfully use binary mode ("rb") for the file stream, but you should also make no assumptions on the file contents and handle null bytes transparently.
Here is a modified version of your program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
unsigned char *xor(unsigned char *file, size_t size, const char *key);
char *gen_key(size_t length);
void print_buffer(const char *msg, unsigned char *buf, size_t size) {
printf("%s: ", msg);
for (size_t i = 0; i < size; i++) {
switch (buf[i]) {
case '\n':
printf("\\n");
break;
case '\\':
printf("\\\\");
break;
default:
if (buf[i] >= ' ' && buf[i] < 127)
putchar(buf[i]);
else
printf("\\%02x", buf[i]);
break;
}
}
printf("\n");
}
int main(int argc, char **argv) {
long file_size, nread, nwritten;
unsigned char *buffer, *encrypted_buffer, *decrypted_buffer;
char *key = gen_key(6);
FILE *finput = fopen("file.txt", "rb");
if (finput == NULL) {
fprintf(stderr, "cannot open file.txt: %s\n", strerror(errno));
return 1;
}
fseek(finput, 0, SEEK_END);
file_size = ftell(finput);
rewind(finput);
printf("File size: %ld\n", file_size);
buffer = calloc(file_size, sizeof(char));
nread = fread(buffer, 1, file_size, finput);
if (nread != file_size) {
fprintf(stderr, "error reading file.txt: read %ld bytes, expected %ld\n",
nread, file_size);
}
fclose(finput);
FILE *foutput = fopen("output.bin", "wb");
if (foutput == NULL) {
fprintf(stderr, "cannot open output.bin: %s\n", strerror(errno));
return 1;
}
encrypted_buffer = xor(buffer, nread, key);
nwritten = fwrite(encrypted_buffer, 1, nread, foutput);
if (nwritten != nread) {
fprintf(stderr, "error writing output.bin: wrote %ld bytes, expected %ld\n",
nwritten, nread);
}
fclose(foutput);
decrypted_buffer = xor(encrypted_buffer, nread, key);
printf("Key : %s\n", key);
print_buffer("Original ", buffer, nread);
print_buffer("Encrypted", encrypted_buffer, nread);
print_buffer("Decrypted", decrypted_buffer, nread);
if (!memcmp(decrypted_buffer, buffer, nread))
printf("OK\n");
free(decrypted_buffer);
free(encrypted_buffer);
free(buffer);
return 0;
}
char *gen_key(size_t length) {
const char charset[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz012345679";
const size_t charset_size = sizeof(charset) - 1;
char *key = (char*)calloc(length + 1, sizeof(char));
size_t i;
srand(time(NULL));
for (i = 0; i < length; i++)
key[i] = charset[rand() % charset_size];
return key;
}
unsigned char *xor(unsigned char *file, size_t size, const char *key) {
size_t i, keylen = strlen(key);
unsigned char *xor = calloc(size, sizeof(char));
for (i = 0; i < size; i++)
xor[i] = file[i] ^ key[i % keylen];
return xor;
}

Memory allocation for AES_cbc_encrypt

I'm trying to encrypt and decrypt keystore using AES_cbc_encrypt function from openssl library.
I have a problem with memory allocation, it will be nice if someone can give me some advices how to do this in proper way.
For now I can't figure out how to do this so I'm getting Segmentation fault while AES_cbc_encrypt is called.
Another question is how to allocate memory for char tables in C (clean way). I want to do this dynamically. Places in code for this question are marked with QUESTION string.
unsigned char *input;
input = malloc(sizeof(unsigned char)*length);
unsigned char input_question[length];
Output:
[enc_dec_keystore] length: 82
[enc_dec_keystore] QUESTION #1: why sizeof(input) != sizeof(input_question)
[enc_dec_keystore] input size: 8
[enc_dec_keystore] input_question size: 82
Why sizeof(input) is different. Because I'm getting sizeof pointer in this way? I expected to get 82 too because sizeof(unsigned char) should be 1, multiplied by 82 should be 82, right?
Here is a code from my program:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#define DEBUG 1
#define KEYLEN 128
void print_data(const char *tittle, const void* data, int len)
{
printf("%s : ",tittle);
const unsigned char * p = (const unsigned char*)data;
int i = 0;
for (; i<len; ++i)
printf("%02X ", *p++);
printf("\n");
}
void PBKDF2_HMAC_SHA_512(const char *pass, const unsigned char *salt, int32_t iterations, uint32_t outputBytes, unsigned char *result) {
if (DEBUG) {
puts("[PBKDF2_HMAC_SHA_512] in");
}
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations, EVP_sha512(), outputBytes, digest);
for (int i = 0; i < sizeof(digest); i++)
{
result[i] = digest[i];
};
if (DEBUG) {
puts("[PBKDF2_HMAC_SHA_512] out");
}
}
int deriver_key(const char *pass, const char *salt, unsigned char *key) {
if (DEBUG) {
puts("[deriver_key] in");
}
/* allocate 16 bytes of memory for key (128 bits) */
key = (unsigned char *) malloc(sizeof(unsigned char)*16);
/* let's make 10 iteration for now */
PBKDF2_HMAC_SHA_512(pass, salt, 10, KEYLEN/8, key);
if (DEBUG) {
printf("[deriver_key] key(string): %s\n", key);
printf("[deriver_key] key(bytes): ");
for(int i=0; i<KEYLEN/8; i++) {
printf("%02x", key[i]);
}
printf("\n");
puts("[deriver_key] out");
}
return 0;
}
int enc_dec_keystore(char *keystore, char *key) {
/* length of keystore */
int length;
/* if (length % AES_BLOCK_SIZE) !=0 -> fill to 16 bytes block */
int pad_length;
int pad_counter = 0;
FILE *fp;
fp = fopen(keystore, "rb");
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (DEBUG) {
printf("[enc_dec_keystore] keystore length: %i\n", length);
}
/* check if input fills blocks correctly */
pad_length = length;
while(pad_length % AES_BLOCK_SIZE !=0) {
pad_length++;
pad_counter++;
}
if (DEBUG) {
printf("[enc_dec_keystore] pad_length: %i\n", pad_length);
}
/* IV - fill with 0 for now */
unsigned char iv[AES_BLOCK_SIZE];
memset(iv, 0x00, AES_BLOCK_SIZE);
unsigned char *input;
input = malloc(sizeof(unsigned char)*length);
unsigned char input_question[length];
if (DEBUG) {
printf("\n[enc_dec_keystore] QUESTION #1: why sizeof(input) != sizeof(input_question)\n");
printf("[enc_dec_keystore] input size: %lu\n", sizeof(input));
printf("[enc_dec_keystore] input_question size: %lu\n\n", sizeof(input_question));
}
/* read data from file */
int ret = 0;
ret = fread(input, 1, length, fp);
if (ret == 0) {
puts("[enc_dec_keystore] file doesn't exist");
}
/* close the file after read */
fclose(fp);
if (DEBUG) {
printf("[enc_dec_keystore] sizeof input: %lu, input length: %lu\n", sizeof(input), strlen(input));
printf("[enc_dec_keystore] input data: \n%s\n", input);
}
/* allocate memory for aes's output */
unsigned char *enc_out;
enc_out = malloc(sizeof(unsigned char)*pad_length);
unsigned char enc_out_question[pad_length];
/* padding with 0 */
memset(enc_out, 0, sizeof(enc_out));
if (DEBUG) {
printf("\n[enc_dec_keystore] QUESTION #2: (again) why sizeof(enc_out) != sizeof(enc_out_question)\n");
printf("[enc_dec_keystore] enc_out size: %lu\n", sizeof(enc_out));
printf("[enc_dec_keystore] enc_out_question size: %lu\n\n", sizeof(enc_out_question));
}
/* AES-128 bit CBC Encryption */
/* set up a key */
/*
unsigned char key_again[KEYLEN/8];
for (int i=0; i<KEYLEN/8; i++) {
key_again[i] = key[i];
}
*/
AES_KEY enc_key;
AES_set_encrypt_key(key, 128, &enc_key);
/* encrypt input data to enc_out */
AES_cbc_encrypt(input, enc_out, sizeof(input), &enc_key, iv, AES_ENCRYPT);
if (DEBUG) {
printf("[enc_dec_keystore] encryption - aes output(string): %s\n", enc_out);
printf("[enc_dec_keystore] encryption - aes output(bytes): ");
for(int i=0; i<pad_length; i++) {
printf("%02x", enc_out[i]);
}
printf("\n");
}
/* AES-128 bit CBC Decryption */
/*
AES_KEY dec_key;
AES_set_encrypt_key(key, 128, &enc_key);
memset(iv, 0x00, AES_BLOCK_SIZE);
unsigned char *enc_out;
enc_out = malloc(sizeof(unsigned char)*pad_length);
AES_cbc_encrypt(enc_out, dec_out, sizeof(enc_out), &enc_key, iv, AES_ENCRYPT);
if (DEBUG) {
printf("[enc_dec_keystore] decryption - aes output(string): %s\n", enc_out);
printf("[enc_dec_keystore] decryption - aes output(bytes): ");
for(int i=0; i<pad_length; i++) {
printf("%02x", enc_out[i]);
}
printf("\n");
puts("[enc_dec_keystore] out");
}
*/
return 0;
}
int main(int argc, char **argv) {
char *user = "user";
char *key_id = "key1";
const char *pass = "temppass";
const char *salt = "tempsalt";
char *keystore = "keystore";
/* Deriver key from password and salt */
unsigned char *key;
if (deriver_key(pass, salt, key) != 0) {
puts("[main] error with key derivery");
}
/* Encrypt and decrypt keystore (for tests) */
if (enc_dec_keystore(keystore, key) != 0) {
puts("[main] error with encrypting/decrypting keystore");
}
return 0;
}
Keystore file contains:
keyid1:01020304050607080900010203040506:
keyid2:06050403020100090807060504030201:
Why sizeof(input) is not 82, because sizeof returns only the size of pointer variable, it does not return the size of dynamically allocated data.
PBKDF2_HMAC_SHA_512 returns a binary key, which may not be delimited by '\0', this could lead to segfault when you print the key

Resources