cencryptioncryptographygnutls

gnutls_cipher_encrypt2 returns "The request is invalid."


I'm messing around with GnuTLS and I want to write a C program which encrypts/decrypts a file using the GnuTLS functions.

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

#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>

#define KEY_LENGTH 32
#define IV_LENGTH 16


int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <input_file> <output_file> <password>\n", argv[0]);
        return 1;
    }

    int ret;

    const char *input_filename = argv[1];
    const char *output_filename = argv[2];

    gnutls_global_init();

    gnutls_cipher_hd_t handle;
    gnutls_cipher_algorithm_t cipher_alg = GNUTLS_CIPHER_AES_256_CBC;
    const gnutls_datum_t key = { (void*)"ThisIsAStrongAESKey1234567890", 32 };
    const gnutls_datum_t iv = { (void*)"RandomInitializationVector", 16 };
 
    // Initialize the session and cipher handle
    ret = gnutls_cipher_init(&handle, cipher_alg, &key, &iv);
    if (ret < 0) {
        fprintf(stderr, "Error initializing cipher: %s\n", gnutls_strerror(ret));
        return 1;
    }

    // Open the input and output files
    FILE *input_file = fopen(input_filename, "rb");
    FILE *output_file = fopen(output_filename, "wb");

    if (!input_file || !output_file) {
        perror("Error opening files");
        return 1;
    }

    // Read and encrypt the input file
    unsigned char plaintext[4096];
    unsigned char ciphertext[4096];
    size_t read_bytes;
    while ((read_bytes = fread(plaintext, 1, sizeof(plaintext), input_file)) > 0) {
        ret = gnutls_cipher_encrypt2(handle, plaintext, read_bytes, ciphertext, sizeof(ciphertext));
        if (ret < 0) {
            fprintf(stderr, "Error encrypting: %s\n", gnutls_strerror(ret));
            return 1;
        }
        fwrite(ciphertext, 1, read_bytes, output_file);
    }

    // Clean up
    fclose(input_file);
    fclose(output_file);

    gnutls_cipher_deinit(handle);
    gnutls_global_deinit();

    return 0;
}

I've actually implemented only the encryption part, to see how is working, but it is not :) When I try to encrypt a .txt file, I've got the following output:

Error encrypting: The request is invalid.

It is possible that I'm missing something on initialising the cipher? Or is there anything else I should configure before using the gnutls_cipher_encrypt2() function?

I have the GnuTLS source code and I think that's where the error code is returned: lib/cipher_int.h:96

inline static int _gnutls_cipher_encrypt2(const cipher_hd_st *handle,
                      const void *text, size_t textlen,
                      void *ciphertext,
                      size_t ciphertextlen)
{
    if (likely(handle != NULL && handle->handle != NULL)) {
        if (handle->encrypt == NULL) {
            return (GNUTLS_E_INVALID_REQUEST);
        }
        return handle->encrypt(handle->handle, text, textlen,
                       ciphertext, ciphertextlen);
    }

    return 0;
}

Should I change handle->encrypt somehow?


Solution

  • I believe that the error is caused by the fact that the size of your plaintext file is not a multiple of the AES block size (16 bytes).

    int gnutls_cipher_encrypt2 (gnutls_cipher_hd_t handle, const void * ptext, size_t ptext_len, void * ctext, size_t ctext_len)
    

    "This function will encrypt the given data using the algorithm specified by the context. For block ciphers the ptext_len MUST be a multiple of the block size. For the supported ciphers the encrypted data length will equal the plaintext size." -- https://www.gnutls.org/manual/html_node/Cryptographic-API.html#gnutls_005fcipher_005finit

    You should pad your plaintext with the 10^* padding. This is also mentioned by the NIST. See Appendix A: Padding from the NIST Special Publication 800-38A 2001. https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf