Search code examples
caeslibgcrypt

AES CCM Encryption and Decryption in Libgcrypt


I have a problem encrypting/decrypting a simple 16 byte message in Libgcrypt while using CCM mode of operation and AES algorithm. In the documentation of Libgcrypt I cannot find which parameters to set for CCM (should I set IV or counter?).

I am stuck with the following code:

gcry_error_t     err;
gcry_cipher_hd_t hd;

char * key = "1234567890123456";
char * plainText = "VNiJkPzAWPFm1234";
size_t messageSize = strlen(plainText);
char * cipherText = malloc(messageSize);
char * recoveredText = malloc(messageSize);    

err = gcry_cipher_open(
    &hd,
    GCRY_CIPHER_AES128,
    GCRY_CIPHER_MODE_CCM,
    0);

err = gcry_cipher_setkey(hd, key, 16);

/* What to do here? */

err = gcry_cipher_encrypt(
    hd,
    cipherText,
    messageSize,
    plainText,
    messageSize);

err = gcry_cipher_decrypt(
    hd,
    recoveredText,
    messageSize,
    cipherText,
    messageSize);

How do I perform simple encryption/decryption using AES128 and CCM in Libgcrypt?


Solution

  • Example

    The example is without additional non-encrypted data, therefore param[1] of gcry_cipher_ctl setting the GCRYCTL_SET_CCM_LENGTHS is 0. But it could be easily adjusted if required.

    The functionality is divided into three parts:

    • prepare (set the key, the nonce (IV), and the CCM lengths)
    • encrypt function
    • decrypt function

    If you run it, it will output the following text to the console:

    decrypted result: 'VNiJkPzAWPFm1234'
    

    Code

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <gcrypt.h>
    
    void error(const char *what, gcry_error_t err) {
        fprintf(stderr, "%s failed: %s\n", what, gcry_strerror(err));
        exit(1);
    }
    
    typedef struct {
        const void *key;
        const void *nonce;
        size_t messageSize;
        int authTagLength;
    } Config;
    
    void prepare(Config config, gcry_cipher_hd_t *hd) {
        gcry_error_t err = gcry_cipher_open(hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CCM, 0);
        if (err) { error("gcry_cipher_open", err); }
    
        err = gcry_cipher_setkey(*hd, config.key, strlen(config.key));
        if (err) { error("gcry_cipher_setkey", err); }
    
        err = gcry_cipher_setiv(*hd, config.nonce, strlen(config.nonce));
        if (err) { error("gcry_cipher_setiv", err); }
    
        uint64_t params[3];
        params[0] = config.messageSize;
        params[1] = 0;
        params[2] = config.authTagLength;
        err = gcry_cipher_ctl(*hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof(params));
        if (err) { error("gcry_cipher_ctl", err); }
    }
    
    void *encrypt(Config config, const void *plainText, void **tag) {
        gcry_cipher_hd_t hdEncrypt;
        prepare(config, &hdEncrypt);
    
        void *cipherText = malloc(config.messageSize);
        gcry_error_t err = gcry_cipher_encrypt(hdEncrypt, cipherText, config.messageSize, plainText, config.messageSize);
        if (err) { error("gcry_cipher_encrypt", err); }
    
        *tag = malloc(config.authTagLength);
        err = gcry_cipher_gettag(hdEncrypt, *tag, config.authTagLength);
        if (err) { error("gcry_cipher_encrypt", err); }
    
        gcry_cipher_close(hdEncrypt);
    
        return cipherText;
    }
    
    void *decrypt(Config config, void *cipherText, void *tag) {
        gcry_cipher_hd_t hdDecrypt;
        prepare(config, &hdDecrypt);
    
        void *recoveredText = malloc(config.messageSize);
        gcry_error_t err = gcry_cipher_decrypt(hdDecrypt, recoveredText, config.messageSize, cipherText, config.messageSize);
        if (err) { error("gcry_cipher_decrypt", err); }
    
        err = gcry_cipher_checktag(hdDecrypt, tag, config.authTagLength);
        if (gpg_err_code (err) == GPG_ERR_CHECKSUM) {
            error("Authentication", err);
        }
    
        gcry_cipher_close(hdDecrypt);
        return recoveredText;
    }
    
    int main() {
        const char *plainText = "VNiJkPzAWPFm1234";
    
        Config config;
        config.key = "1234567890123456";
        config.nonce = "0123456789012";
        config.messageSize = strlen(plainText) + 1;
        config.authTagLength = 10;
    
        void *tag;
        void *cipherText = encrypt(config, plainText, &tag);
    
        char *recoveredText = decrypt(config, cipherText, tag);
        printf("decrypted result: '%s'\n", recoveredText);
    
        free(tag);
        free(cipherText);
        free(recoveredText);
    
        return 0;
    }