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-Q1: AES-CMAC Implementation

Part Number: CC2640R2F-Q1
Other Parts Discussed in Thread: CC2640R2F

Tool/software: TI-RTOS

Dear Experts,

customer would like to implement AES-CMAC algorithm. As far as I could see from the TI-RTOS documentation, we only have drivers for
- AES-ECB
- AES-CCM

In the CC2640R2F TRM, it is mentioned that the crypto-controller supports CBC-MAC authentication modes. Customer is aware of the CCM implementation, but would like to have CBC-MAC.
As there is no example code/driver available for CBC-MAC he tried to follow the pseudo-code programming sequence outlined in the TRM for CBC-MAC implementation (section 10.1.6.3.4 CBC-MAC).
Unfortunately he is facing some issues
- CMAC calculation does not correlate with the expected result

Questions:
1) In the pseudo-code description it is mentioned, that bit AESCTL:CTR needs to be set, but with this the calculation does not work (bit IRQSTAT:DMA_IN_DONE is not getting set).
   Is it correct to set the CTR bit for CMAC calculation?
2) Do the registers AESDATALENx and DMACH0LEN need to be configured with the same value? (not clear from pseudo-code and TRM)

Many thanks and best regards,
Gregor

  • Hi Gregor,

    I'll forward this to the hardware experts to get their input -

    I don't know if this will help; I remember there was interesting results when using the crypto driver, byte order mattered a great deal. Then again, I believe that was for ECC or SHA.

    Regards,
    Rebel
  • Hi Gregor,

    1) CTR bit in AESCTL should not be set for CMAC calculation (0x2000800C).
    2) AESDATALENx and DMACH0LEN should be configured with the same value.

    Please try with these settings and let me know how it goes.

    Regards,
    Reidar
  • Hello,

    I'm the customer in question. I have used the configuration as you mentioned (CTR bit not set and same length in those registers) but the result is not as expected.

    I'll paste the code here, created from the pseudo-code from the Technical Reference Manual (TRM), with the relevant changes (i.e. CTR bit not set).

    You'll also see the result I get and how it differs from the expected result (which is taked from the RFC).

    void Auth_AES_CMAC()
    {
        // This data is taken from the RFC 4493 test code #4: tools.ietf.org/.../rfc4493
        // For more test data see github.com/.../test_CMAC.py
        uint8_t input_data[64] =
        {
            0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
            0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
            0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
            0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
        };
        // the AES key
        uint8_t key[16] =
        {
            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
        };
        // This is the expected result tag of AES-CMAC
        uint8_t expected_tag[16] =
        {
            0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe
        };
        // Here we'll store our calculation result, 4x32 bytes. It should match expected_tag
        uint32_t volatile tag[4];
        int32_t keyIndex;
        CryptoCC26XX_Handle handle;
    
        // Initialize the crypto peripheral
        CryptoCC26XX_init();
        handle = CryptoCC26XX_open(Board_CRYPTO, true /*exclusiveAccess*/, NULL);
        keyIndex = CryptoCC26XX_allocateKey(handle, CRYPTOCC26XX_KEY_0, (const uint32_t *)key);
        /*
        AES-CMAC implementation according to 10.1.6.3.3.1 Programming Sequence from the
        CC2640 Technical Reference Manual SWCU117F–February 2015–Revised June 2016
    
        The following software example in pseudocode describes the actions that are typically executed by the
        host software to authenticate a message, stored in external memory, with AES-CBC-MAC mode. The
        result TAG is read using the slave interface. The following sequence processes a packet of at least 1 input data byte.
    
        // configure the master control module
        write ALGSEL 0x0000_0002 // enable the DMA path to the AES engine
        write IRQCLR 0x0000_0001 // clear any outstanding events
         */
        HWREG(CRYPTO_BASE + CRYPTO_O_ALGSEL) = CRYPTO_ALGSEL_AES;
        HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = CRYPTO_IRQCLR_RESULT_AVAIL;
        /*
        // configure the key store to provide a pre-loaded AES key
        write KEYREADAREA 0x0000_0000 // load the key from ram area 0 (NOTE: The key must be pre-loaded to this area)
        wait KEYREADAREA[31]=='0' // wait until the key is loaded to the AES module
        check IRQSTAT[29] = ‘0'// check that the key is loaded without errors
        */
        HWREG(CRYPTO_BASE + CRYPTO_O_KEYREADAREA) = keyIndex;
        // Wait until key is loaded to the AES module.
        do { CPUdelay(1); } while((HWREG(CRYPTO_BASE + CRYPTO_O_KEYREADAREA) & CRYPTO_KEYREADAREA_BUSY));
        // Check for Key store Read error.
        if((HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & CRYPTO_KEY_ST_RD_ERR))
        {
            return; //(AES_KEYSTORE_READ_ERROR);
        }
        /*
        // write the initialization vector
        write AESIV_0
        ...
        write AESIV_3
        */
        // must be written with zeros for CBC-MAC
        HWREG(CRYPTO_BASE + CRYPTO_O_AESIV0) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESIV1) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESIV2) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESIV3) = 0x00000000;
    
        // zero the AESKEY2_x and AESKEY3_0 registers
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY20) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY21) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY22) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY23) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY30) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY31) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY32) = 0x00000000;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY33) = 0x00000000;
        /*
        // configure the AES engine
        write AESCTL = 0b0010_0000_0000_0000_1000_0000_0100_1100 // program AES-CBC-MAC-128 authentication
        */
        // NOTE(ClCO): If we set the CRYPTO_AESCTL_CTR bit, as in the pseudo-code, the code will hang while
        // checking for CRYPTO_IRQSTAT_DMA_IN_DONE. If we don't set it, the code works.
    	// set AESCTL to 0x2000800C
    	// DEVIATION from the TRM: AESCTL.CTR must not be set
        HWREG(CRYPTO_BASE + CRYPTO_O_AESCTL) = (CRYPTO_AESCTL_SAVE_CONTEXT | CRYPTO_AESCTL_CBC_MAC |
                                               (KEY_STORE_SIZE_128 << CRYPTO_AESCTL_KEY_SIZE_S) | CRYPTO_AESCTL_DIR);
        /*
        write AESDATALEN0 // write length of the crypto block (lo)
        write AESDATALEN1 // write the length of the crypto block (hi) (may be non-block size aligned)
        */
        // NOTE(ClCo): Assume this needs to be set to the length of input_data
        HWREG(CRYPTO_BASE + CRYPTO_O_AESDATALEN0) = 64;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESDATALEN1) = 0;
        HWREG(CRYPTO_BASE + CRYPTO_O_AESAUTHLEN) = 0; // this register is used only for CCM mode
        /*
        write DMACH0CTL 0x0000_00001 // enable DMA channel 0/ configure DMAC
        write DMACH0EXTADDR <address> // base address of the input data in ext. memory
        write DMACH0LEN <length> // input data length in bytes, equal to the message length len({aad data, pad, crypto_data, pad})
        // (may be non-block size aligned)
        */
        HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH0CTL, CRYPTO_DMACH0CTL_EN_BITN) = 1;
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0EXTADDR) = (uint32_t)input_data;
        // NOTE(ClCo): assuming this also needs to be set to the length of the input_data
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0LEN) = 64;
        /*
        // wait for completion
        wait IRQSTAT[0]=='1' // wait for operation completed
        check IRQSTAT[31]==‘0' // check for the absence of errors
        write ALGSEL 0x0000_0000 // disable master control/DMA clock
        */
        // Wait for completion of the input_data data transfer, DMA_IN_DONE.
        do { CPUdelay(1); } while(!(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & CRYPTO_IRQSTAT_DMA_IN_DONE));
        // Check for DMA errors.
        if(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & CRYPTO_DMA_BUS_ERR)
        {
            return;// AES_DMA_BUS_ERROR;
        }
        HWREG(CRYPTO_BASE + CRYPTO_O_ALGSEL) = 0x00000000;
        /*
        // read tag
        wait AESCTL[30]=='1' // wait for the SAVED_CONTEXT_RDY bit [30]
        read AESTAGOUT__0 - AESTAGOUT__3 // this read clears the SAVED_CONTEXT_RDY flag
        */
        // NOTE(ClCo): Probably because the DMA module reads the content of AESTAGOUT, the CRYPTO_AESCTL_SAVED_CONTEXT_RDY
        // is already cleared, so disable the check or else we would wait forever
        //do { CPUdelay(1); } while(!(HWREG(CRYPTO_BASE + CRYPTO_O_AESCTL) & CRYPTO_AESCTL_SAVED_CONTEXT_RDY));
        tag[0] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT0);
        tag[1] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT1);
        tag[2] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT2);
        tag[3] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT3);
        HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = (CRYPTO_IRQCLR_DMA_IN_DONE | CRYPTO_IRQCLR_RESULT_AVAIL);
    
        // end of algorithm
    
        // for the above data, the result is be:
        // tag:          0x12 0x6E 0x35 0xA7 0x66 0x40 0xBB 0x07 0xCE 0xE5 0xE5 0x39 0x93 0xED 0xA9 0xB9
        // expected_tag: 0x51 0xF0 0xBE 0xBF 0x7E 0x3B 0x9D 0x92 0xFC 0x49 0x74 0x17 0x79 0x36 0x3C 0xFE
    
        CryptoCC26XX_releaseKey(handle, &keyIndex);
        CryptoCC26XX_close(handle);
    }

    Any idea what's still wrong with the code? It should be as described in the TRM, so why do I get the wrong results?

    Thanks!

  • Dear Claudiu,

    I am able to reproduce your result but I do not know why these results differs from the RFC results. I suspect it's related to byte order but we need to look more into this.

    Thanks,
    Reidar
  • Dear Claudiu,

    I have discussed this with a TI crypto expert and have some more insight.
    AEC-CMAC is not the same thing as AES CBC-MAC and the RFC test vectors that you refer to are for CMAC and not for CBC-MAC. This is why you do not get the same result when running the test vectors on CC2640R2F with CBC-MAC.
    That said, CMAC uses CBC-MAC to constructs it's output and you can use the CBC-MAC functionality to implement the full scope of CMAC. In the example you refer to you only need to XOR the last 128 bits of you input_data with test vector K1 before feeding it through the crypto core and you'll get the correct result out. I suggest that you read the RFC carefully to understand how this should be done (tools.ietf.org/.../rfc4493)

    If you don't have a hard requirement to use CMAC we recommend using CCM or GCM encryption which is supported by our drivers.

    Thanks,
    Reidar
  • Hello Reidar,

    Thanks a lot for the feedback. I have indeed mixed the CMAC and CBC-MAC specifications.

    Regards,

    Claudiu