Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions libtinyaes/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
NAME := libtinyaes
LOCAL_PATH := $(call my-dir)
ALL_SRCS := $(wildcard $(LOCAL_PATH)*.c)
SRCS := $(filter-out $(LOCAL_PATH)aes_hw_stm32l4.c $(LOCAL_PATH)aes_hw_stm32n6.c, $(ALL_SRCS))

SRCS := $(filter-out $(LOCAL_PATH)aes_hw_stm32l4.c, $(ALL_SRCS))

include $(static-lib.mk)

ifeq ($(TARGET_SUBFAMILY), stm32l4x6)
NAME := libtinyaes-stm32l4
SRCS := $(filter-out $(LOCAL_PATH)aes.c, $(ALL_SRCS))
SRCS := $(filter-out $(LOCAL_PATH)aes.c $(LOCAL_PATH)aes_hw_stm32n6.c, $(ALL_SRCS))
LOCAL_HEADERS_DIR := __none # prevent headers re-installation

include $(static-lib.mk)
else ifeq ($(TARGET_SUBFAMILY), stm32n6)
NAME := libtinyaes
SRCS := $(filter-out $(LOCAL_PATH)aes.c $(LOCAL_PATH)aes_hw_stm32l4.c, $(ALL_SRCS))
# LOCAL_HEADERS_DIR := __none # prevent headers re-installation

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This commented-out line should be removed to improve code cleanliness.

endif

include $(static-lib.mk)
150 changes: 150 additions & 0 deletions libtinyaes/aes_hw_stm32n6.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Phoenix-RTOS
*
* Non-thread safe implementation of libtinyaes API using hardware-assisted libcryp
*
* Copyright 2025 Phoenix Systems
* Author: Krzysztof Radzewicz
*
* %LICENSE%
*/

#include <stdint.h>
#include <string.h>
#include <stdio.h>

#include "tinyaes/aes.h"

/* FIXME - unfortunate compiling order, corelibs depends on devices.
* Just provide HW AES API directly for now */
// #include <libmulti/libcryp.h>
#include "aes_hw_stm32n6.h"

/**************************** WARNING ******************************/
/* This implementation uses a hardware peripheral to perform */
/* cryptographic operations and is NOT thread safe. Access is not */
/* synchronized due to performance (avoiding locking/msg overhead) */
/* and compatibility (retaining the same libtinyaes API) reasons. */
/* If used in a multi-threaded environment, be sure to use */
/* application-level locking or other means of synchronization. */
/*******************************************************************/

#if defined(AES_KEYLEN) && AES_KEYLEN == 16
#define CRYP_KEYLEN aes_128
#elif defined(AES_KEYLEN) && AES_KEYLEN == 24
#define CRYP_KEYLEN aes_192
#else
#define CRYP_KEYLEN aes_256
#endif

static unsigned int is_initialized = 0;

static void AES_init(void)
{
if (!is_initialized) {
is_initialized = 1;
libcryp_init();
}
}

void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key)
{
AES_init();
memcpy(&ctx->RoundKey[0], key, AES_KEYLEN);
ctx->RoundKey[AES_KEYLEN] = 0x0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This line appears to be dead code. In the stm32l4 implementation, this is used as a flag to check if a decryption key has been derived. However, in this stm32n6 implementation, the decryption functions unconditionally call libcryp_deriveDecryptionKey(), so this flag is never checked. This line should be removed to avoid confusion. A similar unused flag is set in AES_init_ctx_iv on line 63 and should also be removed.

}

#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))

void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, const uint8_t *iv)
{
AES_init();
memcpy(&ctx->RoundKey[0], key, AES_KEYLEN);
ctx->RoundKey[AES_KEYLEN] = 0x0;
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}

void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv)
{
AES_init();
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}

#endif

#if defined(ECB) && (ECB == 1)

void AES_ECB_encrypt(struct AES_ctx *ctx, uint8_t *buf)
{
libcryp_prepare(aes_ecb, aes_encrypt);
libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);
libcryp_processBlock(buf, buf);
libcryp_unprepare();
}

void AES_ECB_decrypt(struct AES_ctx *ctx, uint8_t *buf)
{
libcryp_deriveDecryptionKey();
libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);
Comment on lines +87 to +88

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The order of operations here seems incorrect. libcryp_deriveDecryptionKey() should be called after libcryp_setKey(), as the decryption key is derived from the encryption key that has been set. Please swap these two lines. This same issue exists in AES_CBC_decrypt_buffer on line 115.

libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);
	libcryp_deriveDecryptionKey();

libcryp_prepare(aes_ecb, aes_decrypt);
libcryp_enable();
libcryp_processBlock(buf, buf);
libcryp_unprepare();
}

#endif // #if defined(ECB) && (ECB == 1)


#if defined(CBC) && (CBC == 1)

void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length)
{
libcryp_prepare(aes_cbc, aes_encrypt);
libcryp_setIv(&ctx->Iv[0]);
libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);

for (int i = 0; i < length; i += AES_BLOCKLEN) {
libcryp_processBlock(buf, buf);
buf += AES_BLOCKLEN;
}
libcryp_unprepare();
}
Comment on lines +100 to +111

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This implementation of AES_CBC_encrypt_buffer does not update the IV in the AES_ctx after the operation. For CBC mode, the last ciphertext block of an encryption operation should become the IV for the next. By not updating ctx->Iv, this implementation will not work correctly for chunked encryption, as subsequent calls will use a stale IV. This breaks the tinyaes API contract.

A similar issue exists in AES_CBC_decrypt_buffer. For chunked decryption, the IV for the next chunk is the last ciphertext block of the previous chunk. The context's IV must be updated accordingly.

You should retrieve the updated IV from the hardware after processing (or manually update it with the last ciphertext block) and store it back into ctx->Iv in both CBC functions, similar to the stm32l4 and software implementations.


void AES_CBC_decrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length)
{
libcryp_deriveDecryptionKey();
libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);
libcryp_prepare(aes_cbc, aes_decrypt);
libcryp_setIv(&ctx->Iv[0]);
libcryp_enable();

for (int i = 0; i < length; i += AES_BLOCKLEN) {
libcryp_processBlock(buf, buf);
buf += AES_BLOCKLEN;
}
libcryp_unprepare();
}


#endif // #if defined(CBC) && (CBC == 1)


#if defined(CTR) && (CTR == 1)

void AES_CTR_xcrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length)
{
/* RM mentions the counter should start at 0x1, but that is wrong. It should be 0. */
uint8_t nonce[16] = {};
memcpy(nonce, &ctx->Iv, 12);
libcryp_prepare(aes_ctr, aes_encrypt);
libcryp_setIv(nonce);
libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);

for (int i = 0; i < length; i += AES_BLOCKLEN) {
libcryp_processBlock(buf, buf);
buf += AES_BLOCKLEN;
}
libcryp_unprepare();
}
Comment on lines +134 to +148

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This implementation of AES_CTR_xcrypt_buffer has a critical security vulnerability. The counter state (part of the IV) is not preserved across calls. The tinyaes API supports chunked operations, where the counter must be incremented and its state maintained between calls. This implementation re-initializes the counter on every call, which leads to nonce-counter reuse if the function is called multiple times with the same key and initial IV. Reusing a nonce-counter pair in CTR mode is catastrophic for security, as it can allow an attacker to recover the plaintext.

Similar to the stm32l4 implementation, you must retrieve the final counter value from the hardware and update ctx->Iv so that subsequent calls can continue from the correct state.


#endif // #if defined(CTR) && (CTR == 1)
73 changes: 73 additions & 0 deletions libtinyaes/aes_hw_stm32n6.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Phoenix-RTOS
*
* STM32L4 AES driver API
*
* Copyright 2020 Phoenix Systems
* Author: Daniel Sawka
Comment on lines +4 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The header comment appears to be copied from the STM32L4 driver. Please update it to reflect that this is for the STM32N6, and also update the copyright year and author to be consistent with aes_hw_stm32n6.c.

Suggested change
* STM32L4 AES driver API
*
* Copyright 2020 Phoenix Systems
* Author: Daniel Sawka
* STM32N6 CRYP driver API
*
* Copyright 2025 Phoenix Systems
* Author: Krzysztof Radzewicz

*
* %LICENSE%
*/


#ifndef LIBCRYP_H_
#define LIBCRYP_H_


enum {
aes_128 = 0,
aes_256 = 1,
aes_192 = 2
};


enum {
aes_ecb = 0,
aes_cbc = 1,
aes_ctr = 2
};


enum {
aes_encrypt = 0,
aes_decrypt = 2
};


int libcryp_tmp(unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out);

Comment on lines +36 to +38

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function libcryp_tmp is declared here but is not defined or used in aes_hw_stm32n6.c. It should be removed to keep the API clean.


void libcryp_enable(void);


void libcryp_disable(void);


int libcryp_setKey(const unsigned char *key, int keylen);


void libcryp_getKey(unsigned char *key, int keylen);


void libcryp_setIv(const unsigned char *iv);


void libcryp_getIv(unsigned char *iv);


void libcryp_deriveDecryptionKey(void);


void libcryp_prepare(int mode, int dir);


void libcryp_unprepare(void);


void libcryp_processBlock(const unsigned char *in, unsigned char *out);


int libcryp_init(void);


#endif
Loading