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.

CC430F5137 AES Accelerator Examples

Other Parts Discussed in Thread: CC430F5137, MSP430WARE

Hi,

I am trying to use the AES accelerator module of CC430F5137 and having some difficulties. I have been following theusers guide to set registers/flags etc., but wanted to know if there are any examples that I can refer to. I dont see any in the CC430x513x Code Examples folder.

Please help.

Thanks

Fahad

  • Bumping old post and cannot even help, but I'm interested in same thing: is there AES Acceleration example available somewhere? I would like to find one too. Or am I forced to write my own from scrach?

    Thanks.

    Edit: Ok, took the User Guide and did it already. Still wondering how there isn't any example to find...

  • Veikko, could you share it with us? That would help some people starting in MSP430.

  • Folks,

    we have written the DriverLib APIs for the AES module, available here: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430ware/latest/exports/driverlib/html/aes_8h.html  

    The MSP430 DriverLib is part of the MSP430Ware, available either as stand-alone or integrated in CCS: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430ware/latest/index_FDS.html

    If you're only interested in only the DriverLib portion of the MSP430Ware, here's the download link for the open-source DriverLib package: 

    http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430ware/latest/exports/driverlib_1_20_01_00.zip

    Hope this helps!

    ~Dung

  • I did it for our company, so unfortunately I can't share it...

    On the other hand, nice to know about the MSP430Ware, downloading it now!

  • MSP430Ware only seems to implement non-block ciphers. In the cc430 user guide (slau259d.pdf, section 11.2.6) it says that block modes can be implemented:

    "All block cipher modes can be implemented using the AES accelerator together with software. A separate
    application report describes the block cipher modes together with their implementation in software."

    do you know where that report is, or if there are example implementations?

    thanks,

    Adam

  • Adam,

    here's the app note that the UG section refers to AES128 – A C Implementation for Encryption and Decryption.

    Regards,

    Dung

  • Hi Dung,

    Thanks for the reply, but I don't think that's what I'm after. The app note and examples you refer to are software implementation of AES. What I was looking for was block-mode (e.g. CBC/OFB) implementations using the AES accelerator. It's no problem to write one, but if it already exists I didn't want to re-invent the wheel, particularly as it requires switching to DMA mode which my app currently doesn't do so it's another whole layer to think about... :)

    cheers,

    Adam

  • BTW, the driverlib AES implementation in MSP430ware is subtly broken. If the examples AES_ex1 and AES_ex2 used FIPS-197 data instead of a repeating single byte value for it's test arrays this would be immediately obvious.

    The problem is that you're loading a 16 bit register from a byte array, but you load the key with a different word alignment than the data. The FIPS example looks like this:

    Key:        000102030405060708090a0b0c0d0e0f
    Plaintext:  00112233445566778899aabbccddeeff
    Ciphertext: 69c4e0d86a7b0430d8cdb78070b4c55a

    To make this work with driverlib, you must load the arrays like this:

    unsigned char Data[16] = {0x11, 0x00, 0x33, 0x22, 0x55, 0x44, 0x77, 0x66, 0x99, 0x88, 0xbb, \
     0xaa, 0xdd, 0xcc, 0xff, 0xee};
    unsigned char CipherKey[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, \
     0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

    i.e. the Data is byte-swapped, but the CipherKey is not.

    The resulting Ciphertext will also be byte-swapped: c469d8e07b6a3004cdd880b7b4705ac5.

    The fix is simple - just swap the order bytes are loaded into "tempVariable" and "encryptedData"
    in AES_encryptData().

    I haven't tested any of the Decrypt functions, but I assume they will have similar issues.

    cheers,
    Adam
  • Adam,

    thank you for the critical feedback! This is really useful for us actually. We'll digest the examples and update them accordingly. 

    Regarding the Application Report, looks like the User's Guide, at the time it was written, refered to an App Note that it expected to be created. This effort might have gotten into a limbo state at some point, but we were hoping the introduction of DriverLib could somehow cover some of its use cases. As you found out, looks like we could use a few more examples :). 

    Once again, thanks for the feedback! If you ever get around to creating the block mode, feel free to post it up here and we can all take a look.

    Regards,

    Dung

  • Hi Dung,

    No problem, glad it was helpful.

    I'll be happy to contribute my block mode implementation, but it will probably be only a hybrid software/accelerator version for now (i.e. non-DMA and software IV management) as I just don't have the time to do the whole DMA setup on this project.

    cheers,

    Adam

  • As promised, here is a block mode implementation. Note that this assumes driverlib has been 
    fixed as per my previous post.
    [Edit: decryption routine added.]

    For all encryption Modes, load the key with AES_setCipherKey().

    #define AES_AESCMX_ECB    (0x00 << 5)     // Electronic Codebook
    #define AES_AESCMX_CBC    (0x01 << 5)     // Cipher-block Chaining
    #define AES_AESCMX_OFB    (0x02 << 5)     // Output Feedback
    #define AES_AESCMX_CFB    (0x03 << 5)     // Cipher Feedback

    unsigned char AES_encryptDataBlock(unsigned int baseAddress,
            unsigned char * Data,
            unsigned char * encryptedData,
            unsigned char * IV,
            unsigned int Length,
            const unsigned char Mode)
    {
        // crypto operations must be carried out locally in case we are encrypting in-place
        unsigned char inbuff[16], outbuff[16];

        char i;

        if(Length % 16)
            return STATUS_FAIL;

        while(Length)
        {
            // apply IV to input data
            for(i= 0 ; i < 16 ; ++i)
                switch(Mode)
                {
                    case AES_AESCMX_CBC:
                        inbuff[i]= Data[i] ^ IV[i];
                        break;
                    case AES_AESCMX_OFB:
                    case AES_AESCMX_CFB:
                        inbuff[i]= IV[i];
                        break;
                    case AES_AESCMX_ECB:
                    default:
                        inbuff[i]= Data[i];
                        break;
                }

            // do the encryption
            AES_encryptData(baseAddress, inbuff, outbuff);

            // maintain IV & feedback
            for(i= 0 ; i < 16 ; ++i)
                switch(Mode)
                {
                    case AES_AESCMX_CBC:
                        IV[i]= outbuff[i];
                        break;
                    case AES_AESCMX_OFB:
                        IV[i]= outbuff[i];
                        outbuff[i] ^= Data[i];
                        break;
                    case AES_AESCMX_CFB:
                        outbuff[i] ^= Data[i];
                        IV[i]= outbuff[i];
                        break;
                    case AES_AESCMX_ECB:
                    default:
                        break;
                }

            // copy encrypted data to final output
            for(i= 0 ; i < 16 ; ++i)
                encryptedData[i]= outbuff[i];

            Data += 16;
            encryptedData += 16;
            Length -= 16;
        }

        return STATUS_SUCCESS;
    }

    Here is the corresponding decryption routine. This also assumes that AES_decryptData() has 
    been fixed as per Dung's post below..

    Also, note that unlike encryption, the key must be prepared differently according to Mode (due to
    the fact that CFB & OFB actually encrypt to 'decrypt'):

    For CFB, OFB: load the key with AES_setCipherKey()

    For ECB, CBC: load the key with AES_generateFirstRoundKey()

    (I personally think this is a nasty 'gotcha' and could possibly be masked by the
    AES_setCipherKey() routine by requiring Mode & function to be passed at that stage).

    unsigned char AES_decryptDataBlock(unsigned int baseAddress,
            unsigned char * Data,
            unsigned char * decryptedData,
            unsigned char * IV,
            unsigned int Length,
            const unsigned char Mode)
    {
        // crypto operations must be carried out locally in case we are encrypting in-place
        unsigned char inbuff[16], outbuff[16];

        char i;

        if(Length % 16)
            return STATUS_FAIL;

        while(Length)
        {
            // apply IV to input data
            for(i= 0 ; i < 16 ; ++i)
                switch(Mode)
                {
                    case AES_AESCMX_OFB:
                    case AES_AESCMX_CFB:
                        inbuff[i]= IV[i];
                        break;
                    case AES_AESCMX_CBC:
                    case AES_AESCMX_ECB:
                    default:
                        inbuff[i]= Data[i];
                        break;
                }

            // do the 'decryption'
            switch(Mode)
            {
                case AES_AESCMX_OFB:
                case AES_AESCMX_CFB:
                    AES_encryptData(baseAddress, inbuff, outbuff);
                    break;
                default:
                    AES_decryptData(baseAddress, inbuff, outbuff);
                    break;
            }

            // maintain IV & feedback
            for(i= 0 ; i < 16 ; ++i)
                switch(Mode)
                {
                    case AES_AESCMX_CBC:
                        outbuff[i] ^= IV[i];
                        IV[i]= Data[i];
                        break;
                    case AES_AESCMX_OFB:
                        IV[i]= outbuff[i];
                        outbuff[i] ^= Data[i];
                        break;
                    case AES_AESCMX_CFB:
                        outbuff[i] ^= Data[i];
                        IV[i]= Data[i];
                        break;
                    case AES_AESCMX_ECB:
                    default:
                        break;
                }

            // copy decrypted data to final output
            for(i= 0 ; i < 16 ; ++i)
                decryptedData[i]= outbuff[i];

            Data += 16;
            decryptedData += 16;
            Length -= 16;
        }

        return STATUS_SUCCESS;
    }

    cheers,
    Adam
  • Adam,

    FYI, regarding your first feedback, I had some time today to look at the source code of the AES, and found the bug here:

    AES_setCipherKey() loads byte-by-byte correctly (the 16-bit AESAKEY register's [High Byte][Low Byte] = [byte[n+1]][byte[n]]) 

    aes.c said:

    for (i = 0; i < 16; i = i + 2)
    {
        //HWREG(baseAddress + OFS_AESAKEY) = ( unsigned int)(( unsigned int)CipherKey[i] | ( unsigned int) (CipherKey[i + 1] << 8));
        tempVariable = (unsigned int)(CipherKey[i]);
       tempVariable = tempVariable | ((unsigned int)(CipherKey[i + 1]) << 8);
       HWREG(baseAddress + OFS_AESAKEY) = tempVariable;
    }

    This order (byte n = low byte, byte n+1 = high byte) is the same across all buffer registers 

    AES_encryptData() loads the high-byte/low-byte incorrectly, swapping them in the process 

    aes.c said:

    // Write data to encrypt to module
    for (i = 0; i < 16; i = i + 2)
    {
        //HWREG(baseAddress + OFS_AESADIN) = ( unsigned int)(( unsigned int)Data[i] | ( unsigned int) (Data[i + 1] << 8));
        tempVariable = (unsigned int)(Data[i+1]);
        tempVariable = tempVariable | ((unsigned int)(Data[i]) << 8);
        HWREG(baseAddress + OFS_AESADIN) = tempVariable;
    }

    The decryption functions (with/without generation keys) also mix up the order of the bytes and need to be fixed as well. I filed a bug against this for DriverLib, and we should be able to roll the fix into the code soon.

    Thanks again for the feedback!

    Regards,

    Dung

  • Hi Dung,

    I concur with your findings, but don't forget you also need to correct the byte order when copying the output to the encryptedData array or your output will still be byte-swapped.

    BTW, I updated my block-mode implementation to be slightly more code efficient (edited post above).

    cheers,

    Adam

  • Here is the corresponding decryption routine. 

    [edit: code moved to original code posting above]
  • Any idea how long it will be before the fix is released?

    I'm happy to incorporate the workaround myself in the meantime, but am hoping the fix will be released before our project is done.

    Also, the documentation states that

    "The AES accelerator module performs encryption and decryption of 128-bit data with 128-bit keys

    according to the advanced encryption standard (AES) (FIPS PUB 197) in hardware."

    Am I right to assume that if we decide to use it in 256 bit mode, that it would still conform to FIPS PUB 197?

    Thanks,

    Becky

  • Hello Dung,

    I am implementig AES128 on MSP430 right now, using Driverlib source code, which is very helpful by the way.

    I also found this bug in aes256.c that I'm currently using (but still in AES128 mode), and did the same correction than you did in aes.c.

    I think there is still something wrong though, in the way we have to read / write to the AES registers in word access.

    For instance, with the FIPS-197 test vectors (previous example) :

    Key:        000102030405060708090a0b0c0d0e0f
    Plaintext:  00112233445566778899aabbccddeeff
    Ciphertext: 69c4e0d86a7b0430d8cdb78070b4c55a

    If these values are indeed written the standard way, with MSB at the left and LSB at the right, then the following arrays :

    unsigned char Data[16] =      {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, \
                                   0xbb, 0xcc, 0xdd, 0xee, 0xff};
    unsigned char CipherKey[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, \
                                   0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
    are representing values with Data[0] and CipherKey[0] being the MSBs, and Data[15] and CipherKey[15] being the LSBs.

    However,
    In the source code (with the previous bug correction already done), the values written in the registers are tempVariable = {Data[i+1] , Data[i]} for AESADIN and  tempVariable = {CipherKey[i+1] , CipherKey[i]} for AESAKEY.
    That means you are taking index [i+1] for High Byte, and index [i] for Low Byte, whereas the arrays are filled the opposite way !

    In this example, the source code writes in register AESAKEY, for each loop iteration :
    0100
    0302
    0504
    ...
    0d0c
    0f0e

    That means AESAKEY register is filled with swapped bytes (01 should be Low Byte, not High Byte, etc...).
    We have to write words from MSW to LSW, whereas each word is written the other way around, from LSB to MSB.

    Same thing happens for AESADIN :
    1100
    3322
    5544
    ...
    ddcc
    ffee

    The reading of AESDOUT brings the same issue :
    c469
    d8e0
    ...
    b470
    5ac5

    Whereas the Ciphertext is 69c4e0d86a7b0430d8cdb78070b4c55a.

    That means we have to swap bytes in order to have the correct ciphertext (which has to be read with byte swapping too !)
    If we don't swap the bytes while writing to AESAKEY and AESADIN, the result is not correct.

    In the datasheet, what is called (Byte n = BIT[7:0]) is the High Byte, and what is called (Byte n+1 = BIT[15:8]) is the Low Byte.
    This is consistent with how the arrays are filled (n <=> [i] and (n+1) <=> [i+1] ), but it is the opposite of what you wrote in your post:
    " AES_setCipherKey() loads byte-by-byte correctly (the 16-bit AESAKEY register's [High Byte][Low Byte] = [byte[n+1]][byte[n]]) "

    It could be helpful to clarify in the datasheet, that
    - in byte access, the first byte we have to write (in[0]) is the MSB, and the first byte we read (out[0]) is the MSB. The datasheet just tells about in[0] and out[0] without telling what it represents. By reading FIPS-197 it could only be the MSB, so maybe a reference to this specification would be appropriate ?
    - in word access, the first bytes we have to write are the MSW, but in little-endian mode (in[1]in[0]), and the first bytes we read are the MSW, but in little-endian mode (out[1]out[0]). This is really not obvious when reading the datasheet.

    Could you please check this on your side and confirm or infirm this information?

    Thanks for your help.

    Kind Regards,
    Sebastien

  • Thanks Adam for sharing the CBC implementation! 

**Attention** This is a public forum