AES CBC 256 encryption on nrf52840

Hi,

I need to use AES CBC 256 encryption/decryption on nrf52840 but since its cyrpto cell only supports AES 128, I tried using mbedtls to do AES 256 encryption. It works fine but if then I add 

CONFIG_NRF_SECURITY=y in the prj.conf, the decrypted data doesn't match with original string anymore. It works if either I use 128 bits key size of set 
CONFIG_NRF_SECURITY=n.
My problem is that I need to use hardware support for running some other RSA related operations, but I would also like to run AES 256 via software. 
How can I do that?
CONFIG_NRF_SECURITY=y

CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_AES_C=y
CONFIG_MBEDTLS_CIPHER_MODE_CBC=y
CONFIG_ENTROPY_GENERATOR=y
#include <zephyr/kernel.h>
#include <mbedtls/aes.h>
#include <string.h>

#define AES_KEY_SIZE  32  // AES-256 requires 32 bytes (256 bits)
#define AES_BLOCK_SIZE 16


#define KEY_SIZE 256

static const uint8_t key[AES_KEY_SIZE] = {
    0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
    0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
    0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
    0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
};

static uint8_t iv[AES_BLOCK_SIZE] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};

// **PKCS#7 Padding**
static size_t pkcs7_pad(uint8_t *data, size_t data_len, size_t block_size) {
    size_t pad_len = block_size - (data_len % block_size);
    for (size_t i = 0; i < pad_len; i++) {
        data[data_len + i] = (uint8_t)pad_len;
    }
    return data_len + pad_len;
}

// **PKCS#7 Unpadding**
static size_t pkcs7_unpad(uint8_t *data, size_t data_len) {
    uint8_t pad_len = data[data_len - 1];  // Last byte gives padding size
    if (pad_len > AES_BLOCK_SIZE || pad_len == 0) {
        return data_len;  // Invalid padding, return as-is
    }
    return data_len - pad_len;
}


// **AES-256-CBC Encryption**
static int aes_encrypt(const uint8_t *input, size_t input_len, uint8_t *output, size_t *output_len) {
    mbedtls_aes_context aes;
    uint8_t iv_enc[AES_BLOCK_SIZE];
    memcpy(iv_enc, iv, AES_BLOCK_SIZE);  // Use fresh IV

    // **PKCS#7 Pad Input**
    uint8_t padded_input[64];  // Adjust buffer size as needed
    memcpy(padded_input, input, input_len);
    *output_len = pkcs7_pad(padded_input, input_len, AES_BLOCK_SIZE);

    mbedtls_aes_init(&aes);
    mbedtls_aes_setkey_enc(&aes, key, KEY_SIZE);

    int ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, *output_len, iv_enc, padded_input, output);
    mbedtls_aes_free(&aes);

    return ret;
}

// **AES-256-CBC Decryption**
static int aes_decrypt(const uint8_t *input, size_t input_len, uint8_t *output, size_t *output_len) {
    mbedtls_aes_context aes;
    uint8_t iv_dec[AES_BLOCK_SIZE];
    memcpy(iv_dec, iv, AES_BLOCK_SIZE);  // Use fresh IV

    mbedtls_aes_init(&aes);
    mbedtls_aes_setkey_dec(&aes, key, KEY_SIZE);

    int ret = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, input_len, iv_dec, input, output);
    mbedtls_aes_free(&aes);

    // **PKCS#7 Unpad Output**
    *output_len = pkcs7_unpad(output, input_len);
    return ret;
}


// **Main Function**
void main(void) {
    uint8_t plaintext[] = "Hello, AES-256-CBC!";  // 20 bytes
    uint8_t encrypted[64] = {0};
    uint8_t decrypted[64] = {0};
    size_t enc_len, dec_len;

    printf("Plain: %s\n", plaintext);


    // Encrypt
    if (aes_encrypt(plaintext, strlen((char *)plaintext), encrypted, &enc_len) != 0) {
        printf("Encryption failed!\n");
        return;
    }
    printf("Encrypted: ");
    for (size_t i = 0; i < enc_len; i++) {
        printf("%02x", encrypted[i]);
    }
    printf("\n");

    // Decrypt
    if (aes_decrypt(encrypted, enc_len, decrypted, &dec_len) != 0) {
        printf("Decryption failed!\n");
        return;
    }
    decrypted[dec_len] = '\0';  // Null-terminate string
    printf("Decrypted: %s\n", decrypted);
}
Parents
  • Hi,

    I would recommend that you use the PSA crypto APIs, as the legacy APIs are deprecated and will be removed. You can use the Oberon backend fro AES256. To test it you can use the AES CBC sample, modify nrf/samples/crypto/aes_cbc/boards/nrf52840dk_nrf52840.conf so that CONFIG_PSA_CRYPTO_DRIVER_OBERON is enabled isntead of being disabled, and add CONFIG_PSA_WANT_AES_KEY_SIZE_256=y to prj.conf. Lastly, modify the call to psa_set_key_bits() in the sample to set 256 bit key size instead of 128.

    This is a full diff of my modifications from SDK 2.9.1 that works for me on the nRF52840 DK:

    diff --git a/samples/crypto/aes_cbc/boards/nrf52840dk_nrf52840.conf b/samples/crypto/aes_cbc/boards/nrf52840dk_nrf52840.conf
    index 1506df21e8..60a0917f5f 100644
    --- a/samples/crypto/aes_cbc/boards/nrf52840dk_nrf52840.conf
    +++ b/samples/crypto/aes_cbc/boards/nrf52840dk_nrf52840.conf
    @@ -4,7 +4,7 @@
     # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     #
     # Using hardware crypto accelerator
    -CONFIG_PSA_CRYPTO_DRIVER_OBERON=n
    +CONFIG_PSA_CRYPTO_DRIVER_OBERON=y
     CONFIG_PSA_CRYPTO_DRIVER_CC3XX=y
     
     # Mbedtls configuration
    diff --git a/samples/crypto/aes_cbc/prj.conf b/samples/crypto/aes_cbc/prj.conf
    index 8b4ddbcf9f..934b132e36 100644
    --- a/samples/crypto/aes_cbc/prj.conf
    +++ b/samples/crypto/aes_cbc/prj.conf
    @@ -16,7 +16,10 @@ CONFIG_LOG=y
     # Enable nordic security backend and PSA APIs
     CONFIG_NRF_SECURITY=y
     CONFIG_MBEDTLS_PSA_CRYPTO_C=y
    +CONFIG_PSA_CRYPTO_DRIVER_OBERON=y
     
     CONFIG_PSA_WANT_GENERATE_RANDOM=y
     CONFIG_PSA_WANT_KEY_TYPE_AES=y
     CONFIG_PSA_WANT_ALG_CBC_NO_PADDING=y
    +
    +CONFIG_PSA_WANT_AES_KEY_SIZE_256=y
    \ No newline at end of file
    diff --git a/samples/crypto/aes_cbc/src/main.c b/samples/crypto/aes_cbc/src/main.c
    index 37ab449d8a..2b851c628d 100644
    --- a/samples/crypto/aes_cbc/src/main.c
    +++ b/samples/crypto/aes_cbc/src/main.c
    @@ -87,7 +87,7 @@ int generate_key(void)
     	psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
     	psa_set_key_algorithm(&key_attributes, PSA_ALG_CBC_NO_PADDING);
     	psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
    -	psa_set_key_bits(&key_attributes, 128);
    +	psa_set_key_bits(&key_attributes, 256);
     
     	/* Generate a random key. The key is not exposed to the application,
     	 * we can use it to encrypt/decrypt using the key handle
    

  • Thanks, I could make it run in the sample. I have couple of questions though. 


    1) Does CONFIG_PSA_CRYPTO_DRIVER_OBERON=y mean that all crypto related work then be run in software and hardware cell will no longer be used for the app?

    2) I could make it work in aes_cbc sample based on 2.9.0 but the project I have to port this to is based on 2.4.2. If I upgrade that project, it will open up new work scope, so I tried aes_cbc sample based on 2.4.2 and followed exact steps, but over there I still got -134 at psa_cipher_encrypt_setup. Any guesses what could be wrong?

Reply
  • Thanks, I could make it run in the sample. I have couple of questions though. 


    1) Does CONFIG_PSA_CRYPTO_DRIVER_OBERON=y mean that all crypto related work then be run in software and hardware cell will no longer be used for the app?

    2) I could make it work in aes_cbc sample based on 2.9.0 but the project I have to port this to is based on 2.4.2. If I upgrade that project, it will open up new work scope, so I tried aes_cbc sample based on 2.4.2 and followed exact steps, but over there I still got -134 at psa_cipher_encrypt_setup. Any guesses what could be wrong?

Children
  • I get following error when using 2.4.2


    error: PSA_WANT_KEY_TYPE_AES (defined at modules/mbedtls/Kconfig.psa:55,
    /opt/nordic/ncs/v2.4.2/nrf/modules/trusted-firmware-m/Kconfig.mbedtls_minimal.defconfig:89,
    /opt/nordic/ncs/v2.4.2/nrf/modules/trusted-firmware-m/Kconfig.psa.defconfig:11,
    modules/mbedtls/Kconfig.psa:55, modules/mbedtls/Kconfig.psa:55) is assigned in a configuration file,
    but is not directly user-configurable (has no prompt). It gets its value indirectly from other
    symbols. See docs.zephyrproject.org/.../kconfig.html and/or
    look up PSA_WANT_KEY_TYPE_AES in the menuconfig/guiconfig interface. The Application Development
    Primer, Setting Configuration Values, and Kconfig - Tips and Best Practices sections of the manual
    might be helpful too.


    I can make it work if I set the following value in ./aes_cbc/boards/nrf52840dk_nrf52840.conf, but I guess that disables the hardware crypto (not something I want).

    CONFIG_PSA_CRYPTO_DRIVER_CC3XX=n

  • Hi,

    imtiz_ahmed said:
    1) Does CONFIG_PSA_CRYPTO_DRIVER_OBERON=y mean that all crypto related work then be run in software and hardware cell will no longer be used for the app?

    CC310 will still be used and preferred where it can be used.

    imtiz_ahmed said:
    I get following error when using 2.4.2

    I see. You can do something similar in NCS v2.4. Also there, make sure that both Oberon and CC310 are enabled:

    CONFIG_PSA_CRYPTO_DRIVER_OBERON=y
    CONFIG_PSA_CRYPTO_DRIVER_CC3XX=y

    And additionally, disable the CC310 driver for CBC explicitly:

    CONFIG_PSA_CRYPTO_DRIVER_ALG_CBC_NO_PADDING_CC3XX=n

Related