This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 72928d5dec6 arch/arm64: AES using the Armv8 Cryptography Extension.
72928d5dec6 is described below

commit 72928d5dec6849deb7581c05d523a0cffe1fa0f9
Author: Royyan Zahir <[email protected]>
AuthorDate: Sat Sep 19 15:16:06 2026 +0400

    arch/arm64: AES using the Armv8 Cryptography Extension.
    
    NuttX emits no AES instruction on any arm64 core. There is no runtime
    feature dispatch in arch/arm64, so every AES goes through crypto/rijndael.c
    or crypto/aes.c, and the table-driven one indexes memory with key-dependent
    values, so its timing follows the cache.
    
    Provide aes_cypher() for ECB, CBC and CTR built on AESE, AESD and the
    MixColumns pair, and register it with /dev/crypto as a hardware driver
    alongside the existing stm32h7, sam34 and esp32 modules.
    
    ID_AA64ISAR0_EL1.AES is read on every call, which returns -ENOTSUP rather
    than trapping on a core without the extension.
    
    Verified against the NIST SP 800-38A appendix F vectors for ECB-128,
    ECB-256, CBC-128, CBC-192 and CTR-128, encrypt and decrypt, in place and
    out of place.
    
    Signed-off-by: Royyan Zahir <[email protected]>
---
 arch/arm64/Kconfig                   |  13 ++
 arch/arm64/src/common/CMakeLists.txt |   8 +
 arch/arm64/src/common/Make.defs      |  10 +
 arch/arm64/src/common/arm64_aes.c    | 387 +++++++++++++++++++++++++++++++++++
 arch/arm64/src/common/arm64_crypto.c | 138 +++++++++++++
 5 files changed, 556 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 9de3bb3f4c6..fc416bde70e 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -229,6 +229,19 @@ config ARCH_AS_HAS_ARMV8_5
 menu "ARMv8.5 architectural features"
        depends on ARCH_AS_HAS_ARMV8_5
 
+config ARM64_CRYPTO_AES
+       bool "AES using the Armv8 Cryptography Extension"
+       select CRYPTO
+       select CRYPTO_AES
+       ---help---
+               Provide aes_cypher() with AESE, AESD and the MixColumns pair. 
The
+               table-driven cipher indexes memory with key-dependent values, so
+               its timing follows the cache; these instructions do not.
+
+               The core must implement the extension. ID_AA64ISAR0_EL1 is read 
on
+               every call, which returns -ENOTSUP rather than trapping if it 
does
+               not.
+
 config ARM64_MTE
        bool "Memory Tagging Extension support"
        select ARCH_HAVE_MEMTAG
diff --git a/arch/arm64/src/common/CMakeLists.txt 
b/arch/arm64/src/common/CMakeLists.txt
index 670c9f37a0a..bd53b5aba86 100644
--- a/arch/arm64/src/common/CMakeLists.txt
+++ b/arch/arm64/src/common/CMakeLists.txt
@@ -81,6 +81,14 @@ if(CONFIG_ARCH_HAVE_MMU)
   endif()
 endif()
 
+if(CONFIG_ARM64_CRYPTO_AES)
+  list(APPEND SRCS arm64_aes.c)
+endif()
+
+if(CONFIG_ARM64_CRYPTO_AES AND CONFIG_CRYPTO_CRYPTODEV_HARDWARE)
+  list(APPEND SRCS arm64_crypto.c)
+endif()
+
 if(CONFIG_ARM64_MTE)
   list(APPEND SRCS arm64_mte.c)
 endif()
diff --git a/arch/arm64/src/common/Make.defs b/arch/arm64/src/common/Make.defs
index 90d2f89e36e..4bef36a3b1c 100644
--- a/arch/arm64/src/common/Make.defs
+++ b/arch/arm64/src/common/Make.defs
@@ -88,6 +88,16 @@ CMN_CSRCS += arm64_physpgaddr.c
 endif
 endif
 
+ifeq ($(CONFIG_ARM64_CRYPTO_AES),y)
+CMN_CSRCS += arm64_aes.c
+endif
+
+ifeq ($(CONFIG_CRYPTO_CRYPTODEV_HARDWARE),y)
+ifeq ($(CONFIG_ARM64_CRYPTO_AES),y)
+CMN_CSRCS += arm64_crypto.c
+endif
+endif
+
 ifeq ($(CONFIG_ARM64_MTE),y)
 CMN_CSRCS += arm64_mte.c
 endif
diff --git a/arch/arm64/src/common/arm64_aes.c 
b/arch/arm64/src/common/arm64_aes.c
new file mode 100644
index 00000000000..7d44397b1e1
--- /dev/null
+++ b/arch/arm64/src/common/arm64_aes.c
@@ -0,0 +1,387 @@
+/****************************************************************************
+ * arch/arm64/src/common/arm64_aes.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/crypto/crypto.h>
+
+#include "arm64_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* ID_AA64ISAR0_EL1.AES, bits [7:4]. Non-zero means AESE/AESD and the
+ * MixColumns pair are implemented.
+ */
+
+#define ID_AA64ISAR0_AES_SHIFT 4
+#define ID_AA64ISAR0_AES_MASK  0xful
+
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_ROUNDS 14
+#define AES_MAX_WORDS  (4 * (AES_MAX_ROUNDS + 1))
+
+/* The counter occupies the last four bytes of the block, as in
+ * crypto/xform.c, so both agree on what a CTR stream looks like.
+ */
+
+#define AES_CTR_OFFSET 12
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct arm64_aes_s
+{
+  uint32_t ek[AES_MAX_WORDS];
+  uint32_t dk[AES_MAX_WORDS];
+  unsigned rounds;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static bool arm64_aes_present(void)
+{
+  uint64_t isar0;
+
+  __asm__ volatile ("mrs %0, id_aa64isar0_el1" : "=r" (isar0));
+
+  return ((isar0 >> ID_AA64ISAR0_AES_SHIFT) & ID_AA64ISAR0_AES_MASK) != 0;
+}
+
+/* Only SubWord is accelerated; the rest of the schedule is ordinary
+ * arithmetic, so it stays in C.
+ */
+
+static uint32_t arm64_aes_subword(uint32_t in)
+{
+  uint8_t block[AES_BLOCK_SIZE];
+  uint32_t out;
+
+  /* A zero round key leaves AESE doing SubBytes and ShiftRows only, and
+   * ShiftRows cannot disturb a state whose four columns are identical.
+   */
+
+  memcpy(block + 0, &in, 4);
+  memcpy(block + 4, &in, 4);
+  memcpy(block + 8, &in, 4);
+  memcpy(block + 12, &in, 4);
+
+  __asm__ volatile (
+    "ld1   {v0.16b}, [%0]     \n"
+    "movi  v1.16b, #0         \n"
+    "aese  v0.16b, v1.16b     \n"
+    "st1   {v0.16b}, [%0]     \n"
+    :: "r" (block) : "v0", "v1", "memory");
+
+  memcpy(&out, block, sizeof(out));
+  return out;
+}
+
+static void arm64_aes_encrypt(FAR const struct arm64_aes_s *ctx,
+                              FAR const uint8_t *in, FAR uint8_t *out)
+{
+  FAR const uint8_t *rk = (FAR const uint8_t *)ctx->ek;
+  unsigned n = ctx->rounds - 1;
+
+  __asm__ volatile (
+    "ld1   {v0.16b}, [%[in]]              \n"
+    "1:                                   \n"
+    "ld1   {v1.16b}, [%[rk]], #16         \n"
+    "aese  v0.16b, v1.16b                 \n"
+    "aesmc v0.16b, v0.16b                 \n"
+    "subs  %w[n], %w[n], #1               \n"
+    "b.ne  1b                             \n"
+    "ld1   {v1.16b}, [%[rk]], #16         \n"
+    "aese  v0.16b, v1.16b                 \n"
+    "ld1   {v1.16b}, [%[rk]]              \n"
+    "eor   v0.16b, v0.16b, v1.16b         \n"
+    "st1   {v0.16b}, [%[out]]             \n"
+    : [rk] "+r" (rk), [n] "+r" (n)
+    : [in] "r" (in), [out] "r" (out)
+    : "v0", "v1", "cc", "memory");
+}
+
+static void arm64_aes_decrypt(FAR const struct arm64_aes_s *ctx,
+                              FAR const uint8_t *in, FAR uint8_t *out)
+{
+  FAR const uint8_t *rk = (FAR const uint8_t *)&ctx->dk[4 * ctx->rounds];
+  unsigned n = ctx->rounds - 1;
+
+  __asm__ volatile (
+    "ld1    {v0.16b}, [%[in]]             \n"
+    "1:                                   \n"
+    "ld1    {v1.16b}, [%[rk]]             \n"
+    "sub    %[rk], %[rk], #16             \n"
+    "aesd   v0.16b, v1.16b                \n"
+    "aesimc v0.16b, v0.16b                \n"
+    "subs   %w[n], %w[n], #1              \n"
+    "b.ne   1b                            \n"
+    "ld1    {v1.16b}, [%[rk]]             \n"
+    "sub    %[rk], %[rk], #16             \n"
+    "aesd   v0.16b, v1.16b                \n"
+    "ld1    {v1.16b}, [%[rk]]             \n"
+    "eor    v0.16b, v0.16b, v1.16b        \n"
+    "st1    {v0.16b}, [%[out]]            \n"
+    : [rk] "+r" (rk), [n] "+r" (n)
+    : [in] "r" (in), [out] "r" (out)
+    : "v0", "v1", "cc", "memory");
+}
+
+static int arm64_aes_setkey(FAR struct arm64_aes_s *ctx,
+                            FAR const uint8_t *key, size_t len,
+                            bool decrypt)
+{
+  static const uint8_t rcon[] =
+    {
+      0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
+    };
+
+  unsigned nk;
+  unsigned words;
+  unsigned i;
+
+  switch (len)
+    {
+      case 16:
+        ctx->rounds = 10;
+        break;
+
+      case 24:
+        ctx->rounds = 12;
+        break;
+
+      case 32:
+        ctx->rounds = 14;
+        break;
+
+      default:
+        return -EINVAL;
+    }
+
+  nk = (unsigned)len / 4;
+  words = 4 * (ctx->rounds + 1);
+
+  memcpy(ctx->ek, key, len);
+
+  for (i = nk; i < words; i++)
+    {
+      uint32_t t = ctx->ek[i - 1];
+
+      if (i % nk == 0)
+        {
+          t = (t >> 8) | (t << 24);
+          t = arm64_aes_subword(t) ^ (uint32_t)rcon[i / nk - 1];
+        }
+      else if (nk > 6 && i % nk == 4)
+        {
+          t = arm64_aes_subword(t);
+        }
+
+      ctx->ek[i] = ctx->ek[i - nk] ^ t;
+    }
+
+  if (!decrypt)
+    {
+      return OK;
+    }
+
+  /* The equivalent inverse cipher wants InvMixColumns applied to every round
+   * key except the first and the last, so decryption is the same shape as
+   * encryption rather than a separate schedule.
+   */
+
+  memcpy(ctx->dk, ctx->ek, (size_t)words * 4);
+
+  for (i = 1; i < ctx->rounds; i++)
+    {
+      FAR uint8_t *rk = (FAR uint8_t *)&ctx->dk[4 * i];
+
+      __asm__ volatile (
+        "ld1    {v0.16b}, [%0]   \n"
+        "aesimc v0.16b, v0.16b   \n"
+        "st1    {v0.16b}, [%0]   \n"
+        :: "r" (rk) : "v0", "memory");
+    }
+
+  return OK;
+}
+
+static void arm64_aes_xor_block(FAR uint8_t *dst, FAR const uint8_t *src)
+{
+  int i;
+
+  for (i = 0; i < AES_BLOCK_SIZE; i++)
+    {
+      dst[i] ^= src[i];
+    }
+}
+
+static void arm64_aes_ctr_inc(FAR uint8_t *block)
+{
+  int i;
+
+  for (i = AES_BLOCK_SIZE - 1; i >= AES_CTR_OFFSET; i--)
+    {
+      if (++block[i] != 0)
+        {
+          break;
+        }
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: aes_cypher
+ *
+ * Description:
+ *   Encrypt or decrypt a whole number of AES blocks in ECB, CBC or CTR mode
+ *   using the Armv8 Cryptography Extension.
+ *
+ * Returned Value:
+ *   OK, -ENOTSUP on a core without the extension, or -EINVAL for a bad mode,
+ *   key length or size.
+ *
+ ****************************************************************************/
+
+int aes_cypher(FAR void *out, FAR const void *in, size_t size,
+               FAR const void *iv, FAR const void *key, size_t keysize,
+               int mode, int encrypt)
+{
+  struct arm64_aes_s ctx;
+  uint8_t chain[AES_BLOCK_SIZE];
+  FAR const uint8_t *src = in;
+  FAR uint8_t *dst = out;
+  int ret;
+
+  if (!arm64_aes_present())
+    {
+      return -ENOTSUP;
+    }
+
+  if (size % AES_BLOCK_SIZE != 0)
+    {
+      return -EINVAL;
+    }
+
+  switch (mode & AES_MODE_MASK)
+    {
+      case AES_MODE_ECB:
+      case AES_MODE_CBC:
+      case AES_MODE_CTR:
+        break;
+
+      default:
+        return -EINVAL;
+    }
+
+  /* CTR runs the cipher forwards in both directions, so it never needs the
+   * inverse schedule.
+   */
+
+  ret = arm64_aes_setkey(&ctx, key, keysize,
+                         !encrypt && (mode & AES_MODE_MASK) != AES_MODE_CTR);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  if (iv != NULL)
+    {
+      memcpy(chain, iv, AES_BLOCK_SIZE);
+    }
+  else
+    {
+      memset(chain, 0, AES_BLOCK_SIZE);
+    }
+
+  while (size != 0)
+    {
+      switch (mode & AES_MODE_MASK)
+        {
+          case AES_MODE_ECB:
+            if (encrypt)
+              {
+                arm64_aes_encrypt(&ctx, src, dst);
+              }
+            else
+              {
+                arm64_aes_decrypt(&ctx, src, dst);
+              }
+            break;
+
+          case AES_MODE_CBC:
+            if (encrypt)
+              {
+                memcpy(dst, src, AES_BLOCK_SIZE);
+                arm64_aes_xor_block(dst, chain);
+                arm64_aes_encrypt(&ctx, dst, dst);
+                memcpy(chain, dst, AES_BLOCK_SIZE);
+              }
+            else
+              {
+                uint8_t prev[AES_BLOCK_SIZE];
+
+                memcpy(prev, src, AES_BLOCK_SIZE);
+                arm64_aes_decrypt(&ctx, src, dst);
+                arm64_aes_xor_block(dst, chain);
+                memcpy(chain, prev, AES_BLOCK_SIZE);
+              }
+            break;
+
+          case AES_MODE_CTR:
+            {
+              uint8_t stream[AES_BLOCK_SIZE];
+
+              arm64_aes_encrypt(&ctx, chain, stream);
+              memcpy(dst, src, AES_BLOCK_SIZE);
+              arm64_aes_xor_block(dst, stream);
+              arm64_aes_ctr_inc(chain);
+              explicit_bzero(stream, sizeof(stream));
+            }
+            break;
+        }
+
+      src  += AES_BLOCK_SIZE;
+      dst  += AES_BLOCK_SIZE;
+      size -= AES_BLOCK_SIZE;
+    }
+
+  explicit_bzero(&ctx, sizeof(ctx));
+  explicit_bzero(chain, sizeof(chain));
+  return OK;
+}
diff --git a/arch/arm64/src/common/arm64_crypto.c 
b/arch/arm64/src/common/arm64_crypto.c
new file mode 100644
index 00000000000..760bb003845
--- /dev/null
+++ b/arch/arm64/src/common/arm64_crypto.c
@@ -0,0 +1,138 @@
+/****************************************************************************
+ * arch/arm64/src/common/arm64_crypto.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <crypto/cryptodev.h>
+#include <crypto/xform.h>
+#include <nuttx/crypto/crypto.h>
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int arm64_crypto_newsession(FAR uint32_t *sid,
+                                   FAR struct cryptoini *cri)
+{
+  if (sid == NULL || cri == NULL)
+    {
+      return -EINVAL;
+    }
+
+  switch (cri->cri_alg)
+    {
+      case CRYPTO_AES_CBC:
+        break;
+
+      case CRYPTO_AES_CTR:
+
+        /* cri_klen counts the trailing nonce, which is not key material. */
+
+        if ((cri->cri_klen / 8 - AESCTR_NONCESIZE) > 32)
+          {
+            return -EINVAL;
+          }
+        break;
+
+      default:
+        return -EINVAL;
+    }
+
+  return OK;
+}
+
+static int arm64_crypto_freesession(uint64_t tid)
+{
+  return OK;
+}
+
+static int arm64_crypto_process(FAR struct cryptop *crp)
+{
+  FAR struct cryptodesc *crd;
+  uint8_t iv[AESCTR_BLOCKSIZE];
+
+  for (crd = crp->crp_desc; crd != NULL; crd = crd->crd_next)
+    {
+      switch (crd->crd_alg)
+        {
+          case CRYPTO_AES_CBC:
+            return aes_cypher(crp->crp_dst, crp->crp_buf, crd->crd_len,
+                              crd->crd_iv, crd->crd_key, crd->crd_klen / 8,
+                              AES_MODE_CBC,
+                              crd->crd_flags & CRD_F_ENCRYPT);
+
+          case CRYPTO_AES_CTR:
+            memcpy(iv, crd->crd_key + crd->crd_klen / 8 - AESCTR_NONCESIZE,
+                   AESCTR_NONCESIZE);
+            memcpy(iv + AESCTR_NONCESIZE, crd->crd_iv, AESCTR_IVSIZE);
+            memset(iv + AESCTR_NONCESIZE + AESCTR_IVSIZE, 0,
+                   AESCTR_BLOCKSIZE - AESCTR_NONCESIZE - AESCTR_IVSIZE);
+            return aes_cypher(crp->crp_dst, crp->crp_buf, crd->crd_len,
+                              iv, crd->crd_key,
+                              crd->crd_klen / 8 - AESCTR_NONCESIZE,
+                              AES_MODE_CTR,
+                              crd->crd_flags & CRD_F_ENCRYPT);
+
+          default:
+            return -EINVAL;
+        }
+    }
+
+  return -EINVAL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hwcr_init
+ *
+ * Description:
+ *   Register the Armv8 AES instructions with /dev/crypto.
+ *
+ ****************************************************************************/
+
+void hwcr_init(void)
+{
+  int algs[CRYPTO_ALGORITHM_MAX + 1];
+  int hwcr_id;
+
+  hwcr_id = crypto_get_driverid(0);
+  DEBUGASSERT(hwcr_id >= 0);
+
+  memset(algs, 0, sizeof(algs));
+  algs[CRYPTO_AES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
+  algs[CRYPTO_AES_CTR] = CRYPTO_ALG_FLAG_SUPPORTED;
+
+  crypto_register(hwcr_id, algs, arm64_crypto_newsession,
+                  arm64_crypto_freesession, arm64_crypto_process);
+}

Reply via email to