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/nuttx.git

commit 3edc27ba26235a9c174bf150806c380232fabe62
Author: Daniel P. Carvalho <[email protected]>
AuthorDate: Fri Oct 9 09:29:54 2026 -0300

    arch/mips/pic32mz: add WPA3-Personal for the PIC32MZ-W1 Wi-Fi.
    
    Add a driver for the PIC32MZ-W1 BA414E public key engine (modular add,
    subtract, multiply and exponentiation; P-256 point add, double, multiply
    and on-curve check) and use it for the big number and elliptic curve
    services the WLAN library calls for WPA3-Personal (SAE), as a station
    and as a Soft-AP.  When the library passes a completion callback, the
    operation completes at once and the callback is queued and run from the
    WLAN thread, as the library expects.  wapi selects WPA3 with WPA version
    3 (IW_AUTH_WPA_VERSION_WPA3).
    
    The engine runs a microcode program that is under Microchip's license
    and not part of NuttX.  By default the build downloads
    drivers/ba414e/src/drv_ba414e.c from the MPLAB Harmony crypto repository
    (v3.9.0) and generates a C file with the microcode; alternatively the
    microcode is read from a file on first use (the format is documented in
    the Kconfig help and in pic32mz_ba414e.c).  An optional self-test runs
    known-answer tests when the microcode is loaded.
    
    Register addresses come from the PIC32MZ-W_DFP [DFP]; the programming
    model and the library interface facts come from Microchip's Harmony
    sources and are tagged [EX]; no code was copied.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Daniel P. Carvalho <[email protected]>
---
 arch/mips/src/pic32mz/.gitignore               |   2 +
 arch/mips/src/pic32mz/Kconfig                  |  58 +-
 arch/mips/src/pic32mz/Make.defs                |  38 +-
 arch/mips/src/pic32mz/pic32mz_ba414e.c         | 943 +++++++++++++++++++++++++
 arch/mips/src/pic32mz/pic32mz_ba414e.h         | 111 +++
 arch/mips/src/pic32mz/pic32mz_w1_wlan.c        | 112 +++
 arch/mips/src/pic32mz/pic32mz_w1_wlan.h        |   9 +
 arch/mips/src/pic32mz/pic32mz_w1_wlan_crypto.c | 371 +++++++++-
 8 files changed, 1637 insertions(+), 7 deletions(-)

diff --git a/arch/mips/src/pic32mz/.gitignore b/arch/mips/src/pic32mz/.gitignore
index 765a3271c66..a99e7e769d8 100644
--- a/arch/mips/src/pic32mz/.gitignore
+++ b/arch/mips/src/pic32mz/.gitignore
@@ -1,2 +1,4 @@
 /pic32mzw1.a
 /libpic32mzw1_nx.a
+/drv_ba414e.c
+/pic32mz_ba414e_ucode.c
diff --git a/arch/mips/src/pic32mz/Kconfig b/arch/mips/src/pic32mz/Kconfig
index 2d540fdbf99..d32fe474f1d 100644
--- a/arch/mips/src/pic32mz/Kconfig
+++ b/arch/mips/src/pic32mz/Kconfig
@@ -153,9 +153,9 @@ config PIC32MZ_W1_WLAN
                Wi-Fi network device (wlan0) built on Microchip's closed 
PIC32MZ-W1
                WLAN library (pic32mzw1.a from the MPLAB Harmony wireless_wifi
                repository, downloaded at build time under Microchip's license).
-               Station or Soft-AP, open or WPA/WPA2-Personal, configured 
through
-               wapi.  The library is built with XC32 (microMIPS, soft-float), 
so
-               only XC32 is supported.
+               Station or Soft-AP, open or WPA/WPA2-Personal (WPA3-Personal 
with
+               PIC32MZ_W1_BA414E), configured through wapi.  The library is 
built
+               with XC32 (microMIPS, soft-float), so only XC32 is supported.
 
 if PIC32MZ_W1_WLAN
 
@@ -167,6 +167,58 @@ config PIC32MZ_W1_WLAN_STACKSIZE
        int "WLAN thread stack size"
        default 4096
 
+config PIC32MZ_W1_BA414E
+       bool "BA414E public key engine (WPA3-Personal)"
+       default n
+       ---help---
+               Use the BA414E public key engine for the big number and elliptic
+               curve operations of WPA3-Personal (SAE), station and Soft-AP.
+               Without it only open and WPA/WPA2-Personal networks are
+               supported.  The engine runs a microcode program from Microchip's
+               MPLAB Harmony driver source (drivers/ba414e/src/drv_ba414e.c in
+               https://github.com/Microchip-MPLAB-Harmony/crypto), which is
+               under Microchip's license and not part of NuttX.
+
+if PIC32MZ_W1_BA414E
+
+choice
+       prompt "BA414E microcode"
+       default PIC32MZ_W1_BA414E_UCODE_BUILTIN
+
+config PIC32MZ_W1_BA414E_UCODE_BUILTIN
+       bool "Download at build time"
+       ---help---
+               Download drv_ba414e.c (Harmony crypto v3.9.0) at build time and
+               link its microcode into the image.
+
+config PIC32MZ_W1_BA414E_UCODE_FILE
+       bool "Load from a file"
+       ---help---
+               Load the microcode from a file when the engine is first used,
+               that is on the first WPA3 connection or Soft-AP start.  The file
+               holds the 810 32-bit values of the init_ucode_array[810]
+               initializer of drv_ba414e.c, in array order, each little endian:
+               no header, exactly 3240 bytes.  Any file system works (ROMFS,
+               tmpfs, a flash file system...) as long as the file is there
+               before the first use.
+
+endchoice
+
+config PIC32MZ_W1_BA414E_UCODE_PATH
+       string "Microcode file"
+       default "/etc/ba414e.bin"
+       depends on PIC32MZ_W1_BA414E_UCODE_FILE
+
+config PIC32MZ_W1_BA414E_SELFTEST
+       bool "BA414E self-test"
+       default n
+       ---help---
+               Run known-answer tests of the BA414E primitives (P-256 and
+               modular arithmetic) once the microcode is loaded, and log the
+               results and times.
+
+endif # PIC32MZ_W1_BA414E
+
 endif # PIC32MZ_W1_WLAN
 
 menu "PIC32MZ Peripheral Support"
diff --git a/arch/mips/src/pic32mz/Make.defs b/arch/mips/src/pic32mz/Make.defs
index 031a37cc3bb..b0defb27e78 100644
--- a/arch/mips/src/pic32mz/Make.defs
+++ b/arch/mips/src/pic32mz/Make.defs
@@ -106,11 +106,47 @@ LDFLAGS += --defsym=NVMADDR=0xbf800630 
--defsym=NVMDATA0=0xbf800640
 LDFLAGS += --defsym=NVMSRCADDR=0xbf8006c0 --defsym=NVMLBWP=0xbf8006f0
 endif
 
-# Delete the downloaded library and its copy whatever the configuration
+ifeq ($(CONFIG_PIC32MZ_W1_BA414E),y)
+CHIP_CSRCS += pic32mz_ba414e.c
+endif
+
+ifeq ($(CONFIG_PIC32MZ_W1_BA414E_UCODE_BUILTIN),y)
+CHIP_CSRCS += pic32mz_ba414e_ucode.c
+
+# The BA414E microcode is not part of NuttX either: download Microchip's
+# driver source from the MPLAB Harmony crypto repository and generate a C
+# file with its init_ucode_array values (810 32-bit words).
+
+BA414E_VER = v3.9.0
+BA414E_URL = 
https://github.com/Microchip-MPLAB-Harmony/crypto/raw/$(BA414E_VER)/drivers/ba414e/src
+BA414E_SRC = chip$(DELIM)drv_ba414e.c
+BA414E_UCODE = chip$(DELIM)pic32mz_ba414e_ucode.c
+
+$(BA414E_SRC):
+       $(call DOWNLOAD,$(BA414E_URL),drv_ba414e.c,$(BA414E_SRC))
+
+$(BA414E_UCODE): $(BA414E_SRC)
+       $(Q) n=`sed -n '/init_ucode_array\[810\]/,/};/p' $< | grep -o 0x | wc 
-l`; \
+         if [ $$n -ne 810 ]; then \
+           echo "ERROR: no BA414E microcode in $<"; rm -f $<; exit 1; \
+         fi
+       $(Q) { echo "/* Generated from Microchip's drv_ba414e.c $(BA414E_VER) 
*/"; \
+         echo "#include <stdint.h>"; \
+         echo "const uint32_t g_ba414e_ucode[810] ="; \
+         echo "{"; \
+         sed -n '/init_ucode_array\[810\]/,/};/p' $< | sed '1d;$$d'; \
+         echo "};"; } > $@
+
+context:: $(BA414E_UCODE)
+endif
+
+# Delete the downloaded and generated files whatever the configuration
 
 distclean::
        $(call DELFILE, chip$(DELIM)pic32mzw1.a)
        $(call DELFILE, chip$(DELIM)libpic32mzw1_nx.a)
+       $(call DELFILE, chip$(DELIM)drv_ba414e.c)
+       $(call DELFILE, chip$(DELIM)pic32mz_ba414e_ucode.c)
 
 ifeq ($(CONFIG_PIC32MZ_GPIOIRQ),y)
 CHIP_CSRCS += pic32mz_gpioirq.c
diff --git a/arch/mips/src/pic32mz/pic32mz_ba414e.c 
b/arch/mips/src/pic32mz/pic32mz_ba414e.c
new file mode 100644
index 00000000000..d25200a5e69
--- /dev/null
+++ b/arch/mips/src/pic32mz/pic32mz_ba414e.c
@@ -0,0 +1,943 @@
+/****************************************************************************
+ * arch/mips/src/pic32mz/pic32mz_ba414e.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* PIC32MZ-W1 asymmetric crypto engine (BA414E): modular arithmetic and
+ * elliptic curve primitives.  DS70005425 (chapter 26) documents only the
+ * memories; the programming model below comes from Microchip's MPLAB
+ * Harmony driver (crypto/drivers/ba414e) and is marked [EX].  Register
+ * addresses and fields are from the PIC32MZ-W_DFP [DFP].
+ *
+ * The engine runs a microcode program that must be loaded before the
+ * first operation.  It is under Microchip's license and not part of
+ * NuttX: either the build downloads Microchip's driver source and links
+ * the microcode into the image (CONFIG_PIC32MZ_W1_BA414E_UCODE_BUILTIN),
+ * or it is read from a file on first use
+ * (CONFIG_PIC32MZ_W1_BA414E_UCODE_FILE).
+ *
+ * Microcode file format: the 810 32-bit values of the init_ucode_array[810]
+ * initializer in drivers/ba414e/src/drv_ba414e.c of
+ * https://github.com/Microchip-MPLAB-Harmony/crypto, in array order, each
+ * stored little endian; no header, exactly 3240 bytes.  For example, the
+ * first values 0x10032004, 0x48013e00 give the bytes
+ * 04 20 03 10 00 3e 01 48.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <syslog.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/irq.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/semaphore.h>
+
+#include <arch/irq.h>
+
+#include "mips_internal.h"
+#include <arch/board/board.h>
+
+#include "hardware/pic32mzw1_features.h"
+#include "pic32mz_ba414e.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* [DFP] Registers */
+
+#define BA414E_PKCONFIG       0xbf920000
+#define BA414E_PKCOMMAND      0xbf920004
+#define BA414E_PKCONTROL      0xbf920008
+#define BA414E_PKSTATUS       0xbf92000c
+
+/* [DFP] PKCONFIG: operand slot pointers */
+
+#define PKCONFIG_OPPTRA(n)    ((uint32_t)(n) << 0)
+#define PKCONFIG_OPPTRB(n)    ((uint32_t)(n) << 8)
+#define PKCONFIG_OPPTRC(n)    ((uint32_t)(n) << 16)
+
+/* [DFP] PKCOMMAND fields */
+
+#define PKCOMMAND_OPERATION(n) ((uint32_t)(n) << 0)
+#define PKCOMMAND_OPSIZE(n)   ((uint32_t)(n) << 8)
+#define PKCOMMAND_CALCR2      (1u << 31)
+
+/* [DFP] PKCONTROL.START */
+
+#define PKCONTROL_START       (1u << 0)
+
+/* [DFP] PKSTATUS fields */
+
+#define PKSTATUS_PXNOC        (1u << 4)   /* Point not on curve */
+#define PKSTATUS_PXINF        (1u << 5)   /* Point at infinity */
+
+/* [DFP] Shared crypto memory (operand slots) and microcode memory (KSEG1) */
+
+#define BA414E_SCM_BASE       0xbf900000
+#define BA414E_UCM_BASE       0xbf902000
+
+/* [EX] Shared memory size, 64-byte operand slots */
+
+#define BA414E_SCM_SIZE       2432
+#define BA414E_SLOT_SIZE      64
+#define BA414E_SLOT(n)        (BA414E_SCM_BASE + (n) * BA414E_SLOT_SIZE)
+
+/* [EX] Microcode: 90 groups of nine 32-bit words, each packing sixteen
+ * 18-bit microcode words, most significant bits first.
+ */
+
+#define BA414E_UCODE_GROUPS   90
+#define BA414E_UCODE_WORDS    (BA414E_UCODE_GROUPS * 9)
+
+/* [DFP] PMD1.BA414MD: module disable */
+
+#define PMD1_BA414MD          (1u << 23)
+
+/* [EX] Operation codes */
+
+#define OPC_MOD_ADD           0x01
+#define OPC_MOD_SUB           0x02
+#define OPC_MOD_MULT          0x03
+#define OPC_MOD_EXP           0x10
+#define OPC_ECC_DOUBLE        0x20
+#define OPC_ECC_ADD           0x21
+#define OPC_ECC_MULT          0x22
+#define OPC_ECC_ONCURVE       0x26
+
+/* [EX] Slots of the modular primitives (P, A, B -> C) */
+
+#define MOD_SLOT_P            0
+#define MOD_SLOT_A            1
+#define MOD_SLOT_B            2
+#define MOD_SLOT_C            3
+
+/* [EX] Slots of the modular exponentiation (C = M^e mod n) */
+
+#define EXP_SLOT_N            0
+#define EXP_SLOT_E            1
+#define EXP_SLOT_M            2
+#define EXP_SLOT_C            3
+
+/* [EX] Slots of the ECC primitives */
+
+#define ECC_SLOT_P            0
+#define ECC_SLOT_N            1
+#define ECC_SLOT_GX           2
+#define ECC_SLOT_GY           3
+#define ECC_SLOT_A            4
+#define ECC_SLOT_B            5
+#define ECC_SLOT_P1X          6
+#define ECC_SLOT_P1Y          7
+#define ECC_SLOT_P2X          8
+#define ECC_SLOT_P2Y          9
+#define ECC_SLOT_P3X          10
+#define ECC_SLOT_P3Y          11
+#define ECC_SLOT_K            12
+
+#define BA414E_TIMEOUT        SEC2TICK(1)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const uint8_t g_p256_p[32] =
+{
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+static const uint8_t g_p256_n[32] =
+{
+  0x51, 0x25, 0x63, 0xfc, 0xc2, 0xca, 0xb9, 0xf3,
+  0x84, 0x9e, 0x17, 0xa7, 0xad, 0xfa, 0xe6, 0xbc,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+static const uint8_t g_p256_gx[32] =
+{
+  0x96, 0xc2, 0x98, 0xd8, 0x45, 0x39, 0xa1, 0xf4,
+  0xa0, 0x33, 0xeb, 0x2d, 0x81, 0x7d, 0x03, 0x77,
+  0xf2, 0x40, 0xa4, 0x63, 0xe5, 0xe6, 0xbc, 0xf8,
+  0x47, 0x42, 0x2c, 0xe1, 0xf2, 0xd1, 0x17, 0x6b
+};
+
+static const uint8_t g_p256_gy[32] =
+{
+  0xf5, 0x51, 0xbf, 0x37, 0x68, 0x40, 0xb6, 0xcb,
+  0xce, 0x5e, 0x31, 0x6b, 0x57, 0x33, 0xce, 0x2b,
+  0x16, 0x9e, 0x0f, 0x7c, 0x4a, 0xeb, 0xe7, 0x8e,
+  0x9b, 0x7f, 0x1a, 0xfe, 0xe2, 0x42, 0xe3, 0x4f
+};
+
+static const uint8_t g_p256_a[32] =
+{
+  0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+static const uint8_t g_p256_b[32] =
+{
+  0x4b, 0x60, 0xd2, 0x27, 0x3e, 0x3c, 0xce, 0x3b,
+  0xf6, 0xb0, 0x53, 0xcc, 0xb0, 0x06, 0x1d, 0x65,
+  0xbc, 0x86, 0x98, 0x76, 0x55, 0xbd, 0xeb, 0xb3,
+  0xe7, 0x93, 0x3a, 0xaa, 0xd8, 0x35, 0xc6, 0x5a
+};
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_SELFTEST
+/* Self-test vectors (P-256, little endian): 2G, kG for
+ * k = 0xdeadbeef12345678, x of 4G, and 3^(p-2) mod p (the inverse of 3).
+ */
+
+static const uint8_t g_st_2gx[32] =
+{
+  0x78, 0x99, 0x66, 0x47, 0xfc, 0x48, 0x0b, 0xa6,
+  0x35, 0x1b, 0xf2, 0x77, 0xe2, 0x69, 0x89, 0xc0,
+  0xc3, 0x1a, 0xb5, 0x04, 0x03, 0x38, 0x52, 0x8a,
+  0x7e, 0x4f, 0x03, 0x8d, 0x18, 0x7b, 0xf2, 0x7c
+};
+
+static const uint8_t g_st_2gy[32] =
+{
+  0xd1, 0x73, 0x78, 0x22, 0x9d, 0xb7, 0x04, 0x9e,
+  0x29, 0x82, 0xe9, 0x3c, 0xe6, 0xad, 0x7d, 0xba,
+  0xdb, 0x30, 0x74, 0x9f, 0xc6, 0x9a, 0x3d, 0x29,
+  0x40, 0xd0, 0x8e, 0xdb, 0x10, 0x55, 0x77, 0x07
+};
+
+static const uint8_t g_st_kgx[32] =
+{
+  0x60, 0x70, 0xe3, 0x37, 0x8c, 0x9a, 0xbd, 0xfd,
+  0x9f, 0xfc, 0x20, 0xe4, 0xae, 0x29, 0x00, 0x8d,
+  0x2f, 0xf1, 0x51, 0xb2, 0xdb, 0x1e, 0x9f, 0x17,
+  0x13, 0x68, 0xf1, 0x7c, 0x4b, 0xc2, 0x9d, 0xfc
+};
+
+static const uint8_t g_st_kgy[32] =
+{
+  0xbf, 0x2a, 0x5c, 0xe1, 0x3e, 0x15, 0xbb, 0x9f,
+  0x23, 0x35, 0xae, 0xed, 0xb7, 0xf7, 0xcc, 0x55,
+  0x98, 0xde, 0x03, 0x33, 0x39, 0x7b, 0xd8, 0xdc,
+  0x4c, 0xbc, 0xc2, 0x52, 0x4c, 0x4d, 0xbc, 0x62
+};
+
+static const uint8_t g_st_4gx[32] =
+{
+  0x52, 0x08, 0x03, 0x6b, 0x44, 0x02, 0x93, 0x50,
+  0xef, 0x96, 0x55, 0x78, 0xdb, 0xe2, 0x1f, 0x03,
+  0xd0, 0x2b, 0xe6, 0x9e, 0x65, 0xde, 0x2d, 0xa0,
+  0xbb, 0x8f, 0xd0, 0x32, 0x35, 0x4a, 0x53, 0xe2
+};
+
+static const uint8_t g_st_inv3[32] =
+{
+  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+  0x55, 0x55, 0x55, 0x55, 0xab, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+#endif
+
+static rmutex_t g_ba414e_lock = NXRMUTEX_INITIALIZER;
+static bool g_ba414e_loaded;
+static sem_t g_ba414e_done = SEM_INITIALIZER(0);
+static volatile uint32_t g_ba414e_status;
+static volatile bool g_ba414e_fault;
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct ba414e_curve_s g_ba414e_p256 =
+{
+  32, g_p256_p, g_p256_n, g_p256_gx, g_p256_gy, g_p256_a, g_p256_b
+};
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_UCODE_BUILTIN
+/* Microcode, generated at build time (see Make.defs) */
+
+extern const uint32_t g_ba414e_ucode[BA414E_UCODE_WORDS];
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* [EX] Operand size code: number of 64-bit words, 2 (128 bits) to 8 */
+
+static int ba414e_opsize(int len)
+{
+  int size = (len + 7) / 8;
+
+  return size < 2 ? 2 : size;
+}
+
+static void ba414e_write_slot(int slot, FAR const uint8_t *data, int len,
+                              int slotlen)
+{
+  volatile uint32_t *dst = (volatile uint32_t *)BA414E_SLOT(slot);
+  uint32_t word;
+  int i;
+
+  /* Little endian, zero padded to the operand size; 32-bit accesses */
+
+  for (i = 0; i < slotlen; i += 4)
+    {
+      word = 0;
+      if (i < len)
+        {
+          word = data[i];
+        }
+
+      if (i + 1 < len)
+        {
+          word |= (uint32_t)data[i + 1] << 8;
+        }
+
+      if (i + 2 < len)
+        {
+          word |= (uint32_t)data[i + 2] << 16;
+        }
+
+      if (i + 3 < len)
+        {
+          word |= (uint32_t)data[i + 3] << 24;
+        }
+
+      *dst++ = word;
+    }
+}
+
+static void ba414e_read_slot(int slot, FAR uint8_t *data, int len)
+{
+  volatile uint32_t *src = (volatile uint32_t *)BA414E_SLOT(slot);
+  uint32_t word = 0;
+  int i;
+
+  for (i = 0; i < len; i++)
+    {
+      if ((i & 3) == 0)
+        {
+          word = *src++;
+        }
+
+      data[i] = word & 0xff;
+      word >>= 8;
+    }
+}
+
+static void ba414e_clear_scm(void)
+{
+  volatile uint32_t *p = (volatile uint32_t *)BA414E_SCM_BASE;
+  int i;
+
+  for (i = 0; i < BA414E_SCM_SIZE / 4; i++)
+    {
+      p[i] = 0;
+    }
+}
+
+static void ba414e_load_ucode(FAR const uint32_t *in)
+{
+  volatile uint32_t *ucm = (volatile uint32_t *)BA414E_UCM_BASE;
+  uint64_t acc;
+  int bits;
+  int g;
+  int i;
+
+  for (g = 0; g < BA414E_UCODE_GROUPS; g++)
+    {
+      acc  = 0;
+      bits = 0;
+
+      for (i = 0; i < 9; i++)
+        {
+          /* Shift in 32 bits, emit every complete 18-bit word */
+
+          acc   = (acc << 32) | *in++;
+          bits += 32;
+
+          while (bits >= 18)
+            {
+              bits  -= 18;
+              *ucm++ = (uint32_t)(acc >> bits) & 0x3ffff;
+            }
+        }
+    }
+}
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_UCODE_FILE
+/* Read the microcode file (format in the header of this file) */
+
+static FAR uint32_t *ba414e_read_ucode(void)
+{
+  FAR const char *path = CONFIG_PIC32MZ_W1_BA414E_UCODE_PATH;
+  size_t size = BA414E_UCODE_WORDS * sizeof(uint32_t);
+  FAR uint32_t *ucode;
+  struct file file;
+  ssize_t nread;
+  size_t total = 0;
+  char extra;
+  int ret;
+  static bool warned;
+
+  ret = file_open(&file, path, O_RDONLY | O_CLOEXEC);
+  if (ret < 0)
+    {
+      /* Callers retry; report the missing file once */
+
+      if (!warned)
+        {
+          syslog(LOG_ERR, "ba414e: cannot open %s: %d\n", path, ret);
+          warned = true;
+        }
+
+      return NULL;
+    }
+
+  ucode = kmm_malloc(size);
+  if (ucode != NULL)
+    {
+      while (total < size)
+        {
+          nread = file_read(&file, (FAR uint8_t *)ucode + total,
+                            size - total);
+          if (nread <= 0)
+            {
+              break;
+            }
+
+          total += nread;
+        }
+
+      /* The size must match exactly */
+
+      if (total != size || file_read(&file, &extra, 1) != 0)
+        {
+          syslog(LOG_ERR, "ba414e: %s is not %zu bytes\n", path, size);
+          kmm_free(ucode);
+          ucode = NULL;
+        }
+    }
+
+  file_close(&file);
+  return ucode;
+}
+#endif
+
+static int ba414e_isr(int irq, FAR void *context, FAR void *arg)
+{
+  /* [EX] Latch the status, stop the engine, mask the interrupt */
+
+  g_ba414e_status = getreg32(BA414E_PKSTATUS);
+  g_ba414e_fault  = (irq == PIC32MZ_IRQ_CRYPTO1F);
+  putreg32(0, BA414E_PKCONTROL);
+
+  up_disable_irq(PIC32MZ_IRQ_CRYPTO1);
+  up_disable_irq(PIC32MZ_IRQ_CRYPTO1F);
+  mips_clrpend_irq(irq);
+
+  nxsem_post(&g_ba414e_done);
+  return OK;
+}
+
+/* Program the operation; the operands are written afterwards, since the
+ * slot size follows from the operand size.
+ */
+
+static void ba414e_setup(int opcode, int opsize, uint32_t config)
+{
+  putreg32(0, BA414E_PKCONTROL);
+  up_disable_irq(PIC32MZ_IRQ_CRYPTO1);
+  up_disable_irq(PIC32MZ_IRQ_CRYPTO1F);
+  mips_clrpend_irq(PIC32MZ_IRQ_CRYPTO1);
+  mips_clrpend_irq(PIC32MZ_IRQ_CRYPTO1F);
+
+  ba414e_clear_scm();
+
+  putreg32(config, BA414E_PKCONFIG);
+  putreg32(PKCOMMAND_OPERATION(opcode) | PKCOMMAND_OPSIZE(opsize) |
+           PKCOMMAND_CALCR2, BA414E_PKCOMMAND);
+}
+
+static int ba414e_run(void)
+{
+  int ret;
+
+  /* Drain a stale completion */
+
+  while (nxsem_trywait(&g_ba414e_done) == OK);
+
+  g_ba414e_fault = false;
+  up_enable_irq(PIC32MZ_IRQ_CRYPTO1F);
+  up_enable_irq(PIC32MZ_IRQ_CRYPTO1);
+  putreg32(PKCONTROL_START, BA414E_PKCONTROL);
+
+  ret = nxsem_tickwait_uninterruptible(&g_ba414e_done, BA414E_TIMEOUT);
+  if (ret < 0)
+    {
+      putreg32(0, BA414E_PKCONTROL);
+      up_disable_irq(PIC32MZ_IRQ_CRYPTO1);
+      up_disable_irq(PIC32MZ_IRQ_CRYPTO1F);
+      syslog(LOG_ERR, "ba414e: timeout, status %08" PRIx32 "\n",
+             getreg32(BA414E_PKSTATUS));
+      return -ETIMEDOUT;
+    }
+
+  return OK;
+}
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_SELFTEST
+static void ba414e_selftest(void);
+#endif
+
+/* Take the engine; load the microcode on first use */
+
+static int ba414e_lock(void)
+{
+  FAR const uint32_t *ucode;
+
+  nxrmutex_lock(&g_ba414e_lock);
+  if (g_ba414e_loaded)
+    {
+      return OK;
+    }
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_UCODE_FILE
+  ucode = ba414e_read_ucode();
+  if (ucode == NULL)
+    {
+      nxrmutex_unlock(&g_ba414e_lock);
+      return -ENOENT;
+    }
+#else
+  ucode = g_ba414e_ucode;
+#endif
+
+  putreg32(0, BA414E_PKCONTROL);
+  ba414e_load_ucode(ucode);
+  ba414e_clear_scm();
+  g_ba414e_loaded = true;
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_UCODE_FILE
+  kmm_free((FAR void *)ucode);
+#endif
+
+  syslog(LOG_INFO, "ba414e: microcode loaded\n");
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_SELFTEST
+  ba414e_selftest();
+#endif
+
+  return OK;
+}
+
+static int ba414e_modop(int opcode, FAR uint8_t *c, FAR const uint8_t *a,
+                        FAR const uint8_t *b, FAR const uint8_t *p, int len)
+{
+  int opsize = ba414e_opsize(len);
+  int ret;
+
+  if (len <= 0 || len > BA414E_MAX_BYTES)
+    {
+      return -EINVAL;
+    }
+
+  ret = ba414e_lock();
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ba414e_setup(opcode, opsize, PKCONFIG_OPPTRA(MOD_SLOT_A) |
+               PKCONFIG_OPPTRB(MOD_SLOT_B) | PKCONFIG_OPPTRC(MOD_SLOT_C));
+  ba414e_write_slot(MOD_SLOT_P, p, len, opsize * 8);
+  ba414e_write_slot(MOD_SLOT_A, a, len, opsize * 8);
+  ba414e_write_slot(MOD_SLOT_B, b, len, opsize * 8);
+
+  ret = ba414e_run();
+  if (ret == OK)
+    {
+      if (g_ba414e_fault)
+        {
+          ret = -EIO;
+        }
+      else
+        {
+          ba414e_read_slot(MOD_SLOT_C, c, len);
+        }
+    }
+
+  nxrmutex_unlock(&g_ba414e_lock);
+  return ret;
+}
+
+/* Common part of the ECC primitives: load the curve and P1 */
+
+static void ba414e_ecc_setup(FAR const struct ba414e_curve_s *curve,
+                             int opcode, uint32_t config,
+                             FAR const uint8_t *px, FAR const uint8_t *py)
+{
+  int opsize = ba414e_opsize(curve->len);
+  int slotlen = opsize * 8;
+
+  ba414e_setup(opcode, opsize, config);
+  ba414e_write_slot(ECC_SLOT_P,   curve->p,  curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_N,   curve->n,  curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_GX,  curve->gx, curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_GY,  curve->gy, curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_A,   curve->a,  curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_B,   curve->b,  curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_P1X, px,        curve->len, slotlen);
+  ba414e_write_slot(ECC_SLOT_P1Y, py,        curve->len, slotlen);
+}
+
+/* Run an ECC operation with result P3 */
+
+static int ba414e_ecc_finish(FAR const struct ba414e_curve_s *curve,
+                             FAR uint8_t *rx, FAR uint8_t *ry)
+{
+  int ret = ba414e_run();
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* [EX] PXINF flags the point at infinity, also on the fault path */
+
+  if (g_ba414e_status & PKSTATUS_PXINF)
+    {
+      return BA414E_POINT_AT_INF;
+    }
+
+  if (g_ba414e_fault)
+    {
+      return -EIO;
+    }
+
+  ba414e_read_slot(ECC_SLOT_P3X, rx, curve->len);
+  ba414e_read_slot(ECC_SLOT_P3Y, ry, curve->len);
+  return BA414E_OK;
+}
+
+#ifdef CONFIG_PIC32MZ_W1_BA414E_SELFTEST
+static inline uint32_t ba414e_count(void)
+{
+  uint32_t count;
+
+  __asm__ __volatile__("mfc0 %0, $9" : "=r"(count));
+  return count;
+}
+
+static void ba414e_report(FAR const char *name, bool ok, uint32_t start)
+{
+  /* CP0 Count runs at SYSCLK / 2 */
+
+  syslog(LOG_INFO, "ba414e: %-8s %s %6" PRIu32 " us\n", name,
+         ok ? "ok  " : "FAIL",
+         (ba414e_count() - start) / (BOARD_CPU_CLOCK / 2000000));
+}
+
+static void ba414e_selftest(void)
+{
+  FAR const struct ba414e_curve_s *c = &g_ba414e_p256;
+  static const uint8_t k[32] =
+  {
+    0x78, 0x56, 0x34, 0x12, 0xef, 0xbe, 0xad, 0xde
+  };
+
+  uint8_t one[32];
+  uint8_t x[32];
+  uint8_t y[32];
+  uint8_t e[32];
+  uint32_t t;
+  int ret;
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_ecc_check(c, c->gx, c->gy);
+  ba414e_report("oncurve", ret == BA414E_OK, t);
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_ecc_check(c, c->gx, c->gx);
+  ba414e_report("offcurve", ret == BA414E_NOT_ON_CURVE, t);
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_ecc_add(c, x, y, c->gx, c->gy, c->gx, c->gy);
+  ba414e_report("double", ret == BA414E_OK &&
+                memcmp(x, g_st_2gx, 32) == 0 &&
+                memcmp(y, g_st_2gy, 32) == 0, t);
+
+  /* 2G + G + G = 4G */
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_ecc_add(c, x, y, g_st_2gx, g_st_2gy, c->gx, c->gy);
+  if (ret == BA414E_OK)
+    {
+      ret = pic32mz_ba414e_ecc_add(c, x, y, x, y, c->gx, c->gy);
+    }
+
+  ba414e_report("add", ret == BA414E_OK &&
+                memcmp(x, g_st_4gx, 32) == 0, t);
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_ecc_mul(c, x, y, c->gx, c->gy, k);
+  ba414e_report("mul", ret == BA414E_OK &&
+                memcmp(x, g_st_kgx, 32) == 0 &&
+                memcmp(y, g_st_kgy, 32) == 0, t);
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_ecc_mul(c, x, y, c->gx, c->gy, c->n);
+  ba414e_report("mul n", ret == BA414E_POINT_AT_INF, t);
+
+  /* 3^(p-2) mod p, then 3 * 3^-1 mod p = 1 */
+
+  memset(x, 0, sizeof(x));
+  x[0] = 3;
+  memcpy(e, c->p, 32);
+  e[0] -= 2;
+  t = ba414e_count();
+  ret = pic32mz_ba414e_modexp(y, x, e, c->p, 32);
+  ba414e_report("modexp", ret == BA414E_OK &&
+                memcmp(y, g_st_inv3, 32) == 0, t);
+
+  memset(one, 0, sizeof(one));
+  one[0] = 1;
+  t = ba414e_count();
+  ret = pic32mz_ba414e_modmul(e, x, g_st_inv3, c->p, 32);
+  ba414e_report("modmul", ret == BA414E_OK &&
+                memcmp(e, one, 32) == 0, t);
+
+  /* (p - 1) + 1 = 0, 0 - 1 = p - 1 */
+
+  memcpy(e, c->p, 32);
+  e[0] -= 1;
+  t = ba414e_count();
+  ret = pic32mz_ba414e_modadd(x, e, one, c->p, 32);
+  memset(y, 0, sizeof(y));
+  ba414e_report("modadd", ret == BA414E_OK && memcmp(x, y, 32) == 0, t);
+
+  t = ba414e_count();
+  ret = pic32mz_ba414e_modsub(x, y, one, c->p, 32);
+  ba414e_report("modsub", ret == BA414E_OK && memcmp(x, e, 32) == 0, t);
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int pic32mz_ba414e_initialize(void)
+{
+  if (getreg32(PIC32MZ_PMD1) & PMD1_BA414MD)
+    {
+      syslog(LOG_ERR, "ba414e: disabled in PMD1\n");
+      return -ENODEV;
+    }
+
+  putreg32(0, BA414E_PKCONTROL);
+  irq_attach(PIC32MZ_IRQ_CRYPTO1, ba414e_isr, NULL);
+  irq_attach(PIC32MZ_IRQ_CRYPTO1F, ba414e_isr, NULL);
+
+  /* The microcode is loaded on first use, when its file system may be
+   * mounted.
+   */
+
+  return OK;
+}
+
+int pic32mz_ba414e_modadd(FAR uint8_t *c, FAR const uint8_t *a,
+                          FAR const uint8_t *b, FAR const uint8_t *p,
+                          int len)
+{
+  return ba414e_modop(OPC_MOD_ADD, c, a, b, p, len);
+}
+
+int pic32mz_ba414e_modsub(FAR uint8_t *c, FAR const uint8_t *a,
+                          FAR const uint8_t *b, FAR const uint8_t *p,
+                          int len)
+{
+  return ba414e_modop(OPC_MOD_SUB, c, a, b, p, len);
+}
+
+int pic32mz_ba414e_modmul(FAR uint8_t *c, FAR const uint8_t *a,
+                          FAR const uint8_t *b, FAR const uint8_t *p,
+                          int len)
+{
+  return ba414e_modop(OPC_MOD_MULT, c, a, b, p, len);
+}
+
+int pic32mz_ba414e_modexp(FAR uint8_t *c, FAR const uint8_t *m,
+                          FAR const uint8_t *e, FAR const uint8_t *n,
+                          int len)
+{
+  int opsize = ba414e_opsize(len);
+  int ret;
+
+  if (len <= 0 || len > BA414E_MAX_BYTES)
+    {
+      return -EINVAL;
+    }
+
+  ret = ba414e_lock();
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ba414e_setup(OPC_MOD_EXP, opsize, PKCONFIG_OPPTRA(EXP_SLOT_M) |
+               PKCONFIG_OPPTRB(EXP_SLOT_E) | PKCONFIG_OPPTRC(EXP_SLOT_C));
+  ba414e_write_slot(EXP_SLOT_N, n, len, opsize * 8);
+  ba414e_write_slot(EXP_SLOT_M, m, len, opsize * 8);
+  ba414e_write_slot(EXP_SLOT_E, e, len, opsize * 8);
+
+  ret = ba414e_run();
+  if (ret == OK)
+    {
+      if (g_ba414e_fault)
+        {
+          ret = -EIO;
+        }
+      else
+        {
+          ba414e_read_slot(EXP_SLOT_C, c, len);
+        }
+    }
+
+  nxrmutex_unlock(&g_ba414e_lock);
+  return ret;
+}
+
+int pic32mz_ba414e_ecc_add(FAR const struct ba414e_curve_s *curve,
+                           FAR uint8_t *rx, FAR uint8_t *ry,
+                           FAR const uint8_t *px, FAR const uint8_t *py,
+                           FAR const uint8_t *qx, FAR const uint8_t *qy)
+{
+  int slotlen = ba414e_opsize(curve->len) * 8;
+  int ret;
+
+  ret = ba414e_lock();
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* [EX] The addition primitive needs distinct points: use the doubling
+   * primitive for P + P.
+   */
+
+  if (memcmp(px, qx, curve->len) == 0 && memcmp(py, qy, curve->len) == 0)
+    {
+      ba414e_ecc_setup(curve, OPC_ECC_DOUBLE,
+                       PKCONFIG_OPPTRA(ECC_SLOT_P1X) |
+                       PKCONFIG_OPPTRC(ECC_SLOT_P3X), px, py);
+    }
+  else
+    {
+      ba414e_ecc_setup(curve, OPC_ECC_ADD,
+                       PKCONFIG_OPPTRA(ECC_SLOT_P1X) |
+                       PKCONFIG_OPPTRB(ECC_SLOT_P2X) |
+                       PKCONFIG_OPPTRC(ECC_SLOT_P3X), px, py);
+      ba414e_write_slot(ECC_SLOT_P2X, qx, curve->len, slotlen);
+      ba414e_write_slot(ECC_SLOT_P2Y, qy, curve->len, slotlen);
+    }
+
+  ret = ba414e_ecc_finish(curve, rx, ry);
+  nxrmutex_unlock(&g_ba414e_lock);
+  return ret;
+}
+
+int pic32mz_ba414e_ecc_mul(FAR const struct ba414e_curve_s *curve,
+                           FAR uint8_t *rx, FAR uint8_t *ry,
+                           FAR const uint8_t *px, FAR const uint8_t *py,
+                           FAR const uint8_t *k)
+{
+  int slotlen = ba414e_opsize(curve->len) * 8;
+  int ret;
+
+  ret = ba414e_lock();
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ba414e_ecc_setup(curve, OPC_ECC_MULT,
+                   PKCONFIG_OPPTRA(ECC_SLOT_P1X) |
+                   PKCONFIG_OPPTRB(ECC_SLOT_K) |
+                   PKCONFIG_OPPTRC(ECC_SLOT_P3X), px, py);
+  ba414e_write_slot(ECC_SLOT_K, k, curve->len, slotlen);
+
+  ret = ba414e_ecc_finish(curve, rx, ry);
+  nxrmutex_unlock(&g_ba414e_lock);
+  return ret;
+}
+
+int pic32mz_ba414e_ecc_check(FAR const struct ba414e_curve_s *curve,
+                             FAR const uint8_t *px, FAR const uint8_t *py)
+{
+  int ret;
+
+  ret = ba414e_lock();
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ba414e_ecc_setup(curve, OPC_ECC_ONCURVE,
+                   PKCONFIG_OPPTRA(ECC_SLOT_P1X), px, py);
+
+  ret = ba414e_run();
+  if (ret == OK)
+    {
+      /* [EX] PXNOC takes precedence over the fault indication */
+
+      if (g_ba414e_status & PKSTATUS_PXNOC)
+        {
+          ret = BA414E_NOT_ON_CURVE;
+        }
+      else if (g_ba414e_fault)
+        {
+          ret = -EIO;
+        }
+    }
+
+  nxrmutex_unlock(&g_ba414e_lock);
+  return ret;
+}
diff --git a/arch/mips/src/pic32mz/pic32mz_ba414e.h 
b/arch/mips/src/pic32mz/pic32mz_ba414e.h
new file mode 100644
index 00000000000..3531105f988
--- /dev/null
+++ b/arch/mips/src/pic32mz/pic32mz_ba414e.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+ * arch/mips/src/pic32mz/pic32mz_ba414e.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_BA414E_H
+#define __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_BA414E_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Largest operand: 512 bits */
+
+#define BA414E_MAX_BYTES        64
+
+/* Operation results (beyond negated errno values) */
+
+#define BA414E_OK               0
+#define BA414E_POINT_AT_INF     1   /* ECC result is the point at infinity */
+#define BA414E_NOT_ON_CURVE     2   /* Point is not on the curve */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Short Weierstrass curve y^2 = x^3 + ax + b over GF(p).  All values are
+ * little endian, 'len' bytes long.
+ */
+
+struct ba414e_curve_s
+{
+  uint8_t len;
+  FAR const uint8_t *p;
+  FAR const uint8_t *n;
+  FAR const uint8_t *gx;
+  FAR const uint8_t *gy;
+  FAR const uint8_t *a;
+  FAR const uint8_t *b;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* NIST P-256 (FIPS 186-4 D.1.2.3), little endian */
+
+extern const struct ba414e_curve_s g_ba414e_p256;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/* All operands and results are little endian, 'len' bytes (at most
+ * BA414E_MAX_BYTES).  The functions block until the engine is done and
+ * return BA414E_OK (or one of the ECC results above) or a negated errno.
+ */
+
+int pic32mz_ba414e_initialize(void);
+
+int pic32mz_ba414e_modadd(FAR uint8_t *c, FAR const uint8_t *a,
+                          FAR const uint8_t *b, FAR const uint8_t *p,
+                          int len);
+int pic32mz_ba414e_modsub(FAR uint8_t *c, FAR const uint8_t *a,
+                          FAR const uint8_t *b, FAR const uint8_t *p,
+                          int len);
+int pic32mz_ba414e_modmul(FAR uint8_t *c, FAR const uint8_t *a,
+                          FAR const uint8_t *b, FAR const uint8_t *p,
+                          int len);
+int pic32mz_ba414e_modexp(FAR uint8_t *c, FAR const uint8_t *m,
+                          FAR const uint8_t *e, FAR const uint8_t *n,
+                          int len);
+
+int pic32mz_ba414e_ecc_add(FAR const struct ba414e_curve_s *curve,
+                           FAR uint8_t *rx, FAR uint8_t *ry,
+                           FAR const uint8_t *px, FAR const uint8_t *py,
+                           FAR const uint8_t *qx, FAR const uint8_t *qy);
+int pic32mz_ba414e_ecc_mul(FAR const struct ba414e_curve_s *curve,
+                           FAR uint8_t *rx, FAR uint8_t *ry,
+                           FAR const uint8_t *px, FAR const uint8_t *py,
+                           FAR const uint8_t *k);
+int pic32mz_ba414e_ecc_check(FAR const struct ba414e_curve_s *curve,
+                             FAR const uint8_t *px, FAR const uint8_t *py);
+
+#endif /* __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_BA414E_H */
diff --git a/arch/mips/src/pic32mz/pic32mz_w1_wlan.c 
b/arch/mips/src/pic32mz/pic32mz_w1_wlan.c
index 7bff1abbb37..e7e25163fbb 100644
--- a/arch/mips/src/pic32mz/pic32mz_w1_wlan.c
+++ b/arch/mips/src/pic32mz/pic32mz_w1_wlan.c
@@ -26,6 +26,7 @@
  * kernel thread, three RF interrupts and a byte-coded configuration
  * protocol ("WIDs").  The network device is a netdev_upperhalf lower half
  * with the wireless operations used by wapi (scan, essid, psk, mode, freq).
+ * WPA3-Personal needs the BA414E engine (pic32mz_ba414e.c).
  *
  * Every hardware or library fact below is tagged with where it comes from:
  *
@@ -73,6 +74,9 @@
 #include "mips_internal.h"
 #include "hardware/pic32mzw1_pmuclk.h"
 #include "pic32mz_w1_wlan.h"
+#ifdef CONFIG_PIC32MZ_W1_BA414E
+#  include "pic32mz_ba414e.h"
+#endif
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -141,6 +145,7 @@
 #define WID_GET_SCAN_RESULTS    0x3034
 #define WID_REG_DOMAIN          0x4010
 #define WID_STA_JOIN_INFO       0x4008
+#define WID_RSNA_PASSWORD       0x4012
 #define WID_SSID                0x4020
 
 #define WID_TYPE(w)             ((w) >> 12)
@@ -161,7 +166,9 @@
 #define DOT11I_CCMP128          0x0040
 #define DOT11I_TKIP             0x0080
 #define DOT11I_BIPCMAC128       0x0100
+#define DOT11I_MFP_REQUIRED     0x0200
 #define DOT11I_PSK              0x0800
+#define DOT11I_SAE              0x1000
 #define DOT11I_AP               0x8000
 
 /* [EX] Harmony's WPA2-Personal and WPA/WPA2-Personal settings */
@@ -172,6 +179,14 @@
 #define DOT11I_WPAWPA2_PERSONAL \
   (DOT11I_WPA2_PERSONAL | DOT11I_WPAIE | DOT11I_TKIP)
 
+/* [EX] Harmony's WPA3-Personal settings (SAE, management frame protection
+ * required)
+ */
+
+#define DOT11I_WPA3_PERSONAL \
+  (DOT11I_PRIVACY | DOT11I_RSNE | DOT11I_CCMP128 | DOT11I_BIPCMAC128 | \
+   DOT11I_MFP_REQUIRED | DOT11I_SAE)
+
 /* Soft-AP channel when none was requested */
 
 #define WLAN_AP_CHANNEL         1
@@ -188,6 +203,8 @@
 #define WLAN_RX_QUOTA           8
 #define WLAN_TX_QUOTA           4
 
+#define WLAN_CRYPTO_DEFER_MAX   8
+
 #define IW_EVENT_SIZE(field) \
   (offsetof(struct iw_event, u) + sizeof(((union iwreq_data *)0)->field))
 
@@ -217,6 +234,13 @@ struct wid_msg_s
   char op;                      /* 'W' write or 'Q' query */
 };
 
+struct wlan_crypto_defer_s
+{
+  wlan_crypto_cb_t cb;
+  int result;
+  uintptr_t context;
+};
+
 enum wlan_scan_e
 {
   WLAN_SCAN_IDLE = 0,
@@ -241,6 +265,12 @@ struct pic32mz_wlan_s
   bool ready;
   uint8_t mac[6];
 
+  /* Deferred crypto completion callbacks (ring) */
+
+  struct wlan_crypto_defer_s defer[WLAN_CRYPTO_DEFER_MAX];
+  uint8_t defer_head;
+  uint8_t defer_tail;
+
   /* Network device */
 
   spinlock_t rxlock;
@@ -801,6 +831,30 @@ static void wlan_send_init(void)
     }
 }
 
+/* Run the deferred crypto callbacks; called with the library lock held */
+
+static void wlan_crypto_run(void)
+{
+  struct wlan_crypto_defer_s d;
+  irqstate_t flags;
+
+  for (; ; )
+    {
+      flags = enter_critical_section();
+      if (g_wlan.defer_head == g_wlan.defer_tail)
+        {
+          leave_critical_section(flags);
+          break;
+        }
+
+      d = g_wlan.defer[g_wlan.defer_tail];
+      g_wlan.defer_tail = (g_wlan.defer_tail + 1) % WLAN_CRYPTO_DEFER_MAX;
+      leave_critical_section(flags);
+
+      d.cb(d.result, d.context);
+    }
+}
+
 static int wlan_thread(int argc, FAR char *argv[])
 {
   FAR sq_entry_t *entry;
@@ -827,6 +881,7 @@ static int wlan_thread(int argc, FAR char *argv[])
           wdrv_pic32mzw_process_cfg_message(wlan_data(entry));
         }
 
+      wlan_crypto_run();
       wdrv_pic32mzw_mac_controller_task();
       nxmutex_unlock(&g_wlan.lock);
 
@@ -946,6 +1001,12 @@ static int wlan_dot11i(FAR uint32_t *dot11i)
         *dot11i = DOT11I_WPA2_PERSONAL;
         break;
 
+#ifdef CONFIG_PIC32MZ_W1_BA414E
+      case IW_AUTH_WPA_VERSION_WPA3:
+        *dot11i = DOT11I_WPA3_PERSONAL;
+        break;
+#endif
+
       default:
         wlerr("WPA version %" PRIu32 " not supported\n", g_wlan.wpaver);
         return -ENOTSUP;
@@ -987,6 +1048,11 @@ static int wlan_apstart(uint32_t dot11i)
       wid_data(&msg, WID_11I_PSK, g_wlan.psk, g_wlan.psklen);
     }
 
+  if (dot11i & DOT11I_SAE)
+    {
+      wid_data(&msg, WID_RSNA_PASSWORD, g_wlan.psk, g_wlan.psklen);
+    }
+
   wid_value(&msg, WID_11G_OPERATING_MODE, 2);
   wid_value(&msg, WID_ACK_POLICY, 0);
   wid_value(&msg, WID_11N_ENABLE, 1);
@@ -1045,6 +1111,11 @@ static int wlan_connect(FAR struct netdev_lowerhalf_s 
*dev)
       wid_data(&msg, WID_11I_PSK, g_wlan.psk, g_wlan.psklen);
     }
 
+  if (dot11i & DOT11I_SAE)
+    {
+      wid_data(&msg, WID_RSNA_PASSWORD, g_wlan.psk, g_wlan.psklen);
+    }
+
   wid_value(&msg, WID_11G_OPERATING_MODE, 2);
   wid_value(&msg, WID_ACK_POLICY, 0);
   wid_value(&msg, WID_11N_ENABLE, 1);
@@ -1617,6 +1688,39 @@ int pic32mzw1_putchar(int c)
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: pic32mz_wlan_crypto_defer
+ *
+ * Description:
+ *   Queue a crypto completion callback; the WLAN thread runs it with the
+ *   library lock held ([EX] as Harmony's DRV_PIC32MZW_CryptoCallbackPush()).
+ *
+ ****************************************************************************/
+
+void pic32mz_wlan_crypto_defer(wlan_crypto_cb_t cb, int result,
+                               uintptr_t context)
+{
+  irqstate_t flags;
+  uint8_t next;
+
+  flags = enter_critical_section();
+  next = (g_wlan.defer_head + 1) % WLAN_CRYPTO_DEFER_MAX;
+  if (next == g_wlan.defer_tail)
+    {
+      leave_critical_section(flags);
+      wlerr("crypto callback queue full\n");
+      return;
+    }
+
+  g_wlan.defer[g_wlan.defer_head].cb      = cb;
+  g_wlan.defer[g_wlan.defer_head].result  = result;
+  g_wlan.defer[g_wlan.defer_head].context = context;
+  g_wlan.defer_head = next;
+  leave_critical_section(flags);
+
+  nxsem_post(&g_wlan.evsem);
+}
+
 /****************************************************************************
  * Name: pic32mz_wlan_initialize
  *
@@ -1645,6 +1749,14 @@ int pic32mz_wlan_initialize(void)
 
   g_wlan.wpaver = IW_AUTH_WPA_VERSION_DISABLED;
 
+#ifdef CONFIG_PIC32MZ_W1_BA414E
+  ret = pic32mz_ba414e_initialize();
+  if (ret < 0)
+    {
+      return ret;
+    }
+#endif
+
   /* [EX] Turn the WLAN LDO on */
 
   modifyreg32(PIC32MZ_PMUCLKCTRL, PMUCLKCTRL_WLDOOFF, 0);
diff --git a/arch/mips/src/pic32mz/pic32mz_w1_wlan.h 
b/arch/mips/src/pic32mz/pic32mz_w1_wlan.h
index 5493f8ffefe..d7c084130f1 100644
--- a/arch/mips/src/pic32mz/pic32mz_w1_wlan.h
+++ b/arch/mips/src/pic32mz/pic32mz_w1_wlan.h
@@ -40,6 +40,10 @@
  * in pic32mz_w1_wlan.c.
  */
 
+/* Completion callback of the crypto services (DRV_PIC32MZW_CRYPTO_CB) */
+
+typedef CODE void (*wlan_crypto_cb_t)(int result, uintptr_t context);
+
 struct pic32mz_wlan_init_s
 {
   int alarm_1ms;
@@ -97,4 +101,9 @@ int pic32mzw1_putchar(int c);
 
 int pic32mz_wlan_initialize(void);
 
+/* Run a crypto completion callback later from the WLAN thread */
+
+void pic32mz_wlan_crypto_defer(wlan_crypto_cb_t cb, int result,
+                               uintptr_t context);
+
 #endif /* __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_W1_WLAN_H */
diff --git a/arch/mips/src/pic32mz/pic32mz_w1_wlan_crypto.c 
b/arch/mips/src/pic32mz/pic32mz_w1_wlan_crypto.c
index f426cca58a6..79328faa619 100644
--- a/arch/mips/src/pic32mz/pic32mz_w1_wlan_crypto.c
+++ b/arch/mips/src/pic32mz/pic32mz_w1_wlan_crypto.c
@@ -24,8 +24,15 @@
  * ([EX] interface of drv_pic32mzw1_crypto.c, see pic32mz_w1_wlan.c for the
  * tags).  Random, hash and HMAC, all that WPA/WPA2-Personal needs, use
  * NuttX's software crypto.  The big number and elliptic curve operations
- * (WPA3-Personal), DES (MSCHAPv2) and TLS (enterprise) are not implemented
- * and report an error.
+ * of WPA3-Personal (SAE) use the BA414E engine (CONFIG_PIC32MZ_W1_BA414E),
+ * without it they report an error.  DES (MSCHAPv2) and TLS (enterprise)
+ * are not implemented and report an error.
+ *
+ * [EX] When the library passes a completion callback, the operation must
+ * return "pending" and the callback runs later from the WLAN thread, with
+ * the library lock held (Harmony's DRV_PIC32MZW_CryptoCallbackPush()).  The
+ * engine is fast, so the operation completes here and only the callback is
+ * deferred.
  */
 
 /****************************************************************************
@@ -45,6 +52,9 @@
 #include <crypto/sha2.h>
 
 #include "pic32mz_w1_wlan.h"
+#ifdef CONFIG_PIC32MZ_W1_BA414E
+#  include "pic32mz_ba414e.h"
+#endif
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -62,9 +72,18 @@
 /* [EX] Return codes (DRV_PIC32MZW_CRYPTO_RETURN_T) */
 
 #define WLAN_CRYPTO_COMPLETE    0
+#define WLAN_CRYPTO_PENDING     1
 #define WLAN_CRYPTO_INVALID     3
 #define WLAN_CRYPTO_ERROR       4
 
+/* [EX] Curve identifier of P-256 (DRV_PIC32MZW_CRYPTO_CURVE_P256R1) */
+
+#define WLAN_CURVE_P256         2
+
+/* Largest big number handled by BigIntMod */
+
+#define WLAN_BIGINT_MAX         128
+
 #define WLAN_HASH_MAXBLOCK      128
 #define WLAN_HASH_MAXDIGEST     64
 
@@ -349,8 +368,351 @@ int DRV_PIC32MZW_Crypto_HMAC(FAR const uint8_t *key, 
uint16_t keylen,
   return WLAN_CRYPTO_COMPLETE;
 }
 
+#ifdef CONFIG_PIC32MZ_W1_BA414E
+
+/* Big integer and elliptic curve operations ([EX] interface of Harmony's
+ * drv_pic32mzw1_crypto.c).  Values are 'len' bytes, big or little endian
+ * as requested; inputs and outputs may overlap.  The BA414E primitives
+ * take little endian values.
+ */
+
+static void wlan_to_le(FAR uint8_t *dst, FAR const uint8_t *src, int len,
+                       bool is_be)
+{
+  int i;
+
+  for (i = 0; i < len; i++)
+    {
+      dst[i] = is_be ? src[len - 1 - i] : src[i];
+    }
+}
+
+#define wlan_from_le(dst, src, len, is_be) wlan_to_le(dst, src, len, is_be)
+
+/* Report a result: complete now, or pending with a deferred callback */
+
+static int wlan_crypto_done(int ret, wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  if (ret < 0)
+    {
+      return WLAN_CRYPTO_ERROR;
+    }
+
+  if (cb == NULL)
+    {
+      return WLAN_CRYPTO_COMPLETE;
+    }
+
+  pic32mz_wlan_crypto_defer(cb, WLAN_CRYPTO_COMPLETE, ctx);
+  return WLAN_CRYPTO_PENDING;
+}
+
+/* out = ain mod m, by shift and subtract.  In software, as Harmony does:
+ * ain may be longer than the modulus.
+ */
+
+int DRV_PIC32MZW_Crypto_BigIntMod(FAR const uint8_t *mod, FAR uint8_t *out,
+                                  FAR const uint8_t *ain, uint16_t ain_len,
+                                  uint16_t len, bool is_be,
+                                  wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  uint8_t m[WLAN_BIGINT_MAX + 1];
+  uint8_t a[WLAN_BIGINT_MAX];
+  uint8_t r[WLAN_BIGINT_MAX + 1];
+  int bit;
+  int v;
+  int i;
+  int c;
+
+  if (mod == NULL || out == NULL || ain == NULL || len == 0 ||
+      len > WLAN_BIGINT_MAX || ain_len > WLAN_BIGINT_MAX)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  wlan_to_le(m, mod, len, is_be);
+  m[len] = 0;
+  wlan_to_le(a, ain, ain_len, is_be);
+  memset(r, 0, sizeof(r));
+
+  for (bit = ain_len * 8 - 1; bit >= 0; bit--)
+    {
+      /* r = 2r + bit */
+
+      c = (a[bit / 8] >> (bit % 8)) & 1;
+      for (i = 0; i <= len; i++)
+        {
+          v    = (r[i] << 1) | c;
+          r[i] = v & 0xff;
+          c    = v >> 8;
+        }
+
+      /* if (r >= m) r -= m */
+
+      i = len;
+      while (i >= 0 && r[i] == m[i])
+        {
+          i--;
+        }
+
+      if (i < 0 || r[i] > m[i])
+        {
+          c = 0;
+          for (i = 0; i <= len; i++)
+            {
+              v    = r[i] - m[i] - c;
+              r[i] = v & 0xff;
+              c    = v < 0;
+            }
+        }
+    }
+
+  wlan_from_le(out, r, len, is_be);
+  return wlan_crypto_done(OK, cb, ctx);
+}
+
+typedef CODE int (*wlan_modop_t)(FAR uint8_t *c, FAR const uint8_t *a,
+                                 FAR const uint8_t *b, FAR const uint8_t *p,
+                                 int len);
+
+static int wlan_modop(wlan_modop_t op, FAR const uint8_t *mod,
+                      FAR uint8_t *out, FAR const uint8_t *ain,
+                      FAR const uint8_t *bin, uint16_t len, bool is_be,
+                      wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  uint8_t p[BA414E_MAX_BYTES];
+  uint8_t a[BA414E_MAX_BYTES];
+  uint8_t b[BA414E_MAX_BYTES];
+  uint8_t c[BA414E_MAX_BYTES];
+  int ret;
+
+  if (mod == NULL || out == NULL || ain == NULL || bin == NULL ||
+      len == 0 || len > BA414E_MAX_BYTES)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  wlan_to_le(p, mod, len, is_be);
+  wlan_to_le(a, ain, len, is_be);
+  wlan_to_le(b, bin, len, is_be);
+
+  ret = op(c, a, b, p, len);
+  if (ret >= 0)
+    {
+      wlan_from_le(out, c, len, is_be);
+    }
+
+  return wlan_crypto_done(ret, cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_BigIntModAdd(FAR const uint8_t *mod,
+                                     FAR uint8_t *out,
+                                     FAR const uint8_t *ain,
+                                     FAR const uint8_t *bin, uint16_t len,
+                                     bool is_be, wlan_crypto_cb_t cb,
+                                     uintptr_t ctx)
+{
+  return wlan_modop(pic32mz_ba414e_modadd, mod, out, ain, bin, len, is_be,
+                    cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_BigIntModSubtract(FAR const uint8_t *mod,
+                                          FAR uint8_t *out,
+                                          FAR const uint8_t *ain,
+                                          FAR const uint8_t *bin,
+                                          uint16_t len, bool is_be,
+                                          wlan_crypto_cb_t cb,
+                                          uintptr_t ctx)
+{
+  return wlan_modop(pic32mz_ba414e_modsub, mod, out, ain, bin, len, is_be,
+                    cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_BigIntModMultiply(FAR const uint8_t *mod,
+                                          FAR uint8_t *out,
+                                          FAR const uint8_t *ain,
+                                          FAR const uint8_t *bin,
+                                          uint16_t len, bool is_be,
+                                          wlan_crypto_cb_t cb,
+                                          uintptr_t ctx)
+{
+  return wlan_modop(pic32mz_ba414e_modmul, mod, out, ain, bin, len, is_be,
+                    cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_BigIntModExponentiate(FAR const uint8_t *mod,
+                                              FAR uint8_t *out,
+                                              FAR const uint8_t *base,
+                                              FAR const uint8_t *exp,
+                                              uint16_t len, bool is_be,
+                                              wlan_crypto_cb_t cb,
+                                              uintptr_t ctx)
+{
+  return wlan_modop(pic32mz_ba414e_modexp, mod, out, base, exp, len, is_be,
+                    cb, ctx);
+}
+
+static FAR const struct ba414e_curve_s *wlan_curve(int curve)
+{
+  return curve == WLAN_CURVE_P256 ? &g_ba414e_p256 : NULL;
+}
+
+/* [EX] Returns the curve's field (little endian), or NULL for an unknown
+ * curve
+ */
+
+FAR const uint8_t *DRV_PIC32MZW_Crypto_ECCGetField(int curve)
+{
+  FAR const struct ba414e_curve_s *c = wlan_curve(curve);
+
+  return c != NULL ? c->p : NULL;
+}
+
+int DRV_PIC32MZW_Crypto_ECCIsOnCurve(int curve, FAR bool *notoncurve,
+                                     FAR const uint8_t *px,
+                                     FAR const uint8_t *py, bool is_be,
+                                     wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  FAR const struct ba414e_curve_s *c = wlan_curve(curve);
+  uint8_t x[BA414E_MAX_BYTES];
+  uint8_t y[BA414E_MAX_BYTES];
+  int ret;
+
+  if (c == NULL || notoncurve == NULL || px == NULL || py == NULL)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  wlan_to_le(x, px, c->len, is_be);
+  wlan_to_le(y, py, c->len, is_be);
+
+  ret = pic32mz_ba414e_ecc_check(c, x, y);
+  *notoncurve = (ret == BA414E_NOT_ON_CURVE);
+  return wlan_crypto_done(ret, cb, ctx);
+}
+
+/* [EX] out = a * in and out = in + b (mod p), little endian */
+
+int DRV_PIC32MZW_Crypto_ECCBigIntModMultByA(int curve, FAR uint8_t *out,
+                                            FAR const uint8_t *in,
+                                            wlan_crypto_cb_t cb,
+                                            uintptr_t ctx)
+{
+  FAR const struct ba414e_curve_s *c = wlan_curve(curve);
+
+  if (c == NULL)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  return wlan_modop(pic32mz_ba414e_modmul, c->p, out, in, c->a, c->len,
+                    false, cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_ECCBigIntModAddB(int curve, FAR uint8_t *out,
+                                         FAR const uint8_t *in,
+                                         wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  FAR const struct ba414e_curve_s *c = wlan_curve(curve);
+
+  if (c == NULL)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  return wlan_modop(pic32mz_ba414e_modadd, c->p, out, in, c->b, c->len,
+                    false, cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_ECCAdd(int curve, FAR bool *is_infinity,
+                               FAR uint8_t *outx, FAR uint8_t *outy,
+                               FAR const uint8_t *pinx,
+                               FAR const uint8_t *piny,
+                               FAR const uint8_t *qinx,
+                               FAR const uint8_t *qiny, bool is_be,
+                               wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  FAR const struct ba414e_curve_s *c = wlan_curve(curve);
+  uint8_t px[BA414E_MAX_BYTES];
+  uint8_t py[BA414E_MAX_BYTES];
+  uint8_t qx[BA414E_MAX_BYTES];
+  uint8_t qy[BA414E_MAX_BYTES];
+  uint8_t rx[BA414E_MAX_BYTES];
+  uint8_t ry[BA414E_MAX_BYTES];
+  int ret;
+
+  if (c == NULL || is_infinity == NULL || outx == NULL || outy == NULL ||
+      pinx == NULL || piny == NULL || qinx == NULL || qiny == NULL)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  memset(px, 0, sizeof(px));
+  memset(py, 0, sizeof(py));
+  memset(qx, 0, sizeof(qx));
+  memset(qy, 0, sizeof(qy));
+
+  wlan_to_le(px, pinx, c->len, is_be);
+  wlan_to_le(py, piny, c->len, is_be);
+  wlan_to_le(qx, qinx, c->len, is_be);
+  wlan_to_le(qy, qiny, c->len, is_be);
+
+  ret = pic32mz_ba414e_ecc_add(c, rx, ry, px, py, qx, qy);
+  *is_infinity = (ret == BA414E_POINT_AT_INF);
+  if (ret == BA414E_OK)
+    {
+      wlan_from_le(outx, rx, c->len, is_be);
+      wlan_from_le(outy, ry, c->len, is_be);
+    }
+
+  return wlan_crypto_done(ret, cb, ctx);
+}
+
+int DRV_PIC32MZW_Crypto_ECCMultiply(int curve, FAR bool *is_infinity,
+                                    FAR uint8_t *outx, FAR uint8_t *outy,
+                                    FAR const uint8_t *pinx,
+                                    FAR const uint8_t *piny,
+                                    FAR const uint8_t *kin, bool is_be,
+                                    wlan_crypto_cb_t cb, uintptr_t ctx)
+{
+  FAR const struct ba414e_curve_s *c = wlan_curve(curve);
+  uint8_t px[BA414E_MAX_BYTES];
+  uint8_t py[BA414E_MAX_BYTES];
+  uint8_t k[BA414E_MAX_BYTES];
+  uint8_t rx[BA414E_MAX_BYTES];
+  uint8_t ry[BA414E_MAX_BYTES];
+  int ret;
+
+  if (c == NULL || is_infinity == NULL || outx == NULL || outy == NULL ||
+      pinx == NULL || piny == NULL || kin == NULL)
+    {
+      return WLAN_CRYPTO_INVALID;
+    }
+
+  memset(px, 0, sizeof(px));
+  memset(py, 0, sizeof(py));
+  memset(k, 0, sizeof(k));
+
+  wlan_to_le(px, pinx, c->len, is_be);
+  wlan_to_le(py, piny, c->len, is_be);
+  wlan_to_le(k, kin, c->len, is_be);
+
+  ret = pic32mz_ba414e_ecc_mul(c, rx, ry, px, py, k);
+  *is_infinity = (ret == BA414E_POINT_AT_INF);
+  if (ret == BA414E_OK)
+    {
+      wlan_from_le(outx, rx, c->len, is_be);
+      wlan_from_le(outy, ry, c->len, is_be);
+    }
+
+  return wlan_crypto_done(ret, cb, ctx);
+}
+
+#endif /* CONFIG_PIC32MZ_W1_BA414E */
+
 /* Not implemented: every call reports an error.  The library only uses
- * them for WPA3-Personal (SAE), MSCHAPv2 and enterprise TLS.
+ * them for WPA3-Personal (SAE, without the BA414E), MSCHAPv2 and
+ * enterprise TLS.
  */
 
 #define WLAN_CRYPTO_STUB(name) \
@@ -361,6 +723,8 @@ int DRV_PIC32MZW_Crypto_HMAC(FAR const uint8_t *key, 
uint16_t keylen,
   }
 
 WLAN_CRYPTO_STUB(DRV_PIC32MZW_Crypto_DES_Ecb_Crypt)
+
+#ifndef CONFIG_PIC32MZ_W1_BA414E
 WLAN_CRYPTO_STUB(DRV_PIC32MZW_Crypto_BigIntMod)
 WLAN_CRYPTO_STUB(DRV_PIC32MZW_Crypto_BigIntModAdd)
 WLAN_CRYPTO_STUB(DRV_PIC32MZW_Crypto_BigIntModSubtract)
@@ -379,6 +743,7 @@ FAR const uint8_t *DRV_PIC32MZW_Crypto_ECCGetField(int 
curve)
   syslog(LOG_ERR, "wlan: ECC not implemented\n");
   return NULL;
 }
+#endif
 
 #define WLAN_TLS_STUB(name) \
   int name(void) \

Reply via email to