This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/CC2640R2F: AES-CCM

Part Number: CC2640R2F


Tool/software: TI-RTOS

Hi!

I'm using CC2640R2F with simplelink_cc2640r2_sdk_1_50_00_58 in a Bluetooth LE project.
Now I want to use AES-CCM (Counter mode with CBC-MAC).

In the documentation (simplelink_cc2640r2_sdk_1_50_00_58/docs/tidrivers/doxygen/html/_crypto_c_c26_x_x_8h.html) I found this sample code:

AES CCM operation

Perform a crypto and authentication operation with AES-CCM in CRYPTOCC26XX_MODE_BLOCKING.
#define macLength           (4)
#define clearTextLength     (16)
#define cipherTextLength    (macLength + clearTextLength)
#define nonceLength         (12)
#define aadLength           (14)
// Holds the AES-CCM setup for this example
typedef struct
{
    uint8_t key[16];                                // A 128 Bit AES key
    CryptoCC26XX_KeyLocation keyLocation;           // One of 8 key locations in the hardware
    uint8_t clearAndCipherText[cipherTextLength];   // Holds the cleartext before, and the ciphertext
                                                    // after the encryption operation.
                                                    // Ciphertext = encrypted text + message authentication code (MAC).
    uint8_t nonce[nonceLength];                     // A value that is used only once (cryptographic term 'nonce')
    uint8_t header[aadLength];                      // A header that is not encrypted but is authenticated in the operation (AAD).
    uint8_t verificationMAC[macLength];             // Location that the recalculated and encrypted MAC is stored during decryption.
} AesCcmExample;
AesCcmExample ccmSetup =
{
    .key = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
             0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C },
    .keyLocation = CRYPTOCC26XX_KEY_0,
    .clearAndCipherText = { 't','h','i','s','i','s','a','p','l','a','i','n','t','e','x','t','0','0','0','0' },
    .nonce  = { 't','h','i','s','i','s','a','n','o','n','c','e' },
    .header = { 't','h','i','s','i','s','a','h','e','a','d','e','r','1' }
};
CryptoCC26XX_Handle             handle;
int32_t                         keyIndex;
CryptoCC26XX_AESCCM_Transaction trans;
int32_t                         status;
// Initialize Crypto driver structures
CryptoCC26XX_init();
// Open the crypto hardware with non-exclusive access and default parameters.
handle = CryptoCC26XX_open(Board_CRYPTO0, false, NULL);
if (handle == NULL) {
    System_abort("CryptoCC26XX did not open");
}
// Allocate a key storage location in the hardware
keyIndex = CryptoCC26XX_allocateKey(handle, ccmSetup.keyLocation, (const uint32_t *) ccmSetup.key);
if (keyIndex == CRYPTOCC26XX_STATUS_ERROR) {
    System_abort("Key Location was not allocated.");
}
// Encrypt and authenticate the message
CryptoCC26XX_Transac_init((CryptoCC26XX_Transaction *) &trans, CRYPTOCC26XX_OP_AES_CCM);
trans.keyIndex   = keyIndex;
trans.authLength = macLength;
trans.nonce  = (char *) ccmSetup.nonce;
trans.header = (char *) ccmSetup.header;
trans.fieldLength  = 3;
trans.msgInLength  = clearTextLength;
trans.headerLength = aadLength;
trans.msgIn  = (char *) &(ccmSetup.clearAndCipherText[0]);                // Message is encrypted in place
trans.msgOut = (char *) &(ccmSetup.clearAndCipherText[clearTextLength]);  // MAC will be written to this position
status = CryptoCC26XX_transact(handle, (CryptoCC26XX_Transaction *) &trans);
if (status != CRYPTOCC26XX_STATUS_SUCCESS) {
    System_abort("Encryption and signing failed.");
}
// Decrypt and authenticate message
CryptoCC26XX_Transac_init((CryptoCC26XX_Transaction *) &trans, CRYPTOCC26XX_OP_AES_CCMINV);
trans.keyIndex   = keyIndex;
trans.authLength = macLength;
trans.nonce  = (char *) ccmSetup.nonce;
trans.header = (char *) ccmSetup.header;
trans.fieldLength  = 3;
trans.msgInLength  = cipherTextLength;
trans.headerLength = aadLength;
trans.msgIn  = (char *) &(ccmSetup.clearAndCipherText[0]);                // Message is decrypted in place
trans.msgOut = (char *) ccmSetup.verificationMAC;
// Do AES-CCM decryption and authentication
status = CryptoCC26XX_transact(handle, (CryptoCC26XX_Transaction *) &trans);
if(status != CRYPTOCC26XX_STATUS_SUCCESS){
    System_abort("Decryption and authentication failed.");
}
// Release the key location
status = CryptoCC26XX_releaseKey(handle, &keyIndex);
if (status != CRYPTOCC26XX_STATUS_SUCCESS) {
    System_abort("Key release was not successful.");
}

Encryption and decryption works fine.

Now, I want to decrypt the cipher text on a different platform, let's say a PC.
Therefore, I tried some AES online tools/calculators but the results don't match.

Can somebody please explain the relations between TI's AES configuration (ccmSetup in crypto.c) and other AES implementations?

I think key, clearAndCipherText and header are straightforward, but what about IV (initialization vector) and nonce?

Looking at the test vectors in RFC3610 (Counter with CBC-MAC) I am also confused by names like CBC IV in, CBC IV out CTR.

BTW, where is the counter defined in TI's AES-CCM implementation?

Can someone please point me to a software tool or online calculator that is compatible to TI's implementation?

Thank you and best regards,

Andreas

  • One option is to use openSSL, see attached files that give a an indication how it could be done.

    ## Example Summary
    -------------------------------
    This example demonstrates how to correctly use openssl generated keys and
    signatures with the rom crypto functions available in the SDK. Elliptic curve
    cryptography(`ecc`) is used for generation of keys while elliptic curve
    digital signature algorithm(`ecdsa`) is used as the signature algorithm.
    The example consists of two parts, a C application that will be run on a
    cc13x0 launchpad, and a python script run from a computer host. Uart
    will be used for serial communication between the two hosts.
    
    ## Peripherals Exercised
    ----------------------------------------
    * `Board_GPIO_LED0`  - LED0 used to indicate failure of verification
    * `Board_GPIO_LED1`  - LED1 used to indicate successfull verification
    * `Board_UART0`      - Uart used for serial communication between device and
    another host.
    
    ## Dependecies
    ----------------------------------------
    Following dependecies are required on the computer host in order
    to run this example:
    - Openssl, open source toolkit for TLS and SSL protocols
    - pySerial, serial communication package for python
    
    The python script will invoke openssl commands through shell so the openssl
    binary must be in path.
    
    For more information, refer to:
    openssl: https://www.openssl.org
    pySerial: https://pythonhosted.org/pyserial
    
    ## Example Usage
    -----------------------------------------
    After the launchpad has been turned on, run the python script from
    a computer host. The launchpad will turn on `Board_PIN_LED1` for successfull
    verification, while `Board_PIN_LED0` will turn on if the signatures are invalid.
    The application will continously wait for files to verify, allowing the possibility
    of having multiple files verified. The python script will also output every shell
    command invoked and the result of the verification.
    
    ## Application Design Details
    ---------------------------------------------
    This example application consists of two parts, the C application on the device
    and the python script that runs on another host. The C application will process
    the received valus and perform `ecdsa` verification. The sequence of this
    application can be divided into following steps:
    
    1 - Wait for initialization message from the other host.
    2 - Send request for public key and wait until received.
    3 - Send request for signatures and wait until received.
    4 - Send request for the document that has been signed, wait until received.
    5 - Perform SHA256 computation on the received document.
    6 - Reverse the order of bytes in the public key, signatures and hash value
    buffers in order to make it compatible with the ecc functions in rom.
    7 - Perform `ecdsa` verification with the received public key, signatures and
    the computed hash value
    8 - Send status of the verification and turn on `Board_PIN_LED1` if verification
    was successfull. If it was an invalid signature, turn on `Board_PIN_LED0`.
    
    Some parsing functions are used for the C application because the other host
    will transmit string of bytes representing one single hex digit each. The C
    application will parse the hexstring and combine two hex digits into one octet
    string which will then be used for the ECC_ROM module functions.
    
    All the requests sent from the C application are formatted with a
    contentType:contentMsg structure, delimited by ":". This was done so that the
    python script can easily respond with the correct request after reading the
    contentType. The contentMsg will be outputted on screen for users benefit.
    
    The python script will perform the following steps:
    
    1 - Use openssl to generate an ecc private key, stored in the file privateKey.pem
    2 - Use openssl to generate an ecc public key based on the newly generated private
    key. The key is stored in the file publicKey.pem.
    3 - Serialize the public key from the openssl output so it can be easier parsed
    by the target device. The function `serialize_public_key()` is used for this part.
    4 - Use openssl to sign a text file, `exampletext.txt`, with the newly generated
    private key. SHA256 is used as the hash function for this part.
    5 - Use openssl to parse the asn1 structure of the signature so the values
    can be extracted and serialized.
    6 - Serialize the signatures from the openssl asn1parse output, so it can be
    easier parsed by the other device. The function `serialize_signatures()` is
    used for this part.
    7 - Initiate communication with the other device by sending initialization
    message.
    8 - Transmit the public key, signature and text content to the device through
    a serial port.
    9 - Print the result of verification received from the targeted device.
    
    Three files will be generated when this script is completed. Two .pem files
    which contains the `ecc` keys and one .der file which contains the signature.
    The script must parse and serialize the openssl outputs because the C
    application device will only expect strings of hex characters. The output from
    openssl contains other type of data which the python script will remove before
    transmitting to the launchpad. Openssl asn1parse is used to parse the asn1
    structure of the signature so the signature values can be extracted.
    
    The python script invokes openssl commands through the shell, without specifying
    any absolute path. The openssl executable must be in system path for the script
    to run without issues.
    
    ## Important note
    --------------------------------------------------------------
    ### Opposite endinaness between the hosts
    Openssl will generate values that are represented as octetstrings. These values
    are big-endian by convention. The device processor is little-endian, which means
    that when the launchpad device receives the octetstrings, it will process the
    bytes in reversed order. The outputs will not be the same if this is not handled.
    The buffers containing the public key, signatures and SHA256 output must be
    reversed before being used by the ecc functions in rom. This is demonstrated
    in this example.
    
    ### Linefeed used to indicate end of transmission
    In this example application, the C application device will read the `Board_UART0`
    pin untill a newline has been received. This means that the signed textfile can
    not contain multiple linefeeds in the document. The application assumes that the
    content stops at the linefeed, meaning text that proceeds after this point is
    ignored. As a result, the calculated hash value will differ from the other host if
    there is more text after the first linefeed!
    
    ### Newline convention dependent on the operating system
    Textfiles processed by windows will have `CR` + `LF` as newline while only
    `LF` will be used for Unix-like systems. This can cause different SHA256 hash
    values if not properly handled. Make sure the data input for SHA256
    function in ecc rom uses same convention as the operating system the document
    was originally signed on. This means for example that `CR` has to be included
    as a part of the data input if the text was created and signed on a windows
    system.
    
    ### Key length must be specified
    The ECC_ROM functions require all key value arguments to be prepended with a
    4-byte number specifying the length of the key. That is why the effective
    length of the ECC key buffers are extended with 4 more bytes.
    
    ### Linker file
    The default linker files generated by importing an empty project are usually
    not configured with ECC Rom software in mind. The ECC software module uses
    some reserved ram addresses, which the TIRTOS kernel will overwrite if not
    properly configured. Make sure the linker file has reserved the correct ram
    addresses for the ecc rom module. This example will use the correct linker
    file, which can be used as a basis.
    
    ## Resources & Jumper Settings
    
    > If you're using an IDE (such as CCS or IAR), please refer to Board.html in
    your project directory for resources used and board-specific jumper settings.
    Otherwise, you can find Board.html in the directory
    <SDK_INSTALL_DIR>/source/ti/boards/<BOARD>.
    
    TI-RTOS:
    
    * When building in Code Composer Studio, the kernel configuration project will
    be imported along with the example. The kernel configuration project is
    referenced by the example, so it will be built first. The "release" kernel
    configuration is the default project used. It has many debug features disabled.
    These feature include assert checking, logging and runtime stack checks. For a
    detailed difference between the "release" and "debug" kernel configurations and
    how to switch between them, please refer to the SimpleLink MCU SDK User's
    Guide. The "release" and "debug" kernel configuration projects can be found
    under <SDK_INSTALL_DIR>/kernel/tirtos/builds/<BOARD>/(release|debug)/(ccs|gcc).
    
    #
    # Copyright (c) 2017, Texas Instruments Incorporated
    # All rights reserved.
    #
    # Redistribution and use in source and binary forms, with or without
    # modification, are permitted provided that the following conditions
    # are met:
    #
    # *  Redistributions of source code must retain the above copyright
    #    notice, this list of conditions and the following disclaimer.
    #
    # *  Redistributions in binary form must reproduce the above copyright
    #    notice, this list of conditions and the following disclaimer in the
    #    documentation and/or other materials provided with the distribution.
    #
    # *  Neither the name of Texas Instruments Incorporated nor the names of
    #    its contributors may be used to endorse or promote products derived
    #    from this software without specific prior written permission.
    #
    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
    # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
    # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
    # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    #
    
    # Standard imports
    import subprocess
    import string
    
    # Third party packages
    import serial
    from serial import SerialException
    
    # Follow constant lengths are in nibbles
    SIGNATURE_LENGTH = 64
    PUBLIC_KEY_LENGTH= 128
    
    
    def openssl_cmd(cmd):
        print("Following openssl command was executed\ncommand:openssl %s \n"
              % cmd)
        p = subprocess.Popen(["openssl"] + cmd.split(),
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, _ = p.communicate()
    
        if p.returncode != 0:
            print("cmd 'openssl %s' failed: rc=%s, stdout/err was %s" %
                  (cmd, p.returncode, stdout))
            quit()
        return stdout.decode()
    
    
    def serialize_public_key(input_key_string):
        # Remove all white spaces
        split_key_string = "".join(input_key_string.split())
    
        # Remove every instance of :
        key_string = split_key_string.replace(":", "")
    
        # retrieve index of 04 in the string, as that is where the key value begins
        # Added with two because 04 itself is only used to indicate compression or not
        start_index = key_string.find("04") + 2
    
        # Key value length is 128 nibbles long
        public_key = key_string[start_index: start_index + PUBLIC_KEY_LENGTH]
    
        return public_key
    
    
    def serialize_signatures(input_signature_string):
        # Remove all white spaces
        signature_string = "".join(input_signature_string.split())
    
        # Find index of INTEGER specifier for signature 1
        index_1 = signature_string.find("INTEGER") + len("INTEGER:")
    
        # Find index of second INTEGER specifier for signature 2
        index_2 = signature_string.find(
                 "INTEGER", signature_string.find("INTEGER")+ 1) + len("INTEGER:")
    
        # Each signature length is 64 nibbles long
        signature_string_1 = signature_string[index_1:index_1 + SIGNATURE_LENGTH]
        signature_string_2 = signature_string[index_2:index_2 + SIGNATURE_LENGTH]
    
        return signature_string_1, signature_string_2
    
    
    privatekey_file_name = "privateKey.pem"
    publickey_file_name = "publicKey.pem"
    signature_file_name = "signature.der"
    textfile_name = "exampletext.txt"
    
    # List of openssl that will be used
    cmd_generate_private_key    = "ecparam -name prime256v1 -genkey -out %s" \
    % privatekey_file_name
    cmd_generate_public_key     = "ec -in %s -pubout -out %s" \
    % (privatekey_file_name, publickey_file_name)
    cmd_public_key_print_output = "ec -in %s -pubin -text -noout" \
    % (publickey_file_name)
    cmd_sign_document           = "dgst -sha256 -sign %s -out %s %s" \
    % (privatekey_file_name, signature_file_name, textfile_name)
    cmd_asn1parse_signature = "asn1parse -inform DER -in %s" \
    % (signature_file_name)
    
    print("Generating private key, storing in %s\n" % privatekey_file_name)
    openssl_cmd(cmd_generate_private_key)
    
    print("Generating public key, storing in %s\n" % publickey_file_name)
    openssl_cmd(cmd_generate_public_key)
    
    print("Generated public key output:\n")
    key_output = openssl_cmd(cmd_public_key_print_output)
    print(key_output)
    
    print("Serializing public key\n")
    public_key = serialize_public_key(key_output)
    print("Public key: %s \n" % public_key)
    
    print("Will now sign following file: %s" % textfile_name)
    openssl_cmd(cmd_sign_document)
    print("Text file has been signed, signature stored in %s\n" % signature_file_name)
    
    print("Parsing signature file now \n")
    parsing_output = openssl_cmd(cmd_asn1parse_signature)
    print(parsing_output)
    
    signature1, signature2 = serialize_signatures(parsing_output)
    
    print("Serialized signatures \nSignature1: %s \n" % signature1)
    print("Serialized signatures \nSignature2: %s \n" % signature2)
    print("Initializing serial communication with device now")
    
    # Read content of text file in binary format
    file_object = open(textfile_name, "rb")
    textfile_content_binary = file_object.read()
    
    # Configure serial settings
    serial_com = serial.Serial()
    serial_com.baudrate = 9600
    
    # Initialize and open COM port
    while True:
        try:
            com_port = input("Type in correct COM port nr please \n")
            serial_com.port = "COM" + str(com_port)
            serial_com.open()
            break
        except SerialException:
            print ("port is not available! Type another one ")
    
    
    print("Telling device to initialize communication")
    serial_com.write(b"Start communication")
    
    while True:
        content = serial_com.readline()
    
        # Split message type and message content into a list delimited by :
        content = (content.decode("utf-8")).strip(' \0').split(":")
    
        if(content[0] == "PromptPubKeyMsg"):
            print(content[1])
            print("Transmitting public key: %s\n" % public_key)
    
            # Encode input string to byte format before transmitting
            serial_com.write(public_key.encode())
    
        elif(content[0] == "PromptSignature1Msg"):
            print(content[1])
            print("Transmitting signature 1:%s\n" % signature1)
    
            # Encode input string to byte format before transmitting
            serial_com.write(signature1.encode())
    
        elif(content[0] == "PromptSignature2Msg"):
            print(content[1])
            print("Transmitting signature 2:%s\n" % signature2)
    
            # Encode input string to byte format before transmitting
            serial_com.write(signature2.encode())
    
        elif(content[0] == "PromptTextMsg"):
            print(content[1])
            print("Transmitting document %s\n" % textfile_name)
    
            # Document expected to have linefeed already
            serial_com.write(textfile_content_binary)
    
        elif(content[0] == "VerificationStatus"):
            print(content[1])
            serial_com.close()
            # Terminate communication
            break
    
        elif(content[0] == "InvalidDataStatus"):
            print(content[1])
            # Terminate communication
            serial_com.close()
            break
    

  • Thank you, this pointed me to the OpenSSL documentation at www.openssl.org/.../EVP_EncryptFinal_ex.html and in the paragraph "CCM Mode" I found the information I was looking for:
    The following ctrls are supported in CCM mode:

    "EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, taglen, tag);

    This call is made to set the expected CCM tag value when decrypting or the length of the tag (with the tag parameter set to NULL) when encrypting. The tag length is often referred to as M. If not set a default value is used (12 for AES).

    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_L, ivlen, NULL);

    Sets the CCM L value. If not set a default is used (8 for AES).

    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, ivlen, NULL);

    Sets the CCM nonce (IV) length: this call can only be made before specifying an nonce value. The nonce length is given by 15 - L so it is 7 by default for AES."

    Now I have another issue: While encrypting with aes-128-ccm without additional authenticated data (aad) give me same results in openssl and TI's simplelink crypto function, the ciphertext differs if I use aad. When I use aad, the calculated MAC is the same for openssl and simplelink, only encrypted data doesn't match.
    What could be the problem? I double checked the configuration of the two test programs and I am sure, they match.

    Thank you for support and have a nice weekend,

    regards,

    Andreas
  • Difficult to point out the exact cause from the information given. It could be that a pointer is pointing to the wrong data and therefore gives strange result.

    Do you have a full project including the openSSL side that we can take a look at and see if we can find out why you get an unwanted result.
  • Thank you for quick response.

    Here are my detailed results:

    OpenSSL configuration:

    #define PLAIN_LEN            (16)
    #define TAG_MAC_LEN     (12)
    #define NONCE_IV_LEN   (7)
    #define CCM_L                    (8)
    #define CIPHER_LEN        (TAG_MAC_LEN + PLAIN_LEN)
    #define AAD_LEN                (8)

    unsigned char plaintext[PLAIN_LEN] = { 't', 'h', 'i', 's', 'i', 's', 'a', 'p', 'l', 'a', 'i', 'n', 't', 'e', 'x', 't' };
    unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
    unsigned char iv[] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
    unsigned char cipher[CIPHER_LEN];
    unsigned char tag[TAG_MAC_LEN];
    unsigned char aad_header[AAD_LEN] = { 'H', 'A', 'L', 'L', 'O', '1', '2', '3' };

    Openssl result, output:

    >>> OpenSSl aes-128-ccm Test <<<
    Ciphertext: 8108F1B08EEE9272FF809EDB22D2235A
    Ciphertext length: 16
    MAC Tag: 30A42B256491BD6C69261C7B
    Decrypted: thisisaplaintext

    Here is the code file for Openssl:

    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/evp.h>
    #include <openssl/err.h>
    
    #define	PLAIN_LEN			(16)
    #define	TAG_MAC_LEN			(12)
    #define NONCE_IV_LEN		(7)
    #define CCM_L               (8)
    #define CIPHER_LEN			(TAG_MAC_LEN + PLAIN_LEN)
    #define AAD_LEN				(8)
    
    unsigned char plaintext[PLAIN_LEN] = { 't', 'h', 'i', 's', 'i', 's', 'a', 'p',
    		'l', 'a', 'i', 'n', 't', 'e', 'x', 't' };
    unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    		0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
    unsigned char iv[] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
    unsigned char cipher[CIPHER_LEN];
    unsigned char tag[TAG_MAC_LEN];
    unsigned char aad_header[AAD_LEN] = { 'H', 'A', 'L', 'L', 'O', '1', '2', '3' };
    //unsigned char msg[PLAIN_LEN+AAD_LEN] = "HALLOthisisaplaintext";
    unsigned char decrypted[PLAIN_LEN + 1] = { 0 };
    
    void handleErrors() {
    	ERR_print_errors_fp(stderr);
    }
    
    int decryptccm(unsigned char *ciphertext, int ciphertext_len,
    		unsigned char *aad, int aad_len, unsigned char *tag, unsigned char *key,
    		unsigned char *iv, unsigned char *plaintext) {
    	EVP_CIPHER_CTX *ctx;
    	int len;
    	int plaintext_len;
    	int ret;
    
    	/* Create and initialize the context */
    	if (!(ctx = EVP_CIPHER_CTX_new()))
    		handleErrors();
    
    	/* Initialise the decryption operation. */
    	if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL))
    		handleErrors();
    
    	/* Setting IV len to 7. Not strictly necessary as this is the default
    	 * but shown here for the purposes of this example */
    	if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, NONCE_IV_LEN,
    	NULL))
    		handleErrors();
    
    	/* Set expected tag value. */
    	if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, TAG_MAC_LEN, tag))
    		handleErrors();
    
    	/* Initialize key and IV */
    	if (1 != EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
    		handleErrors();
    
    	/* Provide the total ciphertext length
    	 */
    	if (1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len))
    		handleErrors();
    
    	/* Provide any AAD data. This can be called zero or more times as
    	 * required
    	 */
    	if (1 != EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
    		handleErrors();
    
    	/* Provide the message to be decrypted, and obtain the plaintext output.
    	 * EVP_DecryptUpdate can be called multiple times if necessary
    	 */
    	ret = EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
    
    	plaintext_len = len;
    
    	/* Clean up */
    	EVP_CIPHER_CTX_free(ctx);
    
    	if (ret > 0) {
    		/* Success */
    		return plaintext_len;
    	} else {
    		/* Verify failed */
    		return -1;
    	}
    }
    
    int encryptccm(unsigned char *plaintext, int plaintext_len, unsigned char *aad,
    		int aad_len, unsigned char *key, unsigned char *iv,
    		unsigned char *ciphertext, unsigned char *tag) {
    	EVP_CIPHER_CTX *ctx;
    
    	int len;
    
    	int ciphertext_len;
    
    	/* Create and initialise the context */
    	if (!(ctx = EVP_CIPHER_CTX_new()))
    		handleErrors();
    
    	/* Initialize the encryption operation. */
    	if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL))
    		handleErrors();
    
    	/* Setting IV len to 7. Not strictly necessary as this is the default
    	 * but shown here for the purposes of this example */
    	/* nonce in simplelink */
    	if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, NONCE_IV_LEN,
    	NULL))
    		handleErrors();
    
    	/* Set tag length */
    	/* macLength in simplelink */
    	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, TAG_MAC_LEN, NULL);
    
    	/* Sets the CCM L value, if not set a default is used (8 for AES) */
    	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_L, CCM_L, NULL);
    
    	/* Initialize key and IV */
    	if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
    		handleErrors();
    
    	//EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_MSGLEN, plaintext_len+aad_len, NULL);
    
    	/* Provide the total plaintext length
    	 */
    	if (1 != EVP_EncryptUpdate(ctx, NULL, &len, NULL, plaintext_len))
    		handleErrors();
    
    	/* Provide any AAD data. This can be called zero or one times as
    	 * required
    	 */
    	if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad_header, aad_len))
    		handleErrors();
    
    	/* Provide the message to be encrypted, and obtain the encrypted output.
    	 * EVP_EncryptUpdate can only be called once for this
    	 */
    	if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
    		handleErrors();
    	ciphertext_len = len;
    
    	/* Finalize the encryption. Normally ciphertext bytes may be written at
    	 * this stage, but this does not occur in CCM mode
    	 */
    	if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
    		handleErrors();
    	ciphertext_len += len;
    
    	/* Get the tag */
    	if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, TAG_MAC_LEN, tag))
    		handleErrors();
    
    	/* Clean up */
    	EVP_CIPHER_CTX_free(ctx);
    
    	return ciphertext_len;
    }
    
    void print_hex_string(unsigned char *str, int len) {
    	int i;
    
    	if (str != NULL) {
    		for (i = 0; i < len; i++) {
    			printf("%02X", str[i]);
    		}
    		printf("\n");
    	}
    }
    
    int main(void) {
    	int cipher_len;
    
    	printf(">>> OpenSSl aes-128-ccm Test <<<\n");
    	cipher_len = encryptccm(plaintext, PLAIN_LEN, aad_header, AAD_LEN, key, iv,
    			cipher, tag);
    
    	printf("Ciphertext: ");
    	print_hex_string(cipher, cipher_len);
    	printf("Ciphertext length: %d\n", cipher_len);
    
    	printf("MAC Tag: ");
    	print_hex_string(tag, TAG_MAC_LEN);
    
    	decryptccm(cipher, cipher_len, aad_header, AAD_LEN, tag, key, iv,
    			decrypted);
    
    	printf("Decrypted: %s\n", decrypted);
    
    	return EXIT_SUCCESS;
    }
    

    TI Simplelink configuration:

    #define macLength             (12)
    #define clearTextLength     (16)
    #define cipherTextLength   (macLength + clearTextLength)
    #define nonceLength          (7)
    #define aadLength              (8)
    #define CCM_L                    (8)

    // Holds the AES-CCM setup for this example
    typedef struct
    {
        uint8_t key[16];                                                        // A 128 Bit AES key
        CryptoCC26XX_KeyLocation keyLocation;        // One of 8 key locations in the hardware
        uint8_t clearAndCipherText[cipherTextLength]; // Holds the cleartext before, and the ciphertext
                                                                                           // after the encryption operation.
                                                                                           // Ciphertext = encrypted text + message authentication code (MAC).
        uint8_t nonce[nonceLength];                               // A value that is used only once (cryptographic term 'nonce')
        uint8_t header[aadLength];                                  // A header that is not encrypted but is authenticated in the operation (AAD).
        uint8_t verificationMAC[macLength];                  // Location that the recalculated and encrypted MAC is stored during decryption.
    } AesCcmExample;

    AesCcmExample ccmSetup = {
       .key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
       .keyLocation = CRYPTOCC26XX_KEY_0,
       .clearAndCipherText = { 't', 'h', 'i', 's', 'i', 's', 'a', 'p', 'l', 'a', 'i', 'n', 't', 'e', 'x', 't' },
       .nonce = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 },
       .header = { 'H','A','L','L','O','1','2','3'}
    };

    TI Simplelink result:

    Here is the code file for simplelink:

    #include <ti/drivers/crypto/CryptoCC26XX.h>
    #include "board.h"
    #include <xdc/runtime/System.h>
    
    #define macLength           (12)
    #define clearTextLength     (16)
    #define cipherTextLength    (macLength + clearTextLength)
    #define nonceLength         (7)
    #define aadLength           (8)
    #define CCM_L               (8)
    
    // Holds the AES-CCM setup for this example
    typedef struct
    {
        uint8_t key[16];                                // A 128 Bit AES key
        CryptoCC26XX_KeyLocation keyLocation; // One of 8 key locations in the hardware
        uint8_t clearAndCipherText[cipherTextLength]; // Holds the cleartext before, and the ciphertext
                                                      // after the encryption operation.
                                                      // Ciphertext = encrypted text + message authentication code (MAC).
        uint8_t nonce[nonceLength]; // A value that is used only once (cryptographic term 'nonce')
        uint8_t header[aadLength]; // A header that is not encrypted but is authenticated in the operation (AAD).
        uint8_t verificationMAC[macLength]; // Location that the recalculated and encrypted MAC is stored during decryption.
    } AesCcmExample;
    
    AesCcmExample ccmSetup = { .key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                                        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
                                        0x0e, 0x0f },
                               .keyLocation = CRYPTOCC26XX_KEY_0,
                               .clearAndCipherText = { 't', 'h', 'i', 's', 'i', 's',
                                                       'a', 'p', 'l', 'a', 'i', 'n',
                                                       't', 'e', 'x', 't' },
                               .nonce = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 },
                               .header = { 'H','A','L','L','O','1','2','3'}
            };
    
    CryptoCC26XX_Handle handle;
    int32_t keyIndex;
    CryptoCC26XX_AESCCM_Transaction trans;
    int32_t status;
    
    void test_crypto()
    {
        // Initialize Crypto driver structures
        CryptoCC26XX_init();
    
        // Open the crypto hardware with non-exclusive access and default parameters.
        handle = CryptoCC26XX_open(Board_CRYPTO0, false, NULL);
        if (handle == NULL)
        {
            System_abort("CryptoCC26XX did not open");
        }
    
        // Before any encryption/decryption operation starts,
        // the key store must have at least one key loaded.
        // Allocate a key storage location in the hardware
        keyIndex = CryptoCC26XX_allocateKey(handle, ccmSetup.keyLocation,
                                            (const uint32_t *) ccmSetup.key);
        if (keyIndex == CRYPTOCC26XX_STATUS_ERROR)
        {
            System_abort("Key Location was not allocated.");
        }
    
        // Encrypt and authenticate the message
        CryptoCC26XX_Transac_init((CryptoCC26XX_Transaction *) &trans,
                                  CRYPTOCC26XX_OP_AES_CCM);
        trans.keyIndex = keyIndex;
        trans.authLength = macLength;
        trans.nonce = (char *) ccmSetup.nonce;
        trans.header = (char *) ccmSetup.header;
        trans.fieldLength = CCM_L;
        trans.msgInLength = clearTextLength;
        trans.headerLength = aadLength;
        trans.msgIn = (char *) &(ccmSetup.clearAndCipherText[0]); // Message is encrypted in place
        trans.msgOut = (char *) &(ccmSetup.clearAndCipherText[clearTextLength]); // MAC will be written to this position
        status = CryptoCC26XX_transact(handle, (CryptoCC26XX_Transaction *) &trans);
        if (status != CRYPTOCC26XX_STATUS_SUCCESS)
        {
            System_abort("Encryption and signing failed.");
        }
    
        // Decrypt and authenticate message
        CryptoCC26XX_Transac_init((CryptoCC26XX_Transaction *) &trans,
        CRYPTOCC26XX_OP_AES_CCMINV);
        trans.keyIndex = keyIndex;
        trans.authLength = macLength;
        trans.nonce = (char *) ccmSetup.nonce;
        trans.header = (char *) ccmSetup.header;
        trans.fieldLength = CCM_L;
        trans.msgInLength = cipherTextLength;
        trans.headerLength = aadLength;
        trans.msgIn = (char *) &(ccmSetup.clearAndCipherText[0]); // Message is decrypted in place
        trans.msgOut = (char *) ccmSetup.verificationMAC;
    
        // Do AES-CCM decryption and authentication
        status = CryptoCC26XX_transact(handle, (CryptoCC26XX_Transaction *) &trans);
        if (status != CRYPTOCC26XX_STATUS_SUCCESS)
        {
            System_abort("Decryption and authentication failed.");
        }
    
        // Release the key location
        status = CryptoCC26XX_releaseKey(handle, &keyIndex);
        if (status != CRYPTOCC26XX_STATUS_SUCCESS)
        {
            System_abort("Key release was not successful.");
        }
    
    }
    

    As you can see, MAC Tags match between Openssl and TI Simplelink, but ciphertext differs.

    Where is the problem?

    Thank you and best regards,

    Andreas

  • Hi,

    in the CC2640R2F TRM (swcu117g) I just found the following table at page 923:

    There is no mode listed that calculates ciphertext and aad simultaneously.
    So I wonder what the CC2640R2F calculates as ciphertext when aad is given.
    CryptoCC26XX.h gives:

    #define CRYPTOCC26XX_OP_AES_CCM_ENCRYPT             0   /*!< AES-CCM encryption of both AAD and plain text */

    Isn't this in conflict with the TRM?

    Cheers,

    Andreas

  • 0 /*!< AES-CCM encryption of both AAD and plain text */

    Is technically not wrong. But it is misleading. The AAD is only internally encrypted. The resultant ciphertext is discarded and the intermediate MAC is saved for further use.

    The table could also be misleading. CCM authentication + encryption can output either ciphertext and a MAC or just a MAC.
    It also lists no output data for CBC-MAC… As the name suggests, it should return a MAC.
  • Thank you, it works now!

    Best regards,

    Andreas