Skip to content

Conversation

@winderdoot
Copy link

@winderdoot winderdoot commented Sep 29, 2025

Add hardware accelerated libtinyaes implementation on STM32N6. It uses the CRYP peripheral.

Description

Change libtinyaes Makefile to swap implementation under STM32N6 cpu.

Motivation and Context

Similar hardware implementation on STM32L4 using AES peripheral, CRYP is faster and offers more features.

Types of changes

  • [ x] Breaking change (replaces the default software implementation of libtinyaes)

Hardware implementation of tinyaes isn't synchronized on the driver level due to performance considerations.
User applications should handle synchronization.

How Has This Been Tested?

  • Tested by hand on: armv8m55-stm32n6-nucleo (only relevant platform)

Special treatment

  • This PR requires libcryp driver pull request
  • This change isn't urgent.
  • I will merge this PR by myself when appropriate.

@gemini-code-assist
Copy link

Summary of Changes

Hello @winderdoot, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates a hardware-accelerated Advanced Encryption Standard (AES) implementation into the libtinyaes library for the STM32N6 microcontroller. By utilizing the dedicated CRYP peripheral, it aims to significantly enhance cryptographic operation speeds, mirroring a similar performance improvement seen on STM32L4 devices. The change involves updating the build configuration to select the hardware-backed AES when targeting STM32N6, and it's crucial for developers to manage thread synchronization at the application level as the hardware interface is not thread-safe.

Highlights

  • Hardware-Accelerated AES for STM32N6: Introduces a new hardware-assisted implementation of the libtinyaes API specifically for the STM32N6 microcontroller, leveraging its CRYP peripheral for improved performance.
  • Build System Integration: The Makefile for libtinyaes has been updated to conditionally compile the new aes_hw_stm32n6.c source file when the TARGET_SUBFAMILY is stm32n6, replacing the default software AES implementation.
  • Non-Thread-Safe Implementation: The hardware AES implementation is explicitly noted as non-thread-safe due to performance considerations, requiring application-level synchronization if used in a multi-threaded environment.
  • Dependency on libcryp Driver: This pull request depends on a separate libcryp driver pull request (stm32l4-multi: add stm32n6 cryp driver phoenix-rtos-devices#591) to provide the underlying cryptographic peripheral control.
  • Breaking Change: This change is classified as a breaking change because it replaces the default software implementation of libtinyaes for the STM32N6 platform.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@winderdoot winderdoot marked this pull request as draft September 29, 2025 08:53
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a hardware-accelerated AES implementation for STM32N6 by leveraging the CRYP peripheral. The Makefile changes correctly substitute the software implementation with this new hardware-assisted version when targeting STM32N6. However, the C implementation has several significant issues. There is a critical security vulnerability in the CTR mode implementation due to nonce reuse, and correctness bugs in CBC mode for chunked operations because the IV is not updated. Additionally, there's a logic error in the order of operations for key derivation during decryption. I've also noted some minor issues like dead code and copy-paste errors in comments and header files. These issues must be addressed before this can be merged.

Comment on lines +100 to +111
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();
}

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.

Comment on lines +134 to +148
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();
}

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.

Comment on lines +87 to +88
libcryp_deriveDecryptionKey();
libcryp_setKey(&ctx->RoundKey[0], CRYP_KEYLEN);

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();

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.

{
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.

Comment on lines +4 to +7
* STM32L4 AES driver API
*
* Copyright 2020 Phoenix Systems
* Author: Daniel Sawka

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

Comment on lines +36 to +38

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

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants