You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2021/06/14 18:03:22 UTC

[incubator-nuttx] branch master updated: risc-v/esp32c3: Support ESP32-C3 RSA accelerator

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8eaaf6d  risc-v/esp32c3: Support ESP32-C3 RSA accelerator
8eaaf6d is described below

commit 8eaaf6d46287f4087a7b7f091a7194ec67f2cf3b
Author: Liu Han <li...@espressif.com>
AuthorDate: Wed Jun 9 20:17:54 2021 +0800

    risc-v/esp32c3: Support ESP32-C3 RSA accelerator
---
 arch/risc-v/src/esp32c3/Kconfig                    |   30 +
 arch/risc-v/src/esp32c3/Make.defs                  |    8 +
 arch/risc-v/src/esp32c3/esp32c3_bignum.c           | 4067 ++++++++++++++++++++
 arch/risc-v/src/esp32c3/esp32c3_bignum.h           |  892 +++++
 arch/risc-v/src/esp32c3/esp32c3_rsa.c              | 2375 ++++++++++++
 arch/risc-v/src/esp32c3/esp32c3_rsa.h              |  513 +++
 arch/risc-v/src/esp32c3/hardware/esp32c3_rsa.h     |  308 ++
 .../esp32c3/esp32c3-devkit/configs/rsa/defconfig   |   51 +
 8 files changed, 8244 insertions(+)

diff --git a/arch/risc-v/src/esp32c3/Kconfig b/arch/risc-v/src/esp32c3/Kconfig
index 154d677..b9662c8 100644
--- a/arch/risc-v/src/esp32c3/Kconfig
+++ b/arch/risc-v/src/esp32c3/Kconfig
@@ -300,6 +300,18 @@ config ESP32C3_AES_ACCELERATOR
 	bool "AES Accelerator"
 	default n
 
+config ESP32C3_BIGNUM_ACCELERATOR
+	bool "BIGNUM Accelerator"
+	default n
+	---help---
+		Enable ESP32-C3 BIGNUM accelerator support.
+
+config ESP32C3_RSA_ACCELERATOR
+	bool "RSA Accelerator"
+	default n
+	---help---
+		Enable ESP32-C3 RSA accelerator support.
+
 endmenu # ESP32-C3 Peripheral Support
 
 menu "I2C Configuration"
@@ -813,4 +825,22 @@ config ESP32C3_AES_ACCELERATOR_TEST
 
 endmenu # AES accelerator
 
+menu "RSA Accelerate Configuration"
+	depends on ESP32C3_RSA_ACCELERATOR
+
+config ESP32C3_RSA_ACCELERATOR_TEST
+	bool "RSA driver test"
+	default n
+
+menu "BIGNUM"
+	depends on ESP32C3_BIGNUM_ACCELERATOR
+
+config ESP32C3_BIGNUM_ACCELERATOR_TEST
+	bool "BIGNUM driver test"
+	default n
+
+endmenu # ESP32C3_BIGNUM_ACCELERATOR
+
+endmenu # ESP32C3_RSA_ACCELERATOR
+
 endif # ARCH_CHIP_ESP32C3
diff --git a/arch/risc-v/src/esp32c3/Make.defs b/arch/risc-v/src/esp32c3/Make.defs
index 97384fc..1087e7f 100644
--- a/arch/risc-v/src/esp32c3/Make.defs
+++ b/arch/risc-v/src/esp32c3/Make.defs
@@ -109,6 +109,14 @@ ifeq ($(CONFIG_ESP32C3_RT_TIMER),y)
 CHIP_CSRCS += esp32c3_rt_timer.c
 endif
 
+ifeq ($(CONFIG_ESP32C3_BIGNUM_ACCELERATOR),y)
+CHIP_CSRCS += esp32c3_bignum.c
+endif
+
+ifeq ($(CONFIG_ESP32C3_RSA_ACCELERATOR),y)
+CHIP_CSRCS += esp32c3_rsa.c
+endif
+
 ifeq ($(CONFIG_ESP32C3_FREERUN),y)
 CHIP_CSRCS += esp32c3_freerun.c
 endif
diff --git a/arch/risc-v/src/esp32c3/esp32c3_bignum.c b/arch/risc-v/src/esp32c3/esp32c3_bignum.c
new file mode 100644
index 0000000..0e52e7e
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/esp32c3_bignum.c
@@ -0,0 +1,4067 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/esp32c3_bignum.c
+ *
+ * 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>
+
+#ifdef CONFIG_ESP32C3_BIGNUM_ACCELERATOR
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <debug.h>
+#include <semaphore.h>
+
+#include "riscv_arch.h"
+#include "hardware/esp32c3_rsa.h"
+#include "hardware/esp32c3_system.h"
+
+#include "esp32c3_bignum.h"
+
+/****************************************************************************
+ * Pre-processor Macros
+ ****************************************************************************/
+
+#undef MIN
+#undef MAX
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+#define SOC_RSA_MAX_BIT_LEN (3072)
+
+#define CIL  (sizeof(uint32_t))             /* chars in limb */
+#define BIL  (CIL << 3)                     /* bits in limb */
+#define BIH  (CIL << 2)                     /* half limb size */
+
+#define MPI_SIZE_T_MAX  ((size_t) -1)       /* SIZE_T_MAX is not standard */
+
+/* Convert between bits/chars and number of limbs
+ * Divide first in order to avoid potential overflows
+ */
+
+#define BITS_TO_LIMBS(i)  ((i) / BIL + ((i) % BIL != 0))
+#define CHARS_TO_LIMBS(i) ((i) / CIL + ((i) % CIL != 0))
+
+/* Get a specific byte, without range checks. */
+#define BYTE_BITS         (8)
+#define BYTE_CHECKS       (0xff)
+#define GET_BYTE(X, i)    (((X)->p[(i) / CIL] >> \
+                          (((i) % CIL) * BYTE_BITS)) & BYTE_CHECKS)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static sem_t g_rsa_sem = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_mpi_to_mem_block
+ *
+ * Description:
+ *   Copy MPI bignum 'mpi' to hardware memory block.
+ *
+ * Input Parameters:
+ *   mem_base    - The hardware memory block
+ *   mpi         - The bignum 'mpi' from the previous calculation
+ *   num_words   - The number of words to be represented
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_to_mem_block(uint32_t mem_base,
+                                     const struct esp32c3_mpi_s *mpi,
+                                     size_t num_words)
+{
+  uint32_t *pbase = (uint32_t *)mem_base;
+  uint32_t copy_words = MIN(num_words, mpi->n);
+  int i;
+
+  /* Copy MPI data to memory block registers */
+
+  memcpy(pbase, mpi->p, copy_words * sizeof(uint32_t));
+
+  /* Zero any remaining memory block data */
+
+  for (i = copy_words; i < num_words; i++)
+    {
+      pbase[i] = 0;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32c3_mem_block_to_mpi
+ *
+ * Description:
+ *   Read MPI bignum back from hardware memory block.
+ *
+ * Input Parameters:
+ *   x           - The result from the previous calculation
+ *   mem_base    - The hardware memory block
+ *   num_words   - The number of words to be represented
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mem_block_to_mpi(struct esp32c3_mpi_s *x,
+                                     uint32_t mem_base, int num_words)
+{
+  int i;
+
+  /* Copy data from memory block registers */
+
+  const size_t REG_WIDTH = sizeof(uint32_t);
+  for (i = 0; i < num_words; i++)
+    {
+      x->p[i] = getreg32(mem_base + (i * REG_WIDTH));
+    }
+
+  /* Zero any remaining limbs in the bignum,
+   * if the buffer is bigger than num_words
+   */
+
+  for (i = num_words; i < x->n; i++)
+    {
+      x->p[i] = 0;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_start_op
+ *
+ * Description:
+ *   Begin an RSA operation.
+ *
+ * Input Parameters:
+ *   op_reg   - Specifies which 'START' register to write to.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_start_op(uint32_t op_reg)
+{
+  /* Clear interrupt status */
+
+  putreg32(1, RSA_CLEAR_INTERRUPT_REG);
+
+  /* Note: above putreg32 includes a memw, so we know any writes
+   * to the memory blocks are also complete.
+   */
+
+  putreg32(1, op_reg);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_wait_op_complete
+ *
+ * Description:
+ *   Wait for an RSA operation to complete.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_wait_op_complete(void)
+{
+  while (getreg32(RSA_IDLE_REG) != 1)
+    {
+    }
+
+  /* clear the interrupt */
+
+  putreg32(1, RSA_CLEAR_INTERRUPT_REG);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_enable_hardware_hw_op
+ *
+ * Description:
+ *   Enable the MPI hardware and acquire the lock.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_enable_hardware_hw_op(void)
+{
+  nxsem_wait(&g_rsa_sem);
+
+  /* Enable RSA hardware */
+
+  modifyreg32(SYSTEM_PERIP_CLK_EN1_REG, 0, SYSTEM_CRYPTO_RSA_CLK_EN);
+  modifyreg32(SYSTEM_PERIP_RST_EN1_REG, (SYSTEM_CRYPTO_RSA_RST |
+                                         SYSTEM_CRYPTO_DS_RST), 0);
+
+  modifyreg32(SYSTEM_RSA_PD_CTRL_REG, SYSTEM_RSA_MEM_PD, 0);
+
+  while (getreg32(RSA_CLEAN_REG) != 1)
+    {
+    }
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_disable_hardware_hw_op
+ *
+ * Description:
+ *   Disable the MPI hardware and release the lock.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_disable_hardware_hw_op(void)
+{
+  modifyreg32(SYSTEM_RSA_PD_CTRL_REG, 0, SYSTEM_RSA_MEM_PD);
+
+  /* Disable RSA hardware */
+
+  modifyreg32(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_CRYPTO_RSA_CLK_EN, 0);
+  modifyreg32(SYSTEM_PERIP_RST_EN1_REG, 0, SYSTEM_CRYPTO_RSA_RST);
+
+  nxsem_post(&g_rsa_sem);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_read_result_hw_op
+ *
+ * Description:
+ *   Read out the result from the previous calculation.
+ *
+ * Input Parameters:
+ *   Z          - The result from the previous calculation
+ *   z_words    - The number of words to be represented
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_read_result_hw_op(struct esp32c3_mpi_s *Z,
+                                          size_t z_words)
+{
+  esp32c3_mpi_wait_op_complete();
+  esp32c3_mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_REG, z_words);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mul_mpi_hw_op
+ *
+ * Description:
+ *   Starts a (X * Y) calculation in hardware.
+ *
+ * Input Parameters:
+ *   X          - First multiplication argument
+ *   Y          - Second multiplication argument
+ *   n_words    - The number of words to be represented
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_mul_mpi_hw_op(const struct esp32c3_mpi_s *X,
+                                      const struct esp32c3_mpi_s *Y,
+                                      size_t n_words)
+{
+  /* Copy X (right-extended) & Y (left-extended) to memory block */
+
+  esp32c3_mpi_to_mem_block(RSA_MEM_X_BLOCK_REG, X, n_words);
+  esp32c3_mpi_to_mem_block(RSA_MEM_Z_BLOCK_REG + n_words * 4, Y, n_words);
+
+  putreg32(((n_words * 2) - 1), RSA_MODE_REG);
+  esp32c3_mpi_start_op(RSA_MULT_START_REG);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mult_failover_mod_op
+ *
+ * Description:
+ *   Special-case of (X * Y), where we use hardware montgomery mod
+ *   multiplication to calculate result where either A or B are > 2048 bits
+ *   so can't use the standard multiplication method.
+ *
+ * Input Parameters:
+ *   X          - First multiplication argument
+ *   Y          - Second multiplication argument
+ *   num_words  - The number of words to be represented
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_mult_failover_mod_op(const struct esp32c3_mpi_s *X,
+                                             const struct esp32c3_mpi_s *Y,
+                                             size_t num_words)
+{
+  int i;
+
+  /* M = 2^num_words - 1, so block is entirely FF */
+
+  for (i = 0; i < num_words; i++)
+    {
+      putreg32(UINT32_MAX, RSA_MEM_M_BLOCK_REG + i * 4);
+    }
+
+  /* mprime = 1 */
+
+  putreg32(1, RSA_M_PRIME_REG);
+  putreg32(num_words - 1, RSA_MODE_REG);
+
+  /* Load X & Y */
+
+  esp32c3_mpi_to_mem_block(RSA_MEM_X_BLOCK_REG, X, num_words);
+  esp32c3_mpi_to_mem_block(RSA_MEM_Y_BLOCK_REG, Y, num_words);
+
+  /* rinv = 1, write first word */
+
+  putreg32(1, RSA_MEM_RB_BLOCK_REG);
+
+  /* Zero out rest of the rinv words */
+
+  for (i = 1; i < num_words; i++)
+    {
+      putreg32(0, RSA_MEM_RB_BLOCK_REG + i * 4);
+    }
+
+  esp32c3_mpi_start_op(RSA_MODMULT_START_REG);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_zeroize
+ *
+ * Description:
+ *   Zero any limbs data.
+ *
+ * Input Parameters:
+ *   v     - The pointer to limbs
+ *   n     - The total number of limbs
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32c3_mpi_zeroize(uint32_t *v, size_t n)
+{
+  memset(v, 0, CIL * n);
+}
+
+/****************************************************************************
+ * Name: bits_to_words
+ *
+ * Description:
+ *   Convert bit count to 32-bits word count.
+ *
+ * Input Parameters:
+ *   bits  - The number of bit count
+ *
+ * Returned Value:
+ *   Number of words count.
+ *
+ ****************************************************************************/
+
+static size_t bits_to_words(size_t bits)
+{
+  return (bits + 31) / 32;
+}
+
+/****************************************************************************
+ * Name: mpi_sub_hlp
+ *
+ * Description:
+ *   Helper for esp32c3_mpi subtraction
+ *
+ * Input Parameters:
+ *   n       - Number of limbs of \p d and \p s
+ *   d       - On input, the left operand, On output, the result operand
+ *   s       - The right operand
+ *
+ * Returned Value:
+ *   \c 1 if \p `d < \p s`.
+ *   \c 0 if \p `d >= \p s`..
+ *
+ ****************************************************************************/
+
+static uint32_t mpi_sub_hlp(size_t n,
+                            uint32_t *d,
+                            const uint32_t *s)
+{
+  size_t i;
+  uint32_t c;
+  uint32_t z;
+
+  for (i = c = 0; i < n; i++, s++, d++)
+    {
+      z   = (*d <  c);
+      *d -= c;
+      c   = (*d < *s) + z;
+      *d -= *s;
+    }
+
+  return c;
+}
+
+/****************************************************************************
+ * Name: mpi_mul_addc
+ *
+ * Description:
+ *   Helper for esp32c3_mpi multiplication
+ *
+ * Input Parameters:
+ *   count   - The count of limbs
+ *   c       - The result number of limbs
+ *   s       - The target number of limbs
+ *   d       - The pointer to limbs
+ *   b       - The total number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static inline void mpi_mul_addc(uint32_t count, uint32_t *c,
+                                uint32_t **s, uint32_t **d, uint32_t b)
+{
+  uint32_t s0;
+  uint32_t s1;
+  uint32_t b0;
+  uint32_t b1;
+  uint32_t r0;
+  uint32_t r1;
+  uint32_t rx;
+  uint32_t ry;
+
+  b0 = (b << BIH) >> BIH;
+  b1 = (b >> BIH);
+
+  for (int i = 0; i < count; ++i)
+    {
+      s0 = (**s << BIH) >> BIH;
+      s1 = (**s >> BIH);
+      (*s)++;
+      rx = s0 * b1;
+      r0 = s0 * b0;
+      ry = s1 * b0;
+      r1 = s1 * b1;
+      r1 += (rx >> BIH);
+      r1 += (ry >> BIH);
+      rx <<= BIH;
+      ry <<= BIH;
+      r0 += rx;
+      r1 += (r0 < rx);
+      r0 += ry;
+      r1 += (r0 < ry);
+      r0 += *c;
+      r1 += (r0 < *c);
+      r0 += **d;
+      r1 += (r0 < **d);
+      *c = r1;
+      *((*d)++) = r0;
+    }
+}
+
+/****************************************************************************
+ * Name: mpi_mul_hlp
+ *
+ * Description:
+ *   Helper for esp32c3_mpi multiplication
+ *
+ * Input Parameters:
+ *   i       - The MPI context to grow
+ *   s       - The target number of limbs
+ *   d       - The pointer to limbs
+ *   b       - The total number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static void mpi_mul_hlp(size_t i, uint32_t *s, uint32_t *d, uint32_t b)
+{
+  uint32_t c = 0;
+
+  for (; i >= 16; i -= 16)
+    {
+      mpi_mul_addc(16, &c, &s, &d, b);
+    }
+
+  for (; i >= 8; i -= 8)
+    {
+      mpi_mul_addc(8, &c, &s, &d, b);
+    }
+
+  for (; i > 0; i--)
+    {
+      mpi_mul_addc(1, &c, &s, &d, b);
+    }
+
+  do
+    {
+      *d += c;
+      c = (*d < c);
+      d++;
+    }
+  while (c != 0);
+}
+
+/****************************************************************************
+ * Name: mpi_safe_cond_assign
+ *
+ * Description:
+ *   Conditionally assign dest = src, without leaking information
+ *   about whether the assignment was made or not.
+ *
+ * Input Parameters:
+ *   n       - The MPI context to grow
+ *   dest    - The MPI to conditionally assign to
+ *   src     - The MPI to conditionally assign from
+ *   assign  - The condition deciding whether perform the assignment or not
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void mpi_safe_cond_assign(size_t n,
+                                 uint32_t *dest,
+                                 const uint32_t *src,
+                                 unsigned char assign)
+{
+  size_t i;
+  for (i = 0; i < n; i++)
+    {
+      dest[i] = dest[i] * (1 - assign) + src[i] * assign;
+    }
+}
+
+/****************************************************************************
+ * Name: mpi_montg_init
+ *
+ * Description:
+ *   Fast Montgomery initialization
+ *
+ * Input Parameters:
+ *   X       - The MPI context to grow
+ *   nblimbs - The target number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static void mpi_montg_init(uint32_t *mm, const struct esp32c3_mpi_s *N)
+{
+  uint32_t x;
+  uint32_t m0 = N->p[0];
+  unsigned int i;
+
+  x = m0 + (((m0 + 2) & 4) << 1);
+
+  for (i = BIL; i >= 8; i /= 2)
+    {
+      x *= (2 - (m0 * x));
+    }
+
+  *mm = ~x + 1;
+}
+
+/****************************************************************************
+ * Name: mpi_montmul
+ *
+ * Description:
+ *   Montgomery multiplication: A = A * B * R^-1 mod N
+ *
+ * Input Parameters:
+ *   A   - One of the numbers to multiply,
+ *         It must have at least as many limbs as N
+ *         (A->n >= N->n), and any limbs beyond n are ignored.
+ *         On successful completion, A contains the result of
+ *         the multiplication A * B * R^-1 mod N where
+ *         R = (2^CIL)^n.
+ *   B   - One of the numbers to multiply, It must be nonzero
+ *         and must not have more limbs than N (B->n <= N->n)
+ *   N   - The modulo. N must be odd.
+ *   mm  - The value calculated by `mpi_montg_init(&mm, N)`.
+ *         This is -N^-1 mod 2^CIL.
+ *   T   - A bignum for temporary storage.
+ *         It must be at least twice the limb size of N plus 2
+ *         (T->n >= 2 * (N->n + 1)).
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void mpi_montmul(struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B,
+                        const struct esp32c3_mpi_s *N,
+                        uint32_t mm,
+                        const struct esp32c3_mpi_s *T)
+{
+  size_t i;
+  size_t n;
+  size_t m;
+  uint32_t u0;
+  uint32_t u1;
+  uint32_t *d;
+
+  memset(T->p, 0, T->n * CIL);
+
+  d = T->p;
+  n = N->n;
+  m = (B->n < n) ? B->n : n;
+
+  for (i = 0; i < n; i++)
+    {
+      /* T = (T + u0*B + u1*N) / 2^BIL */
+
+      u0 = A->p[i];
+      u1 = (d[0] + u0 * B->p[0]) * mm;
+
+      mpi_mul_hlp(m, B->p, d, u0);
+      mpi_mul_hlp(n, N->p, d, u1);
+
+      *d++ = u0;
+      d[n + 1] = 0;
+    }
+
+  /* At this point, d is either the desired result or the desired result
+   * plus N. We now potentially subtract N, avoiding leaking whether the
+   * subtraction is performed through side channels.
+   */
+
+  /* Copy the n least significant limbs of d to A, so that
+   * A = d if d < N (recall that N has n limbs).
+   */
+
+  memcpy(A->p, d, n * CIL);
+
+  /* If d >= N then we want to set A to d - N. To prevent timing attacks,
+   * do the calculation without using conditional tests.
+   */
+
+  /* Set d to d0 + (2^BIL)^n - N where d0 is the current value of d.
+   */
+
+  d[n] += 1;
+  d[n] -= mpi_sub_hlp(n, d, N->p);
+  /* If d0 < N then d < (2^BIL)^n
+   * so d[n] == 0 and we want to keep A as it is.
+   * If d0 >= N then d >= (2^BIL)^n, and d <= (2^BIL)^n + N < 2 * (2^BIL)^n
+   * so d[n] == 1 and we want to set A to the result of the subtraction
+   * which is d - (2^BIL)^n, i.e. the n least significant limbs of d.
+   * This exactly corresponds to a conditional assignment.
+   */
+
+  mpi_safe_cond_assign(n, A->p, d, (unsigned char) d[n]);
+}
+
+/****************************************************************************
+ * Name: mpi_montred
+ *
+ * Description:
+ *   Montgomery reduction: A = A * R^-1 mod N
+ *
+ * Input Parameters:
+ *   A   - One of the numbers to multiply,
+ *         It must have at least as many limbs as N
+ *         (A->n >= N->n), and any limbs beyond n are ignored.
+ *         On successful completion, A contains the result of
+ *         the multiplication A * B * R^-1 mod N where
+ *         R = (2^CIL)^n.
+ *   N   - The modulo. N must be odd.
+ *   mm  - The value calculated by `mpi_montg_init(&mm, N)`.
+ *         This is -N^-1 mod 2^CIL.
+ *   T   - A bignum for temporary storage.
+ *         It must be at least twice the limb size of N plus 2
+ *         (T->n >= 2 * (N->n + 1)).
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static void mpi_montred(struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *N,
+                        uint32_t mm,
+                        const struct esp32c3_mpi_s *T)
+{
+  uint32_t z = 1;
+  struct esp32c3_mpi_s U;
+
+  U.n = (int) z;
+  U.s = (int) z;
+  U.p = &z;
+
+  mpi_montmul(A, &U, N, mm, T);
+}
+
+/****************************************************************************
+ * Name: mpi_mult_mpi_overlong
+ *
+ * Description:
+ *   Deal with the case when X & Y are too long for the hardware unit,
+ *   by splitting one operand into two halves.
+ *
+ * Input Parameters:
+ *   Z       - The destination MPI
+ *   X       - The first factor
+ *   Y       - The second factor
+ *   y_words - The number of words to be process
+ *   z_words - The number of words to be represented
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpi_mult_mpi_overlong(struct esp32c3_mpi_s *Z,
+                                 const struct esp32c3_mpi_s *X,
+                                 const struct esp32c3_mpi_s *Y,
+                                 size_t y_words, size_t z_words)
+{
+  int ret = 0;
+  struct esp32c3_mpi_s ztemp;
+
+  /* Rather than slicing in two on bits we slice on limbs (32 bit words) */
+
+  const size_t words_slice = y_words / 2;
+
+  /* yp holds lower bits of Y */
+
+  const struct esp32c3_mpi_s yp = {
+    .p = Y->p,
+    .n = words_slice,
+    .s = Y->s
+  };
+
+  /* ypp holds upper bits of Y,
+   * right shifted (also reuses Y's array contents)
+   */
+
+  const struct esp32c3_mpi_s ypp = {
+    .p = Y->p + words_slice,
+    .n = y_words - words_slice,
+    .s = Y->s
+  };
+
+  esp32c3_mpi_init(&ztemp);
+
+  /* Get result ztemp = yp * X (need temporary variable ztemp) */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&ztemp, X, &yp), cleanup);
+
+  /* Z = ypp * Y */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(Z, X, &ypp), cleanup);
+
+  /* Z = Z << b */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(Z, words_slice * 32), cleanup);
+
+  /* Z += ztemp */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(Z, Z, &ztemp), cleanup);
+
+cleanup:
+  esp32c3_mpi_free(&ztemp);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: mpi_mult_mpi_failover_mod_mult
+ *
+ * Description:
+ *   Where we use hardware montgomery mod multiplication to calculate an
+ *   esp32c3_mpi_mult_mpi result where either A or B are > 2048 bits
+ *   so can't use the standard multiplication method.
+ *
+ * Input Parameters:
+ *   Z       - The destination MPI
+ *   X       - The first factor
+ *   Y       - The second factor
+ *   z_words - The number of words to be represented
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpi_mult_mpi_failover_mod_mult(struct esp32c3_mpi_s *Z,
+                                          const struct esp32c3_mpi_s *X,
+                                          const struct esp32c3_mpi_s *Y,
+                                          size_t z_words)
+{
+  int ret;
+
+  esp32c3_mpi_enable_hardware_hw_op();
+
+  esp32c3_mpi_mult_failover_mod_op(X, Y, z_words);
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(Z, z_words), cleanup);
+  esp32c3_mpi_read_result_hw_op(Z, z_words);
+
+  Z->s = X->s * Y->s;
+cleanup:
+  esp32c3_mpi_disable_hardware_hw_op();
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_bignum_clz
+ *
+ * Description:
+ *   Count leading zero bits in a given integer
+ *
+ * Input Parameters:
+ *   x       - The MPI context to query
+ *
+ * Returned Value:
+ *   The count leading zero bits in a given integer.
+ *
+ ****************************************************************************/
+
+static size_t esp32c3_bignum_clz(const uint32_t x)
+{
+  size_t j;
+  uint32_t mask = UINT32_C(1) << (BIL - 1);
+
+  for (j = 0; j < BIL; j++)
+    {
+      if (x & mask)
+        {
+          break;
+        }
+
+      mask >>= 1;
+    }
+
+  return j;
+}
+
+/****************************************************************************
+ * Name: mpi_get_digit
+ *
+ * Description:
+ *   Convert an ASCII character to digit value
+ *
+ * Input Parameters:
+ *   d       - The destination MPI
+ *   radix   - The numeric base of the input character
+ *   c       - An ASCII character
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpi_get_digit(uint32_t *d, int radix, char c)
+{
+  *d = 255;
+
+  if (c >= 0x30 && c <= 0x39)
+    {
+      *d = c - 0x30;
+    }
+
+  if (c >= 0x41 && c <= 0x46)
+    {
+      *d = c - 0x37;
+    }
+
+  if (c >= 0x61 && c <= 0x66)
+    {
+      *d = c - 0x57;
+    }
+
+  if (*d >= (uint32_t) radix)
+    {
+      return ESP32C3_ERR_MPI_INVALID_CHARACTER;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpi_write_hlp
+ *
+ * Description:
+ *   Helper to write the digits high-order first
+ *
+ * Input Parameters:
+ *   X       - The source MPI
+ *   radix   - The numeric base of the output string
+ *   p       - The buffer to write the string to
+ *   buflen  - The available size in Bytes of p
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpi_write_hlp(struct esp32c3_mpi_s *X, int radix,
+                         char **p, const size_t buflen)
+{
+  int ret;
+  uint32_t r;
+  size_t length = 0;
+  char *p_end = *p + buflen;
+
+  do
+    {
+      if (length >= buflen)
+        {
+          return ESP32C3_ERR_MPI_BUFFER_TOO_SMALL;
+        }
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_int(&r, X, radix), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_div_int(X, NULL, X, radix), cleanup);
+
+      /* Write the residue in the current position, as an ASCII character.
+       */
+
+      if (r < 0xa)
+        {
+          *(--p_end) = (char)('0' + r);
+        }
+      else
+        {
+          *(--p_end) = (char)('A' + (r - 0xa));
+        }
+
+      length++;
+    }
+  while (esp32c3_mpi_cmp_int(X, 0) != 0);
+
+  memmove(*p, p_end, length);
+  *p += length;
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: mpi_uint_bigendian_to_host
+ *
+ * Description:
+ *   Convert a big-endian byte array aligned to the size of uint32_t
+ *   into the storage form used by esp32c3_mpi
+ *
+ * Input Parameters:
+ *   X       - The MPI context to convert
+ *
+ * Returned Value:
+ *   The size of uint32_t into the storage form used by esp32c3_mpi.
+ *
+ ****************************************************************************/
+
+static uint32_t mpi_uint_bigendian_to_host(uint32_t x)
+{
+  uint8_t i;
+  unsigned char *x_ptr;
+  uint32_t tmp = 0;
+
+  for (i = 0, x_ptr = (unsigned char *) &x; i < CIL; i++, x_ptr++)
+    {
+      tmp <<= CHAR_BIT;
+      tmp |= (uint32_t) *x_ptr;
+    }
+
+  return (tmp);
+}
+
+/****************************************************************************
+ * Name: mpi_bigendian_to_host
+ *
+ * Description:
+ *   Enlarge an MPI to the specified number of limbs
+ *
+ * Input Parameters:
+ *   p       - The MPI context to grow
+ *   limbs   - The target number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static void mpi_bigendian_to_host(uint32_t * const p, size_t limbs)
+{
+  uint32_t *cur_limb_left;
+  uint32_t *cur_limb_right;
+  if (limbs == 0)
+    {
+      return ;
+    }
+
+  /* Traverse limbs and
+   * - adapt byte-order in each limb
+   * - swap the limbs themselves.
+   * For that, simultaneously traverse the limbs from left to right
+   * and from right to left, as long as the left index is not bigger
+   * than the right index (it's not a problem if limbs is odd and the
+   * indices coincide in the last iteration).
+   */
+
+  for (cur_limb_left = p, cur_limb_right = p + (limbs - 1);
+     cur_limb_left <= cur_limb_right;
+     cur_limb_left++, cur_limb_right--)
+    {
+      uint32_t tmp;
+
+      /* Note that if cur_limb_left == cur_limb_right,
+       * this code effectively swaps the bytes only once.
+       */
+
+      tmp = mpi_uint_bigendian_to_host(*cur_limb_left);
+      *cur_limb_left = mpi_uint_bigendian_to_host(*cur_limb_right);
+      *cur_limb_right = tmp;
+    }
+}
+
+/****************************************************************************
+ * Name: ct_lt_mpi_uint
+ *
+ * Description:
+ *   Decide if an integer is less than the other, without branches.
+ *
+ * Input Parameters:
+ *   X       - First integer
+ *   nblimbs - Second integer
+ *
+ * Returned Value:
+ *   1 if \p x is less than \p y, 0 otherwise.
+ *
+ ****************************************************************************/
+
+static unsigned ct_lt_mpi_uint(const uint32_t x,
+                               const uint32_t y)
+{
+  uint32_t ret;
+  uint32_t cond;
+
+  /* Check if the most significant bits (MSB) of the operands are different.
+   */
+
+  cond = (x ^ y);
+
+  /* If the MSB are the same then the difference x-y will be negative (and
+   * have its MSB set to 1 during conversion to unsigned) if and only if x<y.
+   */
+
+  ret = (x - y) & ~cond;
+
+  /* If the MSB are different, then the operand with the MSB of 1 is the
+   * bigger. (That is if y has MSB of 1, then x<y is true and it is false if
+   * the MSB of y is 0.)
+   */
+
+  ret |= y & cond;
+
+  ret = ret >> (BIL - 1);
+
+  return (unsigned) ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_bignum_int_div_int
+ *
+ * Description:
+ *   Unsigned integer divide - double uint32_t dividend, u1/u0, and
+ *   uint32_t divisor, d
+ *
+ * Input Parameters:
+ *   X       - The MPI context to grow
+ *   nblimbs - The target number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32c3_bignum_int_div_int(uint32_t u1, uint32_t u0,
+                                           uint32_t d, uint32_t *r)
+{
+  const uint32_t radix = (uint32_t) 1 << BIH;
+  const uint32_t uint_halfword_mask = ((uint32_t) 1 << BIH) - 1;
+  uint32_t d0;
+  uint32_t d1;
+  uint32_t q0;
+  uint32_t q1;
+  uint32_t rax;
+  uint32_t r0;
+  uint32_t quotient;
+  uint32_t u0_msw;
+  uint32_t u0_lsw;
+  size_t s;
+
+  /* Check for overflow */
+
+  if (0 == d || u1 >= d)
+    {
+      if (r != NULL)
+        {
+          *r = ~0;
+        }
+
+      return (~0);
+    }
+
+  /* Algorithm D, Section 4.3.1 - The Art of Computer Programming
+   *   Vol. 2 - Seminumerical Algorithms, Knuth
+   */
+
+  /* Normalize the divisor, d, and dividend, u0, u1
+   */
+
+  s = esp32c3_bignum_clz(d);
+  d = d << s;
+
+  u1 = u1 << s;
+  u1 |= (u0 >> (BIL - s)) & (-(int32_t)s >> (BIL - 1));
+  u0 =  u0 << s;
+
+  d1 = d >> BIH;
+  d0 = d & uint_halfword_mask;
+
+  u0_msw = u0 >> BIH;
+  u0_lsw = u0 & uint_halfword_mask;
+
+  /* Find the first quotient and remainder
+   */
+
+  q1 = u1 / d1;
+  r0 = u1 - d1 * q1;
+
+  while (q1 >= radix || (q1 * d0 > radix * r0 + u0_msw))
+    {
+      q1 -= 1;
+      r0 += d1;
+
+      if (r0 >= radix)
+        {
+          break;
+        }
+    }
+
+  rax = (u1 * radix) + (u0_msw - q1 * d);
+  q0 = rax / d1;
+  r0 = rax - q0 * d1;
+
+  while (q0 >= radix || (q0 * d0 > radix * r0 + u0_lsw))
+    {
+      q0 -= 1;
+      r0 += d1;
+
+      if (r0 >= radix)
+        {
+          break;
+        }
+    }
+
+  if (r != NULL)
+    {
+      *r = (rax * radix + u0_lsw - q0 * d) >> s;
+    }
+
+  quotient = q1 * radix + q0;
+
+  return quotient;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_mpi_init
+ *
+ * Description:
+ *   Initialize an MPI context
+ *
+ * Input Parameters:
+ *   X      - The MPI context to initialize
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_mpi_init(struct esp32c3_mpi_s *X)
+{
+  DEBUGASSERT(X != NULL);
+
+  X->s = 1;
+  X->n = 0;
+  X->p = NULL;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_free
+ *
+ * Description:
+ *   Frees the components of an MPI context
+ *
+ * Input Parameters:
+ *   X      - The MPI context to be cleared
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_mpi_free(struct esp32c3_mpi_s *X)
+{
+  if (X == NULL)
+    {
+      return ;
+    }
+
+  if (X->p != NULL)
+    {
+      esp32c3_mpi_zeroize(X->p, X->n);
+      free(X->p);
+    }
+
+  X->s = 1;
+  X->n = 0;
+  X->p = NULL;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_grow
+ *
+ * Description:
+ *   Enlarge an MPI to the specified number of limbs
+ *
+ * Input Parameters:
+ *   X       - The MPI context to grow
+ *   nblimbs - The target number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_grow(struct esp32c3_mpi_s *X, size_t nblimbs)
+{
+  uint32_t *p;
+  DEBUGASSERT(X != NULL);
+
+  if (nblimbs > ESP32C3_MPI_MAX_LIMBS)
+    {
+      return ESP32C3_ERR_MPI_ALLOC_FAILED;
+    }
+
+  if (X->n < nblimbs)
+    {
+      if ((p = (uint32_t *)calloc(nblimbs, CIL)) == NULL)
+        {
+          return ESP32C3_ERR_MPI_ALLOC_FAILED;
+        }
+
+      if (X->p != NULL)
+        {
+          memcpy(p, X->p, X->n * CIL);
+          esp32c3_mpi_zeroize(X->p, X->n);
+          free(X->p);
+        }
+
+      X->n = nblimbs;
+      X->p = p;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_shrink
+ *
+ * Description:
+ *   Resizes an MPI downwards, keeping at least the specified number of limbs
+ *
+ * Input Parameters:
+ *   X       - The MPI context to shrink
+ *   nblimbs - The minimum number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_shrink(struct esp32c3_mpi_s *X, size_t nblimbs)
+{
+  uint32_t *p;
+  size_t i;
+  DEBUGASSERT(X != NULL);
+
+  if (nblimbs > ESP32C3_MPI_MAX_LIMBS)
+    {
+      return ESP32C3_ERR_MPI_ALLOC_FAILED;
+    }
+
+  /* Actually resize up if there are currently fewer than nblimbs limbs. */
+
+  if (X->n <= nblimbs)
+    {
+      return (esp32c3_mpi_grow(X, nblimbs));
+    }
+
+  /* After this point, then X->n > nblimbs and in particular X->n > 0. */
+
+  for (i = X->n - 1; i > 0; i--)
+    {
+      if (X->p[i] != 0)
+        {
+          break;
+        }
+    }
+
+  i++;
+
+  if (i < nblimbs)
+    {
+      i = nblimbs;
+    }
+
+  if ((p = (uint32_t *)calloc(i, CIL)) == NULL)
+    {
+      return ESP32C3_ERR_MPI_ALLOC_FAILED;
+    }
+
+  if (X->p != NULL)
+    {
+      memcpy(p, X->p, i * CIL);
+      esp32c3_mpi_zeroize(X->p, X->n);
+      free(X->p);
+    }
+
+  X->n = i;
+  X->p = p;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_copy
+ *
+ * Description:
+ *   Copy the contents of Y into X
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   Y       - The source MPI
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_copy(struct esp32c3_mpi_s *X,
+                     const struct esp32c3_mpi_s *Y)
+{
+  int ret = 0;
+  size_t i;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+
+  if (X == Y)
+    {
+      return OK;
+    }
+
+  if (Y->n == 0)
+    {
+      esp32c3_mpi_free(X);
+      return OK;
+    }
+
+  for (i = Y->n - 1; i > 0; i--)
+    {
+      if (Y->p[i] != 0)
+        {
+          break;
+        }
+    }
+
+  i ++;
+
+  X->s = Y->s;
+
+  if (X->n < i)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, i), cleanup);
+    }
+  else
+    {
+      memset(X->p + i, 0, (X->n - i) * CIL);
+    }
+
+  memcpy(X->p, Y->p, i * CIL);
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_swap
+ *
+ * Description:
+ *   Swap the contents of X and Y
+ *
+ * Input Parameters:
+ *   X       - The first MPI
+ *   nblimbs - The second MPI
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_mpi_swap(struct esp32c3_mpi_s *X, struct esp32c3_mpi_s *Y)
+{
+  struct esp32c3_mpi_s T;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+
+  memcpy(&T,  X, sizeof(struct esp32c3_mpi_s));
+  memcpy(X,  Y, sizeof(struct esp32c3_mpi_s));
+  memcpy(Y, &T, sizeof(struct esp32c3_mpi_s));
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_safe_cond_assign
+ *
+ * Description:
+ *   Perform a safe conditional copy of MPI which doesn't
+ *   reveal whether the condition was true or not.
+ *
+ * Input Parameters:
+ *   X       - The MPI to conditionally assign to
+ *   Y       - The MPI to be assigned from
+ *   assign  - The condition deciding whether perform the assignment or not
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_safe_cond_assign(struct esp32c3_mpi_s *X,
+                                 const struct esp32c3_mpi_s *Y,
+                                 unsigned char assign)
+{
+  int ret = 0;
+  size_t i;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+
+  /* make sure assign is 0 or 1 in a time-constant manner */
+
+  assign = (assign | (unsigned char)-assign) >> 7;
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, Y->n), cleanup);
+
+  X->s = X->s * (1 - assign) + Y->s * assign;
+
+  mpi_safe_cond_assign(Y->n, X->p, Y->p, assign);
+
+  for (i = Y->n; i < X->n; i++)
+    {
+      X->p[i] *= (1 - assign);
+    }
+
+cleanup:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_safe_cond_swap
+ *
+ * Description:
+ *   Perform a safe conditional swap which doesn't
+ *   reveal whether the condition was true or not.
+ *
+ * Input Parameters:
+ *   X       - The first MPI
+ *   Y       - The second MPI
+ *   swap    - The condition deciding whether to perform the swap or not
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_safe_cond_swap(struct esp32c3_mpi_s *X,
+                               struct esp32c3_mpi_s *Y,
+                               unsigned char swap)
+{
+  int ret;
+  int s;
+  size_t i;
+  uint32_t tmp;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+
+  if (X == Y)
+    {
+      return OK;
+    }
+
+  /* make sure swap is 0 or 1 in a time-constant manner */
+
+  swap = (swap | (unsigned char)-swap) >> 7;
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, Y->n), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(Y, X->n), cleanup);
+
+  s = X->s;
+  X->s = X->s * (1 - swap) + Y->s * swap;
+  Y->s = Y->s * (1 - swap) +  s * swap;
+
+  for (i = 0; i < X->n; i++)
+    {
+      tmp = X->p[i];
+      X->p[i] = X->p[i] * (1 - swap) + Y->p[i] * swap;
+      Y->p[i] = Y->p[i] * (1 - swap) +   tmp * swap;
+    }
+
+cleanup:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_lset
+ *
+ * Description:
+ *   Set value from integer
+ *
+ * Input Parameters:
+ *   X       - The MPI to set
+ *   z       - The value to use
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_lset(struct esp32c3_mpi_s *X, int32_t z)
+{
+  int ret;
+  DEBUGASSERT(X != NULL);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, 1), cleanup);
+  memset(X->p, 0, X->n * CIL);
+
+  X->p[0] = (z < 0) ? -z : z;
+  X->s  = (z < 0) ? -1 : 1;
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_get_bit
+ *
+ * Description:
+ *   Get a specific bit from an MPI
+ *
+ * Input Parameters:
+ *   X       - The MPI context to query
+ *   pos     - Zero-based index of the bit to query
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_get_bit(const struct esp32c3_mpi_s *X, size_t pos)
+{
+  DEBUGASSERT(X != NULL);
+
+  if (X->n * BIL <= pos)
+    {
+      return OK;
+    }
+
+  return ((X->p[pos / BIL] >> (pos % BIL)) & 0x01);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_set_bit
+ *
+ * Description:
+ *   Modify a specific bit in an MPI
+ *
+ * Input Parameters:
+ *   X       - The MPI context to modify
+ *   pos     - Zero-based index of the bit to modify
+ *   val     - The desired value of bit
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_set_bit(struct esp32c3_mpi_s *X,
+                        size_t pos, unsigned char val)
+{
+  int ret = 0;
+  size_t off = pos / BIL;
+  size_t idx = pos % BIL;
+  DEBUGASSERT(X != NULL);
+
+  if (val != 0 && val != 1)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  if (X->n * BIL <= pos)
+    {
+      if (val == 0)
+        {
+          return OK;
+        }
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, off + 1), cleanup);
+    }
+
+  X->p[off] &= ~((uint32_t) 0x01 << idx);
+  X->p[off] |= (uint32_t) val << idx;
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_lsb
+ *
+ * Description:
+ *   Return the number of bits of value
+ *
+ * Input Parameters:
+ *   X       - The MPI context to query
+ *
+ * Returned Value:
+ *   The number of bits of value.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_mpi_lsb(const struct esp32c3_mpi_s *X)
+{
+  size_t i;
+  size_t j;
+  size_t count = 0;
+  DEBUGASSERT(X != NULL);
+
+  for (i = 0; i < X->n; i++)
+    {
+      for (j = 0; j < BIL; j++, count++)
+        {
+          if (((X->p[i] >> j) & 1) != 0)
+            {
+              return (count);
+            }
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_bitlen
+ *
+ * Description:
+ *   Return the number of bits up to and including the most
+ *   significant bit of value
+ *
+ * Input Parameters:
+ *   X       - The MPI context to query
+ *
+ * Returned Value:
+ *   The number of bits up and including the most significant bit of value.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_mpi_bitlen(const struct esp32c3_mpi_s *X)
+{
+  size_t i;
+  size_t j;
+
+  if (X->n == 0)
+    {
+      return OK;
+    }
+
+  for (i = X->n - 1; i > 0; i--)
+    {
+      if (X->p[i] != 0)
+        {
+          break;
+        }
+    }
+
+  j = BIL - esp32c3_bignum_clz(X->p[i]);
+
+  return ((i * BIL) + j);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_size
+ *
+ * Description:
+ *   Return the total size of an MPI value in bytes
+ *
+ * Input Parameters:
+ *   X       - The MPI context to query
+ *
+ * Returned Value:
+ *   The least number of bytes capable of storing the absolute value.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_mpi_size(const struct esp32c3_mpi_s *X)
+{
+  return ((esp32c3_mpi_bitlen(X) + 7) >> 3);
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_read_string
+ *
+ * Description:
+ *   Import from an ASCII string
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   radix   - The numeric base of the input string
+ *   s       - Null-terminated string buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_read_string(struct esp32c3_mpi_s *X,
+                            int radix, const char *s)
+{
+  int ret;
+  size_t i;
+  size_t j;
+  size_t slen;
+  size_t n;
+  uint32_t d;
+  struct esp32c3_mpi_s T;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(s != NULL);
+
+  if (radix < 2 || radix > 16)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  esp32c3_mpi_init(&T);
+
+  slen = strlen(s);
+
+  if (radix == 16)
+    {
+      if (slen > MPI_SIZE_T_MAX >> 2)
+        {
+          return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+        }
+
+      n = BITS_TO_LIMBS(slen << 2);
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, n), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_lset(X, 0), cleanup);
+
+      for (i = slen, j = 0; i > 0; i--, j++)
+        {
+          if (i == 1 && s[i - 1] == '-')
+            {
+              X->s = -1;
+              break;
+            }
+
+          ESP32C3_MPI_CHK(mpi_get_digit(&d, radix, s[i - 1]), cleanup);
+          X->p[j / (2 * CIL)] |= d << ((j % (2 * CIL)) << 2);
+        }
+    }
+  else
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_lset(X, 0), cleanup);
+
+      for (i = 0; i < slen; i++)
+        {
+          if (i == 0 && s[i] == '-')
+            {
+              X->s = -1;
+              continue;
+            }
+
+          ESP32C3_MPI_CHK(mpi_get_digit(&d, radix, s[i]), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_mul_int(&T, X, radix), cleanup);
+
+          if (X->s == 1)
+            {
+              ESP32C3_MPI_CHK(esp32c3_mpi_add_int(X, &T, d), cleanup);
+            }
+          else
+            {
+              ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(X, &T, d), cleanup);
+            }
+        }
+    }
+
+cleanup:
+
+  esp32c3_mpi_free(&T);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_write_string
+ *
+ * Description:
+ *   Export an MPI to an ASCII string
+ *
+ * Input Parameters:
+ *   X       - The source MPI
+ *   radix   - The numeric base of the output string
+ *   buf     - The buffer to write the string to
+ *   buflen  - The available size in Bytes of buf
+ *   olen    - The address at which to store the length of the string written
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_write_string(const struct esp32c3_mpi_s *X, int radix,
+                             char *buf, size_t buflen, size_t *olen)
+{
+  int ret = 0;
+  size_t n;
+  char *p;
+  struct esp32c3_mpi_s T;
+  DEBUGASSERT(X  != NULL);
+  DEBUGASSERT(olen != NULL);
+  DEBUGASSERT(buflen == 0 || buf != NULL);
+
+  if (radix < 2 || radix > 16)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  /* Number of bits necessary to present `n`. */
+
+  n = esp32c3_mpi_bitlen(X);
+  if (radix >=  4)
+    {
+      /* Number of 4-adic digits necessary to present
+       * `n`. If radix > 4, this might be a strict
+       * overapproximation of the number of
+       * radix-adic digits needed to present `n`.
+       */
+
+      n >>= 1;
+    }
+
+  if (radix >= 16)
+    {
+      /* Number of hexadecimal digits necessary to
+       * present `n`.
+       */
+
+      n >>= 1;
+    }
+
+  /* Terminating null byte */
+
+  n += 1;
+
+  /* Compensate for the divisions above, which round down `n`
+   * in case it's not even.
+   */
+
+  n += 1;
+
+  /* Potential '-'-sign. */
+
+  n += 1;
+
+  /* Make n even to have enough space for hexadecimal writing,
+   * which always uses an even number of hex-digits.
+   */
+
+  n += (n & 1);
+
+  if (buflen < n)
+    {
+      *olen = n;
+      return ESP32C3_ERR_MPI_BUFFER_TOO_SMALL;
+    }
+
+  p = buf;
+  esp32c3_mpi_init(&T);
+
+  if (X->s == -1)
+    {
+      *p++ = '-';
+      buflen--;
+    }
+
+  if (radix == 16)
+    {
+      int c;
+      size_t i, j, k;
+
+      for (i = X->n, k = 0; i > 0; i--)
+        {
+          for (j = CIL; j > 0; j--)
+            {
+              c = (X->p[i - 1] >> ((j - 1) << 3)) & 0xff;
+
+              if (c == 0 && k == 0 && (i + j) != 2)
+                {
+                  continue;
+                }
+
+              *(p++) = "0123456789ABCDEF" [c / 16];
+              *(p++) = "0123456789ABCDEF" [c % 16];
+              k = 1;
+            }
+        }
+    }
+  else
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(&T, X), cleanup);
+
+      if (T.s == -1)
+        {
+          T.s = 1;
+        }
+
+      ESP32C3_MPI_CHK(mpi_write_hlp(&T, radix, &p, buflen), cleanup);
+    }
+
+  *p++ = '\0';
+  *olen = p - buf;
+
+cleanup:
+
+  esp32c3_mpi_free(&T);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_read_binary
+ *
+ * Description:
+ *   Import an MPI from unsigned big endian binary data
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   buf     - The input buffer
+ *   buflen  - The length of the input buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_read_binary(struct esp32c3_mpi_s *X,
+                            const unsigned char *buf,
+                            size_t buflen)
+{
+  int ret;
+  size_t const limbs  = CHARS_TO_LIMBS(buflen);
+  size_t const overhead = (limbs * CIL) - buflen;
+  unsigned char *XP;
+
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(buflen == 0 || buf != NULL);
+
+  /* Ensure that target MPI has exactly the necessary number of limbs */
+
+  if (X->n != limbs)
+    {
+      esp32c3_mpi_free(X);
+      esp32c3_mpi_init(X);
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, limbs), cleanup);
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(X, 0), cleanup);
+
+  /* Avoid calling `memcpy` with NULL source argument,
+   * even if buflen is 0.
+   */
+
+  if (buf != NULL)
+    {
+      XP = (unsigned char *) X->p;
+      memcpy(XP + overhead, buf, buflen);
+
+      mpi_bigendian_to_host(X->p, limbs);
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_write_binary
+ *
+ * Description:
+ *   Export X into unsigned binary data, big endian
+ *
+ * Input Parameters:
+ *   X       - The source MPI
+ *   buf     - The output buffer
+ *   buflen  - The length of the output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_write_binary(const struct esp32c3_mpi_s *X,
+                unsigned char *buf, size_t buflen)
+{
+  size_t stored_bytes;
+  size_t bytes_to_copy;
+  unsigned char *p;
+  size_t i;
+
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(buflen == 0 || buf != NULL);
+
+  stored_bytes = X->n * CIL;
+
+  if (stored_bytes < buflen)
+    {
+      /* There is enough space in the output buffer. Write initial
+       * null bytes and record the position at which to start
+       * writing the significant bytes. In this case, the execution
+       * trace of this function does not depend on the value of the
+       * number.
+       */
+
+      bytes_to_copy = stored_bytes;
+      p = buf + buflen - stored_bytes;
+      memset(buf, 0, buflen - stored_bytes);
+    }
+  else
+    {
+      /* The output buffer is smaller than the allocated size of X.
+       * However X may fit if its leading bytes are zero.
+       */
+
+      bytes_to_copy = buflen;
+      p = buf;
+      for (i = bytes_to_copy; i < stored_bytes; i++)
+        {
+          if (GET_BYTE(X, i) != 0)
+            {
+              return ESP32C3_ERR_MPI_BUFFER_TOO_SMALL;
+            }
+        }
+    }
+
+  for (i = 0; i < bytes_to_copy; i++)
+    {
+      p[bytes_to_copy - i - 1] = GET_BYTE(X, i);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_shift_l
+ *
+ * Description:
+ *   Perform a left-shift on an MPI: X <<= count
+ *
+ * Input Parameters:
+ *   X       - The MPI to shift
+ *   count   - The number of bits to shift by
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_shift_l(struct esp32c3_mpi_s *X, size_t count)
+{
+  int ret;
+  size_t i, v0, t1;
+  uint32_t r0 = 0, r1;
+  DEBUGASSERT(X != NULL);
+
+  v0 = count / (BIL);
+  t1 = count & (BIL - 1);
+
+  i = esp32c3_mpi_bitlen(X) + count;
+
+  if (X->n * BIL < i)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, BITS_TO_LIMBS(i)), cleanup);
+    }
+
+  ret = 0;
+
+  /* shift by count / limb_size */
+
+  if (v0 > 0)
+    {
+      for (i = X->n; i > v0; i--)
+        X->p[i - 1] = X->p[i - v0 - 1];
+
+      for (; i > 0; i--)
+        X->p[i - 1] = 0;
+    }
+
+  /* shift by count % limb_size
+   */
+
+  if (t1 > 0)
+    {
+      for (i = v0; i < X->n; i++)
+        {
+          r1 = X->p[i] >> (BIL - t1);
+          X->p[i] <<= t1;
+          X->p[i] |= r0;
+          r0 = r1;
+        }
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_shift_r
+ *
+ * Description:
+ *   Perform a right-shift on an MPI: X >>= count
+ *
+ * Input Parameters:
+ *   X       - The MPI to shift
+ *   count   - The number of bits to shift by
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_shift_r(struct esp32c3_mpi_s *X, size_t count)
+{
+  size_t i, v0, v1;
+  uint32_t r0 = 0, r1;
+  DEBUGASSERT(X != NULL);
+
+  v0 = count /  BIL;
+  v1 = count & (BIL - 1);
+
+  if (v0 > X->n || (v0 == X->n && v1 > 0))
+    {
+      return esp32c3_mpi_lset(X, 0);
+    }
+
+  /* shift by count / limb_size
+   */
+
+  if (v0 > 0)
+    {
+      for (i = 0; i < X->n - v0; i++)
+        {
+          X->p[i] = X->p[i + v0];
+        }
+
+      for (; i < X->n; i++)
+        {
+          X->p[i] = 0;
+        }
+    }
+
+  /* shift by count % limb_size
+   */
+
+  if (v1 > 0)
+    {
+      for (i = X->n; i > 0; i--)
+        {
+          r1 = X->p[i - 1] << (BIL - v1);
+          X->p[i - 1] >>= v1;
+          X->p[i - 1] |= r0;
+          r0 = r1;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_cmp_abs
+ *
+ * Description:
+ *   Compare the absolute values of two MPIs
+ *
+ * Input Parameters:
+ *   X       - The left-hand MPI
+ *   Y       - The right-hand MPI
+ *
+ * Returned Value:
+ *   1 if \p `|X|` is greater than \p `|Y|`.
+ *   -1 if \p `|X|` is lesser than \p `|Y|`.
+ *   0 if \p `|X|` is equal to \p `|Y|`.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_cmp_abs(const struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *Y)
+{
+  size_t i, j;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+
+  for (i = X->n; i > 0; i--)
+    {
+      if (X->p[i - 1] != 0)
+        {
+          break;
+        }
+    }
+
+  for (j = Y->n; j > 0; j--)
+    {
+      if (Y->p[j - 1] != 0)
+        {
+          break;
+        }
+    }
+
+  if (i == 0 && j == 0)
+    {
+      return OK;
+    }
+
+  if (i > j)
+    {
+      return (1);
+    }
+
+  if (j > i)
+    {
+      return (-1);
+    }
+
+  for (; i > 0; i--)
+    {
+      if (X->p[i - 1] > Y->p[i - 1])
+        {
+          return (1);
+        }
+
+      if (X->p[i - 1] < Y->p[i - 1])
+        {
+          return (-1);
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_cmp_mpi
+ *
+ * Description:
+ *   Compare two MPIs.
+ *
+ * Input Parameters:
+ *   X       - The left-hand MPI
+ *   Y       - The right-hand MPI
+ *
+ * Returned Value:
+ *   1 if \p `X` is greater than \p `Y`.
+ *   -1 if \p `X` is lesser than \p `Y`.
+ *   0 if \p `X` is equal to \p `Y`.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_cmp_mpi(const struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *Y)
+{
+  size_t i, j;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+
+  for (i = X->n; i > 0; i--)
+    {
+      if (X->p[i - 1] != 0)
+        {
+          break;
+        }
+    }
+
+  for (j = Y->n; j > 0; j--)
+    {
+      if (Y->p[j - 1] != 0)
+        {
+          break;
+        }
+    }
+
+  if (i == 0 && j == 0)
+    {
+      return OK;
+    }
+
+  if (i > j)
+    {
+      return (X->s);
+    }
+
+  if (j > i)
+    {
+      return (-Y->s);
+    }
+
+  if (X->s > 0 && Y->s < 0)
+    {
+      return (1);
+    }
+
+  if (Y->s > 0 && X->s < 0)
+    {
+      return (-1);
+    }
+
+  for (; i > 0; i--)
+    {
+      if (X->p[i - 1] > Y->p[i - 1])
+        {
+          return (X->s);
+        }
+
+      if (X->p[i - 1] < Y->p[i - 1])
+        {
+          return (-X->s);
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_lt_mpi_ct
+ *
+ * Description:
+ *   Check if an MPI is less than the other in constant time
+ *
+ * Input Parameters:
+ *   X       - The left-hand MPI
+ *   Y       - The right-hand MPI
+ *   ret     - The result of the comparison:
+ *             1 if \p X is less than \p Y.
+ *             0 if \p X is greater than or equal to \p Y.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_lt_mpi_ct(const struct esp32c3_mpi_s *X,
+                          const struct esp32c3_mpi_s *Y,
+                          unsigned *ret)
+{
+  size_t i;
+
+  /* The value of any of these variables is either 0 or 1 at all times. */
+
+  unsigned cond, done, x_is_negative, y_is_negative;
+
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(Y != NULL);
+  DEBUGASSERT(ret != NULL);
+
+  if (X->n != Y->n)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  /* Set sign_N to 1 if N >= 0, 0 if N < 0.
+   * We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
+   */
+
+  x_is_negative = (X->s & 2) >> 1;
+  y_is_negative = (Y->s & 2) >> 1;
+
+  /* If the signs are different, then the positive operand is the bigger.
+   * That is if X is negative (x_is_negative == 1), then X < Y is true and it
+   * is false if X is positive (x_is_negative == 0).
+   */
+
+  cond = (x_is_negative ^ y_is_negative);
+  *ret = cond & x_is_negative;
+
+  /* This is a constant-time function. We might have the result, but we still
+   * need to go through the loop. Record if we have the result already.
+   */
+
+  done = cond;
+
+  for (i = X->n; i > 0; i--)
+    {
+      /* If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
+       * X and Y are negative.
+       *
+       * Again even if we can make a decision, we just mark the result and
+       * the fact that we are done and continue looping.
+       */
+
+      cond = ct_lt_mpi_uint(Y->p[i - 1], X->p[i - 1]);
+      *ret |= cond & (1 - done) & x_is_negative;
+      done |= cond;
+
+      /* If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
+       * X and Y are positive.
+       *
+       * Again even if we can make a decision, we just mark the result and
+       * the fact that we are done and continue looping.
+       */
+
+      cond = ct_lt_mpi_uint(X->p[i - 1], Y->p[i - 1]);
+      *ret |= cond & (1 - done) & (1 - x_is_negative);
+      done |= cond;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_cmp_int
+ *
+ * Description:
+ *   Compare an MPI with an integer
+ *
+ * Input Parameters:
+ *   X       - The left-hand MPI
+ *   z       - The integer value to compare \p X to
+ *
+ * Returned Value:
+ *   \c 1 if \p X is greater than \p z.
+ *   \c -1 if \p X is lesser than \p z.
+ *   \c 0 if \p X is equal to \p z.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_cmp_int(const struct esp32c3_mpi_s *X, int32_t z)
+{
+  struct esp32c3_mpi_s Y;
+  uint32_t p[1];
+  DEBUGASSERT(X != NULL);
+
+  *p  = (z < 0) ? -z : z;
+  Y.s = (z < 0) ? -1 : 1;
+  Y.n = 1;
+  Y.p = p;
+
+  return (esp32c3_mpi_cmp_mpi(X, &Y));
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_add_abs
+ *
+ * Description:
+ *   Perform an unsigned addition of MPIs: X = |A| + |B|
+ *
+ * Input Parameters:
+ *   X       - The left-hand MPI
+ *   z       - The integer value to compare \p X to.
+ *
+ * Returned Value:
+ *   \c 1 if \p X is greater than \p z.
+ *   \c -1 if \p X is lesser than \p z.
+ *   \c 0 if \p X is equal to \p z.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_add_abs(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B)
+{
+  int ret;
+  size_t i, j;
+  uint32_t *o, *p, c, tmp;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  if (X == B)
+    {
+      const struct esp32c3_mpi_s *T = A; A = X; B = T;
+    }
+
+  if (X != A)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(X, A), cleanup);
+    }
+
+  /* X should always be positive as a result of unsigned additions.
+   */
+
+  X->s = 1;
+
+  for (j = B->n; j > 0; j--)
+    {
+      if (B->p[j - 1] != 0)
+        {
+          break;
+        }
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, j), cleanup);
+
+  o = B->p; p = X->p; c = 0;
+
+  /* tmp is used because it might happen that p == o
+   */
+
+  for (i = 0; i < j; i++, o++, p++)
+    {
+      tmp = *o;
+      *p += c; c = (*p < c);
+      *p += tmp; c += (*p < tmp);
+    }
+
+  while (c != 0)
+    {
+      if (i >= X->n)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, i + 1), cleanup);
+          p = X->p + i;
+        }
+
+      *p += c; c = (*p < c); i++; p++;
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_sub_abs
+ *
+ * Description:
+ *   Perform an unsigned subtraction of MPIs: X = |A| - |B|
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The minuend
+ *   B       - The subtrahend
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_sub_abs(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B)
+{
+  struct esp32c3_mpi_s TB;
+  int ret;
+  size_t n;
+  uint32_t carry;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  esp32c3_mpi_init(&TB);
+
+  if (X == B)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(&TB, B), cleanup);
+      B = &TB;
+    }
+
+  if (X != A)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(X, A), cleanup);
+    }
+
+  /* X should always be positive as a result of unsigned subtractions. */
+
+  X->s = 1;
+
+  ret = 0;
+
+  for (n = B->n; n > 0; n--)
+    {
+      if (B->p[n - 1] != 0)
+        {
+          break;
+        }
+    }
+
+  carry = mpi_sub_hlp(n, X->p, B->p);
+  if (carry != 0)
+    {
+      /* Propagate the carry to the first nonzero limb of X. */
+
+      for (; n < X->n && X->p[n] == 0; n++)
+        {
+          --X->p[n];
+        }
+
+      /* If we ran out of space for the carry, it means that the result
+       * is negative.
+       */
+
+      if (n == X->n)
+        {
+          ret = ESP32C3_ERR_MPI_NEGATIVE_VALUE;
+          goto cleanup;
+        }
+
+      --X->p[n];
+    }
+
+cleanup:
+
+  esp32c3_mpi_free(&TB);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_add_mpi
+ *
+ * Description:
+ *   Perform a signed addition of MPIs: X = A + B
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The first summand
+ *   B       - The second summand
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_add_mpi(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B)
+{
+  int ret, s;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  s = A->s;
+  if (A->s * B->s < 0)
+    {
+      if (esp32c3_mpi_cmp_abs(A, B) >= 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_abs(X, A, B), cleanup);
+          X->s =  s;
+        }
+      else
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_abs(X, B, A), cleanup);
+          X->s = -s;
+        }
+    }
+  else
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_abs(X, A, B), cleanup);
+      X->s = s;
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_sub_mpi
+ *
+ * Description:
+ *   Perform a signed subtraction of MPIs: X = A - B
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The minuend
+ *   B       - The subtrahend
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_sub_mpi(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B)
+{
+  int ret, s;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  s = A->s;
+  if (A->s * B->s > 0)
+    {
+      if (esp32c3_mpi_cmp_abs(A, B) >= 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_abs(X, A, B), cleanup);
+          X->s =  s;
+        }
+      else
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_abs(X, B, A), cleanup);
+          X->s = -s;
+        }
+    }
+  else
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_abs(X, A, B), cleanup);
+      X->s = s;
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_add_int
+ *
+ * Description:
+ *   Perform a signed addition of an MPI and an integer: X = A + b
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The first summand
+ *   b       - The second summand
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_add_int(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        int32_t b)
+{
+  struct esp32c3_mpi_s _B;
+  uint32_t p[1];
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+
+  p[0] = (b < 0) ? -b : b;
+  _B.s = (b < 0) ? -1 : 1;
+  _B.n = 1;
+  _B.p = p;
+
+  return (esp32c3_mpi_add_mpi(X, A, &_B));
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_sub_int
+ *
+ * Description:
+ *   Perform a signed subtraction of an MPI and an integer: X = A - b
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The minuend
+ *   b       - The subtrahend
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_sub_int(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        int32_t b)
+{
+  struct esp32c3_mpi_s _B;
+  uint32_t p[1];
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+
+  p[0] = (b < 0) ? -b : b;
+  _B.s = (b < 0) ? -1 : 1;
+  _B.n = 1;
+  _B.p = p;
+
+  return (esp32c3_mpi_sub_mpi(X, A, &_B));
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mul_mpi
+ *
+ * Description:
+ *   Perform a multiplication of two MPIs: Z = X * Y
+ *
+ * Input Parameters:
+ *   Z      - The destination MPI
+ *   X      - The first factor
+ *   Y      - The second factor
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mul_mpi(struct esp32c3_mpi_s *Z,
+                        const struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *Y)
+{
+  int ret = 0;
+  size_t x_bits = esp32c3_mpi_bitlen(X);
+  size_t y_bits = esp32c3_mpi_bitlen(Y);
+  size_t x_words = bits_to_words(x_bits);
+  size_t y_words = bits_to_words(y_bits);
+  size_t z_words = bits_to_words(x_bits + y_bits);
+  size_t hw_words = MAX(x_words, y_words);
+
+  /* Short-circuit eval if either argument is 0 or 1.
+
+   * This is needed as the mpi modular division
+   * argument will sometimes call in here when one
+   * argument is too large for the hardware unit, but the other
+   * argument is zero or one.
+   */
+
+  if (x_bits == 0 || y_bits == 0)
+    {
+      esp32c3_mpi_lset(Z, 0);
+      return 0;
+    }
+
+  if (x_bits == 1)
+    {
+      ret = esp32c3_mpi_copy(Z, Y);
+      Z->s *= X->s;
+      return ret;
+    }
+
+  if (y_bits == 1)
+    {
+      ret = esp32c3_mpi_copy(Z, X);
+      Z->s *= Y->s;
+      return ret;
+    }
+
+  /* Grow Z to result size early, avoid interim allocations */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(Z, z_words), cleanup);
+
+  /* If factor is over 2048 bits, we can't use the standard
+   * hardware multiplier
+   */
+
+  if (hw_words * 32 > SOC_RSA_MAX_BIT_LEN / 2)
+    {
+      if (z_words * 32 <= SOC_RSA_MAX_BIT_LEN)
+        {
+          return mpi_mult_mpi_failover_mod_mult(Z, X, Y, z_words);
+        }
+      else
+        {
+          /* Still too long for the hardware unit... */
+
+          if (y_words > x_words)
+            {
+              return mpi_mult_mpi_overlong(Z, X, Y, y_words, z_words);
+            }
+          else
+            {
+              return mpi_mult_mpi_overlong(Z, Y, X, x_words, z_words);
+            }
+        }
+    }
+
+  /* Otherwise, we can use the (faster) multiply hardware unit */
+
+  esp32c3_mpi_enable_hardware_hw_op();
+
+  esp32c3_mpi_mul_mpi_hw_op(X, Y, hw_words);
+  esp32c3_mpi_read_result_hw_op(Z, z_words);
+
+  esp32c3_mpi_disable_hardware_hw_op();
+
+  Z->s = X->s * Y->s;
+
+cleanup:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mul_int
+ *
+ * Description:
+ *   Perform a multiplication of an MPI with an unsigned integer: X = A * b
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The first factor
+ *   b       - The second factor.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mul_int(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        uint32_t b)
+{
+  struct esp32c3_mpi_s _B;
+  uint32_t p[1];
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+
+  _B.s = 1;
+  _B.n = 1;
+  _B.p = p;
+  p[0] = b;
+
+  return (esp32c3_mpi_mul_mpi(X, A, &_B));
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_div_mpi
+ *
+ * Description:
+ *   Perform a division with remainder of two MPIs: A = Q * B + R
+ *
+ * Input Parameters:
+ *   Q        - The destination MPI for the quotient
+ *   R        - The destination MPI for the remainder value
+ *   A        - The dividend
+ *   B        - The divisor
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_div_mpi(struct esp32c3_mpi_s *Q,
+                        struct esp32c3_mpi_s *R,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B)
+{
+  int ret;
+  size_t i, n, t, k;
+  struct esp32c3_mpi_s X, Y, Z, T1, T2;
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  if (esp32c3_mpi_cmp_int(B, 0) == 0)
+    {
+      return ESP32C3_ERR_MPI_DIVISION_BY_ZERO;
+    }
+
+  esp32c3_mpi_init(&X);
+  esp32c3_mpi_init(&Y);
+  esp32c3_mpi_init(&Z);
+  esp32c3_mpi_init(&T1);
+  esp32c3_mpi_init(&T2);
+
+  if (esp32c3_mpi_cmp_abs(A, B) < 0)
+    {
+      if (Q != NULL)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_lset(Q, 0), cleanup);
+        }
+
+      if (R != NULL)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_copy(R, A), cleanup);
+        }
+
+      return OK;
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&X, A), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&Y, B), cleanup);
+  X.s = Y.s = 1;
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(&Z, A->n + 2), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(&Z,  0), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(&T1, 2), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(&T2, 3), cleanup);
+
+  k = esp32c3_mpi_bitlen(&Y) % BIL;
+  if (k < BIL - 1)
+    {
+      k = BIL - 1 - k;
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&X, k), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&Y, k), cleanup);
+    }
+  else
+    {
+      k = 0;
+    }
+
+  n = X.n - 1;
+  t = Y.n - 1;
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&Y, BIL * (n - t)), cleanup);
+
+  while (esp32c3_mpi_cmp_mpi(&X, &Y) >= 0)
+    {
+      Z.p[n - t]++;
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&X, &X, &Y), cleanup);
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&Y, BIL * (n - t)), cleanup);
+
+  for (i = n; i > t ; i--)
+    {
+      if (X.p[i] >= Y.p[t])
+        {
+          Z.p[i - t - 1] = ~0;
+        }
+      else
+        {
+          Z.p[i - t - 1] = esp32c3_bignum_int_div_int(X.p[i], X.p[i - 1],
+                                                      Y.p[t], NULL);
+        }
+
+      Z.p[i - t - 1]++;
+      do
+        {
+          Z.p[i - t - 1]--;
+
+          ESP32C3_MPI_CHK(esp32c3_mpi_lset(&T1, 0), cleanup);
+          T1.p[0] = (t < 1) ? 0 : Y.p[t - 1];
+          T1.p[1] = Y.p[t];
+          ESP32C3_MPI_CHK(esp32c3_mpi_mul_int(&T1, &T1, Z.p[i - t - 1]),
+                          cleanup);
+
+          ESP32C3_MPI_CHK(esp32c3_mpi_lset(&T2, 0), cleanup);
+          T2.p[0] = (i < 2) ? 0 : X.p[i - 2];
+          T2.p[1] = (i < 1) ? 0 : X.p[i - 1];
+          T2.p[2] = X.p[i];
+        }
+      while (esp32c3_mpi_cmp_mpi(&T1, &T2) > 0);
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_int(&T1, &Y, Z.p[i - t - 1]), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&T1,  BIL * (i - t - 1)), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&X, &X, &T1), cleanup);
+
+      if (esp32c3_mpi_cmp_int(&X, 0) < 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_copy(&T1, &Y), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&T1, BIL * (i - t - 1)),
+                          cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&X, &X, &T1), cleanup);
+          Z.p[i - t - 1]--;
+        }
+    }
+
+  if (Q != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(Q, &Z), cleanup);
+      Q->s = A->s * B->s;
+    }
+
+  if (R != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&X, k), cleanup);
+      X.s = A->s;
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(R, &X), cleanup);
+
+      if (esp32c3_mpi_cmp_int(R, 0) == 0)
+        {
+          R->s = 1;
+        }
+    }
+
+cleanup:
+
+  esp32c3_mpi_free(&X); esp32c3_mpi_free(&Y); esp32c3_mpi_free(&Z);
+  esp32c3_mpi_free(&T1); esp32c3_mpi_free(&T2);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_div_int
+ *
+ * Description:
+ *   Perform a division with remainder of an MPI by an integer: A = Q * b + R
+ *
+ * Input Parameters:
+ *   Q        - The destination MPI for the quotient
+ *   R        - The destination MPI for the remainder value
+ *   A        - The dividend
+ *   B        - The divisor
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_div_int(struct esp32c3_mpi_s *Q,
+                        struct esp32c3_mpi_s *R,
+                        const struct esp32c3_mpi_s *A,
+                        int32_t b)
+{
+  struct esp32c3_mpi_s _B;
+  uint32_t p[1];
+  DEBUGASSERT(A != NULL);
+
+  p[0] = (b < 0) ? -b : b;
+  _B.s = (b < 0) ? -1 : 1;
+  _B.n = 1;
+  _B.p = p;
+
+  return (esp32c3_mpi_div_mpi(Q, R, A, &_B));
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mod_mpi
+ *
+ * Description:
+ *   erform a modular reduction. R = A mod B
+ *
+ * Input Parameters:
+ *   R       - The destination MPI for the residue value
+ *   A       - The MPI to compute the residue of
+ *   B       - The base of the modular reduction
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mod_mpi(struct esp32c3_mpi_s *R,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *B)
+{
+  int ret;
+  DEBUGASSERT(R != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  if (esp32c3_mpi_cmp_int(B, 0) < 0)
+    {
+      return ESP32C3_ERR_MPI_NEGATIVE_VALUE;
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_div_mpi(NULL, R, A, B), cleanup);
+
+  while (esp32c3_mpi_cmp_int(R, 0) < 0)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(R, R, B), cleanup);
+    }
+
+  while (esp32c3_mpi_cmp_mpi(R, B) >= 0)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(R, R, B), cleanup);
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mod_int
+ *
+ * Description:
+ *   Perform a modular reduction with respect to an integer: r = A mod b
+ *
+ * Input Parameters:
+ *   r       - The address at which to store the residue
+ *   A       - The MPI to compute the residue of
+ *   b       - The integer base of the modular reduction
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mod_int(uint32_t *r,
+                        const struct esp32c3_mpi_s *A, int32_t b)
+{
+  size_t i;
+  uint32_t x, y, z;
+  DEBUGASSERT(r != NULL);
+  DEBUGASSERT(A != NULL);
+
+  if (b == 0)
+    {
+      return ESP32C3_ERR_MPI_DIVISION_BY_ZERO;
+    }
+
+  if (b < 0)
+    {
+      return ESP32C3_ERR_MPI_NEGATIVE_VALUE;
+    }
+
+  /* handle trivial cases */
+
+  if (b == 1)
+    {
+      *r = 0;
+      return OK;
+    }
+
+  if (b == 2)
+    {
+      *r = A->p[0] & 1;
+      return OK;
+    }
+
+  /* general case */
+
+  for (i = A->n, y = 0; i > 0; i--)
+    {
+      x  = A->p[i - 1];
+      y  = (y << BIH) | (x >> BIH);
+      z  = y / b;
+      y -= z * b;
+
+      x <<= BIH;
+      y  = (y << BIH) | (x >> BIH);
+      z  = y / b;
+      y -= z * b;
+    }
+
+  /* If A is negative, then the current y represents a negative value.
+   * Flipping it to the positive side.
+   */
+
+  if (A->s < 0 && y != 0)
+    {
+      y = b - y;
+    }
+
+  *r = y;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_exp_mod
+ *
+ * Description:
+ *   Perform a sliding-window exponentiation: X = A^E mod N
+ *
+ * Input Parameters:
+ *   X       - The destination MPI
+ *   A       - The base of the exponentiation
+ *   E       - The exponent MPI
+ *   N       - The base for the modular reduction
+ *   _RR     - A helper MPI depending solely on \p N which can be used to
+ *             speed-up multiple modular exponentiations for the same value
+ *             of \p N.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_exp_mod(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *E,
+                        const struct esp32c3_mpi_s *N,
+                        struct esp32c3_mpi_s *_RR)
+{
+  int ret;
+  size_t wbits, wsize, one = 1;
+  size_t i, j, nblimbs;
+  size_t bufsize, nbits;
+  uint32_t ei, mm, state;
+  struct esp32c3_mpi_s RR, T, W[1 << ESP32C3_MPI_WINDOW_SIZE], apos;
+  int neg;
+
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(E != NULL);
+  DEBUGASSERT(N != NULL);
+
+  if (esp32c3_mpi_cmp_int(N, 0) <= 0 || (N->p[0] & 1) == 0)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  if (esp32c3_mpi_cmp_int(E, 0) < 0)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  if (esp32c3_mpi_bitlen(E) > ESP32C3_MPI_MAX_BITS ||
+      esp32c3_mpi_bitlen(N) > ESP32C3_MPI_MAX_BITS)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  /* Init temps and window size
+   */
+
+  mpi_montg_init(&mm, N);
+  esp32c3_mpi_init(&RR); esp32c3_mpi_init(&T);
+  esp32c3_mpi_init(&apos);
+  memset(W, 0, sizeof(W));
+
+  i = esp32c3_mpi_bitlen(E);
+
+  wsize = (i > 671) ? 6 : (i > 239) ? 5 :
+      (i >  79) ? 4 : (i >  23) ? 3 : 1;
+
+#if (ESP32C3_MPI_WINDOW_SIZE < 6)
+  if (wsize > ESP32C3_MPI_WINDOW_SIZE)
+    {
+      wsize = ESP32C3_MPI_WINDOW_SIZE;
+    }
+#endif
+
+  j = N->n + 1;
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, j), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(&W[1],  j), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_grow(&T, j * 2), cleanup);
+
+  /* Compensate for negative A (and correct at the end) */
+
+  neg = (A->s == -1);
+  if (neg)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(&apos, A), cleanup);
+      apos.s = 1;
+      A = &apos;
+    }
+
+  /* If 1st call, pre-compute R^2 mod N */
+
+  if (_RR == NULL || _RR->p == NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_lset(&RR, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&RR, N->n * 2 * BIL), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&RR, &RR, N), cleanup);
+
+      if (_RR != NULL)
+        {
+          memcpy(_RR, &RR, sizeof(struct esp32c3_mpi_s));
+        }
+    }
+  else
+    {
+      memcpy(&RR, _RR, sizeof(struct esp32c3_mpi_s));
+    }
+
+  /* W[1] = A * R^2 * R^-1 mod N = A * R mod N */
+
+  if (esp32c3_mpi_cmp_mpi(A, N) >= 0)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&W[1], A, N), cleanup);
+    }
+  else
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(&W[1], A), cleanup);
+    }
+
+  mpi_montmul(&W[1], &RR, N, mm, &T);
+
+  /* X = R^2 * R^-1 mod N = R mod N */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(X, &RR), cleanup);
+  mpi_montred(X, N, mm, &T);
+
+  if (wsize > 1)
+    {
+      /* W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) */
+
+      j =  one << (wsize - 1);
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(&W[j], N->n + 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_copy(&W[j], &W[1]), cleanup);
+
+      for (i = 0; i < wsize - 1; i++)
+        {
+          mpi_montmul(&W[j], &W[j], N, mm, &T);
+        }
+
+      /* W[i] = W[i - 1] * W[1] */
+
+      for (i = j + 1; i < (one << wsize); i++)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_grow(&W[i], N->n + 1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_copy(&W[i], &W[i - 1]), cleanup);
+
+          mpi_montmul(&W[i], &W[1], N, mm, &T);
+        }
+    }
+
+  nblimbs = E->n;
+  bufsize = 0;
+  nbits   = 0;
+  wbits   = 0;
+  state   = 0;
+
+  while (1)
+    {
+      if (bufsize == 0)
+        {
+          if (nblimbs == 0)
+            {
+              break;
+            }
+
+          nblimbs--;
+
+          bufsize = sizeof(uint32_t) << 3;
+        }
+
+      bufsize--;
+
+      ei = (E->p[nblimbs] >> bufsize) & 1;
+
+      /* skip leading 0s */
+
+      if (ei == 0 && state == 0)
+        {
+          continue;
+        }
+
+      if (ei == 0 && state == 1)
+        {
+          /* out of window, square X */
+
+          mpi_montmul(X, X, N, mm, &T);
+          continue;
+        }
+
+      /* add ei to current window */
+
+      state = 2;
+
+      nbits++;
+      wbits |= (ei << (wsize - nbits));
+
+      if (nbits == wsize)
+        {
+          /* X = X^wsize R^-1 mod N */
+
+          for (i = 0; i < wsize; i++)
+            {
+              mpi_montmul(X, X, N, mm, &T);
+            }
+
+          /* X = X * W[wbits] R^-1 mod N */
+
+          mpi_montmul(X, &W[wbits], N, mm, &T);
+
+          state--;
+          nbits = 0;
+          wbits = 0;
+        }
+    }
+
+  /* process the remaining bits */
+
+  for (i = 0; i < nbits; i++)
+    {
+      mpi_montmul(X, X, N, mm, &T);
+
+      wbits <<= 1;
+
+      if ((wbits & (one << wsize)) != 0)
+        mpi_montmul(X, &W[1], N, mm, &T);
+    }
+
+  /* X = A^E * R * R^-1 mod N = A^E mod N */
+
+  mpi_montred(X, N, mm, &T);
+
+  if (neg && E->n != 0 && (E->p[0] & 1) != 0)
+    {
+      X->s = -1;
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(X, N, X), cleanup);
+    }
+
+cleanup:
+
+  for (i = (one << (wsize - 1)); i < (one << wsize); i++)
+    {
+      esp32c3_mpi_free(&W[i]);
+    }
+
+  esp32c3_mpi_free(&W[1]);
+  esp32c3_mpi_free(&T);
+  esp32c3_mpi_free(&apos);
+
+  if (_RR == NULL || _RR->p == NULL)
+    {
+      esp32c3_mpi_free(&RR);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_gcd
+ *
+ * Description:
+ *   Compute the greatest common divisor: G = gcd(A, B)
+ *
+ * Input Parameters:
+ *   G       - The destination MPI
+ *   A       - The first operand
+ *   B       - The second operand
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_gcd(struct esp32c3_mpi_s *G,
+                    const struct esp32c3_mpi_s *A,
+                    const struct esp32c3_mpi_s *B)
+{
+  int ret;
+  size_t lz, lzt;
+  struct esp32c3_mpi_s TG, TA, TB;
+
+  DEBUGASSERT(G != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(B != NULL);
+
+  esp32c3_mpi_init(&TG); esp32c3_mpi_init(&TA); esp32c3_mpi_init(&TB);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&TA, A), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&TB, B), cleanup);
+
+  lz = esp32c3_mpi_lsb(&TA);
+  lzt = esp32c3_mpi_lsb(&TB);
+
+  if (lzt < lz)
+    {
+      lz = lzt;
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TA, lz), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TB, lz), cleanup);
+
+  TA.s = TB.s = 1;
+
+  while (esp32c3_mpi_cmp_int(&TA, 0) != 0)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TA, esp32c3_mpi_lsb(&TA)),
+                      cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TB, esp32c3_mpi_lsb(&TB)),
+                      cleanup);
+
+      if (esp32c3_mpi_cmp_mpi(&TA, &TB) >= 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_abs(&TA, &TA, &TB), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TA, 1), cleanup);
+        }
+      else
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_abs(&TB, &TB, &TA), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TB, 1), cleanup);
+        }
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_l(&TB, lz), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(G, &TB), cleanup);
+
+cleanup:
+
+  esp32c3_mpi_free(&TG); esp32c3_mpi_free(&TA); esp32c3_mpi_free(&TB);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_fill_random
+ *
+ * Description:
+ *   Fill an MPI with a number of random bytes
+ *
+ * Input Parameters:
+ *   X        - The destination MPI
+ *   size     - The number of random bytes to generate
+ *   f_rng    - The RNG function to use. This must not be \c NULL
+ *   p_rng    - The RNG parameter to be passed to \p f_rng
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_fill_random(struct esp32c3_mpi_s *X, size_t size,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng)
+{
+  int ret;
+  size_t const limbs = CHARS_TO_LIMBS(size);
+  size_t const overhead = (limbs * CIL) - size;
+  unsigned char *XP;
+
+  DEBUGASSERT(X   != NULL);
+  DEBUGASSERT(f_rng != NULL);
+
+  /* Ensure that target MPI has exactly the necessary number of limbs */
+
+  if (X->n != limbs)
+    {
+      esp32c3_mpi_free(X);
+      esp32c3_mpi_init(X);
+      ESP32C3_MPI_CHK(esp32c3_mpi_grow(X, limbs), cleanup);
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(X, 0), cleanup);
+
+  XP = (unsigned char *) X->p;
+  ESP32C3_MPI_CHK(f_rng(p_rng, XP + overhead, size), cleanup);
+
+  mpi_bigendian_to_host(X->p, limbs);
+
+cleanup:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_mpi_inv_mod
+ *
+ * Description:
+ *   Compute the modular inverse: X = A^-1 mod N
+ *
+ * Input Parameters:
+ *   X        - The destination MPI
+ *   A        - The MPI to calculate the modular inverse of
+ *   N        - The base of the modular inversion
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_inv_mod(struct esp32c3_mpi_s *X,
+                        const struct esp32c3_mpi_s *A,
+                        const struct esp32c3_mpi_s *N)
+{
+  int ret;
+  struct esp32c3_mpi_s G, TA, TU, U1, U2, TB, TV, V1, V2;
+  DEBUGASSERT(X != NULL);
+  DEBUGASSERT(A != NULL);
+  DEBUGASSERT(N != NULL);
+
+  if (esp32c3_mpi_cmp_int(N, 1) <= 0)
+    {
+      return ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+    }
+
+  esp32c3_mpi_init(&TA);
+  esp32c3_mpi_init(&TU);
+  esp32c3_mpi_init(&U1);
+  esp32c3_mpi_init(&U2);
+  esp32c3_mpi_init(&G);
+  esp32c3_mpi_init(&TB);
+  esp32c3_mpi_init(&TV);
+  esp32c3_mpi_init(&V1);
+  esp32c3_mpi_init(&V2);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_gcd(&G, A, N), cleanup);
+
+  if (esp32c3_mpi_cmp_int(&G, 1) != 0)
+    {
+      ret = ESP32C3_ERR_MPI_NOT_ACCEPTABLE;
+      goto cleanup;
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&TA, A, N), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&TU, &TA), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&TB, N), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&TV, N), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(&U1, 1), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(&U2, 0), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(&V1, 0), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_lset(&V2, 1), cleanup);
+
+  do
+    {
+      while ((TU.p[0] & 1) == 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TU, 1), cleanup);
+
+          if ((U1.p[0] & 1) != 0 || (U2.p[0] & 1) != 0)
+            {
+              ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&U1, &U1, &TB), cleanup);
+              ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&U2, &U2, &TA), cleanup);
+            }
+
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&U1, 1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&U2, 1), cleanup);
+        }
+
+      while ((TV.p[0] & 1) == 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&TV, 1), cleanup);
+
+          if ((V1.p[0] & 1) != 0 || (V2.p[0] & 1) != 0)
+            {
+              ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&V1, &V1, &TB), cleanup);
+              ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&V2, &V2, &TA), cleanup);
+            }
+
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&V1, 1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&V2, 1), cleanup);
+        }
+
+      if (esp32c3_mpi_cmp_mpi(&TU, &TV) >= 0)
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&TU, &TU, &TV), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&U1, &U1, &V1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&U2, &U2, &V2), cleanup);
+        }
+      else
+        {
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&TV, &TV, &TU), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&V1, &V1, &U1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&V2, &V2, &U2), cleanup);
+        }
+    }
+  while (esp32c3_mpi_cmp_int(&TU, 0) != 0);
+
+  while (esp32c3_mpi_cmp_int(&V1, 0) < 0)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&V1, &V1, N), cleanup);
+    }
+
+  while (esp32c3_mpi_cmp_mpi(&V1, N) >= 0)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&V1, &V1, N), cleanup);
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(X, &V1), cleanup);
+
+cleanup:
+
+  esp32c3_mpi_free(&TA);
+  esp32c3_mpi_free(&TU);
+  esp32c3_mpi_free(&U1);
+  esp32c3_mpi_free(&U2);
+  esp32c3_mpi_free(&G);
+  esp32c3_mpi_free(&TB);
+  esp32c3_mpi_free(&TV);
+  esp32c3_mpi_free(&V1);
+  esp32c3_mpi_free(&V2);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Test Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32C3_BIGNUM_ACCELERATOR_TEST
+
+#define GCD_PAIR_COUNT  3
+
+/****************************************************************************
+ * Name: esp32c3_mpi_self_test
+ *
+ * Description:
+ *   Checkup routine
+ *
+ * Input Parameters:
+ *   verbose        - The result output or not
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_self_test(int verbose)
+{
+  int ret;
+  int i;
+  struct esp32c3_mpi_s A;
+  struct esp32c3_mpi_s E;
+  struct esp32c3_mpi_s N;
+  struct esp32c3_mpi_s X;
+  struct esp32c3_mpi_s Y;
+  struct esp32c3_mpi_s U;
+  struct esp32c3_mpi_s V;
+
+  const int gcd_pairs[GCD_PAIR_COUNT][3] =
+  {
+    {
+      693, 609, 21
+    },
+
+    {
+      1764, 868, 28
+    },
+
+    {
+      768454923, 542167814, 1
+    }
+  };
+
+  esp32c3_mpi_init(&A);
+  esp32c3_mpi_init(&E);
+  esp32c3_mpi_init(&N);
+  esp32c3_mpi_init(&X);
+  esp32c3_mpi_init(&Y);
+  esp32c3_mpi_init(&U);
+  esp32c3_mpi_init(&V);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&A, 16,
+    "EFE021C2645FD1DC586E69184AF4A31E" \
+    "D5F53E93B5F123FA41680867BA110131" \
+    "944FE7952E2517337780CB0DB80E61AA" \
+    "E7C8DDC6C5C6AADEB34EB38A2F40D5E6"), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&E, 16,
+    "B2E7EFD37075B9F03FF989C7C5051C20" \
+    "34D2A323810251127E7BF8625A4F49A5" \
+    "F3E27F4DA8BD59C47D6DAABA4C8127BD" \
+    "5B5C25763222FEFCCFC38B832366C29E"), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&N, 16,
+    "0066A198186C18C10B2F5ED9B522752A" \
+    "9830B69916E535C8F047518A889A43A5" \
+    "94B6BED27A168D31D4A52F88925AA8F5"), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&X, &A, &N), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&U, 16,
+    "602AB7ECA597A3D6B56FF9829A5E8B85" \
+    "9E857EA95A03512E2BAE7391688D264A" \
+    "A5663B0341DB9CCFD2C4C5F421FEC814" \
+    "8001B72E848A38CAE1C65F78E56ABDEF" \
+    "E12D3C039B8A02D6BE593F0BBBDA56F1" \
+    "ECF677152EF804370C1A305CAF3B5BF1" \
+    "30879B56C61DE584A0F53A2447A51E"), cleanup);
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "  MPI test #1 (mul_mpi): ");
+    }
+
+  if (esp32c3_mpi_cmp_mpi(&X, &U) != 0)
+    {
+      if (verbose != 0)
+        {
+          syslog(LOG_INFO, "failed\n");
+        }
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "passed\n");
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_div_mpi(&X, &Y, &A, &N), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&U, 16,
+    "256567336059E52CAE22925474705F39A94"), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&V, 16,
+    "6613F26162223DF488E9CD48CC132C7A" \
+    "0AC93C701B001B092E4E5B9F73BCD27B" \
+    "9EE50D0657C77F374E903CDFA4C642"), cleanup);
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "  MPI test #2 (div_mpi): ");
+    }
+
+  if (esp32c3_mpi_cmp_mpi(&X, &U) != 0 ||
+      esp32c3_mpi_cmp_mpi(&Y, &V) != 0)
+    {
+      if (verbose != 0)
+        {
+          syslog(LOG_INFO, "failed\n");
+        }
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "passed\n");
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&X, &A, &E, &N, NULL), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&U, 16,
+    "36E139AEA55215609D2816998ED020BB" \
+    "BD96C37890F65171D948E9BC7CBAA4D9" \
+    "325D24D6A3C12710F10A09FA08AB87"), cleanup);
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "  MPI test #3 (exp_mod): ");
+    }
+
+  if (esp32c3_mpi_cmp_mpi(&X, &U) != 0)
+    {
+      if (verbose != 0)
+        {
+          syslog(LOG_INFO, "failed\n");
+        }
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "passed\n");
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_inv_mod(&X, &A, &N), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&U, 16,
+    "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \
+    "C3DBA76456363A10869622EAC2DD84EC" \
+    "C5B8A74DAC4D09E03B5E0BE779F2DF61"), cleanup);
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "  MPI test #4 (inv_mod): ");
+    }
+
+  if (esp32c3_mpi_cmp_mpi(&X, &U) != 0)
+    {
+      if (verbose != 0)
+        {
+          syslog(LOG_INFO, "failed\n");
+        }
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "passed\n");
+    }
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "  MPI test #5 (simple gcd): ");
+    }
+
+  for (i = 0; i < GCD_PAIR_COUNT; i++)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_lset(&X, gcd_pairs[i][0]), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_lset(&Y, gcd_pairs[i][1]), cleanup);
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_gcd(&A, &X, &Y), cleanup);
+
+      if (esp32c3_mpi_cmp_int(&A, gcd_pairs[i][2]) != 0)
+        {
+          if (verbose != 0)
+            {
+              syslog(LOG_INFO, "failed at %d\n", i);
+            }
+
+          ret = 1;
+          goto cleanup;
+        }
+    }
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "passed\n");
+    }
+
+cleanup:
+
+  if (ret != 0 && verbose != 0)
+    {
+      syslog(LOG_INFO, "Unexpected error, return code = %08X\n", ret);
+    }
+
+  esp32c3_mpi_free(&A);
+  esp32c3_mpi_free(&E);
+  esp32c3_mpi_free(&N);
+  esp32c3_mpi_free(&X);
+  esp32c3_mpi_free(&Y);
+  esp32c3_mpi_free(&U);
+  esp32c3_mpi_free(&V);
+
+  if (verbose != 0)
+    {
+      syslog(LOG_INFO, "\n");
+    }
+
+  return ret;
+}
+
+#endif /* CONFIG_ESP32C3_BIGNUM_ACCELERATOR_TEST */
+
+#endif /* CONFIG_ESP32C3_BIGNUM_ACCELERATOR */
+
diff --git a/arch/risc-v/src/esp32c3/esp32c3_bignum.h b/arch/risc-v/src/esp32c3/esp32c3_bignum.h
new file mode 100644
index 0000000..3ee4a58
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/esp32c3_bignum.h
@@ -0,0 +1,892 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/esp32c3_bignum.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_ESP32C3_ESP32C3_BIGNUM_H
+#define __ARCH_RISCV_SRC_ESP32C3_ESP32C3_BIGNUM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Pre-processor Macros
+ ****************************************************************************/
+
+#define ESP32C3_ERR_MPI_FILE_IO_ERROR           -0x0002  /**< An error occurred while reading from or writing to a file. */
+#define ESP32C3_ERR_MPI_BAD_INPUT_DATA          -0x0004  /**< Bad input parameters to function. */
+#define ESP32C3_ERR_MPI_INVALID_CHARACTER       -0x0006  /**< There is an invalid character in the digit string. */
+#define ESP32C3_ERR_MPI_BUFFER_TOO_SMALL        -0x0008  /**< The buffer is too small to write to. */
+#define ESP32C3_ERR_MPI_NEGATIVE_VALUE          -0x000A  /**< The input arguments are negative or result in illegal output. */
+#define ESP32C3_ERR_MPI_DIVISION_BY_ZERO        -0x000C  /**< The input argument for division is zero, which is not allowed. */
+#define ESP32C3_ERR_MPI_NOT_ACCEPTABLE          -0x000E  /**< The input arguments are not acceptable. */
+#define ESP32C3_ERR_MPI_ALLOC_FAILED            -0x0010  /**< Memory allocation failed. */
+
+#define ESP32C3_MPI_CHK(f, a)               \
+  do                                        \
+    {                                       \
+      ret = (f);                            \
+      if (ret != 0)                         \
+        {                                   \
+          goto a;                           \
+        }                                   \
+    }                                       \
+  while(0)
+
+/* Maximum size MPIs are allowed to grow to in number of limbs. */
+#define ESP32C3_MPI_MAX_LIMBS               10000
+
+/* Maximum window size used for modular exponentiation */
+#define ESP32C3_MPI_WINDOW_SIZE               6
+
+/* Maximum size of MPIs allowed in bits and bytes for user-MPIs. */
+#define ESP32C3_MPI_MAX_SIZE                1024
+
+/**< Maximum number of bits for usable MPIs. */
+#define ESP32C3_MPI_MAX_BITS                (8 * ESP32C3_MPI_MAX_SIZE)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* MPI structure */
+
+struct esp32c3_mpi_s
+{
+  int s;                /* Sign: -1 if the mpi is negative, 1 otherwise */
+  size_t n;             /* total number of limbs */
+  uint32_t *p;          /* pointer to limbs */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_mpi_init
+ *
+ * Description:
+ *   Initialize an MPI context
+ *
+ * Input Parameters:
+ *   X    - The MPI context to initialize
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_mpi_init(struct esp32c3_mpi_s *X);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_free
+ *
+ * Description:
+ *   Frees the components of an MPI context
+ *
+ * Input Parameters:
+ *   X    - The MPI context to be cleared
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_mpi_free(struct esp32c3_mpi_s *X);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_grow
+ *
+ * Description:
+ *   Enlarge an MPI to the specified number of limbs
+ *
+ * Input Parameters:
+ *   X     - The MPI context to grow
+ *   nblimbs - The target number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_grow(struct esp32c3_mpi_s *X, size_t nblimbs);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_shrink
+ *
+ * Description:
+ *   Resizes an MPI downwards, keeping at least the specified number of limbs
+ *
+ * Input Parameters:
+ *   X     - The MPI context to shrink
+ *   nblimbs - The minimum number of limbs
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_shrink(struct esp32c3_mpi_s *X, size_t nblimbs);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_copy
+ *
+ * Description:
+ *   Copy the contents of Y into X
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   Y     - The source MPI
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_copy(struct esp32c3_mpi_s *X,
+           const struct esp32c3_mpi_s *Y);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_swap
+ *
+ * Description:
+ *   Swap the contents of X and Y
+ *
+ * Input Parameters:
+ *   X     - The first MPI
+ *   nblimbs - The second MPI
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_mpi_swap(struct esp32c3_mpi_s *X,
+            struct esp32c3_mpi_s *Y);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_safe_cond_assign
+ *
+ * Description:
+ *   Perform a safe conditional copy of MPI which doesn't
+ *   reveal whether the condition was true or not.
+ *
+ * Input Parameters:
+ *   X     - The MPI to conditionally assign to
+ *   Y     - The MPI to be assigned from
+ *   assign  - The condition deciding whether perform the assignment or not
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_safe_cond_assign(struct esp32c3_mpi_s *X,
+                 const struct esp32c3_mpi_s *Y,
+                 unsigned char assign);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_safe_cond_swap
+ *
+ * Description:
+ *   Perform a safe conditional swap which doesn't
+ *   reveal whether the condition was true or not.
+ *
+ * Input Parameters:
+ *   X     - The first MPI
+ *   Y     - The second MPI
+ *   swap  - The condition deciding whether to perform the swap or not
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_safe_cond_swap(struct esp32c3_mpi_s *X,
+                 struct esp32c3_mpi_s *Y,
+                 unsigned char assign);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_lset
+ *
+ * Description:
+ *   Set value from integer
+ *
+ * Input Parameters:
+ *   X     - The MPI to set
+ *   z     - The value to use
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_lset(struct esp32c3_mpi_s *X, int32_t z);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_get_bit
+ *
+ * Description:
+ *   Get a specific bit from an MPI
+ *
+ * Input Parameters:
+ *   X     - The MPI context to query
+ *   pos   - Zero-based index of the bit to query
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_get_bit(const struct esp32c3_mpi_s *X, size_t pos);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_set_bit
+ *
+ * Description:
+ *   Modify a specific bit in an MPI
+ *
+ * Input Parameters:
+ *   X     - The MPI context to modify
+ *   pos   - Zero-based index of the bit to modify
+ *   val   - The desired value of bit
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_set_bit(struct esp32c3_mpi_s *X,
+            size_t pos, unsigned char val);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_lsb
+ *
+ * Description:
+ *   Return the number of bits of value
+ *
+ * Input Parameters:
+ *   X     - The MPI context to query
+ *
+ * Returned Value:
+ *   The number of bits of value.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_mpi_lsb(const struct esp32c3_mpi_s *X);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_bitlen
+ *
+ * Description:
+ *   Return the number of bits up to and including the most
+ *   significant bit of value
+ *
+ * Input Parameters:
+ *   X     - The MPI context to query
+ *
+ * Returned Value:
+ *   The number of bits up and including the most significant bit of value.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_mpi_bitlen(const struct esp32c3_mpi_s *X);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_size
+ *
+ * Description:
+ *   Return the total size of an MPI value in bytes
+ *
+ * Input Parameters:
+ *   X     - The MPI context to query
+ *
+ * Returned Value:
+ *   The least number of bytes capable of storing the absolute value.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_mpi_size(const struct esp32c3_mpi_s *X);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_read_string
+ *
+ * Description:
+ *   Import from an ASCII string
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   radix   - The numeric base of the input string
+ *   s     - Null-terminated string buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_read_string(struct esp32c3_mpi_s *X,
+              int radix, const char *s);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_write_string
+ *
+ * Description:
+ *   Export an MPI to an ASCII string
+ *
+ * Input Parameters:
+ *   X     - The source MPI
+ *   radix   - The numeric base of the output string
+ *   buf   - The buffer to write the string to
+ *   buflen  - The available size in Bytes of buf
+ *   olen  - The address at which to store the length of the string written
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_write_string(const struct esp32c3_mpi_s *X, int radix,
+                char *buf, size_t buflen, size_t *olen);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_read_binary
+ *
+ * Description:
+ *   Import an MPI from unsigned big endian binary data
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   buf   - The input buffer
+ *   buflen  - The length of the input buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_read_binary(struct esp32c3_mpi_s *X,
+              const unsigned char *buf, size_t buflen);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_write_binary
+ *
+ * Description:
+ *   Export X into unsigned binary data, big endian
+ *
+ * Input Parameters:
+ *   X     - The source MPI
+ *   buf   - The output buffer
+ *   buflen  - The length of the output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_write_binary(const struct esp32c3_mpi_s *X,
+               unsigned char *buf, size_t buflen);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_shift_l
+ *
+ * Description:
+ *   Perform a left-shift on an MPI: X <<= count
+ *
+ * Input Parameters:
+ *   X     - The MPI to shift
+ *   count   - The number of bits to shift by
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_shift_l(struct esp32c3_mpi_s *X, size_t count);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_shift_r
+ *
+ * Description:
+ *   Perform a right-shift on an MPI: X >>= count
+ *
+ * Input Parameters:
+ *   X     - The MPI to shift
+ *   count   - The number of bits to shift by
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_shift_r(struct esp32c3_mpi_s *X, size_t count);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_cmp_abs
+ *
+ * Description:
+ *   Compare the absolute values of two MPIs
+ *
+ * Input Parameters:
+ *   X     - The left-hand MPI
+ *   Y     - The right-hand MPI
+ *
+ * Returned Value:
+ *   1 if \p `|X|` is greater than \p `|Y|`.
+ *   -1 if \p `|X|` is lesser than \p `|Y|`.
+ *   0 if \p `|X|` is equal to \p `|Y|`.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_cmp_abs(const struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *Y);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_cmp_mpi
+ *
+ * Description:
+ *   Compare two MPIs.
+ *
+ * Input Parameters:
+ *   X     - The left-hand MPI
+ *   Y     - The right-hand MPI
+ *
+ * Returned Value:
+ *   1 if \p `X` is greater than \p `Y`.
+ *   -1 if \p `X` is lesser than \p `Y`.
+ *   0 if \p `X` is equal to \p `Y`.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_cmp_mpi(const struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *Y);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_lt_mpi_ct
+ *
+ * Description:
+ *   Check if an MPI is less than the other in constant time
+ *
+ * Input Parameters:
+ *   X     - The left-hand MPI
+ *   Y     - The right-hand MPI
+ *   ret   - The result of the comparison:
+ *       1 if \p X is less than \p Y.
+ *       0 if \p X is greater than or equal to \p Y.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_lt_mpi_ct(const struct esp32c3_mpi_s *X,
+              const struct esp32c3_mpi_s *Y,
+              unsigned *ret);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_cmp_int
+ *
+ * Description:
+ *   Compare an MPI with an integer
+ *
+ * Input Parameters:
+ *   X     - The left-hand MPI
+ *   z     - The integer value to compare \p X to
+ *
+ * Returned Value:
+ *   \c 1 if \p X is greater than \p z.
+ *   \c -1 if \p X is lesser than \p z.
+ *   \c 0 if \p X is equal to \p z.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_cmp_int(const struct esp32c3_mpi_s *X, int32_t z);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_add_abs
+ *
+ * Description:
+ *   Perform an unsigned addition of MPIs: X = |A| + |B|
+ *
+ * Input Parameters:
+ *   X     - The left-hand MPI
+ *   z     - The integer value to compare \p X to.
+ *
+ * Returned Value:
+ *   \c 1 if \p X is greater than \p z.
+ *   \c -1 if \p X is lesser than \p z.
+ *   \c 0 if \p X is equal to \p z.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_add_abs(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_sub_abs
+ *
+ * Description:
+ *   Perform an unsigned subtraction of MPIs: X = |A| - |B|
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The minuend
+ *   B     - The subtrahend
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_sub_abs(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_add_mpi
+ *
+ * Description:
+ *   Perform a signed addition of MPIs: X = A + B
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The first summand
+ *   B     - The second summand
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_add_mpi(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_sub_mpi
+ *
+ * Description:
+ *   Perform a signed subtraction of MPIs: X = A - B
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The minuend
+ *   B     - The subtrahend
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_sub_mpi(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_add_int
+ *
+ * Description:
+ *   Perform a signed addition of an MPI and an integer: X = A + b
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The first summand
+ *   b     - The second summand
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_add_int(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            int32_t b);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_sub_int
+ *
+ * Description:
+ *   Perform a signed subtraction of an MPI and an integer: X = A - b
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The minuend
+ *   b     - The subtrahend
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_sub_int(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            int32_t b);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mul_mpi
+ *
+ * Description:
+ *   Perform a multiplication of two MPIs: Z = X * Y
+ *
+ * Input Parameters:
+ *   Z    - The destination MPI
+ *   X    - The first factor
+ *   Y    - The second factor
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mul_mpi(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mul_int
+ *
+ * Description:
+ *   Perform a multiplication of an MPI with an unsigned integer: X = A * b
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The first factor
+ *   b     - The second factor.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mul_int(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            uint32_t b);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_div_mpi
+ *
+ * Description:
+ *   Perform a division with remainder of two MPIs: A = Q * B + R
+ *
+ * Input Parameters:
+ *   Q    - The destination MPI for the quotient
+ *   R    - The destination MPI for the remainder value
+ *   A    - The dividend
+ *   B    - The divisor
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_div_mpi(struct esp32c3_mpi_s *Q,
+            struct esp32c3_mpi_s *R,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_div_int
+ *
+ * Description:
+ *   Perform a division with remainder of an MPI by an integer: A = Q * b + R
+ *
+ * Input Parameters:
+ *   Q    - The destination MPI for the quotient
+ *   R    - The destination MPI for the remainder value
+ *   A    - The dividend
+ *   B    - The divisor
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_div_int(struct esp32c3_mpi_s *Q,
+            struct esp32c3_mpi_s *R,
+            const struct esp32c3_mpi_s *A,
+            int32_t b);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mod_mpi
+ *
+ * Description:
+ *   erform a modular reduction. R = A mod B
+ *
+ * Input Parameters:
+ *   R     - The destination MPI for the residue value
+ *   A     - The MPI to compute the residue of
+ *   B     - The base of the modular reduction
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mod_mpi(struct esp32c3_mpi_s *R,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_mod_int
+ *
+ * Description:
+ *   Perform a modular reduction with respect to an integer: r = A mod b
+ *
+ * Input Parameters:
+ *   r     - The address at which to store the residue
+ *   A     - The MPI to compute the residue of
+ *   b     - The integer base of the modular reduction
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_mod_int(uint32_t *r,
+            const struct esp32c3_mpi_s *A,
+            int32_t b);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_exp_mod
+ *
+ * Description:
+ *   Perform a sliding-window exponentiation: X = A^E mod N
+ *
+ * Input Parameters:
+ *   X     - The destination MPI
+ *   A     - The base of the exponentiation
+ *   E     - The exponent MPI
+ *   N     - The base for the modular reduction
+ *   _RR   - A helper MPI depending solely on \p N which can be used to
+ *       speed-up multiple modular exponentiations for the same value
+ *       of \p N.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_exp_mod(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *E,
+            const struct esp32c3_mpi_s *N,
+            struct esp32c3_mpi_s *_RR);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_gcd
+ *
+ * Description:
+ *   Compute the greatest common divisor: G = gcd(A, B)
+ *
+ * Input Parameters:
+ *   G     - The destination MPI
+ *   A     - The first operand
+ *   B     - The second operand
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_gcd(struct esp32c3_mpi_s *G,
+          const struct esp32c3_mpi_s *A,
+          const struct esp32c3_mpi_s *B);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_fill_random
+ *
+ * Description:
+ *   Fill an MPI with a number of random bytes
+ *
+ * Input Parameters:
+ *   X    - The destination MPI
+ *   size   - The number of random bytes to generate
+ *   f_rng  - The RNG function to use. This must not be \c NULL
+ *   p_rng  - The RNG parameter to be passed to \p f_rng
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_fill_random(struct esp32c3_mpi_s *X, size_t size,
+           int (*f_rng)(void *, unsigned char *, size_t),
+           void *p_rng);
+
+/****************************************************************************
+ * Name: esp32c3_mpi_inv_mod
+ *
+ * Description:
+ *   Compute the modular inverse: X = A^-1 mod N
+ *
+ * Input Parameters:
+ *   X    - The destination MPI
+ *   A    - The MPI to calculate the modular inverse of
+ *   N    - The base of the modular inversion
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_inv_mod(struct esp32c3_mpi_s *X,
+            const struct esp32c3_mpi_s *A,
+            const struct esp32c3_mpi_s *N);
+
+#ifdef CONFIG_ESP32C3_BIGNUM_ACCELERATOR_TEST
+
+/****************************************************************************
+ * Name: esp32c3_mpi_self_test
+ *
+ * Description:
+ *   Checkup routine
+ *
+ * Input Parameters:
+ *   verbose    - The result output or not
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_mpi_self_test(int verbose);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#undef EXTERN
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_ESP32C3_ESP32C3_BIGNUM_H */
diff --git a/arch/risc-v/src/esp32c3/esp32c3_rsa.c b/arch/risc-v/src/esp32c3/esp32c3_rsa.c
new file mode 100644
index 0000000..fd4d21f
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/esp32c3_rsa.c
@@ -0,0 +1,2375 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/esp32c3_rsa.c
+ *
+ * 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>
+
+#ifdef CONFIG_ESP32C3_RSA_ACCELERATOR
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <debug.h>
+#include <semaphore.h>
+
+#include "riscv_arch.h"
+#include "esp32c3_rsa.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ESP32C3_PKCS1_V15
+#define RSA_EXPONENT_BLINDING 28
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rsa_check_context
+ *
+ * Description:
+ *   Checks whether the context fields are set in such a way
+ *   that the RSA primitives will be able to execute without error.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to store the parameters in
+ *   N        - The RSA modulus
+ *   NL    - The Byte length of \p N
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int rsa_check_context(struct esp32c3_rsa_context_s const *ctx,
+                             int is_priv, int blinding_needed)
+{
+#if !defined(ESP32C3_RSA_NO_CRT)
+  ((void) blinding_needed);
+#endif
+
+  if (ctx->len != esp32c3_mpi_size(&ctx->N) ||
+      ctx->len > ESP32C3_MPI_MAX_SIZE)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  /* 1. Modular exponentiation needs positive, odd moduli */
+
+  if (esp32c3_mpi_cmp_int(&ctx->N, 0) <= 0 ||
+      esp32c3_mpi_get_bit(&ctx->N, 0) == 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  if (is_priv &&
+     (esp32c3_mpi_cmp_int(&ctx->P, 0) <= 0 ||
+      esp32c3_mpi_get_bit(&ctx->P, 0) == 0 ||
+      esp32c3_mpi_cmp_int(&ctx->Q, 0) <= 0 ||
+      esp32c3_mpi_get_bit(&ctx->Q, 0) == 0))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+#endif /* !ESP32C3_RSA_NO_CRT */
+
+  /* 2. Exponents must be positive */
+
+  /* Always need E for public key operations */
+
+  if (esp32c3_mpi_cmp_int(&ctx->E, 0) <= 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+#if defined(ESP32C3_RSA_NO_CRT)
+  /* For private key operations, use D or DP & DQ as exponents */
+
+  if (is_priv && esp32c3_mpi_cmp_int(&ctx->D, 0) <= 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+#else
+  if (is_priv &&
+     (esp32c3_mpi_cmp_int(&ctx->DP, 0) <= 0 ||
+      esp32c3_mpi_cmp_int(&ctx->DQ, 0) <= 0))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+#endif /* ESP32C3_RSA_NO_CRT */
+
+#if defined(ESP32C3_RSA_NO_CRT)
+  if (is_priv && blinding_needed &&
+     (esp32c3_mpi_cmp_int(&ctx->P, 0) <= 0 ||
+      esp32c3_mpi_cmp_int(&ctx->Q, 0) <= 0))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+#endif
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  if (is_priv && esp32c3_mpi_cmp_int(&ctx->QP, 0) <= 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: rsa_prepare_blinding
+ *
+ * Description:
+ *   Generate or update blinding values.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to store the parameters in.
+ *   f_rng    - The RNG function
+ *   p_rng    - The RNG context to pass to \p f_rng
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int rsa_prepare_blinding(struct esp32c3_rsa_context_s *ctx,
+         int (*f_rng)(void *, unsigned char *, size_t), void *p_rng)
+{
+  int ret;
+  int count = 0;
+  struct esp32c3_mpi_s R;
+
+  esp32c3_mpi_init(&R);
+
+  if (ctx->VF.p != NULL)
+    {
+      /* We already have blinding values, just update them by squaring */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&ctx->VI, &ctx->VI, &ctx->VI),
+                      cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&ctx->VI, &ctx->VI, &ctx->N),
+                      cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&ctx->VF, &ctx->VF, &ctx->VF),
+                      cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&ctx->VF, &ctx->VF, &ctx->N),
+                      cleanup);
+
+      goto cleanup;
+    }
+
+  /* Unblinding value: VF = random number, invertible mod N */
+
+  do
+    {
+      if (count++ > 10)
+        {
+          ret = ESP32C3_ERR_RSA_RNG_FAILED;
+          goto cleanup;
+        }
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_fill_random(&ctx->VF, ctx->len - 1,
+                                              f_rng, p_rng), cleanup);
+
+      /* Compute VF^-1 as R * (R VF)^-1 to avoid leaks from inv_mod. */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_fill_random(&R, ctx->len - 1,
+                                              f_rng, p_rng), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&ctx->VI, &ctx->VF, &R),
+                      cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&ctx->VI, &ctx->VI, &ctx->N),
+                      cleanup);
+
+      ret = esp32c3_mpi_inv_mod(&ctx->VI, &ctx->VI, &ctx->N);
+      if (ret != 0 && ret != ESP32C3_ERR_MPI_NOT_ACCEPTABLE)
+        {
+          goto cleanup;
+        }
+    }
+  while (ret == ESP32C3_ERR_MPI_NOT_ACCEPTABLE);
+
+  /* Finish the computation of VF^-1 = R * (R VF)^-1 */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&ctx->VI, &ctx->VI, &R),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&ctx->VI, &ctx->VI, &ctx->N),
+                  cleanup);
+
+  /* Blinding value: VI = VF^(-e) mod N */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&ctx->VI, &ctx->VI,
+                                      &ctx->E, &ctx->N, &ctx->RN),
+                  cleanup);
+
+cleanup:
+  esp32c3_mpi_free(&R);
+
+  return ret;
+}
+
+#if defined(ESP32C3_PKCS1_V15)
+/* Implementation of the PKCS1-V1_5-ENCRYPT function */
+
+static int esp32c3_rsa_pkcs1_v15_encrypt(struct esp32c3_rsa_context_s *ctx,
+                 int (*f_rng)(void *, unsigned char *, size_t),
+                 void *p_rng,
+                 int mode, size_t ilen,
+                 const unsigned char *input,
+                 unsigned char *output)
+{
+  size_t nb_pad;
+  size_t olen;
+  int ret;
+  unsigned char *p = output;
+
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(mode == ESP32C3_RSA_PRIVATE ||
+              mode == ESP32C3_RSA_PUBLIC);
+  DEBUGASSERT(output != NULL);
+  DEBUGASSERT(input != NULL);
+
+  if (mode == ESP32C3_RSA_PRIVATE && ctx->padding != ESP32C3_RSA_PKCS_V15)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  olen = ctx->len;
+
+  /* first comparison checks for overflow */
+
+  if (ilen + 11 < ilen || olen < ilen + 11)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  nb_pad = olen - 3 - ilen;
+
+  *p++ = 0;
+  if (mode == ESP32C3_RSA_PUBLIC)
+    {
+      if (f_rng == NULL)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+        }
+
+      *p++ = ESP32C3_RSA_CRYPT;
+
+      while (nb_pad-- > 0)
+        {
+          int rng_dl = 100;
+
+          do
+            {
+              ret = f_rng(p_rng, p, 1);
+            }
+          while (*p == 0 && --rng_dl && ret == 0);
+
+          /* Check if RNG failed to generate data */
+
+          if (rng_dl == 0 || ret != 0)
+            {
+              return (ESP32C3_ERR_RSA_RNG_FAILED + ret);
+            }
+
+          p++;
+        }
+    }
+  else
+    {
+      *p++ = ESP32C3_RSA_SIGN;
+
+      while (nb_pad-- > 0)
+        {
+          *p++ = 0xff;
+        }
+    }
+
+  *p++ = 0;
+  memcpy(p, input, ilen);
+
+  return ((mode == ESP32C3_RSA_PUBLIC)
+          ? esp32c3_rsa_public(ctx, output, output)
+          : esp32c3_rsa_private(ctx, f_rng, p_rng, output, output));
+}
+#endif /* ESP32C3_PKCS1_V15 */
+
+#if defined(ESP32C3_PKCS1_V15)
+
+/****************************************************************************
+ * Name: all_or_nothing_int
+ *
+ * Description:
+ *   Turn zero-or-nonzero into zero-or-all-bits-one, without branches.
+ *
+ * Input Parameters:
+ *   value    - The value to analyze.
+ *
+ * Returned Value:
+ *   Zero if \p value is zero, otherwise all-bits-one.
+ *
+ ****************************************************************************/
+
+static unsigned all_or_nothing_int(unsigned value)
+{
+  return (- ((value | - value) >> (sizeof(value) * 8 - 1)));
+}
+
+/****************************************************************************
+ * Name: size_greater_than
+ *
+ * Description:
+ *   Check whether a size is out of bounds, without branches.
+ *
+ * Input Parameters:
+ *   size     - Size to check.
+ *   max      - Maximum desired value for \p size.
+ *
+ * Returned Value:
+ *   \c 0 if `size <= max`, \c 1 if `size > max`.
+ *
+ ****************************************************************************/
+
+static unsigned size_greater_than(size_t size, size_t max)
+{
+  /* Return the sign bit (1 for negative) of (max - size). */
+
+  return ((max - size) >> (sizeof(size_t) * 8 - 1));
+}
+
+/****************************************************************************
+ * Name: if_int
+ *
+ * Description:
+ *   Choose between two integer values, without branches.
+ *
+ * Input Parameters:
+ *   cond     - Condition to test.
+ *   if1      - Value to use if \p cond is nonzero.
+ *   if0      - Value to use if \p cond is zero.
+ *
+ * Returned Value:
+ *   \c if1 if \p cond is nonzero, otherwise \c if0.
+ *
+ ****************************************************************************/
+
+static unsigned if_int(unsigned cond, unsigned if1, unsigned if0)
+{
+  unsigned mask = all_or_nothing_int(cond);
+  return ((mask & if1) | (~mask & if0));
+}
+
+/****************************************************************************
+ * Name: mem_move_to_left
+ *
+ * Description:
+ *   Shift some data towards the left inside a buffer without leaking
+ *   the length of the data through side channels.
+ *
+ * Input Parameters:
+ *   start    - Pointer to the start of the buffer.
+ *   total    - Total size of the buffer.
+ *   offset   - Offset from which to copy bytes.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void mem_move_to_left(void *start,
+                size_t total,
+                size_t offset)
+{
+  volatile unsigned char *buf = start;
+  size_t i;
+  size_t n;
+  if (total == 0)
+    {
+      return;
+    }
+
+  for (i = 0; i < total; i++)
+    {
+      unsigned no_op = size_greater_than(total - offset, i);
+      for (n = 0; n < total - 1; n++)
+        {
+          unsigned char current = buf[n];
+          unsigned char next = buf[n + 1];
+          buf[n] = if_int(no_op, current, next);
+        }
+
+      buf[total - 1] = if_int(no_op, buf[total - 1], 0);
+    }
+}
+
+/* Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function */
+
+static int esp32c3_rsa_pkcs1_v15_decrypt(struct esp32c3_rsa_context_s *ctx,
+                 int (*f_rng)(void *, unsigned char *, size_t),
+                 void *p_rng,
+                 int mode, size_t *olen,
+                 const unsigned char *input,
+                 unsigned char *output,
+                 size_t output_max_len)
+{
+  int ret;
+  size_t ilen;
+  size_t i;
+  size_t plaintext_max_size;
+  unsigned char buf[ESP32C3_MPI_MAX_SIZE];
+  size_t pad_count = 0;
+  unsigned bad = 0;
+  unsigned char pad_done = 0;
+  size_t plaintext_size = 0;
+  unsigned output_too_large;
+
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(mode == ESP32C3_RSA_PRIVATE ||
+              mode == ESP32C3_RSA_PUBLIC);
+  DEBUGASSERT(output_max_len == 0 || output != NULL);
+  DEBUGASSERT(input != NULL);
+  DEBUGASSERT(olen != NULL);
+
+  ilen = ctx->len;
+  plaintext_max_size = (output_max_len > ilen - 11 ?
+               ilen - 11 :
+               output_max_len);
+
+  if (mode == ESP32C3_RSA_PRIVATE && ctx->padding != ESP32C3_RSA_PKCS_V15)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  if (ilen < 16 || ilen > sizeof(buf))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  ret = (mode == ESP32C3_RSA_PUBLIC)
+      ? esp32c3_rsa_public(ctx, input, buf)
+      : esp32c3_rsa_private(ctx, f_rng, p_rng, input, buf);
+
+  if (ret != 0)
+    {
+      goto cleanup;
+    }
+
+  /* Check and get padding length in constant time */
+
+  bad |= buf[0];
+
+  if (mode == ESP32C3_RSA_PRIVATE)
+    {
+      /* Decode EME-PKCS1-v1_5 padding */
+
+      bad |= buf[1] ^ ESP32C3_RSA_CRYPT;
+
+      /* Read the whole buffer */
+
+      for (i = 2; i < ilen; i++)
+        {
+          pad_done  |= ((buf[i] | (unsigned char)-buf[i]) >> 7) ^ 1;
+          pad_count += ((pad_done | (unsigned char)-pad_done) >> 7) ^ 1;
+        }
+    }
+  else
+    {
+      /* Decode EMSA-PKCS1-v1_5 padding */
+
+      bad |= buf[1] ^ ESP32C3_RSA_SIGN;
+
+      /* Read the whole buffer */
+
+      for (i = 2; i < ilen; i++)
+        {
+          pad_done |= if_int(buf[i], 0, 1);
+          pad_count += if_int(pad_done, 0, 1);
+          bad |= if_int(pad_done, 0, buf[i] ^ 0xff);
+        }
+    }
+
+  /* If pad_done is still zero, there's no data, only unfinished padding. */
+
+  bad |= if_int(pad_done, 0, 1);
+
+  /* There must be at least 8 bytes of padding. */
+
+  bad |= size_greater_than(8, pad_count);
+  plaintext_size = if_int(bad,
+               (unsigned) plaintext_max_size,
+               (unsigned) (ilen - pad_count - 3));
+  output_too_large = size_greater_than(plaintext_size,
+                      plaintext_max_size);
+
+  /* Set ret without branches to avoid timing attacks */
+
+  ret = - (int) if_int(bad, - ESP32C3_ERR_RSA_INVALID_PADDING,
+          if_int(output_too_large, - ESP32C3_ERR_RSA_OUTPUT_TOO_LARGE,
+              0));
+
+  /* If the padding is bad or the plaintext is too large, zero the data */
+
+  bad = all_or_nothing_int(bad | output_too_large);
+  for (i = 11; i < ilen; i++)
+    buf[i] &= ~bad;
+
+  /* If the plaintext is too large, truncate it to the buffer size. */
+
+  plaintext_size = if_int(output_too_large,
+               (unsigned) plaintext_max_size,
+               (unsigned) plaintext_size);
+
+  /* Move the plaintext to the leftmost position */
+
+  mem_move_to_left(buf + ilen - plaintext_max_size,
+            plaintext_max_size,
+            plaintext_max_size - plaintext_size);
+
+  /* Copy the decrypted plaintext into the output buffer */
+
+  memcpy(output, buf + ilen - plaintext_max_size, plaintext_max_size);
+
+  /* Report the amount of data we copied to the output buffer */
+
+  *olen = plaintext_size;
+
+cleanup:
+  memset(buf, 0, sizeof(buf));
+
+  return ret;
+}
+#endif /* ESP32C3_PKCS1_V15 */
+
+/****************************************************************************
+ * Name: esp32c3_rsa_deduce_primes
+ *
+ * Description:
+ *   Compute RSA prime moduli P, Q from public modulus N=PQ
+ *   and a pair of private and public key.
+ *
+ * Input Parameters:
+ *   N        - RSA modulus N = PQ, with P, Q to be found
+ *   E        - RSA public exponent
+ *   D        - RSA private exponent
+ *   P        - Pointer to MPI holding first prime factor of N on success
+ *   Q        - Pointer to MPI holding second prime factor of N on success
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int esp32c3_rsa_deduce_primes(struct esp32c3_mpi_s const *N,
+           struct esp32c3_mpi_s const *E, struct esp32c3_mpi_s const *D,
+           struct esp32c3_mpi_s *P, struct esp32c3_mpi_s *Q)
+{
+  int ret = 0;
+
+  uint16_t attempt;  /* Number of current attempt  */
+  uint16_t iter;     /* Number of squares computed in the current attempt */
+
+  uint16_t order;    /* Order of 2 in DE - 1 */
+
+  struct esp32c3_mpi_s T;     /* Holds largest odd divisor of DE - 1   */
+  struct esp32c3_mpi_s K;     /* Temporary holding the current candidate */
+
+  const unsigned char primes[] =
+  {
+    2, 3,  5,  7,   11,   13,   17,   19,   23,
+    29,   31,   37,   41,   43,   47,   53,   59,
+    61,   67,   71,   73,   79,   83,   89,   97,
+    101,  103,  107,  109,  113,  127,  131,  137,
+    139,  149,  151,  157,  163,  167,  173,  179,
+    181,  191,  193,  197,  199,  211,  223,  227,
+    229,  233,  239,  241,  251
+  };
+
+  const size_t num_primes = sizeof(primes) / sizeof(*primes);
+
+  if (P == NULL || Q == NULL || P->p != NULL || Q->p != NULL)
+    {
+      return (ESP32C3_ERR_MPI_BAD_INPUT_DATA);
+    }
+
+  if (esp32c3_mpi_cmp_int(N, 0) <= 0 ||
+      esp32c3_mpi_cmp_int(D, 1) <= 0 ||
+      esp32c3_mpi_cmp_mpi(D, N) >= 0 ||
+      esp32c3_mpi_cmp_int(E, 1) <= 0 ||
+      esp32c3_mpi_cmp_mpi(E, N) >= 0)
+    {
+      return (ESP32C3_ERR_MPI_BAD_INPUT_DATA);
+    }
+
+  /* Initializations and temporary changes */
+
+  esp32c3_mpi_init(&K);
+  esp32c3_mpi_init(&T);
+
+  /* T := DE - 1 */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&T, D,  E), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&T, &T, 1), cleanup);
+
+  if ((order = (uint16_t) esp32c3_mpi_lsb(&T)) == 0)
+    {
+      ret = ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+      goto cleanup;
+    }
+
+  /* After this operation, T holds the largest odd divisor of DE - 1. */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_shift_r(&T, order), cleanup);
+
+  /* Skip trying 2 if N == 1 mod 8 */
+
+  attempt = 0;
+  if (N->p[0] % 8 == 1)
+    {
+        attempt = 1;
+    }
+
+  for (; attempt < num_primes; ++attempt)
+    {
+      esp32c3_mpi_lset(&K, primes[attempt]);
+
+      /* Check if gcd(K,N) = 1 */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_gcd(P, &K, N), cleanup);
+      if (esp32c3_mpi_cmp_int(P, 1) != 0)
+        {
+          continue;
+        }
+
+      /* Go through K^T + 1, K^(2T) + 1, K^(4T) + 1, ... */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&K, &K, &T, N, Q), cleanup);
+
+      for (iter = 1; iter <= order; ++iter)
+        {
+          /* Continuing to square K */
+
+          if (esp32c3_mpi_cmp_int(&K, 1) == 0)
+            {
+              break;
+            }
+
+          ESP32C3_MPI_CHK(esp32c3_mpi_add_int(&K, &K, 1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_gcd(P, &K, N), cleanup);
+
+          if (esp32c3_mpi_cmp_int(P, 1) ==  1 &&
+              esp32c3_mpi_cmp_mpi(P, N) == -1)
+            {
+              /* Q := N / P */
+
+              ESP32C3_MPI_CHK(esp32c3_mpi_div_mpi(Q, NULL, N, P), cleanup);
+              goto cleanup;
+            }
+
+          ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, &K, 1), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&K, &K, &K), cleanup);
+          ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&K, &K, N), cleanup);
+        }
+
+      if (esp32c3_mpi_cmp_int(&K, 1) != 0)
+        {
+          break;
+        }
+    }
+
+  ret = ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+
+cleanup:
+
+  esp32c3_mpi_free(&K);
+  esp32c3_mpi_free(&T);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_deduce_private_exponent
+ *
+ * Description:
+ *   Compute RSA private exponent from prime moduli and public key.
+ *
+ * Input Parameters:
+ *   P        First prime factor of RSA modulus
+ *   Q        Second prime factor of RSA modulus
+ *   E        RSA public exponent
+ *   D        Pointer to MPI holding the private exponent on success.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int esp32c3_rsa_deduce_private_exponent(struct esp32c3_mpi_s const *P,
+                     struct esp32c3_mpi_s const *Q,
+                     struct esp32c3_mpi_s const *E,
+                     struct esp32c3_mpi_s *D)
+{
+  int ret = 0;
+  struct esp32c3_mpi_s K;
+  struct esp32c3_mpi_s L;
+
+  if (D == NULL || esp32c3_mpi_cmp_int(D, 0) != 0)
+    {
+      return (ESP32C3_ERR_MPI_BAD_INPUT_DATA);
+    }
+
+  if (esp32c3_mpi_cmp_int(P, 1) <= 0 ||
+      esp32c3_mpi_cmp_int(Q, 1) <= 0 ||
+      esp32c3_mpi_cmp_int(E, 0) == 0)
+    {
+      return (ESP32C3_ERR_MPI_BAD_INPUT_DATA);
+    }
+
+  esp32c3_mpi_init(&K);
+  esp32c3_mpi_init(&L);
+
+  /* Temporarily put K := P-1 and L := Q-1 */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, P, 1), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&L, Q, 1), cleanup);
+
+  /* Temporarily put D := gcd(P-1, Q-1) */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_gcd(D, &K, &L), cleanup);
+
+  /* K := LCM(P-1, Q-1) */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&K, &K, &L), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_div_mpi(&K, NULL, &K, D), cleanup);
+
+  /* Compute modular inverse of E in LCM(P-1, Q-1) */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_inv_mod(D, E, &K), cleanup);
+
+cleanup:
+
+  esp32c3_mpi_free(&K);
+  esp32c3_mpi_free(&L);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_validate_crt
+ *
+ * Description:
+ *   Check validity of core RSA parameters
+ *
+ * Input Parameters:
+ *   P        - First prime factor of RSA modulus
+ *   Q        - Second prime factor of RSA modulus
+ *   D        - RSA private exponent
+ *   DP       - MPI to check for D modulo P-1
+ *   DQ       - MPI to check for D modulo P-1
+ *   QP       - MPI to check for the modular inverse of Q modulo P.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int esp32c3_rsa_validate_crt(
+        const struct esp32c3_mpi_s *P,
+        const struct esp32c3_mpi_s *Q,
+        const struct esp32c3_mpi_s *D,
+        const struct esp32c3_mpi_s *DP,
+        const struct esp32c3_mpi_s *DQ,
+        const struct esp32c3_mpi_s *QP)
+{
+  int ret = 0;
+
+  struct esp32c3_mpi_s K;
+  struct esp32c3_mpi_s L;
+
+  esp32c3_mpi_init(&K);
+  esp32c3_mpi_init(&L);
+
+  /* Check that DP - D == 0 mod P - 1 */
+
+  if (DP != NULL)
+    {
+      if (P == NULL)
+        {
+          ret = ESP32C3_ERR_RSA_BAD_INPUT_DATA;
+          goto cleanup;
+        }
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, P, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&L, DP, D), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&L, &L, &K), cleanup);
+
+      if (esp32c3_mpi_cmp_int(&L, 0) != 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+    }
+
+  /* Check that DQ - D == 0 mod Q - 1 */
+
+  if (DQ != NULL)
+    {
+      if (Q == NULL)
+        {
+          ret = ESP32C3_ERR_RSA_BAD_INPUT_DATA;
+          goto cleanup;
+        }
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, Q, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&L, DQ, D), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&L, &L, &K), cleanup);
+
+      if (esp32c3_mpi_cmp_int(&L, 0) != 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+    }
+
+  /* Check that QP * Q - 1 == 0 mod P */
+
+  if (QP != NULL)
+    {
+      if (P == NULL || Q == NULL)
+        {
+          ret = ESP32C3_ERR_RSA_BAD_INPUT_DATA;
+          goto cleanup;
+        }
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&K, QP, Q), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, &K, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&K, &K, P), cleanup);
+      if (esp32c3_mpi_cmp_int(&K, 0) != 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+    }
+
+cleanup:
+
+  /* Wrap MPI error codes by RSA check failure error code */
+
+  if (ret != 0 &&
+      ret != ESP32C3_ERR_RSA_KEY_CHECK_FAILED &&
+      ret != ESP32C3_ERR_RSA_BAD_INPUT_DATA)
+    {
+      ret += ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+    }
+
+  esp32c3_mpi_free(&K);
+  esp32c3_mpi_free(&L);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_validate_params
+ *
+ * Description:
+ *   Check validity of core RSA parameters
+ *
+ * Input Parameters:
+ *   N        - RSA modulus N = PQ
+ *   P        - First prime factor of N
+ *   Q        - Second prime factor of N
+ *   D        - RSA private exponent
+ *   E        - RSA public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int esp32c3_rsa_validate_params(const struct esp32c3_mpi_s *N,
+                 const struct esp32c3_mpi_s *P,
+                 const struct esp32c3_mpi_s *Q,
+                 const struct esp32c3_mpi_s *D,
+                 const struct esp32c3_mpi_s *E,
+                 int (*f_rng)(void *, unsigned char *, size_t),
+                 void *p_rng)
+{
+  int ret = 0;
+  struct esp32c3_mpi_s K;
+  struct esp32c3_mpi_s L;
+
+  esp32c3_mpi_init(&K);
+  esp32c3_mpi_init(&L);
+
+  /* Step 1: Check that 1 < N = P * Q */
+
+  if (P != NULL && Q != NULL && N != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&K, P, Q), cleanup);
+      if (esp32c3_mpi_cmp_int(N, 1)  <= 0 ||
+          esp32c3_mpi_cmp_mpi(&K, N) != 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+    }
+
+  /* Step 2: Check and 1 < D, E < N if present */
+
+  if (N != NULL && D != NULL && E != NULL)
+    {
+      if (esp32c3_mpi_cmp_int(D, 1) <= 0 ||
+          esp32c3_mpi_cmp_int(E, 1) <= 0 ||
+          esp32c3_mpi_cmp_mpi(D, N) >= 0 ||
+          esp32c3_mpi_cmp_mpi(E, N) >= 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+    }
+
+  /* Step 3: Check that D, E are inverse modulo P-1 and Q-1 */
+
+  if (P != NULL && Q != NULL && D != NULL && E != NULL)
+    {
+      if (esp32c3_mpi_cmp_int(P, 1) <= 0 ||
+          esp32c3_mpi_cmp_int(Q, 1) <= 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+
+      /* Compute DE-1 mod P-1 */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&K, D, E), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, &K, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&L, P, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&K, &K, &L), cleanup);
+      if (esp32c3_mpi_cmp_int(&K, 0) != 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+
+      /* Compute DE-1 mod Q-1 */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&K, D, E), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, &K, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&L, Q, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&K, &K, &L), cleanup);
+      if (esp32c3_mpi_cmp_int(&K, 0) != 0)
+        {
+          ret = ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+          goto cleanup;
+        }
+    }
+
+cleanup:
+
+  esp32c3_mpi_free(&K);
+  esp32c3_mpi_free(&L);
+
+  /* Wrap MPI error codes by RSA check failure error code */
+
+  if (ret != 0 && ret != ESP32C3_ERR_RSA_KEY_CHECK_FAILED)
+    {
+      ret += ESP32C3_ERR_RSA_KEY_CHECK_FAILED;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_deduce_crt
+ *
+ * Description:
+ *   Generate RSA-CRT parameters
+ *
+ * Input Parameters:
+ *   P        - First prime factor of N
+ *   Q        - Second prime factor of N
+ *   D        - RSA private exponent
+ *   DP       - Output variable for D modulo P-1
+ *   DQ       - Output variable for D modulo Q-1
+ *   QP       - Output variable for the modular inverse of Q modulo P
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int esp32c3_rsa_deduce_crt(const struct esp32c3_mpi_s *P,
+                                  const struct esp32c3_mpi_s *Q,
+                                  const struct esp32c3_mpi_s *D,
+                                  struct esp32c3_mpi_s *DP,
+                                  struct esp32c3_mpi_s *DQ,
+                                  struct esp32c3_mpi_s *QP)
+{
+  int ret = 0;
+  struct esp32c3_mpi_s K;
+  esp32c3_mpi_init(&K);
+
+  /* DP = D mod P-1 */
+
+  if (DP != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, P, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(DP, D, &K), cleanup);
+    }
+
+  /* DQ = D mod Q-1 */
+
+  if (DQ != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&K, Q, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(DQ, D, &K), cleanup);
+    }
+
+  /* QP = Q^{-1} mod P */
+
+  if (QP != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_inv_mod(QP, Q, P), cleanup);
+    }
+
+cleanup:
+  esp32c3_mpi_free(&K);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_rsa_import
+ *
+ * Description:
+ *   Imports a set of core parameters into an RSA context.
+ *
+ * Input Parameters:
+ *   ctx  - The initialized RSA context to store the parameters in
+ *   N    - The RSA modulus
+ *   P    - The first prime factor of \p N
+ *   Q    - The second prime factor of \p N
+ *   D    - The private exponent
+ *   E    - The public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_import(struct esp32c3_rsa_context_s *ctx,
+            const struct esp32c3_mpi_s *N,
+            const struct esp32c3_mpi_s *P, const struct esp32c3_mpi_s *Q,
+            const struct esp32c3_mpi_s *D, const struct esp32c3_mpi_s *E)
+{
+  int ret;
+  DEBUGASSERT(ctx != NULL);
+
+  if ((N != NULL && (ret = esp32c3_mpi_copy(&ctx->N, N)) != 0) ||
+     (P != NULL && (ret = esp32c3_mpi_copy(&ctx->P, P)) != 0) ||
+     (Q != NULL && (ret = esp32c3_mpi_copy(&ctx->Q, Q)) != 0) ||
+     (D != NULL && (ret = esp32c3_mpi_copy(&ctx->D, D)) != 0) ||
+     (E != NULL && (ret = esp32c3_mpi_copy(&ctx->E, E)) != 0))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+    }
+
+  if (N != NULL)
+    {
+      ctx->len = esp32c3_mpi_size(&ctx->N);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_import_raw
+ *
+ * Description:
+ *   Imports core RSA parameters into an RSA context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to store the parameters in
+ *   N        - The RSA modulus
+ *   NL    - The Byte length of \p N
+ *   P        - The first prime factor of \p N
+ *   PL    - The Byte length of \p P
+ *   Q        - The second prime factor of \p N
+ *   QL    - The Byte length of \p Q
+ *   D        - The private exponent
+ *   DL    - The Byte length of \p D
+ *   E        - The public exponent
+ *   EL    - The Byte length of \p E
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_import_raw(struct esp32c3_rsa_context_s *ctx,
+              unsigned char const *N, size_t NL,
+              unsigned char const *P, size_t PL,
+              unsigned char const *Q, size_t QL,
+              unsigned char const *D, size_t DL,
+              unsigned char const *E, size_t EL)
+{
+  int ret = 0;
+  DEBUGASSERT(ctx != NULL);
+
+  if (N != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&ctx->N, N, NL), cleanup);
+      ctx->len = esp32c3_mpi_size(&ctx->N);
+    }
+
+  if (P != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&ctx->P, P, PL), cleanup);
+    }
+
+  if (Q != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&ctx->Q, Q, QL), cleanup);
+    }
+
+  if (D != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&ctx->D, D, DL), cleanup);
+    }
+
+  if (E != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&ctx->E, E, EL), cleanup);
+    }
+
+cleanup:
+
+  if (ret != 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_complete
+ *
+ * Description:
+ *   Completes an RSA context from a set of imported core parameters.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context holding imported parameters
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_complete(struct esp32c3_rsa_context_s *ctx)
+{
+  int ret = 0;
+  int have_n;
+  int have_p;
+  int have_q;
+  int have_d;
+  int have_e;
+#if !defined(ESP32C3_RSA_NO_CRT)
+  int have_dp;
+  int have_dq;
+  int have_qp;
+#endif
+  int n_missing;
+  int pq_missing;
+  int d_missing;
+  int is_pub;
+  int is_priv;
+
+  DEBUGASSERT(ctx != NULL);
+
+  have_n = (esp32c3_mpi_cmp_int(&ctx->N, 0) != 0);
+  have_p = (esp32c3_mpi_cmp_int(&ctx->P, 0) != 0);
+  have_q = (esp32c3_mpi_cmp_int(&ctx->Q, 0) != 0);
+  have_d = (esp32c3_mpi_cmp_int(&ctx->D, 0) != 0);
+  have_e = (esp32c3_mpi_cmp_int(&ctx->E, 0) != 0);
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  have_dp = (esp32c3_mpi_cmp_int(&ctx->DP, 0) != 0);
+  have_dq = (esp32c3_mpi_cmp_int(&ctx->DQ, 0) != 0);
+  have_qp = (esp32c3_mpi_cmp_int(&ctx->QP, 0) != 0);
+#endif
+
+  /* Check whether provided parameters are enough */
+
+  n_missing  =  have_p &&  have_q &&  have_d && have_e;
+  pq_missing =  have_n && !have_p && !have_q &&  have_d && have_e;
+  d_missing  =  have_p &&  have_q && !have_d && have_e;
+  is_pub     =  have_n && !have_p && !have_q && !have_d && have_e;
+
+  /* These three alternatives are mutually exclusive */
+
+  is_priv = n_missing || pq_missing || d_missing;
+
+  if (!is_priv && !is_pub)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  /* Step 1: Deduce N if P, Q are provided */
+
+  if (!have_n && have_p && have_q)
+    {
+      if ((ret = esp32c3_mpi_mul_mpi(&ctx->N, &ctx->P,
+                      &ctx->Q)) != 0)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+        }
+
+      ctx->len = esp32c3_mpi_size(&ctx->N);
+    }
+
+  /* Step 2: Deduce and verify all remaining core parameters */
+
+  if (pq_missing)
+    {
+      ret = esp32c3_rsa_deduce_primes(&ctx->N, &ctx->E, &ctx->D,
+                                      &ctx->P, &ctx->Q);
+      if (ret != 0)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+        }
+    }
+  else if (d_missing)
+    {
+      if ((ret = esp32c3_rsa_deduce_private_exponent(&ctx->P, &ctx->Q,
+                                                     &ctx->E, &ctx->D)) != 0)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+        }
+    }
+
+  /* Step 3: Deduce all additional parameters to RSA implementation */
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  if (is_priv && ! (have_dp && have_dq && have_qp))
+    {
+      ret = esp32c3_rsa_deduce_crt(&ctx->P, &ctx->Q, &ctx->D,
+                                   &ctx->DP, &ctx->DQ, &ctx->QP);
+      if (ret != 0)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+        }
+    }
+#endif /* ESP32C3_RSA_NO_CRT */
+
+  /* Step 3: Basic sanity checks */
+
+  return (rsa_check_context(ctx, is_priv, 1));
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_export_raw
+ *
+ * Description:
+ *   Eexports core parameters of an RSA key in raw big-endian binary format.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *   N        - The Byte array to store the RSA modulus
+ *   NL    - The size of the buffer for the modulus
+ *   P        - The Byte array to hold the first prime factor of \p N
+ *   PL    - The size of the buffer for the first prime factor
+ *   Q        - The Byte array to hold the second prime factor of \p N
+ *   QL    - The size of the buffer for the second prime factor
+ *   D        - The Byte array to hold the private exponent
+ *   DL    - The size of the buffer for the private exponent
+ *   E        - The Byte array to hold the public exponent
+ *   EL    - The size of the buffer for the public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_export_raw(const struct esp32c3_rsa_context_s *ctx,
+              unsigned char *N, size_t NL,
+              unsigned char *P, size_t PL,
+              unsigned char *Q, size_t QL,
+              unsigned char *D, size_t DL,
+              unsigned char *E, size_t EL)
+{
+  int ret = 0;
+  int is_priv;
+  DEBUGASSERT(ctx != NULL);
+
+  /* Check if key is private or public */
+
+  is_priv = esp32c3_mpi_cmp_int(&ctx->N, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->P, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->Q, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->D, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->E, 0) != 0;
+
+  if (!is_priv)
+    {
+      /* It can't try to export private parameters for a public key */
+
+      if (P != NULL || Q != NULL || D != NULL)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+        }
+    }
+
+  if (N != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&ctx->N, N, NL), cleanup);
+    }
+
+  if (P != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&ctx->P, P, PL), cleanup);
+    }
+
+  if (Q != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&ctx->Q, Q, QL), cleanup);
+    }
+
+  if (D != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&ctx->D, D, DL), cleanup);
+    }
+
+  if (E != NULL)
+    {
+      ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&ctx->E, E, EL), cleanup);
+    }
+
+cleanup:
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_export
+ *
+ * Description:
+ *   Exports the core parameters of an RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *   N        - The MPI to hold the RSA modulus
+ *   P        - The MPI to hold the first prime factor of \p N
+ *   Q        - The MPI to hold the second prime factor of \p N
+ *   D        - The MPI to hold the private exponent
+ *   E        - The MPI to hold the public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_export(const struct esp32c3_rsa_context_s *ctx,
+                       struct esp32c3_mpi_s *N,
+                       struct esp32c3_mpi_s *P,
+                       struct esp32c3_mpi_s *Q,
+                       struct esp32c3_mpi_s *D,
+                       struct esp32c3_mpi_s *E)
+{
+  int ret;
+  int is_priv;
+  DEBUGASSERT(ctx != NULL);
+
+  /* Check if key is private or public */
+
+  is_priv =
+    esp32c3_mpi_cmp_int(&ctx->N, 0) != 0 &&
+    esp32c3_mpi_cmp_int(&ctx->P, 0) != 0 &&
+    esp32c3_mpi_cmp_int(&ctx->Q, 0) != 0 &&
+    esp32c3_mpi_cmp_int(&ctx->D, 0) != 0 &&
+    esp32c3_mpi_cmp_int(&ctx->E, 0) != 0;
+
+  if (!is_priv)
+    {
+      /* It can't try to export private parameters for a public key */
+
+      if (P != NULL || Q != NULL || D != NULL)
+        {
+          return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+        }
+    }
+
+  /* Export all requested core parameters. */
+
+  if ((N != NULL && (ret = esp32c3_mpi_copy(N, &ctx->N)) != 0) ||
+      (P != NULL && (ret = esp32c3_mpi_copy(P, &ctx->P)) != 0) ||
+      (Q != NULL && (ret = esp32c3_mpi_copy(Q, &ctx->Q)) != 0) ||
+      (D != NULL && (ret = esp32c3_mpi_copy(D, &ctx->D)) != 0) ||
+      (E != NULL && (ret = esp32c3_mpi_copy(E, &ctx->E)) != 0))
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_export_crt
+ *
+ * Description:
+ *   Exports CRT parameters of a private RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *   DP       - The MPI to hold \c D modulo `P-1`
+ *   DQ       - The MPI to hold \c D modulo `Q-1`
+ *   QP       - The MPI to hold modular inverse of \c Q modulo \c P
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_export_crt(const struct esp32c3_rsa_context_s *ctx,
+                           struct esp32c3_mpi_s *DP,
+                           struct esp32c3_mpi_s *DQ,
+                           struct esp32c3_mpi_s *QP)
+{
+  int ret;
+  int is_priv;
+  DEBUGASSERT(ctx != NULL);
+
+  /* Check if key is private or public */
+
+  is_priv = esp32c3_mpi_cmp_int(&ctx->N, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->P, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->Q, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->D, 0) != 0 &&
+            esp32c3_mpi_cmp_int(&ctx->E, 0) != 0;
+
+  if (!is_priv)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  /* Export all requested blinding parameters. */
+
+  if ((DP != NULL && (ret = esp32c3_mpi_copy(DP, &ctx->DP)) != 0) ||
+      (DQ != NULL && (ret = esp32c3_mpi_copy(DQ, &ctx->DQ)) != 0) ||
+      (QP != NULL && (ret = esp32c3_mpi_copy(QP, &ctx->QP)) != 0))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+    }
+#else
+  if ((ret = esp32c3_rsa_deduce_crt(&ctx->P, &ctx->Q, &ctx->D,
+                                    DP, DQ, QP)) != 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA + ret);
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_init
+ *
+ * Description:
+ *   Initializes an RSA context
+ *
+ * Input Parameters:
+ *   ctx      - The RSA context to initialize
+ *   padding  - The padding mode to use
+ *   hash_id  - The hash identifier of
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_rsa_init(struct esp32c3_rsa_context_s *ctx,
+         int padding,
+         int hash_id)
+{
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(padding == ESP32C3_RSA_PKCS_V15 ||
+              padding == ESP32C3_RSA_PKCS_V21);
+
+  memset(ctx, 0, sizeof(struct esp32c3_rsa_context_s));
+
+  esp32c3_rsa_set_padding(ctx, padding, hash_id);
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_set_padding
+ *
+ * Description:
+ *   Sets padding for an already initialized RSA context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to be configured
+ *   padding  - The padding mode to use
+ *   hash_id  - The hash identifier
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_rsa_set_padding(struct esp32c3_rsa_context_s *ctx,
+                             int padding, int hash_id)
+{
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(padding == ESP32C3_RSA_PKCS_V15 ||
+              padding == ESP32C3_RSA_PKCS_V21);
+
+  ctx->padding = padding;
+  ctx->hash_id = hash_id;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_get_len
+ *
+ * Description:
+ *   Exports CRT parameters of a private RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *
+ * Returned Value:
+ *   length of the RSA modulus in Bytes.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_rsa_get_len(const struct esp32c3_rsa_context_s *ctx)
+{
+  return (ctx->len);
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_check_pubkey
+ *
+ * Description:
+ *   checks if a context contains at least an RSA public key..
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to check
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_check_pubkey(const struct esp32c3_rsa_context_s *ctx)
+{
+  DEBUGASSERT(ctx != NULL);
+
+  if (rsa_check_context(ctx, 0 /* public */, 0 /* no blinding */) != 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+  if (esp32c3_mpi_bitlen(&ctx->N) < 128)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+  if (esp32c3_mpi_get_bit(&ctx->E, 0) == 0 ||
+      esp32c3_mpi_bitlen(&ctx->E)   < 2  ||
+      esp32c3_mpi_cmp_mpi(&ctx->E, &ctx->N) >= 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_check_privkey
+ *
+ * Description:
+ *   Checks if a context contains at least an RSA private key
+ *   and perform basic consistency checks.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to check
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_check_privkey(const struct esp32c3_rsa_context_s *ctx)
+{
+  DEBUGASSERT(ctx != NULL);
+
+  if (esp32c3_rsa_check_pubkey(ctx) != 0 ||
+      rsa_check_context(ctx, 1 /* private */, 1 /* blinding */) != 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+  if (esp32c3_rsa_validate_params(&ctx->N, &ctx->P, &ctx->Q,
+                                  &ctx->D, &ctx->E, NULL, NULL) != 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  else if (esp32c3_rsa_validate_crt(&ctx->P, &ctx->Q, &ctx->D,
+                                    &ctx->DP, &ctx->DQ, &ctx->QP) != 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_check_pub_priv
+ *
+ * Description:
+ *   Checks a public-private RSA key pair. It checks each of the contexts,
+ *   and makes sure they match.
+ *
+ * Input Parameters:
+ *   pub      - The initialized RSA context holding the public key
+ *   prv      - The initialized RSA context holding the private key
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_check_pub_priv(const struct esp32c3_rsa_context_s *pub,
+                const struct esp32c3_rsa_context_s *prv)
+{
+  DEBUGASSERT(pub != NULL);
+  DEBUGASSERT(prv != NULL);
+
+  if (esp32c3_rsa_check_pubkey(pub)  != 0 ||
+      esp32c3_rsa_check_privkey(prv) != 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+  if (esp32c3_mpi_cmp_mpi(&pub->N, &prv->N) != 0 ||
+      esp32c3_mpi_cmp_mpi(&pub->E, &prv->E) != 0)
+    {
+      return (ESP32C3_ERR_RSA_KEY_CHECK_FAILED);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_public
+ *
+ * Description:
+ *   Performs an RSA public key operation.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to use
+ *   input    - The input buffer
+ *   output   - The output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_public(struct esp32c3_rsa_context_s *ctx,
+        const unsigned char *input,
+        unsigned char *output)
+{
+  int ret;
+  size_t olen;
+  struct esp32c3_mpi_s T;
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(input != NULL);
+  DEBUGASSERT(output != NULL);
+
+  if (rsa_check_context(ctx, 0 /* public */, 0 /* no blinding */))
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  esp32c3_mpi_init(&T);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&T, input, ctx->len), cleanup);
+
+  if (esp32c3_mpi_cmp_mpi(&T, &ctx->N) >= 0)
+    {
+      ret = ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+      goto cleanup;
+    }
+
+  olen = ctx->len;
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&T, &T, &ctx->E, &ctx->N, &ctx->RN),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&T, output, olen), cleanup);
+
+cleanup:
+  esp32c3_mpi_free(&T);
+
+  if (ret != 0)
+    {
+      return (ESP32C3_ERR_RSA_PUBLIC_FAILED + ret);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_private
+ *
+ * Description:
+ *   Performs an RSA private key operation.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to use
+ *   f_rng    - The RNG function
+ *   p_rng    - The RNG context to pass to \p f_rng
+ *   input    - The input buffer
+ *   output   - The output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_private(struct esp32c3_rsa_context_s *ctx,
+         int (*f_rng)(void *, unsigned char *, size_t),
+         void *p_rng,
+         const unsigned char *input,
+         unsigned char *output)
+{
+  int ret;
+  size_t olen;
+
+  /* Temporary holding the result */
+
+  struct esp32c3_mpi_s T;
+
+  /* Temporaries holding P-1, Q-1 and the exponent blinding factor */
+
+  struct esp32c3_mpi_s P1, Q1, R;
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  /* Temporaries holding the results mod p resp. mod q. */
+
+  struct esp32c3_mpi_s TP, TQ;
+
+  /* Temporaries holding the blinded exponents */
+
+  struct esp32c3_mpi_s dp_blind, dq_blind;
+
+  /* Pointers to actual exponents to be used */
+
+  struct esp32c3_mpi_s *DP = &ctx->DP;
+  struct esp32c3_mpi_s *DQ = &ctx->DQ;
+#else
+  /* Temporary holding the blinded exponent */
+
+  struct esp32c3_mpi_s d_blind;
+
+  /* Pointer to actual exponent to be used */
+
+  struct esp32c3_mpi_s *D = &ctx->D;
+#endif /* ESP32C3_RSA_NO_CRT */
+
+  /* Temporaries holding the initial input */
+
+  struct esp32c3_mpi_s I, C;
+
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(input  != NULL);
+  DEBUGASSERT(output != NULL);
+
+  if (rsa_check_context(ctx, 1, f_rng != NULL) != 0)
+    {
+      return (ESP32C3_ERR_RSA_BAD_INPUT_DATA);
+    }
+
+  /* MPI Initialization */
+
+  esp32c3_mpi_init(&T);
+
+  esp32c3_mpi_init(&P1);
+  esp32c3_mpi_init(&Q1);
+  esp32c3_mpi_init(&R);
+
+  if (f_rng != NULL)
+    {
+#if defined(ESP32C3_RSA_NO_CRT)
+      esp32c3_mpi_init(&d_blind);
+#else
+      esp32c3_mpi_init(&dp_blind);
+      esp32c3_mpi_init(&dq_blind);
+#endif
+    }
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  esp32c3_mpi_init(&TP); esp32c3_mpi_init(&TQ);
+#endif
+
+  esp32c3_mpi_init(&I);
+  esp32c3_mpi_init(&C);
+
+  /* End of MPI initialization */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_binary(&T, input, ctx->len), cleanup);
+  if (esp32c3_mpi_cmp_mpi(&T, &ctx->N) >= 0)
+    {
+      ret = ESP32C3_ERR_MPI_BAD_INPUT_DATA;
+      goto cleanup;
+    }
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&I, &T), cleanup);
+
+  if (f_rng != NULL)
+    {
+      /* Blinding T = T * VI mod N */
+
+      ESP32C3_MPI_CHK(rsa_prepare_blinding(ctx, f_rng, p_rng), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&T, &T, &ctx->VI), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&T, &T, &ctx->N), cleanup);
+
+      /* Exponent blinding */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&P1, &ctx->P, 1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_sub_int(&Q1, &ctx->Q, 1), cleanup);
+
+#if defined(ESP32C3_RSA_NO_CRT)
+      /* d_blind = (P - 1) * (Q - 1) * R + D */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_fill_random(&R, RSA_EXPONENT_BLINDING,
+                      f_rng, p_rng), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&d_blind, &P1, &Q1), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&d_blind, &d_blind, &R), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&d_blind, &d_blind, &ctx->D),
+                      cleanup);
+
+      D = &d_blind;
+#else
+      /* dp_blind = (P - 1) * R + DP */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_fill_random(&R, RSA_EXPONENT_BLINDING,
+                      f_rng, p_rng), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&dp_blind, &P1, &R), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&dp_blind, &dp_blind,
+                      &ctx->DP), cleanup);
+
+      DP = &dp_blind;
+
+      /* dq_blind = (Q - 1) * R + DQ */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_fill_random(&R, RSA_EXPONENT_BLINDING,
+                      f_rng, p_rng), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&dq_blind, &Q1, &R), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&dq_blind, &dq_blind,
+                      &ctx->DQ), cleanup);
+
+      DQ = &dq_blind;
+#endif /* ESP32C3_RSA_NO_CRT */
+    }
+
+#if defined(ESP32C3_RSA_NO_CRT)
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&T, &T, D, &ctx->N, &ctx->RN),
+                  cleanup);
+#else
+  /* TP = input ^ dP mod P and TQ = input ^ dQ mod Q */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&TP, &T, DP, &ctx->P, &ctx->RP),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&TQ, &T, DQ, &ctx->Q, &ctx->RQ),
+                  cleanup);
+
+  /* T = (TP - TQ) * (Q^-1 mod P) mod P */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_sub_mpi(&T, &TP, &TQ), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&TP, &T, &ctx->QP), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&T, &TP, &ctx->P), cleanup);
+
+  /* T = TQ + T * Q */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&TP, &T, &ctx->Q), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_add_mpi(&T, &TQ, &TP), cleanup);
+#endif /* ESP32C3_RSA_NO_CRT */
+
+  if (f_rng != NULL)
+    {
+      /* Unblind T = T * VF mod N */
+
+      ESP32C3_MPI_CHK(esp32c3_mpi_mul_mpi(&T, &T, &ctx->VF), cleanup);
+      ESP32C3_MPI_CHK(esp32c3_mpi_mod_mpi(&T, &T, &ctx->N), cleanup);
+    }
+
+  /* Verify the result to prevent glitching attacks. */
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_exp_mod(&C, &T, &ctx->E,
+                  &ctx->N, &ctx->RN), cleanup);
+  if (esp32c3_mpi_cmp_mpi(&C, &I) != 0)
+    {
+      ret = ESP32C3_ERR_RSA_VERIFY_FAILED;
+      goto cleanup;
+    }
+
+  olen = ctx->len;
+  ESP32C3_MPI_CHK(esp32c3_mpi_write_binary(&T, output, olen), cleanup);
+
+cleanup:
+  esp32c3_mpi_free(&P1);
+  esp32c3_mpi_free(&Q1);
+  esp32c3_mpi_free(&R);
+
+  if (f_rng != NULL)
+    {
+#if defined(ESP32C3_RSA_NO_CRT)
+      esp32c3_mpi_free(&d_blind);
+#else
+      esp32c3_mpi_free(&dp_blind);
+      esp32c3_mpi_free(&dq_blind);
+#endif
+    }
+
+  esp32c3_mpi_free(&T);
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  esp32c3_mpi_free(&TP); esp32c3_mpi_free(&TQ);
+#endif
+
+  esp32c3_mpi_free(&C);
+  esp32c3_mpi_free(&I);
+
+  if (ret != 0)
+    {
+      return (ESP32C3_ERR_RSA_PRIVATE_FAILED + ret);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_encrypt
+ *
+ * Description:
+ *   Adds the message padding, then performs an RSA operation. It is the
+ *   generic wrapper for performing a PKCS#1 encryption operation using the
+ *   \p mode from the context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to use
+ *   f_rng    - The RNG to use
+ *   p_rng    - The RNG context to be passed to \p f_rng
+ *   mode     - The mode of operation
+ *   ilen     - The length of the plaintext in Bytes
+ *   input    - The input data to encrypt
+ *   output   - The output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_encrypt(struct esp32c3_rsa_context_s *ctx,
+             int (*f_rng)(void *, unsigned char *, size_t),
+             void *p_rng,
+             int mode, size_t ilen,
+             const unsigned char *input,
+             unsigned char *output)
+{
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(mode == ESP32C3_RSA_PRIVATE ||
+              mode == ESP32C3_RSA_PUBLIC);
+  DEBUGASSERT(output != NULL);
+  DEBUGASSERT(input != NULL);
+
+  switch (ctx->padding)
+    {
+#if defined(ESP32C3_PKCS1_V15)
+      case ESP32C3_RSA_PKCS_V15:
+        return esp32c3_rsa_pkcs1_v15_encrypt(ctx, f_rng, p_rng, mode, ilen,
+                                             input, output);
+#endif
+      default:
+        return (ESP32C3_ERR_RSA_INVALID_PADDING);
+    }
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_decrypt
+ *
+ * Description:
+ *   Performs an RSA operation, then removes the message padding.
+ *
+ * Input Parameters:
+ *   ctx            - The initialized RSA context to use
+ *   f_rng          - The RNG function
+ *   p_rng          - The RNG context to be passed to \p f_rng
+ *   mode           - The mode of operation
+ *   olen           - The point which to store the length of the plaintext
+ *   input          - The ciphertext buffer
+ *   output         - The buffer used to hold the plaintext
+ *   output_max_len - The length in Bytes of the output buffer \p output
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_decrypt(struct esp32c3_rsa_context_s *ctx,
+             int (*f_rng)(void *, unsigned char *, size_t),
+             void *p_rng,
+             int mode, size_t *olen,
+             const unsigned char *input,
+             unsigned char *output,
+             size_t output_max_len)
+{
+  DEBUGASSERT(ctx != NULL);
+  DEBUGASSERT(mode == ESP32C3_RSA_PRIVATE ||
+              mode == ESP32C3_RSA_PUBLIC);
+  DEBUGASSERT(output_max_len == 0 || output != NULL);
+  DEBUGASSERT(input != NULL);
+  DEBUGASSERT(olen != NULL);
+
+  switch (ctx->padding)
+    {
+#if defined(ESP32C3_PKCS1_V15)
+      case ESP32C3_RSA_PKCS_V15:
+        return esp32c3_rsa_pkcs1_v15_decrypt(ctx, f_rng, p_rng, mode, olen,
+                                             input, output, output_max_len);
+#endif
+      default:
+        return (ESP32C3_ERR_RSA_INVALID_PADDING);
+    }
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_copy
+ *
+ * Description:
+ *   Copies the components of an RSA context.
+ *
+ * Input Parameters:
+ *   dst      - The destination context
+ *   src      - The source context
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_copy(struct esp32c3_rsa_context_s *dst,
+                     const struct esp32c3_rsa_context_s *src)
+{
+  int ret;
+  DEBUGASSERT(dst != NULL);
+  DEBUGASSERT(src != NULL);
+
+  dst->ver = src->ver;
+  dst->len = src->len;
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->N, &src->N), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->E, &src->E), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->D, &src->D), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->P, &src->P), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->Q, &src->Q), cleanup);
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->DP, &src->DP), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->DQ, &src->DQ), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->QP, &src->QP), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->RP, &src->RP), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->RQ, &src->RQ), cleanup);
+#endif
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->RN, &src->RN), cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->VI, &src->VI), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_copy(&dst->VF, &src->VF), cleanup);
+
+  dst->padding = src->padding;
+  dst->hash_id = src->hash_id;
+
+cleanup:
+  if (ret != 0)
+    {
+      esp32c3_rsa_free(dst);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_free
+ *
+ * Description:
+ *   Frees the components of an RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The RSA context to free
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_rsa_free(struct esp32c3_rsa_context_s *ctx)
+{
+  if (ctx == NULL)
+    {
+      return;
+    }
+
+  esp32c3_mpi_free(&ctx->VI);
+  esp32c3_mpi_free(&ctx->VF);
+  esp32c3_mpi_free(&ctx->RN);
+  esp32c3_mpi_free(&ctx->D);
+  esp32c3_mpi_free(&ctx->Q);
+  esp32c3_mpi_free(&ctx->P);
+  esp32c3_mpi_free(&ctx->E);
+  esp32c3_mpi_free(&ctx->N);
+
+#if !defined(ESP32C3_RSA_NO_CRT)
+  esp32c3_mpi_free(&ctx->RQ);
+  esp32c3_mpi_free(&ctx->RP);
+  esp32c3_mpi_free(&ctx->QP);
+  esp32c3_mpi_free(&ctx->DQ);
+  esp32c3_mpi_free(&ctx->DP);
+#endif /* ESP32C3_RSA_NO_CRT */
+}
+
+#endif
+
+/****************************************************************************
+ * Test Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32C3_RSA_ACCELERATOR_TEST
+
+/* Example RSA-1024 keypair, for test purposes */
+
+#define KEY_LEN 128
+
+#define RSA_N   "9292758453063D803DD603D5E777D788" \
+                "8ED1D5BF35786190FA2F23EBC0848AEA" \
+                "DDA92CA6C3D80B32C4D109BE0F36D6AE" \
+                "7130B9CED7ACDF54CFC7555AC14EEBAB" \
+                "93A89813FBF3C4F8066D2D800F7C38A8" \
+                "1AE31942917403FF4946B0A83D3D3E05" \
+                "EE57C6F5F5606FB5D4BC6CD34EE0801A" \
+                "5E94BB77B07507233A0BC7BAC8F90F79"
+
+#define RSA_E   "10001"
+
+#define RSA_D   "24BF6185468786FDD303083D25E64EFC" \
+                "66CA472BC44D253102F8B4A9D3BFA750" \
+                "91386C0077937FE33FA3252D28855837" \
+                "AE1B484A8A9A45F7EE8C0C634F99E8CD" \
+                "DF79C5CE07EE72C7F123142198164234" \
+                "CABB724CF78B8173B9F880FC86322407" \
+                "AF1FEDFDDE2BEB674CA15F3E81A1521E" \
+                "071513A1E85B5DFA031F21ECAE91A34D"
+
+#define RSA_P   "C36D0EB7FCD285223CFB5AABA5BDA3D8" \
+                "2C01CAD19EA484A87EA4377637E75500" \
+                "FCB2005C5C7DD6EC4AC023CDA285D796" \
+                "C3D9E75E1EFC42488BB4F1D13AC30A57"
+
+#define RSA_Q   "C000DF51A7C77AE8D7C7370C1FF55B69" \
+                "E211C2B9E5DB1ED0BF61D0D9899620F4" \
+                "910E4168387E3C30AA1E00C339A79508" \
+                "8452DD96A9A5EA5D9DCA68DA636032AF"
+
+#define PT_LEN  24
+#define RSA_PT  "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \
+                "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD"
+
+#if defined(ESP32C3_PKCS1_V15)
+static int myrand(void *rng_state, unsigned char *output, size_t len)
+{
+#if !defined(__OpenBSD__) && !defined(__NetBSD__)
+  size_t i;
+
+  if (rng_state != NULL)
+    rng_state  = NULL;
+
+  for (i = 0; i < len; ++i)
+    output[i] = rand();
+#else
+  if (rng_state != NULL)
+    rng_state = NULL;
+
+  arc4random_buf(output, len);
+#endif /* !OpenBSD && !NetBSD */
+
+  return OK;
+}
+#endif /* ESP32C3_PKCS1_V15 */
+
+/* Checkup routine */
+
+int esp32c3_rsa_self_test(int verbose)
+{
+  int ret = 0;
+#if defined(ESP32C3_PKCS1_V15)
+  size_t len;
+  struct esp32c3_rsa_context_s rsa;
+  unsigned char rsa_plaintext[PT_LEN];
+  unsigned char rsa_decrypted[PT_LEN];
+  unsigned char rsa_ciphertext[KEY_LEN];
+
+  struct esp32c3_mpi_s K;
+
+  esp32c3_mpi_init(&K);
+  esp32c3_rsa_init(&rsa, ESP32C3_RSA_PKCS_V15, 0);
+
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&K, 16, RSA_N), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_rsa_import(&rsa, &K, NULL, NULL, NULL, NULL),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&K, 16, RSA_P), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_rsa_import(&rsa, NULL, &K, NULL, NULL, NULL),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&K, 16, RSA_Q), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_rsa_import(&rsa, NULL, NULL, &K, NULL, NULL),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&K, 16, RSA_D), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_rsa_import(&rsa, NULL, NULL, NULL, &K, NULL),
+                  cleanup);
+  ESP32C3_MPI_CHK(esp32c3_mpi_read_string(&K, 16, RSA_E), cleanup);
+  ESP32C3_MPI_CHK(esp32c3_rsa_import(&rsa, NULL, NULL, NULL, NULL, &K),
+                  cleanup);
+
+  ESP32C3_MPI_CHK(esp32c3_rsa_complete(&rsa), cleanup);
+
+  if (verbose != 0)
+    syslog(LOG_INFO, "  RSA key validation: ");
+
+  if (esp32c3_rsa_check_pubkey(&rsa) != 0 ||
+    esp32c3_rsa_check_privkey(&rsa) != 0)
+    {
+      if (verbose != 0)
+        syslog(LOG_INFO, "failed\n");
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    syslog(LOG_INFO, "passed\n  PKCS#1 encryption : ");
+
+  memcpy(rsa_plaintext, RSA_PT, PT_LEN);
+
+  if (esp32c3_rsa_encrypt(&rsa, myrand, NULL, ESP32C3_RSA_PUBLIC,
+                   PT_LEN, rsa_plaintext,
+                   rsa_ciphertext) != 0)
+    {
+      if (verbose != 0)
+        syslog(LOG_INFO, "failed\n");
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    syslog(LOG_INFO, "passed\n  PKCS#1 decryption : ");
+
+  if (esp32c3_rsa_decrypt(&rsa, myrand, NULL, ESP32C3_RSA_PRIVATE,
+                   &len, rsa_ciphertext, rsa_decrypted,
+                   sizeof(rsa_decrypted)) != 0)
+    {
+      if (verbose != 0)
+        syslog(LOG_INFO, "failed\n");
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (memcmp(rsa_decrypted, rsa_plaintext, len) != 0)
+    {
+      if (verbose != 0)
+        syslog(LOG_INFO, "failed\n");
+
+      ret = 1;
+      goto cleanup;
+    }
+
+  if (verbose != 0)
+    syslog(LOG_INFO, "passed\n");
+
+cleanup:
+  esp32c3_mpi_free(&K);
+  esp32c3_rsa_free(&rsa);
+#else /* ESP32C3_PKCS1_V15 */
+  ((void) verbose);
+#endif /* ESP32C3_PKCS1_V15 */
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3_rsa_main
+ ****************************************************************************/
+
+int esp32c3_rsa_main(int argc, char *argv[])
+{
+  int ret = 0;
+
+  syslog(LOG_INFO, "----- BEGIN TEST -----\n");
+
+  ret = esp32c3_mpi_self_test(true);
+  if (ret)
+    {
+      goto test_end;
+    }
+
+  ret = esp32c3_rsa_self_test(true);
+  if (ret)
+    {
+      goto test_end;
+    }
+
+test_end:
+  syslog(LOG_INFO, "----- END TEST -----\n");
+
+  syslog(LOG_INFO, "\n");
+
+  syslog(LOG_INFO, "----- RESULT: %s -----\n",
+         !ret ? "SUCCESS" : "FAILED");
+
+  return 0;
+}
+
+#endif
diff --git a/arch/risc-v/src/esp32c3/esp32c3_rsa.h b/arch/risc-v/src/esp32c3/esp32c3_rsa.h
new file mode 100644
index 0000000..cba4d76
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/esp32c3_rsa.h
@@ -0,0 +1,513 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/esp32c3_rsa.h
+ *
+ * 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
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_ESP32C3_ESP32C3_RSA_H
+#define __ARCH_RISCV_SRC_ESP32C3_ESP32C3_RSA_H
+
+#include <nuttx/config.h>
+#include <stdint.h>
+#include "esp32c3_bignum.h"
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Pre-processor Macros
+ ****************************************************************************/
+
+/* RSA Error codes */
+
+#define ESP32C3_ERR_RSA_BAD_INPUT_DATA                    -0x4080  /**< Bad input parameters to function. */
+#define ESP32C3_ERR_RSA_INVALID_PADDING                   -0x4100  /**< Input data contains invalid padding and is rejected. */
+#define ESP32C3_ERR_RSA_KEY_GEN_FAILED                    -0x4180  /**< Something failed during generation of a key. */
+#define ESP32C3_ERR_RSA_KEY_CHECK_FAILED                  -0x4200  /**< Key failed to pass the validity check of the library. */
+#define ESP32C3_ERR_RSA_PUBLIC_FAILED                     -0x4280  /**< The public key operation failed. */
+#define ESP32C3_ERR_RSA_PRIVATE_FAILED                    -0x4300  /**< The private key operation failed. */
+#define ESP32C3_ERR_RSA_VERIFY_FAILED                     -0x4380  /**< The PKCS#1 verification failed. */
+#define ESP32C3_ERR_RSA_OUTPUT_TOO_LARGE                  -0x4400  /**< The output buffer for decryption is not large enough. */
+#define ESP32C3_ERR_RSA_RNG_FAILED                        -0x4480  /**< The random generator failed to generate non-zeros. */
+
+/* RSA constants */
+
+#define ESP32C3_RSA_PUBLIC      0 /**< Request private key operation. */
+#define ESP32C3_RSA_PRIVATE     1 /**< Request public key operation. */
+
+#define ESP32C3_RSA_PKCS_V15    0 /**< Use PKCS#1 v1.5 encoding. */
+#define ESP32C3_RSA_PKCS_V21    1 /**< Use PKCS#1 v2.1 encoding. */
+
+#define ESP32C3_RSA_SIGN        1 /**< Identifier for RSA signature operations. */
+#define ESP32C3_RSA_CRYPT       2 /**< Identifier for RSA encryption and decryption operations. */
+
+#define ESP32C3_RSA_SALT_LEN_ANY    -1
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/**
+ * \brief   - The RSA context structure.
+ */
+
+struct esp32c3_rsa_context_s
+{
+    int ver;                    /* Always 0 */
+    size_t len;                 /* The size of \p N in Bytes */
+
+    struct esp32c3_mpi_s N;              /* The public modulus */
+    struct esp32c3_mpi_s E;              /* The public exponent */
+
+    struct esp32c3_mpi_s D;              /* The private exponent */
+    struct esp32c3_mpi_s P;              /* The first prime factor */
+    struct esp32c3_mpi_s Q;              /* The second prime factor */
+
+    struct esp32c3_mpi_s DP;             /* <code>D % (P - 1)</code> */
+    struct esp32c3_mpi_s DQ;             /* <code>D % (Q - 1)</code> */
+    struct esp32c3_mpi_s QP;             /* <code>1 / (Q % P)</code> */
+
+    struct esp32c3_mpi_s RN;             /* cached <code>R^2 mod N</code> */
+
+    struct esp32c3_mpi_s RP;             /* cached <code>R^2 mod P</code> */
+    struct esp32c3_mpi_s RQ;             /* cached <code>R^2 mod Q</code> */
+
+    struct esp32c3_mpi_s VI;             /* The cached blinding value */
+    struct esp32c3_mpi_s VF;             /* The cached un-blinding value */
+
+    int padding;                /* Selects padding mode */
+    int hash_id;                /* Hash identifier */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_rsa_init
+ *
+ * Description:
+ *   Initializes an RSA context
+ *
+ * Input Parameters:
+ *   ctx      - The RSA context to initialize
+ *   padding  - The padding mode to use
+ *   hash_id  - The hash identifier of
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_rsa_init(struct esp32c3_rsa_context_s *ctx,
+                       int padding,
+                       int hash_id);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_import
+ *
+ * Description:
+ *   Imports a set of core parameters into an RSA context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to store the parameters in
+ *   N        - The RSA modulus
+ *   P        - The first prime factor of \p N
+ *   Q        - The second prime factor of \p N
+ *   D        - The private exponent
+ *   E        - The public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_import(struct esp32c3_rsa_context_s *ctx,
+                        const struct esp32c3_mpi_s *N,
+                        const struct esp32c3_mpi_s *P,
+                        const struct esp32c3_mpi_s *Q,
+                        const struct esp32c3_mpi_s *D,
+                        const struct esp32c3_mpi_s *E);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_import_raw
+ *
+ * Description:
+ *   Imports core RSA parameters into an RSA context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to store the parameters in
+ *   N        - The RSA modulus
+ *   NL       - The Byte length of \p N
+ *   P        - The first prime factor of \p N
+ *   PL       - The Byte length of \p P
+ *   Q        - The second prime factor of \p N
+ *   QL       - The Byte length of \p Q
+ *   D        - The private exponent
+ *   DL       - The Byte length of \p D
+ *   E        - The public exponent
+ *   EL       - The Byte length of \p E
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_import_raw(struct esp32c3_rsa_context_s *ctx,
+                            unsigned char const *N, size_t NL,
+                            unsigned char const *P, size_t PL,
+                            unsigned char const *Q, size_t QL,
+                            unsigned char const *D, size_t DL,
+                            unsigned char const *E, size_t EL);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_complete
+ *
+ * Description:
+ *   Completes an RSA context from a set of imported core parameters.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context holding imported parameters
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_complete(struct esp32c3_rsa_context_s *ctx);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_export
+ *
+ * Description:
+ *   Exports the core parameters of an RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *   N        - The MPI to hold the RSA modulus
+ *   P        - The MPI to hold the first prime factor of \p N
+ *   Q        - The MPI to hold the second prime factor of \p N
+ *   D        - The MPI to hold the private exponent
+ *   E        - The MPI to hold the public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_export(const struct esp32c3_rsa_context_s *ctx,
+                        struct esp32c3_mpi_s *N,
+                        struct esp32c3_mpi_s *P,
+                        struct esp32c3_mpi_s *Q,
+                        struct esp32c3_mpi_s *D,
+                        struct esp32c3_mpi_s *E);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_export_raw
+ *
+ * Description:
+ *   Eexports core parameters of an RSA key in raw big-endian binary format.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *   N        - The Byte array to store the RSA modulus
+ *   NL       - The size of the buffer for the modulus
+ *   P        - The Byte array to hold the first prime factor of \p N
+ *   PL       - The size of the buffer for the first prime factor
+ *   Q        - The Byte array to hold the second prime factor of \p N
+ *   QL       - The size of the buffer for the second prime factor
+ *   D        - The Byte array to hold the private exponent
+ *   DL       - The size of the buffer for the private exponent
+ *   E        - The Byte array to hold the public exponent
+ *   EL       - The size of the buffer for the public exponent
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_export_raw(const struct esp32c3_rsa_context_s *ctx,
+                            unsigned char *N, size_t NL,
+                            unsigned char *P, size_t PL,
+                            unsigned char *Q, size_t QL,
+                            unsigned char *D, size_t DL,
+                            unsigned char *E, size_t EL);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_export_crt
+ *
+ * Description:
+ *   Exports CRT parameters of a private RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *   DP       - The MPI to hold \c D modulo `P-1`
+ *   DQ       - The MPI to hold \c D modulo `Q-1`
+ *   QP       - The MPI to hold modular inverse of \c Q modulo \c P
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_export_crt(const struct esp32c3_rsa_context_s *ctx,
+                           struct esp32c3_mpi_s *DP,
+                           struct esp32c3_mpi_s *DQ,
+                           struct esp32c3_mpi_s *QP);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_set_padding
+ *
+ * Description:
+ *   Sets padding for an already initialized RSA context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to be configured
+ *   padding  - The padding mode to use
+ *   hash_id  - The hash identifier
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_rsa_set_padding(struct esp32c3_rsa_context_s *ctx,
+                             int padding, int hash_id);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_get_len
+ *
+ * Description:
+ *   Exports CRT parameters of a private RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context
+ *
+ * Returned Value:
+ *   length of the RSA modulus in Bytes.
+ *
+ ****************************************************************************/
+
+size_t esp32c3_rsa_get_len(const struct esp32c3_rsa_context_s *ctx);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_check_pubkey
+ *
+ * Description:
+ *   checks if a context contains at least an RSA public key..
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to check
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_check_pubkey(const struct esp32c3_rsa_context_s *ctx);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_check_privkey
+ *
+ * Description:
+ *   Checks if a context contains at least an RSA private key
+ *   and perform basic consistency checks.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to check
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_check_privkey(const struct esp32c3_rsa_context_s *ctx);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_check_pub_priv
+ *
+ * Description:
+ *   Checks a public-private RSA key pair. It checks each of the contexts,
+ *   and makes sure they match.
+ *
+ * Input Parameters:
+ *   pub      - The initialized RSA context holding the public key
+ *   prv      - The initialized RSA context holding the private key
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_check_pub_priv(const struct esp32c3_rsa_context_s *pub,
+                                const struct esp32c3_rsa_context_s *prv);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_public
+ *
+ * Description:
+ *   Performs an RSA public key operation.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to use
+ *   input    - The input buffer
+ *   output   - The output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_public(struct esp32c3_rsa_context_s *ctx,
+                const unsigned char *input,
+                unsigned char *output);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_private
+ *
+ * Description:
+ *   Performs an RSA private key operation.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to use
+ *   f_rng    - The RNG function
+ *   p_rng    - The RNG context to pass to \p f_rng
+ *   input    - The input buffer
+ *   output   - The output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_private(struct esp32c3_rsa_context_s *ctx,
+                 int (*f_rng)(void *, unsigned char *, size_t),
+                 void *p_rng,
+                 const unsigned char *input,
+                 unsigned char *output);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_encrypt
+ *
+ * Description:
+ *   Adds the message padding, then performs an RSA operation. It is the
+ *   generic wrapper for performing a PKCS#1 encryption operation using the
+ *   \p mode from the context.
+ *
+ * Input Parameters:
+ *   ctx      - The initialized RSA context to use
+ *   f_rng    - The RNG to use
+ *   p_rng    - The RNG context to be passed to \p f_rng
+ *   mode     - The mode of operation
+ *   ilen     - The length of the plaintext in Bytes
+ *   input    - The input data to encrypt
+ *   output   - The output buffer
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_encrypt(struct esp32c3_rsa_context_s *ctx,
+                       int (*f_rng)(void *, unsigned char *, size_t),
+                       void *p_rng,
+                       int mode, size_t ilen,
+                       const unsigned char *input,
+                       unsigned char *output);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_decrypt
+ *
+ * Description:
+ *   Performs an RSA operation, then removes the message padding.
+ *
+ * Input Parameters:
+ *   ctx            - The initialized RSA context to use
+ *   f_rng          - The RNG function
+ *   p_rng          - The RNG context to be passed to \p f_rng
+ *   mode           - The mode of operation
+ *   olen           - The point which to store the length of the plaintext
+ *   input          - The ciphertext buffer
+ *   output         - The buffer used to hold the plaintext
+ *   output_max_len - The length in Bytes of the output buffer \p output
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_decrypt(struct esp32c3_rsa_context_s *ctx,
+                       int (*f_rng)(void *, unsigned char *, size_t),
+                       void *p_rng,
+                       int mode, size_t *olen,
+                       const unsigned char *input,
+                       unsigned char *output,
+                       size_t output_max_len);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_copy
+ *
+ * Description:
+ *   Copies the components of an RSA context.
+ *
+ * Input Parameters:
+ *   dst      - The destination context
+ *   src      - The source context
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int esp32c3_rsa_copy(struct esp32c3_rsa_context_s *dst,
+                     const struct esp32c3_rsa_context_s *src);
+
+/****************************************************************************
+ * Name: esp32c3_rsa_free
+ *
+ * Description:
+ *   Frees the components of an RSA key.
+ *
+ * Input Parameters:
+ *   ctx      - The RSA context to free
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void esp32c3_rsa_free(struct esp32c3_rsa_context_s *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+#undef EXTERN
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_ESP32C3_ESP32C3_RSA_H */
diff --git a/arch/risc-v/src/esp32c3/hardware/esp32c3_rsa.h b/arch/risc-v/src/esp32c3/hardware/esp32c3_rsa.h
new file mode 100644
index 0000000..e11582c
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/hardware/esp32c3_rsa.h
@@ -0,0 +1,308 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/hardware/esp32c3_rsa.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_ESP32C3_HARDWARE_ESP32C3_RSA_H
+#define __ARCH_RISCV_SRC_ESP32C3_HARDWARE_ESP32C3_RSA_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "esp32c3_soc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* RSA_MEM_M_BLOCK_REG register
+ * Memory M
+ */
+
+#define RSA_MEM_M_BLOCK_REG (DR_REG_RSA_BASE + 0x0)
+
+/* RSA_M_MEMORY : R/W; bitpos: [16:0]; default: 384;
+ * Memory M
+ */
+
+#define RSA_M_MEMORY    0x0001FFFF
+#define RSA_M_MEMORY_M  (RSA_M_MEMORY_V << RSA_M_MEMORY_S)
+#define RSA_M_MEMORY_V  0x0001FFFF
+#define RSA_M_MEMORY_S  0
+
+/* RSA_MEM_RB_BLOCK_REG register
+ * Memory RB
+ */
+
+#define RSA_MEM_RB_BLOCK_REG (DR_REG_RSA_BASE + 0x200)
+
+/* RSA_RB_MEMORY : R/W; bitpos: [16:0]; default: 384;
+ * Memory RB
+ */
+
+#define RSA_RB_MEMORY    0x0001FFFF
+#define RSA_RB_MEMORY_M  (RSA_RB_MEMORY_V << RSA_RB_MEMORY_S)
+#define RSA_RB_MEMORY_V  0x0001FFFF
+#define RSA_RB_MEMORY_S  0
+
+/* RSA_MEM_Z_BLOCK_REG register
+ * Memory Z
+ */
+
+#define RSA_MEM_Z_BLOCK_REG (DR_REG_RSA_BASE + 0x200)
+
+/* RSA_Z_MEMORY : R/W; bitpos: [16:0]; default: 384;
+ * Memory Z
+ */
+
+#define RSA_Z_MEMORY    0x0001FFFF
+#define RSA_Z_MEMORY_M  (RSA_Z_MEMORY_V << RSA_Z_MEMORY_S)
+#define RSA_Z_MEMORY_V  0x0001FFFF
+#define RSA_Z_MEMORY_S  0
+
+/* RSA_MEM_Y_BLOCK_REG register
+ * Memory Y
+ */
+
+#define RSA_MEM_Y_BLOCK_REG (DR_REG_RSA_BASE + 0x400)
+
+/* RSA_Y_MEMORY : R/W; bitpos: [16:0]; default: 384;
+ * Memory Y
+ */
+
+#define RSA_Y_MEMORY    0x0001FFFF
+#define RSA_Y_MEMORY_M  (RSA_Y_MEMORY_V << RSA_Y_MEMORY_S)
+#define RSA_Y_MEMORY_V  0x0001FFFF
+#define RSA_Y_MEMORY_S  0
+
+/* RSA_MEM_X_BLOCK_REG register
+ * Memory X
+ */
+
+#define RSA_MEM_X_BLOCK_REG (DR_REG_RSA_BASE + 0x600)
+
+/* RSA_X_MEMORY : R/W; bitpos: [16:0]; default: 384;
+ * Memory X
+ */
+
+#define RSA_X_MEMORY    0x0001FFFF
+#define RSA_X_MEMORY_M  (RSA_X_MEMORY_V << RSA_X_MEMORY_S)
+#define RSA_X_MEMORY_V  0x0001FFFF
+#define RSA_X_MEMORY_S  0
+
+/* RSA_M_PRIME_REG register
+ * Register to store M'
+ */
+
+#define RSA_M_PRIME_REG (DR_REG_RSA_BASE + 0x800)
+
+/* RSA_M_PRIME : R/W; bitpos: [31:0]; default: 0;
+ * Stores M'
+ */
+
+#define RSA_M_PRIME    0xFFFFFFFF
+#define RSA_M_PRIME_M  (RSA_M_PRIME_V << RSA_M_PRIME_S)
+#define RSA_M_PRIME_V  0xFFFFFFFF
+#define RSA_M_PRIME_S  0
+
+/* RSA_MODE_REG register
+ * RSA length mode
+ */
+
+#define RSA_MODE_REG (DR_REG_RSA_BASE + 0x804)
+
+/* RSA_MODE : R/W; bitpos: [6:0]; default: 0;
+ * Stores the mode of modular exponentiation.
+ */
+
+#define RSA_MODE    0x0000007F
+#define RSA_MODE_M  (RSA_MODE_V << RSA_MODE_S)
+#define RSA_MODE_V  0x0000007F
+#define RSA_MODE_S  0
+
+/* RSA_CLEAN_REG register
+ * RSA clean register
+ */
+
+#define RSA_CLEAN_REG (DR_REG_RSA_BASE + 0x808)
+
+/* RSA_CLEAN : RO; bitpos: [0]; default: 0;
+ * The content of this bit is 1 when memories complete initialization.
+ */
+
+#define RSA_CLEAN    (BIT(0))
+#define RSA_CLEAN_M  (RSA_CLEAN_V << RSA_CLEAN_S)
+#define RSA_CLEAN_V  0x00000001
+#define RSA_CLEAN_S  0
+
+/* RSA_MODEXP_START_REG register
+ * Modular exponentiation starting bit
+ */
+
+#define RSA_MODEXP_START_REG (DR_REG_RSA_BASE + 0x80c)
+
+/* RSA_MODEXP_START : WO; bitpos: [0]; default: 0;
+ * Set this bit to 1 to start the modular exponentiation.
+ */
+
+#define RSA_MODEXP_START    (BIT(0))
+#define RSA_MODEXP_START_M  (RSA_MODEXP_START_V << RSA_MODEXP_START_S)
+#define RSA_MODEXP_START_V  0x00000001
+#define RSA_MODEXP_START_S  0
+
+/* RSA_MODMULT_START_REG register
+ * Modular multiplication starting bit
+ */
+
+#define RSA_MODMULT_START_REG (DR_REG_RSA_BASE + 0x810)
+
+/* RSA_MODMULT_START : WO; bitpos: [0]; default: 0;
+ * Set this bit to 1 to start the modular multiplication.
+ */
+
+#define RSA_MODMULT_START    (BIT(0))
+#define RSA_MODMULT_START_M  (RSA_MODMULT_START_V << RSA_MODMULT_START_S)
+#define RSA_MODMULT_START_V  0x00000001
+#define RSA_MODMULT_START_S  0
+
+/* RSA_MULT_START_REG register
+ * Normal multiplicaiton starting bit
+ */
+
+#define RSA_MULT_START_REG (DR_REG_RSA_BASE + 0x814)
+
+/* RSA_MULT_START : WO; bitpos: [0]; default: 0;
+ * Set this bit to 1 to start the multiplication.
+ */
+
+#define RSA_MULT_START    (BIT(0))
+#define RSA_MULT_START_M  (RSA_MULT_START_V << RSA_MULT_START_S)
+#define RSA_MULT_START_V  0x00000001
+#define RSA_MULT_START_S  0
+
+/* RSA_IDLE_REG register
+ * RSA idle register
+ */
+
+#define RSA_IDLE_REG (DR_REG_RSA_BASE + 0x818)
+
+/* RSA_IDLE : RO; bitpos: [0]; default: 0;
+ * The content of this bit is 1 when the RSA accelerator is idle.
+ */
+
+#define RSA_IDLE    (BIT(0))
+#define RSA_IDLE_M  (RSA_IDLE_V << RSA_IDLE_S)
+#define RSA_IDLE_V  0x00000001
+#define RSA_IDLE_S  0
+
+/* RSA_CLEAR_INTERRUPT_REG register
+ * RSA clear interrupt register
+ */
+
+#define RSA_CLEAR_INTERRUPT_REG (DR_REG_RSA_BASE + 0x81c)
+
+/* RSA_CLEAR_INTERRUPT : WO; bitpos: [0]; default: 0;
+ * Set this bit to 1 to clear the RSA interrupts.
+ */
+
+#define RSA_CLEAR_INTERRUPT    (BIT(0))
+#define RSA_CLEAR_INTERRUPT_M  (RSA_CLEAR_INTERRUPT_V << RSA_CLEAR_INTERRUPT_S)
+#define RSA_CLEAR_INTERRUPT_V  0x00000001
+#define RSA_CLEAR_INTERRUPT_S  0
+
+/* RSA_CONSTANT_TIME_REG register
+ * The constant_time option
+ */
+
+#define RSA_CONSTANT_TIME_REG (DR_REG_RSA_BASE + 0x820)
+
+/* RSA_CONSTANT_TIME : R/W; bitpos: [0]; default: 1;
+ * Set this bit to 0 to enable the acceleration option of constant_time for
+ * modular exponentiation. Set to 1 to disable the acceleration (by default).
+ */
+
+#define RSA_CONSTANT_TIME    (BIT(0))
+#define RSA_CONSTANT_TIME_M  (RSA_CONSTANT_TIME_V << RSA_CONSTANT_TIME_S)
+#define RSA_CONSTANT_TIME_V  0x00000001
+#define RSA_CONSTANT_TIME_S  0
+
+/* RSA_SEARCH_ENABLE_REG register
+ * The search option
+ */
+
+#define RSA_SEARCH_ENABLE_REG (DR_REG_RSA_BASE + 0x824)
+
+/* RSA_SEARCH_ENABLE : R/W; bitpos: [0]; default: 0;
+ * Set this bit to 1 to enable the acceleration option of search for modular
+ * exponentiation. Set to 0 to disable the acceleration (by default).
+ */
+
+#define RSA_SEARCH_ENABLE    (BIT(0))
+#define RSA_SEARCH_ENABLE_M  (RSA_SEARCH_ENABLE_V << RSA_SEARCH_ENABLE_S)
+#define RSA_SEARCH_ENABLE_V  0x00000001
+#define RSA_SEARCH_ENABLE_S  0
+
+/* RSA_SEARCH_POS_REG register
+ * The search position
+ */
+
+#define RSA_SEARCH_POS_REG (DR_REG_RSA_BASE + 0x828)
+
+/* RSA_SEARCH_POS : R/W; bitpos: [11:0]; default: 0;
+ * Is used to configure the starting address when the acceleration option of
+ * search is used.
+ */
+
+#define RSA_SEARCH_POS    0x00000FFF
+#define RSA_SEARCH_POS_M  (RSA_SEARCH_POS_V << RSA_SEARCH_POS_S)
+#define RSA_SEARCH_POS_V  0x00000FFF
+#define RSA_SEARCH_POS_S  0
+
+/* RSA_INTERRUPT_ENA_REG register
+ * RSA interrupt enable register
+ */
+
+#define RSA_INTERRUPT_ENA_REG (DR_REG_RSA_BASE + 0x82c)
+
+/* RSA_INTERRUPT_ENA : R/W; bitpos: [0]; default: 0;
+ * Set this bit to 1 to enable the RSA interrupt. This option is enabled by
+ * default.
+ */
+
+#define RSA_INTERRUPT_ENA    (BIT(0))
+#define RSA_INTERRUPT_ENA_M  (RSA_INTERRUPT_ENA_V << RSA_INTERRUPT_ENA_S)
+#define RSA_INTERRUPT_ENA_V  0x00000001
+#define RSA_INTERRUPT_ENA_S  0
+
+/* RSA_DATE_REG register
+ * Version control register
+ */
+
+#define RSA_DATE_REG (DR_REG_RSA_BASE + 0x830)
+
+/* RSA_DATE : R/W; bitpos: [29:0]; default: 538510373;
+ * Version control register
+ */
+
+#define RSA_DATE    0x3FFFFFFF
+#define RSA_DATE_M  (RSA_DATE_V << RSA_DATE_S)
+#define RSA_DATE_V  0x3FFFFFFF
+#define RSA_DATE_S  0
+
+#endif /* __ARCH_RISCV_SRC_ESP32C3_HARDWARE_ESP32C3_RSA_H */
diff --git a/boards/risc-v/esp32c3/esp32c3-devkit/configs/rsa/defconfig b/boards/risc-v/esp32c3/esp32c3-devkit/configs/rsa/defconfig
new file mode 100644
index 0000000..52a7b8e
--- /dev/null
+++ b/boards/risc-v/esp32c3/esp32c3-devkit/configs/rsa/defconfig
@@ -0,0 +1,51 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_CMDPARMS is not set
+CONFIG_ARCH="risc-v"
+CONFIG_ARCH_BOARD="esp32c3-devkit"
+CONFIG_ARCH_BOARD_ESP32C3_DEVKIT=y
+CONFIG_ARCH_CHIP="esp32c3"
+CONFIG_ARCH_CHIP_ESP32C3=y
+CONFIG_ARCH_CHIP_ESP32C3WROOM02=y
+CONFIG_ARCH_INTERRUPTSTACK=1536
+CONFIG_ARCH_RISCV=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARD_LOOPSPERMSEC=15000
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_ASSERTIONS=y
+CONFIG_DEBUG_FEATURES=y
+CONFIG_DEV_ZERO=y
+CONFIG_ESP32C3_BIGNUM_ACCELERATOR=y
+CONFIG_ESP32C3_BIGNUM_ACCELERATOR_TEST=y
+CONFIG_ESP32C3_RSA_ACCELERATOR=y
+CONFIG_ESP32C3_RSA_ACCELERATOR_TEST=y
+CONFIG_FS_PROCFS=y
+CONFIG_IDLETHREAD_STACKSIZE=2048
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LIBC_PERROR_STDOUT=y
+CONFIG_LIBC_STRERROR=y
+CONFIG_MAX_TASKS=16
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_NSH_STRERROR=y
+CONFIG_PREALLOC_TIMERS=0
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_START_DAY=29
+CONFIG_START_MONTH=11
+CONFIG_START_YEAR=2019
+CONFIG_SYSTEM_NSH=y
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_USERMAIN_STACKSIZE=4096
+CONFIG_USER_ENTRYPOINT="esp32c3_rsa_main"