Home | History | Annotate | Line # | Download | only in cipher
      1 /*
      2  * Copyright 2013-2024 The OpenSSL Project Authors. All Rights Reserved.
      3  *
      4  * Licensed under the Apache License 2.0 (the "License").  You may not use
      5  * this file except in compliance with the License.  You can obtain a copy
      6  * in the file LICENSE in the source distribution or at
      7  * https://www.openssl.org/source/license.html
      8  */
      9 
     10 /*
     11  * Simple AES CCM authenticated encryption with additional data (AEAD)
     12  * demonstration program.
     13  */
     14 
     15 #include <stdio.h>
     16 #include <openssl/err.h>
     17 #include <openssl/bio.h>
     18 #include <openssl/evp.h>
     19 #include <openssl/core_names.h>
     20 
     21 /* AES-CCM test data obtained from NIST public test vectors */
     22 
     23 /* AES key */
     24 static const unsigned char ccm_key[] = {
     25     0xce, 0xb0, 0x09, 0xae, 0xa4, 0x45, 0x44, 0x51, 0xfe, 0xad, 0xf0, 0xe6,
     26     0xb3, 0x6f, 0x45, 0x55, 0x5d, 0xd0, 0x47, 0x23, 0xba, 0xa4, 0x48, 0xe8
     27 };
     28 
     29 /* Unique nonce to be used for this message */
     30 static const unsigned char ccm_nonce[] = {
     31     0x76, 0x40, 0x43, 0xc4, 0x94, 0x60, 0xb7
     32 };
     33 
     34 /*
     35  * Example of Additional Authenticated Data (AAD), i.e. unencrypted data
     36  * which can be authenticated using the generated Tag value.
     37  */
     38 static const unsigned char ccm_adata[] = {
     39     0x6e, 0x80, 0xdd, 0x7f, 0x1b, 0xad, 0xf3, 0xa1, 0xc9, 0xab, 0x25, 0xc7,
     40     0x5f, 0x10, 0xbd, 0xe7, 0x8c, 0x23, 0xfa, 0x0e, 0xb8, 0xf9, 0xaa, 0xa5,
     41     0x3a, 0xde, 0xfb, 0xf4, 0xcb, 0xf7, 0x8f, 0xe4
     42 };
     43 
     44 /* Example plaintext to encrypt */
     45 static const unsigned char ccm_pt[] = {
     46     0xc8, 0xd2, 0x75, 0xf9, 0x19, 0xe1, 0x7d, 0x7f, 0xe6, 0x9c, 0x2a, 0x1f,
     47     0x58, 0x93, 0x9d, 0xfe, 0x4d, 0x40, 0x37, 0x91, 0xb5, 0xdf, 0x13, 0x10
     48 };
     49 
     50 /* Expected ciphertext value */
     51 static const unsigned char ccm_ct[] = {
     52     0x8a, 0x0f, 0x3d, 0x82, 0x29, 0xe4, 0x8e, 0x74, 0x87, 0xfd, 0x95, 0xa2,
     53     0x8a, 0xd3, 0x92, 0xc8, 0x0b, 0x36, 0x81, 0xd4, 0xfb, 0xc7, 0xbb, 0xfd
     54 };
     55 
     56 /* Expected AEAD Tag value */
     57 static const unsigned char ccm_tag[] = {
     58     0x2d, 0xd6, 0xef, 0x1c, 0x45, 0xd4, 0xcc, 0xb7, 0x23, 0xdc, 0x07, 0x44,
     59     0x14, 0xdb, 0x50, 0x6d
     60 };
     61 
     62 /*
     63  * A library context and property query can be used to select & filter
     64  * algorithm implementations. If they are NULL then the default library
     65  * context and properties are used.
     66  */
     67 static OSSL_LIB_CTX *libctx = NULL;
     68 static const char *propq = NULL;
     69 
     70 static int aes_ccm_encrypt(void)
     71 {
     72     int ret = 0;
     73     EVP_CIPHER_CTX *ctx;
     74     EVP_CIPHER *cipher = NULL;
     75     int outlen, tmplen;
     76     size_t ccm_nonce_len = sizeof(ccm_nonce);
     77     size_t ccm_tag_len = sizeof(ccm_tag);
     78     unsigned char outbuf[1024];
     79     unsigned char outtag[16];
     80     OSSL_PARAM params[3] = {
     81         OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END
     82     };
     83 
     84     printf("AES CCM Encrypt:\n");
     85     printf("Plaintext:\n");
     86     BIO_dump_fp(stdout, ccm_pt, sizeof(ccm_pt));
     87 
     88     /* Create a context for the encrypt operation */
     89     if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
     90         goto err;
     91 
     92     /* Fetch the cipher implementation */
     93     if ((cipher = EVP_CIPHER_fetch(libctx, "AES-192-CCM", propq)) == NULL)
     94         goto err;
     95 
     96     /* Default nonce length for AES-CCM is 7 bytes (56 bits). */
     97     params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN,
     98         &ccm_nonce_len);
     99     /* Set tag length */
    100     params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
    101         NULL, ccm_tag_len);
    102 
    103     /*
    104      * Initialise encrypt operation with the cipher & mode,
    105      * nonce length and tag length parameters.
    106      */
    107     if (!EVP_EncryptInit_ex2(ctx, cipher, NULL, NULL, params))
    108         goto err;
    109 
    110     /* Initialise key and nonce */
    111     if (!EVP_EncryptInit_ex(ctx, NULL, NULL, ccm_key, ccm_nonce))
    112         goto err;
    113 
    114     /* Set plaintext length: only needed if AAD is used */
    115     if (!EVP_EncryptUpdate(ctx, NULL, &outlen, NULL, sizeof(ccm_pt)))
    116         goto err;
    117 
    118     /* Zero or one call to specify any AAD */
    119     if (!EVP_EncryptUpdate(ctx, NULL, &outlen, ccm_adata, sizeof(ccm_adata)))
    120         goto err;
    121 
    122     /* Encrypt plaintext: can only be called once */
    123     if (!EVP_EncryptUpdate(ctx, outbuf, &outlen, ccm_pt, sizeof(ccm_pt)))
    124         goto err;
    125 
    126     /* Output encrypted block */
    127     printf("Ciphertext:\n");
    128     BIO_dump_fp(stdout, outbuf, outlen);
    129 
    130     /* Finalise: note get no output for CCM */
    131     if (!EVP_EncryptFinal_ex(ctx, NULL, &tmplen))
    132         goto err;
    133 
    134     /* Get tag */
    135     params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
    136         outtag, ccm_tag_len);
    137     params[1] = OSSL_PARAM_construct_end();
    138 
    139     if (!EVP_CIPHER_CTX_get_params(ctx, params))
    140         goto err;
    141 
    142     /* Output tag */
    143     printf("Tag:\n");
    144     BIO_dump_fp(stdout, outtag, ccm_tag_len);
    145 
    146     ret = 1;
    147 err:
    148     if (!ret)
    149         ERR_print_errors_fp(stderr);
    150 
    151     EVP_CIPHER_free(cipher);
    152     EVP_CIPHER_CTX_free(ctx);
    153 
    154     return ret;
    155 }
    156 
    157 static int aes_ccm_decrypt(void)
    158 {
    159     int ret = 0;
    160     EVP_CIPHER_CTX *ctx;
    161     EVP_CIPHER *cipher = NULL;
    162     int outlen, rv;
    163     unsigned char outbuf[1024];
    164     size_t ccm_nonce_len = sizeof(ccm_nonce);
    165     OSSL_PARAM params[3] = {
    166         OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END
    167     };
    168 
    169     printf("AES CCM Decrypt:\n");
    170     printf("Ciphertext:\n");
    171     BIO_dump_fp(stdout, ccm_ct, sizeof(ccm_ct));
    172 
    173     if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
    174         goto err;
    175 
    176     /* Fetch the cipher implementation */
    177     if ((cipher = EVP_CIPHER_fetch(libctx, "AES-192-CCM", propq)) == NULL)
    178         goto err;
    179 
    180     /* Set nonce length if default 96 bits is not appropriate */
    181     params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN,
    182         &ccm_nonce_len);
    183     /* Set tag length */
    184     params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
    185         (unsigned char *)ccm_tag,
    186         sizeof(ccm_tag));
    187     /*
    188      * Initialise decrypt operation with the cipher & mode,
    189      * nonce length and expected tag parameters.
    190      */
    191     if (!EVP_DecryptInit_ex2(ctx, cipher, NULL, NULL, params))
    192         goto err;
    193 
    194     /* Specify key and IV */
    195     if (!EVP_DecryptInit_ex(ctx, NULL, NULL, ccm_key, ccm_nonce))
    196         goto err;
    197 
    198     /* Set ciphertext length: only needed if we have AAD */
    199     if (!EVP_DecryptUpdate(ctx, NULL, &outlen, NULL, sizeof(ccm_ct)))
    200         goto err;
    201 
    202     /* Zero or one call to specify any AAD */
    203     if (!EVP_DecryptUpdate(ctx, NULL, &outlen, ccm_adata, sizeof(ccm_adata)))
    204         goto err;
    205 
    206     /* Decrypt plaintext, verify tag: can only be called once */
    207     rv = EVP_DecryptUpdate(ctx, outbuf, &outlen, ccm_ct, sizeof(ccm_ct));
    208 
    209     /* Output decrypted block: if tag verify failed we get nothing */
    210     if (rv > 0) {
    211         printf("Tag verify successful!\nPlaintext:\n");
    212         BIO_dump_fp(stdout, outbuf, outlen);
    213     } else {
    214         printf("Tag verify failed!\nPlaintext not available\n");
    215         goto err;
    216     }
    217     ret = 1;
    218 err:
    219     if (!ret)
    220         ERR_print_errors_fp(stderr);
    221 
    222     EVP_CIPHER_free(cipher);
    223     EVP_CIPHER_CTX_free(ctx);
    224 
    225     return ret;
    226 }
    227 
    228 int main(int argc, char **argv)
    229 {
    230     if (!aes_ccm_encrypt())
    231         return EXIT_FAILURE;
    232 
    233     if (!aes_ccm_decrypt())
    234         return EXIT_FAILURE;
    235 
    236     return EXIT_SUCCESS;
    237 }
    238