The decryption and encryption code (included below) may be possibly wrong. They're compiling error free, however, the decrypted file is not same as the plaintext file. It is garbled.
I am trying to find out why it is garbled. I doubt my code to be fine.
I did a bunch of things which could possibly solve the issue, but none worked. I have a specific way of using AES-CTR, i.e., I am not directly including OpenSSL. I am depending upon /dev/urandom /dev/random for randomness (of IV, et cetera).
Those bunch of things includes the way I am dealing with my encrypt and decrypt function (like: using *(ptr)
instead of ptr[]
and several other equivalent replacements, which usually are unnecessary), what arguments I am passing and checking for certain validations before finally saving the encrypted or decrypted texts.
I am not incrementing (or altering) the initialized IV. IV is 16 bytes.
Earlier, I was doing this to increment the last byte of IV:
size_t blocksize = CCA_STRENGTH; /* CCA_STRENGTH is "#define CCA_STRENGTH 16" */
char *iv = (char *) malloc(16 * sizeof(char));
ri(); /* PRNG initialized.*/
prng_getbytes(iv, blocksize);
*
*
*
*
size_t iv_counter = 0;
while loop starts here...
*
*
*(iv +(block_size - 1)) += iv_counter;
*
*
while loop ends here...
This while loop where the encryption and decryption is happening is present below (this part is temporarily removed as it is a security requirement and not a coding standard or encrypt or decrypt function's requirement).
Here is my decrypt code:
#include "pv.h"
void decrypt_file (const char *ptxt_fname, void *raw_sk, size_t raw_len, int fin)
{
int fd;
if((fd = open(ptxt_fname, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0)
{
/*fd will become -1 on failure.*/
perror("plaintext file permission error\n");
exit(EXIT_FAILURE);
}
size_t block_size = CCA_STRENGTH;
const char *aes_key;
aes_key = (const char *) raw_sk;
aes_ctx aes;
aes_setkey(&aes, aes_key, block_size);
char *iv;
iv = (char *) malloc(block_size * sizeof(char));
size_t bytes_read;
bytes_read = read(fin, iv, block_size);
char *buf = (char *) malloc(block_size * sizeof(char)); /*ctxt-file read buffer*/
char *ptxt = (char *) malloc(block_size * sizeof(char)); /*p-text buffer*/
memset(ptxt, 0, block_size);
bytes_read = read(fin, buf, block_size);
while(bytes_read >= 1)
{
aes_encrypt(&aes, ptxt, iv); /* earlier it was "aes_decrypt(&aes, ptxt, iv);" which was not the correct reverse transformation function */
for(loop_variable = 0; loop_variable < bytes_read; loop_variable++)
{
*(ptxt + loop_variable) = *(ptxt + loop_variable) ^ *(buf + loop_variable);
}
if((result = write_chunk(fd, ptxt, bytes_read)) == -1)
{
perror("Problem when writing to ptxt file... \n");
close(fd);
unlink(ptxt_fname); /*for file deletion*/
aes_clrkey(&aes);
free(ptxt);
free(iv);
free(buf);
exit(EXIT_FAILURE);
}
if((bytes_read = read(fin, buf, block_size)) < 1)
{
close(fd);
aes_clrkey(&aes);
free(ptxt);
free(iv);
free(buf);
break;
}
}
}
void
usage (const char *pname)
{
printf ("Simple File Decryption Utility\n");
printf ("Usage: %s SK-FILE CTEXT-FILE PTEXT-FILE\n", pname);
printf (" Exits if either SK-FILE or CTEXT-FILE don't exist, or\n");
printf (" if a symmetric key sk cannot be found in SK-FILE.\n");
printf (" Otherwise, tries to use sk to decrypt the content of\n");
printf (" CTEXT-FILE: upon success, places the resulting plaintext\n");
printf (" in PTEXT-FILE; if a decryption problem is encountered\n");
printf (" after the processing started, PTEXT-FILE is truncated\n");
printf (" to zero-length and its previous content is lost.\n");
exit (1);
}
int main (int argc, char **argv)
{
int fdsk, fdctxt;
char *sk = NULL;
size_t sk_len = 0;
if (argc != 4) {
usage (argv[0]);
}
else if (((fdsk = open (argv[1], O_RDONLY)) == -1)
|| ((fdctxt = open (argv[2], O_RDONLY)) == -1)) {
if (errno == ENOENT) {
usage (argv[0]);
}
else {
perror (argv[0]);
exit (-1);
}
}
else {
setprogname (argv[0]);
if (!(sk = import_sk_from_file (&sk, &sk_len, fdsk))) {
printf ("%s: no symmetric key found in %s\n", argv[0], argv[1]);
close (fdsk);
exit (2);
}
close (fdsk);
decrypt_file (argv[3], sk, sk_len, fdctxt);
bzero(sk, sk_len);
free(sk);
close (fdctxt);
}
return 0;
}
Here is my encrypt code:
#include "pv.h"
void encrypt_file (const char *ctxt_fname, void *raw_sk, size_t raw_len, int fin)
{
size_t block_size = CCA_STRENGTH;
int fd; /* ctxt fd */
if((fd = open(ctxt_fname,O_WRONLY|O_TRUNC|O_CREAT,0600)) < 0)
{
perror("Ciphertext file permission error\n");
exit(EXIT_FAILURE);
}
char *iv;
iv = (char *) malloc(block_size * sizeof(char));
ri(); /*IV initialized*/
prng_getbytes(iv, block_size);
struct aes_ctx aes;
const char *aes_key = aes_key = (const char *) raw_sk;
aes_setkey(&aes, aes_key, block_size); /*sets the encryption key.*/
char *buf = buf = (char *) malloc(block_size * sizeof(char)); /*file read buffer*/
char *ctxt = ctxt = (char *) malloc(block_size * sizeof(char)); /*ciphertext buffer*/
int result;
size_t looper = 0;
size_t bytes_read;
result = write_chunk(fd, iv, block_size);
if(result == -1)
{
exit(-1);
}
bytes_read = read(fin, buf, block_size); /*returns how many bytes read*/
while(bytes_read >= 1)
{
aes_encrypt(&aes, ctxt, iv);
for(looper = 0; looper < bytes_read; looper++)
{
*(ctxt + looper) = *(ctxt + looper) ^ *(buf + looper);
}
result = write_chunk(fd, ctxt, bytes_read);
if(result == -1)
{
perror("Problem when writing to ctxt file... \n");
close(fd);
unlink(ctxt_fname); /*for file deletion*/
aes_clrkey(&aes);
free(ctxt);
free(iv);
free(buf);
exit(EXIT_FAILURE);
}
printf("crossed written to file\n");
if((bytes_read = read(fin, buf, block_size)) < 1)
{
close(fd);
aes_clrkey(&aes);
free(ctxt);
free(iv);
free(buf);
break;
}
}
}
void usage (const char *pname)
{
printf ("Personal Vault: Encryption \n");
printf ("Usage: %s SK-FILE PTEXT-FILE CTEXT-FILE\n", pname);
printf (" Exits if either SK-FILE or PTEXT-FILE don't exist.\n");
printf (" Otherwise, encrpyts the content of PTEXT-FILE under\n");
printf (" sk, and place the resulting ciphertext in CTEXT-FILE.\n");
printf (" If CTEXT-FILE existed, any previous content is lost.\n");
exit (1);
}
int main (int argc, char **argv)
{
int fdsk, fdptxt;
char *raw_sk;
size_t raw_len;
if (argc != 4)
{
usage (argv[0]);
}
else if (((fdsk = open (argv[1], O_RDONLY)) == -1) || ((fdptxt = open (argv[2], O_RDONLY)) == -1))
{
if (errno == ENOENT)
{
usage (argv[0]);
}
else
{
perror (argv[0]);
exit (-1);
}
}
else
{
setprogname (argv[0]);
if (!(import_sk_from_file (&raw_sk, &raw_len, fdsk)))
{
printf ("%s: no symmetric key found in %s\n", argv[0], argv[1]);
close (fdsk);
exit (2);
}
close (fdsk);
bzero(raw_sk, raw_len);
free(raw_sk);
close (fdptxt);
}
return 0;
}
The encrypt code is prepending 16 bytes of IV to the ciphertext file first (as it should be I guess; we read these 16 bytes from the ctxt file in the decryption process) and then the actual encryption happens (considering it as a black box) where we send the IV and the key; 16 bytes of are returned back. Those 16 bytes which are returned back has to be XORed with the plaintext file buffer (up to 16 bytes, for each round if a file is more than 16 bytes).
Post that, the XORed bytes (which will be up to 16 bytes) are written to file. This thing happens until the last round which finally breaks the loop on trying to read the file which is having no more content to read. read function, on every call, tries to read the next available bytes (up to the number of bytes specified).
This is not a perfected implementation as I have relaxed the security aspect (randomizing the IV for each cycle), however, it has to work like this first.
Mathematically, what I am trying to do should be the exact replica of this concept:
ciphertext_block = message_block ^ [AES(IV, Key)]
message_block = ciphertext_block ^ [AES(IV, Key)]
Here, ciphertext/message_block refers to a block of characters which will be 16 bytes for 1st to n-1 rounds but can or cannot be 16 bytes in the last round.
In any case, XORing will be done between 16 bytes characters (output of AES-CTR) and another block (message block for encryption, ciphertext block for decryption, any case, it can be upto 16 bytes and hence they are the XOR constraints, i.e., they will be the deciders of the length of the XOR output).
Since they are the deciders, as soon as the XORing operation covers their length, XORing loop stops and we move forward towards writing that in a file (ctxt file for encryption, ptxt file for decryption).
There should not be any padding requirements.
I am not sure if I should use realloc
functions at any place.
I will gladly provide extra documentations if any reader faces issues in understanding what I am trying to do.
The program has several library-dependencies, however, the code compiles error-free.
This is my compilation command:
gcc -g -O2 -ansi -Wall -Wsign-compare -Wchar-subscripts -Werror -I. -I/usr/include/ -I/home/devel/libdcrypt/include/ -c pv_keygen.c pv_misc.c
gcc -g -O2 -ansi -Wall -Wsign-compare -Wchar-subscripts -Werror -o pv_keygen pv_keygen.o pv_misc.o -L. -L/usr/lib/ -L/home/devel/libdcrypt/lib/ -ldcrypt -lgmp
This is just for the compilation of my encrypt file. Almost equal (and equally rigorous) commands are for my keygen and decrypt file. Keygen code seems to work fine (I haven't included that here yet); I'm able to generate a key and serialize it to a file. That key file actually holds two keys and I am only reading the first half for AES-CTR. The next half will be for MACing purposes (using AES-CBC).
File size details
Plaintext file: x bytes
Ciphertext file: (x + 16) bytes
Deciphered text file: x bytes
Statistics are right, content is not. Deciphered text file and plaintext file has to be same.
I am trying: diff plaintext_file decrypted_file on RedHat for file comparison.
Key file is actually 32 bytes of which first 16 bytes are being used for encryption and the later 16 bytes will be used for MACing post encryption is done.
Key file (which is serialized to base-64) (hex):
0000000 4662 6e4b 6631 7268 4876 676c 772f 664e
0000010 4d5a 6f32 384e 5141 7139 6635 3442 7245
0000020 646c 4a77 5553 4c30 4f63 3d6f 000a
000002d
Input plaintext file (hex):
0000000 6161 6161 6161 6161 6161 6161 610a 6161
0000010 000a
0000011
Encrypted file (hex):
0000000 540e 0e30 d74d 5000 78c1 13e3 0476 d4a2
0000010 61c9 76ac e717 cd6d 013e e872 8e16 4827
0000020 00a2
0000021
Decrypted file (hex):
0000000 8bea 616a 1f1b d6b0 fd13 da46 5824 ec00
0000010 0081
0000011
External references (I have trimmed down things to where the error can exist):
1.) http://www.scs.stanford.edu/nyu/05sp/lab/lab1.html
2) http://www.scs.stanford.edu/nyu/05sp/lab/install-libs.html
Here's is the fix:
The problem existed the way I wanted to do it and the way I am doing it. Since there is no implementation-related issue, the issue existed in my decrypt code.
In my decrypt code, there was no point of including the aes_decrypt function when the thing which I am actually looking for is an exact reverse transformation (correct technical term over decryption w.r.t. AES) on the contrary same key is being used to decrypt the same file.
As a fix, aes_decrypt should just be aes_encrypt and it will work out.
Now, I can proceed towards extra additions (security strengthening and data integrity considerations).