Example for using mbed-tls for partial file encryption? Need help with my attempted sample code

I am attempting to create an example that encrypts only the first 2K, 4K, or 8K of a file but my encryption → decryption always seems to be 52 bytes of (encryption always outputs 4148 bytes, for example and the decryption specifically 4096 with no final cipher update output). What am I missing?

#define _POSIX_C_SOURCE 200112L

#include “mbedtls/build_info.h”
#include “mbedtls/platform.h”

#include “mbedtls/cipher.h”
#include “mbedtls/md.h”
#include “mbedtls/platform.h”
#include “mbedtls/platform_util.h”

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <unistd.h>

#define MODE_ENCRYPT 1
#define MODE_DECRYPT 0

// File encryption helper function

int encryptFile(char *strFileName)
{
unsigned n;
int mode, i, ret = 1;
int exit_code = MBEDTLS_EXIT_FAILURE;

size_t keylen, ilen, olen;
FILE *fin = NULL, *fout = NULL;

char *p;
unsigned char IV[16];
unsigned char key[512];
unsigned char digest[MBEDTLS_MD_MAX_SIZE];
unsigned char buffer[1024];
unsigned char output[1024];
unsigned char diff;

const mbedtls_cipher_info_t *cipher_info;
const mbedtls_md_info_t *md_info;

off_t filesize, offset;

mbedtls_cipher_context_t cipher_ctx;
mbedtls_md_context_t md_ctx;

mbedtls_cipher_init(&cipher_ctx);
mbedtls_md_init(&md_ctx);

// Set to encrypt
mode = MODE_DECRYPT;

// Open source file
if ((fin = fopen(strFileName, "rb")) == NULL) {
    mbedtls_fprintf(stderr, "fopen(%s,rb) failed\n",strFileName);
    goto exit;
}

mbedtls_fprintf(stdout,"encryptFile() -> DEBUG: Done opening:%s\n", strFileName);

// Open destination file
char strOutputFileName[255] = "";
char suffix[] = ".enc";

strcpy(strOutputFileName, strFileName);
strcat(strOutputFileName, suffix);

// mbedtls_fprintf(stdout,"DEBUG: Output filename=%s\n", strOutputFileName);

if ((fout = fopen(strOutputFileName, "wb+")) == NULL) {
    mbedtls_fprintf(stderr, "fopen(%s,wb+) failed\n",strOutputFileName);
    goto exit;
}

/* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
mbedtls_setbuf(fin, NULL);
mbedtls_setbuf(fout, NULL);

/*
 * Read the Cipher and MD from the command line
 */

cipher_info = mbedtls_cipher_info_from_string("AES-128-CBC");
if (cipher_info == NULL) {
    mbedtls_fprintf(stderr, "Cipher 'AES-128-CBC' not found\n");
    goto exit;
}

if ((ret = mbedtls_cipher_setup(&cipher_ctx, cipher_info)) != 0) {
    mbedtls_fprintf(stderr, "mbedtls_cipher_setup failed\n");
    goto exit;
}

md_info = mbedtls_md_info_from_string("SHA1");
if (md_info == NULL) {
    mbedtls_fprintf(stderr, "Message Digest 'SHA1' not found\n");
    goto exit;
}

if (mbedtls_md_setup(&md_ctx, md_info, 1) != 0) {
    mbedtls_fprintf(stderr, "mbedtls_md_setup failed\n");
    goto exit;
}

/*
 * TODO: Read the secret key from file or command line
 */

const char secretKey[] = "E76B2413958B00E193";
keylen = strlen(secretKey);
        
// mbedtls_fprintf(stderr, "key1:%s\n",key);
// mbedtls_fprintf(stderr, "keylen1:%i\n",keylen);
// mbedtls_fprintf(stderr, "sizeof-keylen1:%i\n",(int) sizeof(key));
        
if (keylen > (int) sizeof(key)) {
    keylen = (int) sizeof(key);
}

memcpy(key, secretKey, keylen);
        
// mbedtls_fprintf(stderr, "key:%s\n",key);
// mbedtls_fprintf(stderr, "keylen2:%i\n",keylen);
    
if ((filesize = lseek(fileno(fin), 0, SEEK_END)) < 0) {
    perror("lseek");
    goto exit;
}

if (fseek(fin, 0, SEEK_SET) < 0) {
    mbedtls_fprintf(stderr, "fseek(0,SEEK_SET) failed\n");
    goto exit;
}

/* ENCRYPT */

if (mode == MODE_ENCRYPT) {
    mbedtls_fprintf(stdout, "DEBUG -> Mode: Encrypt.\n");
    
    /*
     * Generate the initialization vector as:
     * IV = MD( filesize || filename )[0..15]
     */
    
    for (i = 0; i < 8; i++) {
        buffer[i] = (unsigned char) (filesize >> (i << 3));
    }

    p = strFileName;

    if (mbedtls_md_starts(&md_ctx) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_starts() returned error\n");
        goto exit;
    }
    if (mbedtls_md_update(&md_ctx, buffer, 8) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_update() returned error\n");
        goto exit;
    }
    if (mbedtls_md_update(&md_ctx, (unsigned char *) p, strlen(p))
        != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_update() returned error\n");
        goto exit;
    }
    if (mbedtls_md_finish(&md_ctx, digest) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_finish() returned error\n");
        goto exit;
    }

    memcpy(IV, digest, 16);

    /*
     * Append the IV at the beginning of the output.
     */
    
    if (fwrite(IV, 1, 16, fout) != 16) {
        mbedtls_fprintf(stderr, "fwrite(%d bytes) failed\n", 16);
        goto exit;
    }
    
    // Allocate memory for mBuffer
    
    unsigned char mBuffer[16000];
    size_t totalBytesCopied = 0;
    
    // Append to mBuffer
    
    memcpy(mBuffer, IV, 16);
    totalBytesCopied += 16;
    
    // mbedtls_fprintf(stdout, "DEBUG: Length of mBuffer: %d bytes\n", strlen(mBuffer));
    // mbedtls_fprintf(stdout, "DEBUG: Total bytes copied\n", strlen(mBuffer));
    
    /*
     * Hash the IV and the secret key together 8192 times
     * using the result to setup the AES context and HMAC.
     */
    
    memset(digest, 0,  32);
    memcpy(digest, IV, 16);

    for (i = 0; i < 8192; i++) {
        if (mbedtls_md_starts(&md_ctx) != 0) {
            mbedtls_fprintf(stderr,
                            "mbedtls_md_starts() returned error\n");
            goto exit;
        }
        if (mbedtls_md_update(&md_ctx, digest, 32) != 0) {
            mbedtls_fprintf(stderr,
                            "mbedtls_md_update() returned error\n");
            goto exit;
        }
        if (mbedtls_md_update(&md_ctx, key, keylen) != 0) {
            mbedtls_fprintf(stderr,
                            "mbedtls_md_update() returned error\n");
            goto exit;
        }
        if (mbedtls_md_finish(&md_ctx, digest) != 0) {
            mbedtls_fprintf(stderr,
                            "mbedtls_md_finish() returned error\n");
            goto exit;
        }

    }

    if (mbedtls_cipher_setkey(&cipher_ctx,
                              digest,
                              (int) mbedtls_cipher_info_get_key_bitlen(cipher_info),
                              MBEDTLS_ENCRYPT) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_setkey() returned error\n");
        goto exit;
    }
    if (mbedtls_cipher_set_iv(&cipher_ctx, IV, 16) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_set_iv() returned error\n");
        goto exit;
    }
    if (mbedtls_cipher_reset(&cipher_ctx) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_reset() returned error\n");
        goto exit;
    }

    if (mbedtls_md_hmac_starts(&md_ctx, digest, 32) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_hmac_starts() returned error\n");
        goto exit;
    }

    /*
     * Encrypt and write the ciphertext.
     */
    
    int bytesProcessed = 0;
    
    for (offset = 0; offset < filesize; offset += mbedtls_cipher_get_block_size(&cipher_ctx)) {
        ilen = ((unsigned int) filesize - offset > mbedtls_cipher_get_block_size(&cipher_ctx)) ?
        mbedtls_cipher_get_block_size(&cipher_ctx) : (unsigned int) (filesize - offset);

        if (fread(buffer, 1, ilen, fin) != ilen) {
            mbedtls_fprintf(stderr, "fread(%ld bytes) failed\n", (long) ilen);
            goto exit;
        }

        if (mbedtls_cipher_update(&cipher_ctx, buffer, ilen, output, &olen) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_cipher_update() returned error\n");
            goto exit;
        }

        if (mbedtls_md_hmac_update(&md_ctx, output, olen) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_md_hmac_update() returned error\n");
            goto exit;
        }

        if (fwrite(output, 1, olen, fout) != olen) {
            mbedtls_fprintf(stderr, "fwrite(%ld bytes) failed\n", (long) olen);
            goto exit;
        }
        
        // Append to mBuffer
        memcpy(mBuffer + totalBytesCopied, output, olen);
        totalBytesCopied += olen;
        
        bytesProcessed += olen;
        mbedtls_fprintf(stdout, "DEBUG2 FOR LOOP: Bytes processed   : >>%ld<<\n", (int) bytesProcessed);
        mbedtls_fprintf(stdout, "DEBUG2 FOR LOOP: Total bytes copied: >>%ld<<\n", (long) totalBytesCopied);
        
        // Break if the 4KB limit is reached
        
        if (bytesProcessed >= 4096) {
            break;
        }
        
    }

    if (mbedtls_cipher_finish(&cipher_ctx, output, &olen) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_finish() returned error\n");
        goto exit;
    }
    if (mbedtls_md_hmac_update(&md_ctx, output, olen) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_hmac_update() returned error\n");
        goto exit;
    }

    if (fwrite(output, 1, olen, fout) != olen) {
        mbedtls_fprintf(stderr, "fwrite(%ld bytes) failed\n", (long) olen);
        goto exit;
    }
    
    // Append to mBuffer
    memcpy(mBuffer + totalBytesCopied, output, olen);
    totalBytesCopied += olen;
    
    mbedtls_fprintf(stdout, "DEBUG2.5 FOR LOOP: Bytes processed   : >>%ld<<\n", (int) bytesProcessed);
    mbedtls_fprintf(stdout, "DEBUG2.5 FOR LOOP: Total bytes copied: >>%ld<<\n", (long) totalBytesCopied);
    
    /*
     * Finally write the HMAC.
     */
    
    if (mbedtls_md_hmac_finish(&md_ctx, digest) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_hmac_finish() returned error\n");
        goto exit;
    }

    if (fwrite(digest, 1, mbedtls_md_get_size(md_info), fout) != mbedtls_md_get_size(md_info)) {
        mbedtls_fprintf(stderr, "fwrite(%d bytes) failed\n", mbedtls_md_get_size(md_info));
        goto exit;
    } else {
        mbedtls_fprintf(stdout, "fwrite(%d bytes) success\n", mbedtls_md_get_size(md_info));
    }

    mbedtls_fprintf(stdout, "DEBUG3 BEFORE HMAC: Total bytes copied    : >>%ld<<\n", (long) totalBytesCopied);
    mbedtls_fprintf(stdout, "DEBUG3 BEFORE mbedtls_md_get_size(md_info): >>%ld<<\n", mbedtls_md_get_size(md_info));
    
    // Append HMAC to mBuffer
    
    memcpy(mBuffer + totalBytesCopied, digest, mbedtls_md_get_size(md_info));
    totalBytesCopied += mbedtls_md_get_size(md_info);
    
    mbedtls_fprintf(stdout, "DEBUG3 HMAC: Total bytes copied    : >>%ld<<\n", (long) totalBytesCopied);
    mbedtls_fprintf(stdout, "DEBUG3 mbedtls_md_get_size(md_info): >>%ld<<\n", mbedtls_md_get_size(md_info));

// FILE* pFile;
// pFile = fopen(“/tmp/files/example.txt.enc.2”,“wb”);
// if (pFile) {
// fwrite(mBuffer, 1, totalBytesCopied, pFile);
// }
// fclose(pFile);

    // Overwrite the original file
    
    if (fin) {
        fclose(fin);
    }
    
    FILE* oFile;
    oFile = fopen(strFileName,"rb+");
    if (oFile) {
        fseek(oFile, 0, SEEK_SET);
        fwrite(mBuffer, 1, totalBytesCopied, oFile);
        mbedtls_fprintf(stdout, "DEBUG: Sucessfully wrote to %s %d bytes\n",strFileName,totalBytesCopied);
    } else {
        mbedtls_fprintf(stdout, "DEBUG: Unable to open original file for overwriting!\n");
    }
    fclose(oFile);
    
}

/* DECRYPT */

if (mode == MODE_DECRYPT) {
    mbedtls_fprintf(stdout, "DEBUG -> Mode: Decrypt.\n");
    /*
     *  The encrypted file must be structured as follows:
     *
     *        00 .. 15              Initialization Vector
     *        16 .. 31              Encrypted Block #1
     *           ..
     *      N*16 .. (N+1)*16 - 1    Encrypted Block #N
     *  (N+1)*16 .. (N+1)*16 + n    Hash(ciphertext)
     */
    
    if (filesize < 16 + mbedtls_md_get_size(md_info)) {
        mbedtls_fprintf(stderr, "File too short to be decrypted.\n");
        goto exit;
    }

    if (mbedtls_cipher_get_block_size(&cipher_ctx) == 0) {
        mbedtls_fprintf(stderr, "Invalid cipher block size: 0. \n");
        goto exit;
    }

    /*
     * Check the file size.
     */
    
    if (mbedtls_cipher_info_get_mode(cipher_info) != MBEDTLS_MODE_GCM &&
        ((filesize - mbedtls_md_get_size(md_info)) %
         mbedtls_cipher_get_block_size(&cipher_ctx)) != 0) {
        mbedtls_fprintf(stderr, "File content not a multiple of the block size (%u).\n",
                        mbedtls_cipher_get_block_size(&cipher_ctx));
        // TODO: 4KB goto exit;
    }

    /*
     * Subtract the IV + HMAC length.
     */
    
    filesize -= (16 + mbedtls_md_get_size(md_info));

    /*
     * Read the IV and original filesize modulo 16.
     */
    
    if (fread(buffer, 1, 16, fin) != 16) {
        mbedtls_fprintf(stderr, "fread(%d bytes) failed\n", 16);
        goto exit;
    }

    memcpy(IV, buffer, 16);

    /*
     * Hash the IV and the secret key together 8192 times
     * using the result to setup the AES context and HMAC.
     */
    
    memset(digest, 0,  32);
    memcpy(digest, IV, 16);

    for (i = 0; i < 8192; i++) {
        if (mbedtls_md_starts(&md_ctx) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_md_starts() returned error\n");
            goto exit;
        }
        if (mbedtls_md_update(&md_ctx, digest, 32) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_md_update() returned error\n");
            goto exit;
        }
        if (mbedtls_md_update(&md_ctx, key, keylen) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_md_update() returned error\n");
            goto exit;
        }
        if (mbedtls_md_finish(&md_ctx, digest) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_md_finish() returned error\n");
            goto exit;
        }
    }

    if (mbedtls_cipher_setkey(&cipher_ctx,
                              digest,
                              (int) mbedtls_cipher_info_get_key_bitlen(cipher_info),
                              MBEDTLS_DECRYPT) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_setkey() returned error\n");
        goto exit;
    }

    if (mbedtls_cipher_set_iv(&cipher_ctx, IV, 16) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_set_iv() returned error\n");
        goto exit;
    }

    if (mbedtls_cipher_reset(&cipher_ctx) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_reset() returned error\n");
        goto exit;
    }

    if (mbedtls_md_hmac_starts(&md_ctx, digest, 32) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_hmac_starts() returned error\n");
        goto exit;
    }
    
    // Allocate memory for dBuffer
    
    unsigned char dBuffer[16000];
    size_t totalBytesDecrypted = 0;

    /*
     * Decrypt and write the plaintext.
     */
    
    int bytesProcessed = 0;
    
    for (offset = 0; offset < filesize; offset += mbedtls_cipher_get_block_size(&cipher_ctx)) {
        ilen = ((unsigned int) filesize - offset > mbedtls_cipher_get_block_size(&cipher_ctx)) ?
               mbedtls_cipher_get_block_size(&cipher_ctx) : (unsigned int) (filesize - offset);

        if (fread(buffer, 1, ilen, fin) != ilen) {
            mbedtls_fprintf(stderr, "fread(%u bytes) failed\n",
                            mbedtls_cipher_get_block_size(&cipher_ctx));
            goto exit;
        }

        if (mbedtls_md_hmac_update(&md_ctx, buffer, ilen) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_md_hmac_update() returned error\n");
            goto exit;
        }
        if (mbedtls_cipher_update(&cipher_ctx, buffer, ilen, output,
                                  &olen) != 0) {
            mbedtls_fprintf(stderr, "mbedtls_cipher_update() returned error\n");
            goto exit;
        }

        // if (fwrite(output, 1, olen, fout) != olen) {
        // mbedtls_fprintf(stderr, "fwrite(%ld bytes) failed\n", (long) olen);
        //    goto exit;
        // }
        
        // Append to mBuffer
        memcpy(dBuffer + totalBytesDecrypted, output, olen);
        totalBytesDecrypted += olen;
        
        bytesProcessed += olen;
        mbedtls_fprintf(stdout, "DEBUG2 FOR LOOP: Bytes processed   : >>%ld<<\n", (int) bytesProcessed);
        mbedtls_fprintf(stdout, "DEBUG2 FOR LOOP: Bytes decrypted   : >>%ld<<\n", (int) totalBytesDecrypted);
        
        // Break if the 4KB limit is reached
        if (bytesProcessed >= 4096) {
            break;
        }
    }

    /*
     * Verify the message authentication code.
     */
    
    if (mbedtls_md_hmac_finish(&md_ctx, digest) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_md_hmac_finish() returned error\n");
        // goto exit;
    }
    
    if (fread(buffer, 1, mbedtls_md_get_size(md_info), fin) != mbedtls_md_get_size(md_info)) {
        mbedtls_fprintf(stderr, "fread(%d bytes) failed\n", mbedtls_md_get_size(md_info));
        // goto exit;
    }

    /* Use constant-time buffer comparison */

    diff = 0;
    for (i = 0; i < mbedtls_md_get_size(md_info); i++) {
        diff |= digest[i] ^ buffer[i];
    }
    
    if (diff != 0) {
        mbedtls_fprintf(stderr, "HMAC check failed: wrong key, or file corrupted.\n");
        // TODO: 4KB goto exit;
    }
    
    // Write the final block of data
    
    if (mbedtls_cipher_finish(&cipher_ctx, output, &olen) != 0) {
        mbedtls_fprintf(stderr, "mbedtls_cipher_finish() returned error\n");
        // TODO: 4KB goto exit;
    }

    // if (fwrite(output, 1, olen, fout) != olen) {
    //    mbedtls_fprintf(stderr, "fwrite(%ld bytes) failed\n", (long) olen);
    //    // TODO: 4KB goto exit;
    // }

    // Append to mBuffer
    memcpy(dBuffer + totalBytesDecrypted, output, olen);
    totalBytesDecrypted += olen;
    mbedtls_fprintf(stderr, "mbedtls_cipher_finish() Writing final block: (%ld bytes)\n", (long) olen);

    mbedtls_fprintf(stdout, "DEBUG2 FOR LOOP: Bytes processed   : >>%ld<<\n", (int) bytesProcessed);
    mbedtls_fprintf(stdout, "DEBUG2 FOR LOOP: Bytes decrypted   : >>%ld<<\n", (int) totalBytesDecrypted);
    
    // Overwrite the original file
    
    if (fin) {
        fclose(fin);
    }
    if (fout) {
        fclose(fout);
    }
    
    FILE* oFile;
    oFile = fopen(strFileName,"rb+");
    if (oFile) {
        fseek(oFile, 0, SEEK_SET);
        fwrite(dBuffer, 1, totalBytesDecrypted, oFile);
        mbedtls_fprintf(stdout,"Successfully wrote to oFile: %d bytes\n",totalBytesDecrypted);
    } else {
        mbedtls_fprintf(stderr, "DEBUG2: Unable to open original file for overwriting!\n");
    }
    fclose(oFile);
}

exit_code = MBEDTLS_EXIT_SUCCESS;
return 0;

exit:

if (fin) {
    fclose(fin);
}
if (fout) {
    fclose(fout);
}

mbedtls_platform_zeroize(IV,     sizeof(IV));
mbedtls_platform_zeroize(key,    sizeof(key));
mbedtls_platform_zeroize(buffer, sizeof(buffer));
mbedtls_platform_zeroize(output, sizeof(output));
mbedtls_platform_zeroize(digest, sizeof(digest));

mbedtls_cipher_free(&cipher_ctx);
mbedtls_md_free(&md_ctx);

mbedtls_exit(exit_code);
return -1;

}

/* Main */

int main() {

char *fileName = "/tmp/files/example.txt";
encryptFile(fileName);

return 0;

}