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) \
