Reputation: 745
I'm using libgcrypt to encrypt and decrypt files. When I take in the proper amount of bytes using fread, I need to pad it with 16-n bytes in order for it to properly be encrypted by gcry_cipher_encrypt
. Upon decryption however, the null bytes/padding is still present. Is there any way to read and write in 16 byte blocks and still strip the padding at the end?
#include <stdio.h>
#include <gcrypt.h>
#define algo GCRY_CIPHER_AES128
#define mode GCRY_CIPHER_MODE_CBC
#define KEY_LENGTH 16
#define BLOCK_LENGTH 16
int main(){
char IV[16];
char *encBuffer = NULL;
FILE *in, *out, *reopen;
char *key = "A key goes here!";
gcry_cipher_hd_t handle;
int bufSize = 16, bytes;
memset(IV, 0, 16);
encBuffer = malloc(bufSize);
in = fopen("in.txt", "r");
out = fopen("out.txt", "w");
gcry_cipher_open(&handle, algo, mode, 0);
gcry_cipher_setkey(handle, key, KEY_LENGTH);
gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);
while(1){
bytes = fread(encBuffer, 1, bufSize, in);
if (!bytes) break;
while(bytes < bufSize)
encBuffer[bytes++] = 0x0;
gcry_cipher_encrypt(handle, encBuffer, bufSize, NULL, 0);
bytes = fwrite(encBuffer, 1, bufSize, out);
}
gcry_cipher_close(handle);
fclose(in);
fclose(out);
gcry_cipher_open(&handle, algo, mode, 0);
gcry_cipher_setkey(handle, key, KEY_LENGTH);
gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);
reopen = fopen("out.txt", "r");
out = fopen("decoded.txt", "w");
while(1){
bytes = fread(encBuffer, 1, bufSize, reopen);
if (!bytes) break;
gcry_cipher_decrypt(handle, encBuffer, bufSize, NULL, 0);
bytes = fwrite(encBuffer, 1, bufSize, out);
}
gcry_cipher_close(handle);
free(encBuffer);
return 0;
}
Upvotes: 2
Views: 939
Reputation: 8467
When you are writing the file and come to the last block, then just as your answer is doing, pad it out to a full 16 bytes. What you are missing is that if the file happens to end on a full 16 byte block, then add another full block of padding -- the padding size is 16 -- 16 bytes of the value 16 in a row.
Now when you read the file, just read to the end. The last block will be a full 16 bytes. Just look at the very last byte. Whatever value that last byte is, that is your padding. Remove that many bytes from the end of the last block. If the value is 16 remove the entire last block.
If you don't add a full 16-byte pad, you will never know if those last three bytes of 3, 3, 3 at the end of the file were padding, or, important data. It will look exactly the same.
Upvotes: 0
Reputation: 745
I was able to fix it by correctly storing the amount of padding and then checking for it later, suggested by zaph. I used PKCS#7 in order to determine how many bytes to write and of what type. I could've written NULL bytes, but there would've been no difference, so might as well stick to a standard.
#include <stdio.h>
#include <gcrypt.h>
#define algo GCRY_CIPHER_AES128
#define mode GCRY_CIPHER_MODE_CBC
#define KEY_LENGTH 16
#define BLOCK_LENGTH 16
int main(){
char IV[16], *encBuffer = NULL;
char *key = "A key goes here!";
int bufSize = 16, bytes, i=0, padding;
FILE *in, *out, *reopen;
gcry_cipher_hd_t handle;
memset(IV, 0, 16);
encBuffer = malloc(bufSize);
// Open in/out for reading and writing
in = fopen("in.txt", "r");
out = fopen("out.txt", "w");
// Set handle for encryption
gcry_cipher_open(&handle, algo, mode, 0);
gcry_cipher_setkey(handle, key, KEY_LENGTH);
gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);
// Read from in, write encrypted to out
while(1){
bytes = fread(encBuffer, 1, bufSize, in);
if (!bytes) break;
// If fread grabbed less than 16 bytes, that's our final line
// Use the byte number for padding and pad N bytes of N
if ( bytes < BLOCK_LENGTH ){ padding = 16-bytes; }
while(bytes < bufSize)
encBuffer[bytes++] = padding;
gcry_cipher_encrypt(handle, encBuffer, bytes, NULL, 0);
bytes = fwrite(encBuffer, 1, bufSize, out);
}
// Close handle and i/o files
gcry_cipher_close(handle);
fclose(in);
fclose(out);
// Set handle for decryption
gcry_cipher_open(&handle, algo, mode, 0);
gcry_cipher_setkey(handle, key, KEY_LENGTH);
gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);
// Reopen outfile, open decoded file
reopen = fopen("out.txt", "r");
out = fopen("decoded.txt", "w");
//Loop until EOF
while(1){
i=0;
bytes = fread(encBuffer, 1, bufSize, reopen);
if (!bytes) break;
gcry_cipher_decrypt(handle, encBuffer, bufSize, NULL, 0);
// Read each block and check for padding
while ( i++ < BLOCK_LENGTH ){
// If padding is found write 16-padding bytes
if ( encBuffer[i] == padding ){
bytes = fwrite(encBuffer, 1, (16-padding), out);
return 0;
}
}
// If padding isn't found, write the whole buffer
bytes = fwrite(encBuffer, 1, bufSize, out);
}
// Close the handle and free the buffer
gcry_cipher_close(handle);
free(encBuffer);
return 0;
}
Upvotes: 1
Reputation: 119877
Block ciphers have many modes of operation. Some require input data length to be a mmultiple of block size, and thus essentially require plaintext padding; some don't. See more about this here.
If you must use a mode that requires padding, you must save the plaintext length along with the encrypted data. The simplest way is to write it in an additional block in the end (encrypt that block too!). There are other, more sophisticated schemes, that don't always require addimg a block; see this.
Upvotes: 1
Reputation: 5265
This is in my comment, but I suspect you need to call gcry_cipher_encrypt(handle, encBuffer, bytes, NULL, 0);
instead. fread
returns the number of bytes it read from the file. If you constantly encrypt with bufSize
bytes, then when you get to the end of the file in the loop, there are going to be extra bytes fed to the encryption function that were not part of the file, unless the file is a perfect multiple of 16 bytes. The encryption library doesn't know what's file data and what isn't, and it's correctly encrypting and decrypting the extra NULLs at the end that are in your encBuffer
buffer. My guess is the user shouldn't have to worry about padding their own data, the library you're using should take care of that under the hood.
A quick check would be to initialize encBuffer with 0x20 and then see if your decrypted file has extra spaces at the end of it
Upvotes: 0
Reputation: 112857
cry_cipher does not seem to make any mention of padding but padding is necessary if the data to be encrypted is not always a multiple of the block size. Kind of a Bozo move not to mention padding, perhaps it is always PKCS#7 padding.
If there is padding the usual padding is PKCS#7 (PKCS#5 is essential the same thing). PKCS#7 always adds at least one to the block size bytes of padding and the padding value is the number or padding bytes. On decryption the padding is removed.
This means that if the input data to be encrypted is an exact multiple of the block size (16-bytes for AES) a block of padding will be added. SO with 16-bytes in the encrypted output will be 32-bytes.
Upvotes: 0