I am working on using OpenSSL in Linux Ubuntu to perform authenticated encryption/decryption. I am currently using C for the code, AES-CTR Encryption method for ciphering the text, HMAC-SHA256 for the tag. My code works by using Makefile for command "make"; after that, using the two instructions shown below and original.txt for original text, shared.key for the key to encrypt/decrypt. My problem here is that although no error occurs(I tried to handle as many errors as possible, but there can be more…) during encryption and decryption, when I open the decryption.txt file, generated after the decrpytion, the words are broken into stange symbols…
Here is the C code that I’ve written:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
char* readFile(char* fileName){
int size;
int count;
FILE *fp = fopen(fileName, "r");
fseek(fp, 0, SEEK_END);
size = ftell(fp);
char* buffer = malloc(size+1);
char* result = malloc(size+1);
memset(buffer, 0, size+1);
memset(result, 0, size+1);
fseek(fp, 0, SEEK_SET);
while(1){
char* pStr = fgets(buffer, size+1, fp);
if(pStr==NULL)break;
strcat(result, pStr);
}
fclose(fp);
free(buffer);
return result;
}
int main(int argc, char* argv[]){
if(argc != 10){
printf("ERRORn");
exit(2);
}
int ikey, iin, iout, itag;
for(int i=2; i < 9;i+=2){
if(!strcmp(argv[i], "-key")) ikey = i+1;
else if(!strcmp(argv[i], "-in")) iin = i+1;
else if(!strcmp(argv[i], "-out")) iout = i+1;
else if(!strcmp(argv[i], "-tag")) itag = i+1;
}
if(ikey+iin+iout+itag != 24){
printf("ERRORn");
exit(2);
}
char* key = readFile(argv[ikey]);
char *iv = "0123456789012345";
if(!strcmp(argv[1], "enc")){
FILE *inFp = fopen(argv[iin],"rb");
FILE *outFp = fopen(argv[iout], "wb");
int inLen, outLen;
char inBuf[BUFSIZ], outBuf[BUFSIZ+EVP_MAX_BLOCK_LENGTH];
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv);
while((inLen=fread(inBuf, 1, sizeof(inBuf), inFp))>0){
if(!EVP_EncryptUpdate(ctx, outBuf, &outLen, inBuf, inLen)){
printf("ERRORn");
EVP_CIPHER_CTX_cleanup(ctx);
exit(2);
}
fwrite(outBuf, 1, outLen, outFp);
}
if(!EVP_EncryptFinal_ex(ctx, outBuf, &outLen)){
printf("ERRORn");
EVP_CIPHER_CTX_cleanup(ctx);
exit(2);
}
fwrite(outBuf, 1, outLen, outFp);
EVP_CIPHER_CTX_cleanup(ctx);
fclose(inFp);
fclose(outFp);
char* cipher = readFile(argv[iout]);
char* hashVal;
hashVal = HMAC(EVP_sha256(), key,strlen((char*)key), cipher, strlen((char*)cipher), NULL, NULL);
FILE *tagFp = fopen(argv[itag], "w");
fwrite(hashVal, 1, strlen(hashVal), tagFp);
exit(0);
}
else if(!strcmp(argv[1], "dec")){
FILE *inFp = fopen(argv[iin],"rb");
char* cipher = readFile(argv[iin]);
char* hashVal;
hashVal = HMAC(EVP_sha256(), key,strlen(key), cipher, strlen(cipher), NULL, NULL);
char* compareVal = readFile(argv[itag]);
if(strcmp(compareVal, hashVal) != 0){
printf("VERIFICATION FAILUREn");
exit(1);
}
FILE *outFp = fopen(argv[iout], "wb");
int inLen, outLen;
char inBuf[BUFSIZ], outBuf[BUFSIZ+EVP_MAX_BLOCK_LENGTH];
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv);
//printf("First Buf %sn", outBuf);
//printf("First Buf %sn", inBuf);
while((inLen=fread(inBuf, 1, sizeof(inBuf), inFp))>0){
//printf("Second Buf %sn", outBuf);
//printf("Second Buf %sn", inBuf);
if(!EVP_DecryptUpdate(ctx, outBuf, &outLen, inBuf, inLen)){
printf("ERRORn");
EVP_CIPHER_CTX_cleanup(ctx);
exit(2);
}
//printf("Third Buf %sn", outBuf);
//printf("Third Buf %sn", inBuf);
//printf("First number %dn", inLen);
fwrite(outBuf, 1, outLen, outFp);
}
if(!EVP_DecryptFinal_ex(ctx, outBuf, &outLen)){
printf("ERRORn");
EVP_CIPHER_CTX_cleanup(ctx);
exit(2);
}
fwrite(outBuf, 1, outLen, outFp);
EVP_CIPHER_CTX_cleanup(ctx);
fclose(inFp);
fclose(outFp);
exit(0);
}
exit(0);
}
And below is the Makefile that I have used to compile the upper code with gcc, using "make" command:
cryp: cryp.o
gcc -o $@ cryp.o -lssl -lcrypto
cryp.o: cryp.c
For the original.txt and shared.key, I just typed a simple sentence like "This is original text" and "This is shared key".
With these elements, I used ./cryp enc -key shared.key -in original.txt -out encrypted.txt -tag encrypted.tag
instruction in Linux terminal(Within the file storage that contains the elements) for encryption, and ./cryp dec -key shared.key -in encrypted.txt -tag encrypted.tag -out decrypted.txt
instuction for decryption.
After these actions, I expected a decrypted.txt that contains same content as original.txt, but the generated decrypted.txt showed words that are broken into stange symbols…What can I do to fix this situation? PLEASE help…
————————————————————————————————————
I fixed the code(To be specific, the readFile() function part) and it worked for me! I posted the fixed function below, but as I can declare it solved by my code after two days, I want to modify the code if there are any problems left to solve. Please help!
2
Answers
After the kind comments and answers, I examined the code, and fixed the readFile() function part, and it worked! I am posting this to save others who may suffer from same problem... The changed readFile() function looks like this:
Thanks for the help, AGAIN!
One problem that could be causing your issue if you’re running on Windows is this code
does not tell you the size of the file. (Nevermind even if it does "work" it will still fail anyway for files larger than 2 GB…)
fp
is a text stream, and per 7.21.9.4 The ftell function, paragraph 2 of the (draft) C11 standard (bolding mine):Whoever taught you to use
fseek()
/ftell()
to get the size of a file taught you a fundamentally flawed method. It’s not portable – it won’t work on text streams on Windows, for just one example.And you can’t fix that by using a binary stream, either:
In fact:
See How do you determine the size of a file in C?.