cencryptionlibgcrypt

Strip AES padding during fread/fwrite


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;
}

Solution

  • 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;
    }