cmacoscryptographylibsodium

crypto_secretbox_easy appends several '[NUL]' characters to the end of text


I have created two functions 'encryptf', and 'decrytpf' (named with 'f' to avoid naming conflict with pre-existing libsodium functions. These functions are pretty self explanatory, they encrypt, and decrypt a given input string using a provided key.

The following is the encryption method. It should be noted that the 'key' is simple a randomly generated string of ANSI characters.

int encryptf(const char *input_file, const char *output_file, const unsigned char *key) {  
    if (sodium_init() < 0) {  
        printf("Error initializing\n");  
        return 1;  
    }  
  
    // Get input and output files and ensure read/write, creation if needed.  
  FILE *input = fopen(input_file, "r");  
    FILE *output = fopen(output_file, "ab+");  
    if (!input || !output) {  
        printf("Error opening files to encrypt\n");  
        return 1;  
    }  
  
    // Get file size  
  fseek(input, 0, SEEK_END);  
    size_t file_size = ftell(input);  
    fseek(input, 0, SEEK_SET);  
  
    // Allocate memory for plaintext and ciphertext  
  unsigned char *plaintext = (unsigned char *) malloc(file_size);  
    unsigned char *ciphertext = (unsigned char *) malloc(file_size + crypto_secretbox_MACBYTES);  
  
    // Create nonce char array, and populate it with secure randombytes from libsodium library  
  unsigned char nonce[crypto_secretbox_NONCEBYTES];  
    randombytes(nonce, sizeof(nonce));  
  
    // Read plaintext from file  
  fread(plaintext, 1, file_size, input);  
  
    // Create 'mac' array to get rid of authentication tag.  
  unsigned char mac[crypto_secretbox_NONCEBYTES];  
  
    // Encrypt plaintext  
  crypto_secretbox_easy(ciphertext, plaintext, file_size, nonce, key);  
  
    // Write nonce and ciphertext to output file  
  fwrite(nonce, 1, sizeof(nonce), output);  
    fwrite(ciphertext, 1, file_size + crypto_secretbox_MACBYTES, output);  
  
    // Cleanup  
  fclose(input);  
    fclose(output);  
    free(plaintext);  
    free(ciphertext);  
  
    return 0;  
}

And the decryption function:

int decryptf(const char *input_file, const char *output_file, const unsigned char *key) {  
    if (sodium_init() < 0) {  
        printf("Error initializing Libsodium\n");  
        return 1;  
    }  
  
    FILE *input = fopen(input_file, "rb");  
    FILE *output = fopen(output_file, "wb");  
    if (!input || !output) {  
        printf("Error opening files to decrypt\n");  
        return 1;  
    }  
  
    // Read nonce from file  
  unsigned char nonce[crypto_secretbox_NONCEBYTES];  
    fread(nonce, 1, sizeof(nonce), input);  
  
    // Get file size excluding nonce  
  fseek(input, 0, SEEK_END);  
    size_t file_size = ftell(input) - crypto_secretbox_NONCEBYTES;  
    fseek(input, crypto_secretbox_NONCEBYTES, SEEK_SET);  
  
    // Allocate memory for ciphertext and plaintext  
  unsigned char *ciphertext = (unsigned char *) malloc(file_size);  
    unsigned char *plaintext = (unsigned char *) malloc(file_size);  
  
    // Read ciphertext from file  
  fread(ciphertext, 1, file_size, input);  
  
    // Decrypt ciphertext  
  if (crypto_secretbox_open_easy(plaintext, ciphertext, file_size, nonce, key) != 0) {  
        printf("Error decrypting %s\n", input_file);  
        return 1;  
    }  
  
    // Write plaintext to output file  
  fwrite(plaintext, 1, file_size, output);  
  
    // Cleanup  
  fclose(input);  
    fclose(output);  
    free(ciphertext);  
    free(plaintext);  
  
    return 0;  
}

What follows is a basic example of how I am running these functions. This is not my exact code, but is an ample representation of how I am using these functions without exposing my codebase:

int main(void) {
    FILE *f1 = fopen("tests/file.txt", "ab+");  
  
    if (!f1 || !f2) {  
        printf("Failed create test files.\n");  
        exit(EXIT_SUCCESS);  
    }  
    fprintf(f1, "HELLO\n\nworld!\n..\n\thello\n../14hf\n1");  
    fclose(f1); 

    char *key = rand_string(32);
    encryptf("tests/file.txt", "tests/file_enc.txt", key);
        printf("File encrypted.\n");

        decryptf("tests/file_enc.txt", "tests/file.txt", fkey);
}

This does decrypt the contents of the file, but also appends the following strange characters to the end:

Strange characters

(I am aware stack overflow is not fond of images, but it seems as though my keyboard cannot copy them, or cannot display them, as copy/pasting them does nothing.)

I am sure it's important that these characters get removed, as I do not want to be leaving any trace, or way to decrypt, or gain any information about these files without the key, before or after encryption/decryption.

I have tried removing the authentication code provided by libsodium, as I was hoping that could be the issue, but it seems as though doing so ruins the file decryption process.

I'm not sure if it will be important, but I am running on a 2023 MacBook Pro with apple's M2 chip.

Thank you all for any help you can provide!


Solution

  • To all those who might run into this same issue, I solved it by changing the way I used fwrite, from:

    fwrite(plaintext, 1, file_size, output);
    

    to

    fwrite(plaintext, 1, file_size - crypto_secretbox_MACBYTES, output);
    

    To account for the MAC authentication tag that crypto_secretbox_easy adds.