Download raw body.
add WPA3 support
hi,
On Sat, Jul 18, 2026 at 4:42 AM Stefan Sperling <stsp@stsp.name> wrote:
>
> The patch below adds support for WPA3 to the net80211 subsystem.
>
> This effort is supported by the NLnet Foundation's NGI0 Commons Fund.
> Without such support, I would not have been able to invest the time
> required to work on this. I am very grateful to be given this opportunity.
>
> More review will be needed to ensure that this implementation of WPA3
> is free of bugs. Please keep this in mind while running this patch.
> NLnet will arrange an independent security audit once we consider our
> part of this work complete.
>
> All drivers which support PMF can use WPA3, which are: iwm, iwx, and qwx
> So far, I have tested this patch on iwx AX200 only. I will roll out
> this patch to more of my devices now. Help with testing is welcome.
>
> There are both userland and kernel changes involved.
>
> First, rebuild ifconfig:
>
> cd /usr/src/
> make obj
> doas make includes
> cd sbin/ifconfig
> make
> doas make install
>
> Now rebuild the kernel and install it as usual, and reboot.
>
> Configuring WPA3 network with ifconfig works just like WPA2 does.
> The SAE handshake is suitable for password authentication only.
> "WPA3 Enterprise" setups are out of scope.
>
> Snapshot test builds on all supported platforms would be welcome.
tested on my lenovo a485 with
iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless-AC 9260" rev 0x29, msix
and qualcomm ipq5332/ath12k-based AP. works fine, and i verified
AP-side WPA3/SAE is in use.
mischief@a485.home.arpa:/home/mischief $ ifconfig iwm0
iwm0: flags=a48843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF6TEMP,AUTOCONF6,AUTOCONF4>
mtu 1500
lladdr f8:ac:65:fb:39:fc
index 1 priority 4 llprio 3
groups: wlan egress
media: IEEE802.11 autoselect (VHT-MCS6 mode 11ac)
status: active
ieee80211: join labratory chan 36 bssid c4:a8:16:cd:b5:c7 76%
wpakey wpaprotos wpa2,wpa3 wpaakms psk,sha256-psk,sae wpaciphers ccmp
wpagroupcipher ccmp
inet6 fe80::faac:65ff:fefb:39fc%iwm0 prefixlen 64 scopeid 0x1
inet 192.168.0.32 netmask 0xfffffc00 broadcast 192.168.3.255
one comment way down below in sys/net80211/ieee80211_input.c
> This patch adds WPA3 support to all net80211-enabled kernels, so they
> will all grow a bit. I don't expect huge growth since the code is kept
> as small as possible. It's a cost we will have to carry unless we want
> this to be under infdef SMALL. But I would prefer to be always present.
>
> However, the patch only adds WPA3 support to ifconfig in an installed
> system. The ramdisk/bsd.rd version of ifconfig will not support WPA3 yet.
> The ifconfig binary grows a lot (ifconfig now links to libcrypto) and could
> cause trouble for building snapshots. I am postponing this part for now,
> and would prefer to work this problem out in-tree.
>
> WPA3 has a complicated history. There are two versions of WPA3.
> The initially standardized version suffered from side-channel leaks
> found by Mathy Vanhoef and dubbed "Dragonblood". For details, see
> https://wpa3.mathyvanhoef.com/ A revised and fixed version has been
> standardized and is mandatory in the 6 GHz band as of Wifi 6e (11ax) and
> mandatory on all bands as of Wifi 7 (11be).
>
> The password-derivation strategy used in the initial version of WPA3 is
> known as "hunting-and-pecking". The revised version uses a "hash to element"
> strategy instead. For details about the differences, see:
> https://wizardfi.com/security/2024/03/29/hash-to-curve.html
>
> I am only adding support for the revised version. Access points which
> use the initial version of WPA3 will remain incompatible with OpenBSD.
>
> Apart from being side-channel-free, the revised version has the advantage
> that some computations required for the SAE handshake can be done ahead
> of time, provided the network name and password are known. Which is the
> case in our version of ifconfig, just like it was with WPA2.
>
> The initial WPA3 version required knowledge of the peer's MAC address before
> anything could be computed. This pushes all the work to the point in time
> when a connection attempt is made. For us, this is inside the kernel.
>
> Since we handle the WPA handshake in the kernel, being able to pre-compute
> values in ifconfig is a huge simplification for us. It avoids adding a lot
> more crypto code to the kernel, and keeps the kernel code's complexity low.
>
> The kernel-side crypto used in this implementation is based on stripped
> down code lifted from BearSSL. Several alternatives were evaluated with
> help from Theo Buehler (tb@) and we ended up settling on this approach.
>
> ifconfig uses code that calls into libcrypto, and most of this was modeled
> on code found in the well-known w1.fi hostapd/wpa_supplicant implementation.
>
> I don't expect that requiring "hash-to-element" will be a huge problem
> because we are now late enough in the history of WPA3. Supporting the
> initial version would have downsides for us. The "hunting-and-pecking"
> approach is known to be vulnerable. The workarounds for this problem are
> costly and we would have to carry these workarounds in the kernel as an
> entirely separate implementation of the SAE handshake. Adding such
> complexity only to support known-broken devices is not worth it to me.
> Broken access points should either be patched (reasaonable vendors should
> be providing software upgrades) or be run in WPA3/WPA2 mixed mode, such
> that we can connect with WPA2.
>
> Diff follows:
>
>
> M sbin/ifconfig/Makefile | 3+ 3-
> A sbin/ifconfig/const_time.h | 113+ 0-
> M sbin/ifconfig/ifconfig.8 | 30+ 10-
> M sbin/ifconfig/ifconfig.c | 59+ 9-
> M sbin/ifconfig/ifconfig.h | 3+ 0-
> A sbin/ifconfig/sae.c | 727+ 0-
> M sys/conf/files | 3+ 0-
> A sys/crypto/ec_p256_m31.c | 1541+ 0-
> A sys/crypto/ec_p256_m31.h | 301+ 0-
> A sys/crypto/i31.c | 516+ 0-
> A sys/crypto/i31.h | 258+ 0-
> M sys/net80211/ieee80211.h | 29+ 3-
> M sys/net80211/ieee80211_crypto.c | 2+ 2-
> M sys/net80211/ieee80211_crypto.h | 5+ 1-
> M sys/net80211/ieee80211_input.c | 42+ 7-
> M sys/net80211/ieee80211_ioctl.c | 54+ 5-
> M sys/net80211/ieee80211_ioctl.h | 12+ 0-
> M sys/net80211/ieee80211_node.c | 92+ 12-
> M sys/net80211/ieee80211_node.h | 51+ 0-
> M sys/net80211/ieee80211_output.c | 168+ 6-
> M sys/net80211/ieee80211_pae_input.c | 13+ 2-
> M sys/net80211/ieee80211_pae_output.c | 28+ 9-
> M sys/net80211/ieee80211_priv.h | 21+ 0-
> M sys/net80211/ieee80211_proto.c | 149+ 0-
> M sys/net80211/ieee80211_proto.h | 6+ 0-
> A sys/net80211/ieee80211_sae.c | 590+ 0-
> M sys/net80211/ieee80211_var.h | 5+ 0-
>
> 27 files changed, 4821 insertions(+), 69 deletions(-)
>
> commit - 97f4e10dc0be0456fb9246e256166af6a33d48b2
> commit + ea1d43729184b215b7f99a329815d1b28e519055
> blob - b674f82f00422b46f899c100a77d8ce875a4342b
> blob + c17956894edb1e9bfd8a844aeac81766295c9654
> --- sbin/ifconfig/Makefile
> +++ sbin/ifconfig/Makefile
> @@ -1,10 +1,10 @@
> # $OpenBSD: Makefile,v 1.17 2020/06/22 02:08:43 dlg Exp $
>
> PROG= ifconfig
> -SRCS= ifconfig.c brconfig.c sff.c
> +SRCS= ifconfig.c brconfig.c sff.c sae.c
> MAN= ifconfig.8
>
> -LDADD= -lutil -lm
> -DPADD= ${LIBUTIL}
> +LDADD= -lutil -lm -lcrypto
> +DPADD= ${LIBUTIL} ${LIBCRYPTO}
>
> .include <bsd.prog.mk>
> blob - 0a3c6da74081a815a6ef41f1709286ff7d615bd0
> blob + d5e3a2f9b4d18d7d5268932702ae5544f00e8dcf
> --- sbin/ifconfig/ifconfig.8
> +++ sbin/ifconfig/ifconfig.8
> @@ -1271,10 +1271,14 @@ protocols.
> The supported values are
> .Dq psk ,
> .Dq sha256-psk ,
> +.Dq sae ,
> and
> .Dq 802.1x .
> .Ar psk
> authentication (also known as personal mode) uses a 256-bit pre-shared key.
> +.Ar sae
> +authentication (Simultaneous Authentication of Equals) uses a passphrase
> +to derive a shared secret with elliptic curve cryptography.
> .Ar 802.1x
> authentication (also known as enterprise mode) is used with
> an external IEEE 802.1X authentication server,
> @@ -1282,11 +1286,12 @@ such as wpa_supplicant.
> The default value is
> .Dq psk ,
> or
> -.Dq psk,sha256-psk
> +.Dq psk,sha256-psk,sae
> if the driver for the interface supports protected management frames (PMF).
> -.Dq psk
> +.Dq psk ,
> +.Dq sha256-psk ,
> and
> -.Dq sha256-psk
> +.Dq sae
> can only be used if a pre-shared key is configured using the
> .Cm wpakey
> option.
> @@ -1348,26 +1353,41 @@ or
> option must first be specified, since
> .Nm
> will hash the nwid along with the passphrase to create the key.
> +.Pp
> +Passphrases are supported for WPA1, WPA2, and WPA3.
> +Hex keys are interpreted as a pre-hashed PSK for use with WPA1 and WPA2 only.
> .It Cm -wpakey
> Delete the pre-shared WPA key and disable WPA.
> .It Cm wpaprotos Ar proto,proto,...
> Set the comma-separated list of allowed WPA protocol versions.
> .Pp
> The supported values are
> -.Dq wpa1
> +.Dq wpa1 ,
> +.Dq wpa2 ,
> and
> -.Dq wpa2 .
> +.Dq wpa3 .
> .Ar wpa1
> is based on draft 3 of the IEEE 802.11i standard whereas
> .Ar wpa2
> is based on the ratified standard.
> +.Ar wpa3
> +is based on the 802.11-2024 standard and is restricted to the secure
> +and now mandatory hash-to-element SAE key derivation method.
> +The problematic hunting-and-pecking key derivation method responsible for
> +side-channel leaks discovered in WPA3 after initial standardization is not
> +supported.
> +Software on defective devices should be upgraded to a secure version of
> +WPA3 for interoperability with
> +.Ox .
> +.Pp
> The default value is
> +.Dq wpa2,wpa3
> +if the driver for the interface supports protected management frames (PMF).
> +Otherwise, the default value is
> .Dq wpa2 .
> -If
> -.Dq wpa1,wpa2
> -is specified, a station will always use the
> -.Ar wpa2
> -protocol when supported by the access point.
> +.Pp
> +If multiple protocol versions are specified, a station will always use the
> +highest protocol version mutually supported by the access point.
> .El
> .Sh INET6
> .nr nS 1
> blob - /dev/null
> blob + 8c58df017ca55c041d3dd13424c47a882f54060e (mode 644)
> --- /dev/null
> +++ sbin/ifconfig/const_time.h
> @@ -0,0 +1,113 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Helper functions for constant time operations.
> + * Copyright (c) 2019, The Linux Foundation
> + *
> + * This software may be distributed, used, and modified under the terms of
> + * BSD license:
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are
> + * met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + *
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + *
> + * 3. Neither the name(s) of the above-listed copyright holder(s) nor the
> + * names of its contributors may be used to endorse or promote products
> + * derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * These helper functions can be used to implement logic that needs to minimize
> + * externally visible differences in execution path by avoiding use of branches,
> + * avoiding early termination or other time differences, and forcing same memory
> + * access pattern regardless of values.
> + */
> +
> +/**
> + * const_time_fill_msb - Fill all bits with MSB value
> + * @val: Input value
> + * Returns: Value with all the bits set to the MSB of the input val
> + */
> +static inline unsigned int const_time_fill_msb(unsigned int val)
> +{
> + /* Move the MSB to LSB and multiple by -1 to fill in all bits. */
> + return (val >> (sizeof(val) * 8 - 1)) * ~0U;
> +}
> +
> +/* Returns: -1 if val is zero; 0 if val is not zero */
> +static inline unsigned int const_time_is_zero(unsigned int val)
> +{
> + /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
> + return const_time_fill_msb(~val & (val - 1));
> +}
> +
> +/* Returns: -1 if a == b; 0 if a != b */
> +static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
> +{
> + return const_time_is_zero(a ^ b);
> +}
> +
> +/**
> + * const_time_select - Constant time unsigned int selection
> + * @mask: 0 (false) or -1 (true) to identify which value to select
> + * @true_val: Value to select for the true case
> + * @false_val: Value to select for the false case
> + * Returns: true_val if mask == -1, false_val if mask == 0
> + */
> +static inline unsigned int const_time_select(unsigned int mask,
> + unsigned int true_val,
> + unsigned int false_val)
> +{
> + return (mask & true_val) | (~mask & false_val);
> +}
> +
> +/**
> + * const_time_select_u8 - Constant time uint8_t selection
> + * @mask: 0 (false) or -1 (true) to identify which value to select
> + * @true_val: Value to select for the true case
> + * @false_val: Value to select for the false case
> + * Returns: true_val if mask == -1, false_val if mask == 0
> + */
> +static inline uint8_t const_time_select_u8(uint8_t mask, uint8_t true_val,
> + uint8_t false_val)
> +{
> + return (uint8_t)const_time_select(mask, true_val, false_val);
> +}
> +
> +/**
> + * const_time_select_bin - Constant time binary buffer selection copy
> + * @mask: 0 (false) or -1 (true) to identify which value to copy
> + * @true_val: Buffer to copy for the true case
> + * @false_val: Buffer to copy for the false case
> + * @len: Number of octets to copy
> + * @dst: Destination buffer for the copy
> + *
> + * This function copies the specified buffer into the destination buffer using
> + * operations with identical memory access pattern regardless of which buffer
> + * is being copied.
> + */
> +static inline void const_time_select_bin(uint8_t mask, const uint8_t *true_val,
> + const uint8_t *false_val, size_t len, uint8_t *dst)
> +{
> + size_t i;
> +
> + for (i = 0; i < len; i++)
> + dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
> +}
> blob - 22d582cbaa9e10d54b1fbaca1a9c5652531bc891
> blob + fb059ee8203f908c82fd1be5c902898bd811a0b4
> --- sbin/ifconfig/ifconfig.c
> +++ sbin/ifconfig/ifconfig.c
> @@ -2079,6 +2079,8 @@ setifwpaprotos(const char *val, int d)
> rval |= IEEE80211_WPA_PROTO_WPA1;
> else if (strcasecmp(str, "wpa2") == 0)
> rval |= IEEE80211_WPA_PROTO_WPA2;
> + else if (strcasecmp(str, "wpa3") == 0)
> + rval |= IEEE80211_WPA_PROTO_WPA3;
> else
> errx(1, "wpaprotos: unknown protocol: %s", str);
> str = strtok(NULL, ",");
> @@ -2119,6 +2121,8 @@ setifwpaakms(const char *val, int d)
> rval |= IEEE80211_WPA_AKM_PSK;
> else if (strcasecmp(str, "sha256-psk") == 0)
> rval |= IEEE80211_WPA_AKM_SHA256_PSK;
> + else if (strcasecmp(str, "sae") == 0)
> + rval |= IEEE80211_WPA_AKM_SAE;
> else if (strcasecmp(str, "802.1x") == 0)
> rval |= IEEE80211_WPA_AKM_8021X;
> else
> @@ -2236,10 +2240,16 @@ setifwpakey(const char *val, int d)
> {
> struct ieee80211_wpaparams wpa;
> struct ieee80211_wpapsk psk;
> +#ifndef SMALL
> + struct ieee80211_wpasae sae;
> +#endif
> struct ieee80211_nwid nwid;
> int passlen;
>
> memset(&psk, 0, sizeof(psk));
> +#ifndef SMALL
> + memset(&sae, 0, sizeof(sae));
> +#endif
> if (d != -1) {
> memset(&ifr, 0, sizeof(ifr));
> ifr.ifr_data = (caddr_t)&nwid;
> @@ -2277,16 +2287,28 @@ setifwpakey(const char *val, int d)
> if (pkcs5_pbkdf2(val, passlen, nwid.i_nwid, nwid.i_len,
> psk.i_psk, sizeof(psk.i_psk), 4096) != 0)
> errx(1, "wpakey: passphrase hashing failed");
> +#ifndef SMALL
> + if (sae_get_pt(val, passlen, nwid.i_nwid, nwid.i_len,
> + sae.i_pt, sizeof(sae.i_pt)) != 0)
> + errx(1, "wpakey: passphrase hashing failed");
> + sae.i_enabled = 1;
> +#endif
> }
> psk.i_enabled = 1;
> } else
> psk.i_enabled = 0;
>
> (void)strlcpy(psk.i_name, ifname, sizeof(psk.i_name));
> -
> +#ifndef SMALL
> + (void)strlcpy(sae.i_name, ifname, sizeof(sae.i_name));
> +#endif
> if (actions & A_JOIN) {
> memcpy(&join.i_wpapsk, &psk, sizeof(join.i_wpapsk));
> join.i_flags |= IEEE80211_JOIN_WPAPSK;
> +#ifndef SMALL
> + memcpy(&join.i_wpasae, &sae, sizeof(join.i_wpasae));
> + join.i_flags |= IEEE80211_JOIN_WPASAE;
> +#endif
> if (!join.i_wpaparams.i_enabled)
> setifwpa(NULL, join.i_wpapsk.i_enabled);
> return;
> @@ -2294,13 +2316,20 @@ setifwpakey(const char *val, int d)
>
> if (ioctl(sock, SIOCS80211WPAPSK, (caddr_t)&psk) == -1)
> err(1, "%s: SIOCS80211WPAPSK", psk.i_name);
> -
> +#ifndef SMALL
> + if (ioctl(sock, SIOCS80211WPASAE, (caddr_t)&sae) == -1)
> + warn("%s: SIOCS80211WPASAE", sae.i_name);
> +#endif
> /* And ... automatically enable or disable WPA */
> memset(&wpa, 0, sizeof(wpa));
> (void)strlcpy(wpa.i_name, ifname, sizeof(wpa.i_name));
> if (ioctl(sock, SIOCG80211WPAPARMS, (caddr_t)&wpa) == -1)
> err(1, "%s: SIOCG80211WPAPARMS", psk.i_name);
> - wpa.i_enabled = psk.i_enabled;
> + wpa.i_enabled = (psk.i_enabled
> +#ifndef SMALL
> + || sae.i_enabled
> +#endif
> + );
> if (ioctl(sock, SIOCS80211WPAPARMS, (caddr_t)&wpa) == -1)
> err(1, "%s: SIOCS80211WPAPARMS", psk.i_name);
> }
> @@ -2422,7 +2451,7 @@ print_cipherset(u_int32_t cipherset)
> void
> print_rsnprotocol(u_int proto, u_int akm)
> {
> - if (proto & IEEE80211_WPA_PROTO_WPA2) {
> + if (proto & (IEEE80211_WPA_PROTO_WPA2 | IEEE80211_WPA_PROTO_WPA3)) {
> if (akm & IEEE80211_WPA_AKM_SAE) {
> if (akm == IEEE80211_WPA_AKM_SAE)
> fputs(",wpa3", stdout);
> @@ -2453,12 +2482,13 @@ print_assoc_failures(uint32_t assoc_fail)
> void
> ieee80211_status(void)
> {
> - int len, inwid, ijoin, inwkey, ipsk, ichan, ipwr;
> + int len, inwid, ijoin, inwkey, ipsk, isae, ichan, ipwr;
> int ibssid, iwpa, assocfail = 0;
> struct ieee80211_nwid nwid;
> struct ieee80211_join join;
> struct ieee80211_nwkey nwkey;
> struct ieee80211_wpapsk psk;
> + struct ieee80211_wpasae sae;
> struct ieee80211_power power;
> struct ieee80211chanreq channel;
> struct ieee80211_bssid bssid;
> @@ -2485,6 +2515,10 @@ ieee80211_status(void)
> strlcpy(psk.i_name, ifname, sizeof(psk.i_name));
> ipsk = ioctl(sock, SIOCG80211WPAPSK, (caddr_t)&psk);
>
> + memset(&sae, 0, sizeof(sae));
> + strlcpy(sae.i_name, ifname, sizeof(sae.i_name));
> + isae = ioctl(sock, SIOCG80211WPASAE, (caddr_t)&sae);
> +
> memset(&power, 0, sizeof(power));
> strlcpy(power.i_name, ifname, sizeof(power.i_name));
> ipwr = ioctl(sock, SIOCG80211POWER, &power);
> @@ -2503,7 +2537,7 @@ ieee80211_status(void)
>
> /* check if any ieee80211 option is active */
> if (inwid == 0 || ijoin == 0 || inwkey == 0 || ipsk == 0 ||
> - ipwr == 0 || ichan == 0 || ibssid == 0 || iwpa == 0)
> + isae == 0 || ipwr == 0 || ichan == 0 || ibssid == 0 || iwpa == 0)
> fputs("\tieee80211:", stdout);
> else
> return;
> @@ -2546,7 +2580,7 @@ ieee80211_status(void)
> if (inwkey == 0 && nwkey.i_wepon > IEEE80211_NWKEY_OPEN)
> fputs(" nwkey", stdout);
>
> - if (ipsk == 0 && psk.i_enabled)
> + if ((ipsk == 0 && psk.i_enabled) || (isae == 0 && sae.i_enabled))
> fputs(" wpakey", stdout);
> if (iwpa == 0 && wpa.i_enabled) {
> const char *sep;
> @@ -2556,8 +2590,12 @@ ieee80211_status(void)
> fputs("wpa1", stdout);
> sep = ",";
> }
> - if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2)
> + if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) {
> printf("%swpa2", sep);
> + sep = ",";
> + }
> + if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA3)
> + printf("%swpa3", sep);
>
> fputs(" wpaakms ", stdout); sep = "";
> if (wpa.i_akms & IEEE80211_WPA_AKM_PSK) {
> @@ -2568,6 +2606,10 @@ ieee80211_status(void)
> printf("%ssha256-psk", sep);
> sep = ",";
> }
> + if (wpa.i_akms & IEEE80211_WPA_AKM_SAE) {
> + printf("%ssae", sep);
> + sep = ",";
> + }
> if (wpa.i_akms & IEEE80211_WPA_AKM_8021X)
> printf("%s802.1x", sep);
>
> @@ -2675,8 +2717,12 @@ join_status(void)
> printf("wpa1");
> sep = ",";
> }
> - if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA2)
> + if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA2) {
> printf("%swpa2", sep);
> + sep = ",";
> + }
> + if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA3)
> + printf("%swpa3", sep);
>
> printf(" wpaakms "); sep = "";
> if (wpa->i_akms & IEEE80211_WPA_AKM_PSK) {
> @@ -2688,6 +2734,10 @@ join_status(void)
> printf("%ssha256-psk", sep);
> sep = ",";
> }
> + if (wpa->i_akms & IEEE80211_WPA_AKM_SAE) {
> + printf("%ssae", sep);
> + sep = ",";
> + }
> if (wpa->i_akms & IEEE80211_WPA_AKM_8021X)
> printf("%s802.1x", sep);
>
> blob - f2f1f8de41eb4087a45c73198a8354bb37472e7f
> blob + 166dbb8f44fd341f9bf29f148658b85db00ed6be
> --- sbin/ifconfig/ifconfig.h
> +++ sbin/ifconfig/ifconfig.h
> @@ -88,3 +88,6 @@ void bridge_status(void);
> int bridge_rule(int, char **, int);
>
> int if_sff_info(int);
> +
> +int sae_get_pt(const char *, size_t, const uint8_t *, size_t,
> + uint8_t *, size_t);
> blob - /dev/null
> blob + 4b814b5f26b8e0b40c15f52bf6fc9eab4ca4dfde (mode 644)
> --- /dev/null
> +++ sbin/ifconfig/sae.c
> @@ -0,0 +1,727 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2026 Stefan Sperling <stsp@openbsd.org>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +/*
> + * An implementation of 802.11-2024 "12.4.4.2.3 Hash-to-element generation
> + * of the password element with ECC groups" for OpenBSD's ifconfig.
> + *
> + * The secret "PT" generated here can be used to compute the password
> + * element (PWE) needed for the WPA3 SAE handshake. PWE computation and
> + * the SAE handshake are implemented in the OpenBSD kernel.
> + *
> + * The only supported curve is the mandatory curve IANA 19 (NIST p256).
> + */
> +
> +/*
> + * Simultaneous authentication of equals
> + * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
> + *
> + * Wrapper functions for OpenSSL libcrypto
> + * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
> + *
> + * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
> + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
> + *
> + * This software may be distributed, used, and modified under the terms of
> + * BSD license:
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are
> + * met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + *
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + *
> + * 3. Neither the name(s) of the above-listed copyright holder(s) nor the
> + * names of its contributors may be used to endorse or promote products
> + * derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <limits.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <openssl/hmac.h>
> +#include <openssl/sha.h>
> +#include <openssl/ec.h>
> +#include <openssl/bn.h>
> +
> +#include <net80211/ieee80211.h>
> +
> +#include "const_time.h"
> +
> +#ifndef SMALL
> +
> +#define SAE_MAX_ECC_PRIME_LEN IEEE80211_SAE_MAX_ECC_PRIME_LEN
> +
> +static void
> +hexdump(const char *label, const uint8_t *s, size_t len)
> +{
> +#if 0
> + size_t i;
> +
> + printf("%s: len=%zd:", label, len);
> + for (i = 0; i < len; i++)
> + printf(" %.2x", s[i]);
> + putchar('\n');
> +#endif
> +}
> +
> +static int
> +hkdf_extract(const uint8_t *salt, size_t salt_len, size_t num_elem,
> + const uint8_t *addr[], const size_t *len, uint8_t *prk)
> +{
> + HMAC_CTX *ctx;
> + size_t i;
> + int res;
> + unsigned int mdlen;
> +
> + ctx = HMAC_CTX_new();
> + if (!ctx)
> + return -1;
> + res = HMAC_Init_ex(ctx, salt, salt_len, EVP_sha256(), NULL);
> + if (res != 1)
> + goto done;
> +
> + for (i = 0; i < num_elem; i++)
> + HMAC_Update(ctx, addr[i], len[i]);
> +
> + res = HMAC_Final(ctx, prk, &mdlen);
> +done:
> + HMAC_CTX_free(ctx);
> +
> + return res == 1 ? 0 : -1;
> +}
> +
> +/**
> + * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
> + * @secret: Key for KDF
> + * @secret_len: Length of the key in bytes
> + * @label: A unique label for each purpose of the KDF or %NULL to select
> + * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
> + * @seed: Seed value to bind into the key
> + * @seed_len: Length of the seed
> + * @out: Buffer for the generated pseudo-random key
> + * @outlen: Number of bytes of key to generate
> + * Returns: 0 on success, -1 on failure.
> + *
> + * This function is used to derive new, cryptographically separate keys from a
> + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
> + * with label = NULL and seed = info, this matches HKDF-Expand() defined in
> + * RFC 5869, Chapter 2.3.
> + */
> +static int
> +hmac_sha256_kdf(const uint8_t *secret, size_t secret_len,
> + const char *label, const uint8_t *seed, size_t seed_len,
> + uint8_t *out, size_t outlen)
> +{
> + uint8_t digest[SHA256_DIGEST_LENGTH];
> + uint8_t iter = 1;
> + const unsigned char *addr[4];
> + size_t len[4];
> + size_t pos, clen;
> +
> + addr[0] = digest;
> + len[0] = SHA256_DIGEST_LENGTH;
> + if (label) {
> + addr[1] = (const unsigned char *) label;
> + len[1] = strlen(label) + 1;
> + } else {
> + addr[1] = (const uint8_t *) "";
> + len[1] = 0;
> + }
> + addr[2] = seed;
> + len[2] = seed_len;
> + addr[3] = &iter;
> + len[3] = 1;
> +
> + if (hkdf_extract(secret, secret_len, 3, &addr[1], &len[1], digest) < 0)
> + return -1;
> +
> + pos = 0;
> + for (;;) {
> + clen = outlen - pos;
> + if (clen > SHA256_DIGEST_LENGTH)
> + clen = SHA256_DIGEST_LENGTH;
> + memcpy(out + pos, digest, clen);
> + pos += clen;
> +
> + if (pos == outlen)
> + break;
> +
> + if (iter == 255) {
> + memset(out, 0, outlen);
> + explicit_bzero(digest, SHA256_DIGEST_LENGTH);
> + return -1;
> + }
> + iter++;
> +
> + if (hkdf_extract(secret, secret_len, 4, addr,
> + len, digest) < 0) {
> + memset(out, 0, outlen);
> + explicit_bzero(digest, SHA256_DIGEST_LENGTH);
> + return -1;
> + }
> + }
> +
> + explicit_bzero(digest, SHA256_DIGEST_LENGTH);
> + return 0;
> +}
> +
> +static int
> +hkdf_expand(const uint8_t *prk, size_t prk_len, const char *info,
> + uint8_t *okm, size_t okm_len)
> +{
> + return hmac_sha256_kdf(prk, prk_len, NULL,
> + (const uint8_t *) info, strlen(info), okm, okm_len);
> +}
> +
> +/*
> + * pwd-seed = HKDF-Extract(ssid, password [ || identifier ])
> + */
> +static int
> +get_pwd_seed(uint8_t *pwd_seed, size_t pwd_seed_size,
> + const char *ssid, size_t ssid_len,
> + const char *password, size_t password_len)
> +{
> + const uint8_t *key = password;
> + const size_t key_len = password_len;
> +
> + if (pwd_seed_size < SHA256_DIGEST_LENGTH)
> + return -1;
> +
> + /* TODO: password identifier? */
> + if (hkdf_extract(ssid, ssid_len, 1, &key, &key_len, pwd_seed) < 0)
> + return -1;
> +
> + return 0;
> +}
> +
> +/*
> + * pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u[1,2] P[1,2]", len)
> + */
> +static int
> +get_pwd_value(uint8_t *pwd_seed, size_t pwd_seed_size,
> + const char *info, uint8_t *pwd_value, size_t pwd_value_size)
> +{
> + return hkdf_expand(pwd_seed, pwd_seed_size, info, pwd_value,
> + pwd_value_size);
> +}
> +
> +/* u = pwd-value modulo p */
> +static int
> +get_u(uint8_t *u1, size_t u1_len, uint8_t *pwd_value, size_t pwd_value_len,
> + BIGNUM *prime, int prime_len, BN_CTX *bnctx)
> +{
> + BIGNUM *bn;
> + int ret = -1;
> +
> + if (prime_len > u1_len)
> + return -1;
> +
> + bn = BN_bin2bn(pwd_value, pwd_value_len, NULL);
> + if (bn == NULL)
> + goto done;
> + if (!BN_mod(bn, bn, prime, bnctx))
> + goto done;
> + if (BN_bn2binpad(bn, u1, prime_len) < 0)
> + goto done;
> +
> + ret = 0;
> +done:
> + return ret;
> +}
> +
> +static int
> +sswu(uint8_t *x_y, size_t x_y_len, uint8_t *u1, size_t u1_len,
> + BIGNUM *prime, BIGNUM *a, BIGNUM *b, BN_CTX *bnctx)
> +{
> + const int p256_z = -10;
> + BIGNUM *u, *u2, *t1, *t2, *z, *t, *v, *y;
> + BIGNUM *zero, *one, *two, *three;
> + BIGNUM *x1, *x1a, *x1b, *x2, *gx1, *gx2;
> + int ret = -1;
> + uint8_t bin[SAE_MAX_ECC_PRIME_LEN * 2];
> + uint8_t bin1[SAE_MAX_ECC_PRIME_LEN * 2];
> + uint8_t bin2[SAE_MAX_ECC_PRIME_LEN * 2];
> + int prime_len = BN_num_bytes(prime);
> + unsigned int m_is_zero, is_qr, is_eq;
> +
> + u = u2 = t1 = t2 = z = t = v = y = NULL;
> + zero = one = two = three = NULL;
> + x1 = x1a = x1b = x2 = gx1 = gx2 = NULL;
> +
> + if (x_y_len < SAE_MAX_ECC_PRIME_LEN * 2)
> + return -1;
> +
> + u = BN_bin2bn(u1, u1_len, NULL);
> + if (u == NULL)
> + goto done;
> +
> + u2 = BN_new();
> + if (u2 == NULL)
> + goto done;
> +
> + t1 = BN_new();
> + if (t1 == NULL)
> + goto done;
> +
> + t2 = BN_new();
> + if (t2 == NULL)
> + goto done;
> +
> + z = BN_new();
> + if (z == NULL)
> + goto done;
> + if (BN_set_word(z, abs(p256_z)) != 1)
> + goto done;
> +
> + t = BN_new();
> + if (t == NULL)
> + goto done;
> +
> + zero = BN_new();
> + if (zero == NULL)
> + goto done;
> + if (BN_set_word(zero, 0) != 1)
> + goto done;
> +
> + one = BN_new();
> + if (one == NULL)
> + goto done;
> +
> + if (BN_set_word(one, 1) != 1)
> + goto done;
> +
> + two = BN_new();
> + if (two == NULL)
> + goto done;
> +
> + if (BN_set_word(two, 2) != 1)
> + goto done;
> +
> + three = BN_new();
> + if (three == NULL)
> + goto done;
> + if (BN_set_word(three, 3) != 1)
> + goto done;
> +
> + x1a = BN_new();
> + if (x1a == NULL)
> + goto done;
> +
> + x1b = BN_new();
> + if (x1b == NULL)
> + goto done;
> +
> + x2 = BN_new();
> + if (x2 == NULL)
> + goto done;
> +
> + gx1 = BN_new();
> + if (gx1 == NULL)
> + goto done;
> +
> + gx2 = BN_new();
> + if (gx2 == NULL)
> + goto done;
> +
> + if (!BN_sub(z, prime, z))
> + goto done;
> +
> + /*
> + * m = z^2 * u^4 + z * u^2 --> tmp = z * u^2, m = tmp^2 + tmp
> + */
> + /* u2 = u^2 */
> + if (!BN_mod_sqr(u2, u, prime, bnctx))
> + goto done;
> + /* t1 = z * u2*/
> + if (!BN_mod_mul(t1, z, u2, prime, bnctx))
> + goto done;
> + /* t2 = t1^2 */
> + if (!BN_mod_sqr(t2, t1, prime, bnctx))
> + goto done;
> + /* m = t1 = t1 + t2 */
> + if (!BN_mod_add(t1, t1, t2, prime, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(t1, bin, prime_len) < 0)
> + goto done;
> + hexdump("m", bin, prime_len);
> +
> + /* l = CEQ(m, 0) */
> + /* TODO: Make sure BN_is_zero() is constant time */
> + m_is_zero = const_time_eq(BN_is_zero(t1), 1);
> +
> + /* t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as
> + * x^(p-2) modulo p which will handle m == 0 case correctly */
> + /* t = m^(p-2) modulo p */
> + if (!BN_sub(t2, prime, two))
> + goto done;
> + if (!BN_mod_exp_mont_consttime(t, t1, t2, prime, bnctx, NULL))
> + goto done;
> +
> + if (BN_bn2binpad(t, bin, prime_len) < 0)
> + goto done;
> + hexdump("t", bin, prime_len);
> +
> + /* x1a = b / (z * a) --> x1a = (1 / (z * a)) * b */
> + if (!BN_mod_mul(t1, z, a, prime, bnctx))
> + goto done;
> + if (!BN_mod_inverse(t1, t1, prime, bnctx))
> + goto done;
> + if (!BN_mod_mul(x1a, b, t1, prime, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(x1a, bin, prime_len) < 0)
> + goto done;
> + hexdump("x1a", bin, prime_len);
> +
> + /* x1b = (-b/a) * (1 + t) */
> + if (!BN_sub(t1, prime, b))
> + goto done;
> + if (!BN_mod_inverse(t2, a, prime, bnctx))
> + goto done;
> + if (!BN_mod_mul(t1, t1, t2, prime, bnctx))
> + goto done;
> + if (!BN_mod_add(t2, one, t, prime, bnctx))
> + goto done;
> + if (!BN_mod_mul(x1b, t1, t2, prime, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(x1b, bin, prime_len) < 0)
> + goto done;
> + hexdump("x1b", bin, prime_len);
> +
> + /* x1 = CSEL(CEQ(m, 0), x1a, x1b) */
> + if (BN_bn2binpad(x1a, bin1, prime_len) < 0)
> + goto done;
> + if (BN_bn2binpad(x1b, bin2, prime_len) < 0)
> + goto done;
> + const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
> + hexdump("selected x1", bin, prime_len);
> +
> + x1 = BN_bin2bn(bin, prime_len, NULL);
> + if (x1 == NULL)
> + goto done;
> +
> + /* gx1 = x1^3 + a * x1 + b */
> + if (!BN_mod_exp_mont_consttime(t1, x1, three, prime, bnctx, NULL))
> + goto done;
> + if (!BN_mod_mul(t2, a, x1, prime, bnctx))
> + goto done;
> + if (!BN_mod_add(t1, t1, t2, prime, bnctx))
> + goto done;
> + if (!BN_mod_add(gx1, t1, b, prime, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(gx1, bin, prime_len) < 0)
> + goto done;
> + hexdump("gx1", bin, prime_len);
> +
> + /* x2 = z * u^2 * x1 */
> + if (!BN_mod_mul(t1, z, u2, prime, bnctx))
> + goto done;
> + if (!BN_mod_mul(x2, t1, x1, prime, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(x2, bin, prime_len) < 0)
> + goto done;
> + hexdump("x2", bin, prime_len);
> +
> + /* gx2 = x2^3 + a * x2 + b */
> + if (!BN_mod_exp_mont_consttime(t1, x2, three, prime, bnctx, NULL))
> + goto done;
> + if (!BN_mod_mul(t2, a, x2, prime, bnctx))
> + goto done;
> + if (!BN_mod_add(t1, t1, t2, prime, bnctx))
> + goto done;
> + if (!BN_mod_add(gx2, t1, b, prime, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(gx2, bin, prime_len) < 0)
> + goto done;
> + hexdump("gx2", bin, prime_len);
> +
> + /*
> + * l = gx1 is a quadratic residue modulo p
> + * --> gx1^((p-1)/2) modulo p is zero or one
> + */
> + if (!BN_sub(t1, prime, one))
> + goto done;
> + if (!BN_rshift(t1, t1, 1))
> + goto done;
> + if (!BN_mod_exp_mont_consttime(t1, gx1, t1, prime, bnctx, NULL))
> + goto done;
> + is_qr = const_time_eq(BN_is_zero(t1) | BN_is_one(t1), 1);
> +
> + if (BN_bn2binpad(t1, bin, prime_len) < 0)
> + goto done;
> + hexdump("t1", bin, prime_len);
> +
> + /* v = CSEL(l, gx1, gx2) */
> + if (BN_bn2binpad(gx1, bin1, prime_len) < 0)
> + goto done;
> + if (BN_bn2binpad(gx2, bin2, prime_len) < 0)
> + goto done;
> + const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
> + v = BN_bin2bn(bin, prime_len, NULL);
> + if (v == NULL)
> + goto done;
> +
> + hexdump("v", bin, prime_len);
> +
> + /* x = CSEL(l, x1, x2) */
> + if (BN_bn2binpad(x1, bin1, prime_len) < 0)
> + goto done;
> + if (BN_bn2binpad(x2, bin2, prime_len) < 0)
> + goto done;
> + const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y);
> +
> + hexdump("x_y", x_y, prime_len);
> +
> + /*
> + * y = sqrt(v)
> + * For prime p such that p = 3 mod 4, sqrt(w) = w^((p+1)/4) mod p
> + */
> + y = BN_new();
> + if (y == NULL)
> + goto done;
> + if (!BN_add(t1, prime, one))
> + goto done;
> + if (!BN_rshift(t1, t1, 2))
> + goto done;
> + if (!BN_mod_exp_mont_consttime(y, v, t1, prime, bnctx, NULL))
> + goto done;
> +
> + if (BN_bn2binpad(y, bin, prime_len) < 0)
> + goto done;
> + hexdump("y", bin, prime_len);
> +
> + /* l = CEQ(LSB(u), LSB(y)) */
> + if (BN_bn2binpad(u, bin1, prime_len) < 0)
> + goto done;
> + if (BN_bn2binpad(y, bin2, prime_len) < 0)
> + goto done;
> + is_eq = const_time_eq(bin1[prime_len - 1] & 0x01,
> + bin2[prime_len - 1] & 0x01);
> +
> + /* P = CSEL(l, (x,y), (x, p-y)) */
> + if (!BN_sub(t1, prime, y))
> + goto done;
> + if (BN_bn2binpad(y, bin1, prime_len) < 0)
> + goto done;
> + if (BN_bn2binpad(t1, bin2, prime_len) < 0)
> + goto done;
> + const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]);
> +
> + /* output P */
> + hexdump("P.x", x_y, prime_len);
> + hexdump("P.y", &x_y[prime_len], prime_len);
> +
> + ret = 0;
> +done:
> + BN_free(u); BN_free(u2); BN_free(t1); BN_free(t2);
> + BN_free(z); BN_free(t); BN_free(v); BN_free(y);
> + BN_free(zero); BN_free(one); BN_free(two); BN_free(three);
> + BN_free(x1); BN_free(x1a); BN_free(x1b); BN_free(x2);
> + BN_free(gx1); BN_free(gx2);
> + return ret;
> +}
> +
> +int
> +sae_get_pt(const char *password, size_t password_len, const uint8_t *ssid,
> + size_t ssid_len, uint8_t *pt_x_y, size_t pt_size)
> +{
> + uint8_t pwd_seed[SHA256_DIGEST_LENGTH];
> + uint8_t pwd_value[SAE_MAX_ECC_PRIME_LEN * 2];
> + uint8_t u1[SAE_MAX_ECC_PRIME_LEN * 2];
> + uint8_t u2[SAE_MAX_ECC_PRIME_LEN * 2];
> + uint8_t x_y1[2 * SAE_MAX_ECC_PRIME_LEN];
> + uint8_t x_y2[2 * SAE_MAX_ECC_PRIME_LEN];
> + size_t pwd_value_len;
> + const char *info1 = "SAE Hash to Element u1 P1";
> + const char *info2 = "SAE Hash to Element u2 P2";
> + const int nid = NID_X9_62_prime256v1; /* Group 19, ECC P-256 */
> + BN_CTX *bnctx = NULL;
> + BIGNUM *prime = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL;
> + int prime_len;
> + EC_GROUP *group = NULL;
> + EC_POINT *p1 = NULL, *p2 = NULL, *pt = NULL;
> + int ret = -1;
> +
> + if (pt_size < 2 * SAE_MAX_ECC_PRIME_LEN)
> + return -1;
> +
> + memset(pwd_seed, 0, sizeof(pwd_seed));
> + memset(pwd_value, 0, sizeof(pwd_value));
> + memset(u1, 0, sizeof(u1));
> + memset(u2, 0, sizeof(u2));
> +
> + hexdump("SSID", ssid, ssid_len);
> + hexdump("Password", password, password_len);
> + if (get_pwd_seed(pwd_seed, sizeof(pwd_seed), ssid, ssid_len,
> + password, password_len) == -1)
> + goto done;
> +
> + hexdump("password seed", pwd_seed, sizeof(pwd_seed));
> +
> + /* len = olen(p) + ceil(olen(p)/2) */
> + pwd_value_len = SHA256_DIGEST_LENGTH + (SHA256_DIGEST_LENGTH + 1) / 2;
> +
> + if (get_pwd_value(pwd_seed, sizeof(pwd_seed), info1, pwd_value,
> + pwd_value_len) < 0)
> + goto done;
> +
> + hexdump("password value 1", pwd_value, pwd_value_len);
> +
> + bnctx = BN_CTX_new();
> + if (bnctx == NULL)
> + goto done;
> +
> + prime = BN_new();
> + if (prime == NULL)
> + goto done;
> +
> + a = BN_new();
> + if (a == NULL)
> + goto done;
> +
> + b = BN_new();
> + if (b == NULL)
> + goto done;
> +
> + group = EC_GROUP_new_by_curve_name(nid);
> + if (group == NULL)
> + goto done;
> +
> + if (!EC_GROUP_get_curve(group, prime, a, b, bnctx))
> + goto done;
> +
> + prime_len = BN_num_bytes(prime);
> +
> + if (get_u(u1, pwd_value_len, pwd_value, pwd_value_len,
> + prime, prime_len, bnctx) < 0)
> + goto done;
> +
> + hexdump("u1", u1, prime_len);
> +
> + if (sswu(x_y1, sizeof(x_y1), u1, prime_len, prime, a, b, bnctx) < 0)
> + goto done;
> +
> + if (get_pwd_value(pwd_seed, sizeof(pwd_seed), info2, pwd_value,
> + pwd_value_len) < 0)
> + goto done;
> +
> + hexdump("password value 2", pwd_value, pwd_value_len);
> +
> + if (get_u(u2, pwd_value_len, pwd_value, pwd_value_len,
> + prime, prime_len, bnctx) < 0)
> + goto done;
> +
> + hexdump("u2", u2, prime_len);
> +
> + if (sswu(x_y2, sizeof(x_y2), u2, prime_len, prime, a, b, bnctx) < 0)
> + goto done;
> +
> + x = BN_new();
> + if (x == NULL)
> + goto done;
> +
> + y = BN_new();
> + if (y == NULL)
> + goto done;
> +
> + p1 = EC_POINT_new(group);
> + if (p1 == NULL)
> + goto done;
> +
> + p2 = EC_POINT_new(group);
> + if (p2 == NULL)
> + goto done;
> +
> + if (BN_bin2bn(x_y1, prime_len, x) == NULL)
> + goto done;
> +
> + if (BN_bin2bn(&x_y1[prime_len], prime_len, y) == NULL)
> + goto done;
> + if (!EC_POINT_set_affine_coordinates(group, p1, x, y, bnctx))
> + goto done;
> +
> + if (BN_bin2bn(x_y2, prime_len, x) == NULL)
> + goto done;
> +
> + if (BN_bin2bn(&x_y2[prime_len], prime_len, y) == NULL)
> + goto done;
> +
> + p2 = EC_POINT_new(group);
> + if (p2 == NULL)
> + goto done;
> + if (!EC_POINT_set_affine_coordinates(group, p2, x, y, bnctx))
> + goto done;
> +
> + /* PT = elem-op(P1, P2) */
> + pt = EC_POINT_new(group);
> + if (pt == NULL)
> + goto done;
> + if (!EC_POINT_add(group, pt, p1, p2, bnctx))
> + goto done;
> +
> + if (!EC_POINT_get_affine_coordinates(group, pt, x, y, bnctx))
> + goto done;
> +
> + if (BN_bn2binpad(x, pt_x_y, prime_len) < 0)
> + goto done;
> +
> + hexdump("PT.x", pt_x_y, prime_len);
> +
> + if (BN_bn2binpad(y, &pt_x_y[prime_len], prime_len) < 0)
> + goto done;
> +
> + hexdump("PT.y", &pt_x_y[prime_len], prime_len);
> +
> + ret = 0;
> +done:
> + BN_free(prime);
> + BN_free(a);
> + BN_free(b);
> + EC_GROUP_free(group);
> + EC_POINT_free(p1);
> + EC_POINT_free(p2);
> + EC_POINT_free(pt);
> + BN_CTX_free(bnctx);
> + return ret;
> +}
> +
> +#endif /* SMALL */
> blob - 389e63cf1bca4e66d7699725a35142173b265b32
> blob + 36adb178798b47df52a1dec60c364c534738a571
> --- sys/conf/files
> +++ sys/conf/files
> @@ -906,6 +906,7 @@ file net80211/ieee80211_ra.c wlan
> file net80211/ieee80211_ra_vht.c wlan
> file net80211/ieee80211_rssadapt.c wlan
> file net80211/ieee80211_regdomain.c wlan
> +file net80211/ieee80211_sae.c wlan
> file netinet/if_ether.c ether
> file netinet/igmp.c
> file netinet/in.c
> @@ -965,6 +966,8 @@ file crypto/poly1305.c ipsec | crypto
> file crypto/siphash.c
> file crypto/blake2s.c wg
> file crypto/curve25519.c wg
> +file crypto/i31.c wlan
> +file crypto/ec_p256_m31.c wlan
> file netmpls/mpls_input.c mpls
> file netmpls/mpls_output.c mpls
> file netmpls/mpls_proto.c mpls
> blob - /dev/null
> blob + 4d4f747bad959b06281f89144bcf2ab1f542885c (mode 644)
> --- /dev/null
> +++ sys/crypto/ec_p256_m31.c
> @@ -0,0 +1,1541 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include <sys/types.h>
> +#include <sys/systm.h>
> +
> +#include <crypto/i31.h>
> +#include <crypto/ec_p256_m31.h>
> +
> +/*
> + * If BR_NO_ARITH_SHIFT is undefined, or defined to 0, then we _assume_
> + * that right-shifting a signed negative integer copies the sign bit
> + * (arithmetic right-shift). This is "implementation-defined behaviour",
> + * i.e. it is not undefined, but it may differ between compilers. Each
> + * compiler is supposed to document its behaviour in that respect. GCC
> + * explicitly defines that an arithmetic right shift is used. We expect
> + * all other compilers to do the same, because underlying CPU offer an
> + * arithmetic right shift opcode that could not be used otherwise.
> + */
> +#if BR_NO_ARITH_SHIFT
> +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \
> + | ((-((uint32_t)(x) >> 31)) << (32 - (n))))
> +#define ARSHW(x, n) (((uint64_t)(x) >> (n)) \
> + | ((-((uint64_t)(x) >> 63)) << (64 - (n))))
> +#else
> +#define ARSH(x, n) ((*(int32_t *)&(x)) >> (n))
> +#define ARSHW(x, n) ((*(int64_t *)&(x)) >> (n))
> +#endif
> +
> +/*
> + * Convert an integer from unsigned big-endian encoding to a sequence of
> + * 30-bit words in little-endian order. The final "partial" word is
> + * returned.
> + */
> +static uint32_t
> +be8_to_le30(uint32_t *dst, const unsigned char *src, size_t len)
> +{
> + uint32_t acc;
> + int acc_len;
> +
> + acc = 0;
> + acc_len = 0;
> + while (len -- > 0) {
> + uint32_t b;
> +
> + b = src[len];
> + if (acc_len < 22) {
> + acc |= b << acc_len;
> + acc_len += 8;
> + } else {
> + *dst ++ = (acc | (b << acc_len)) & 0x3FFFFFFF;
> + acc = b >> (30 - acc_len);
> + acc_len -= 22;
> + }
> + }
> + return acc;
> +}
> +
> +/*
> + * Convert an integer (30-bit words, little-endian) to unsigned
> + * big-endian encoding. The total encoding length is provided; all
> + * the destination bytes will be filled.
> + */
> +static void
> +le30_to_be8(unsigned char *dst, size_t len, const uint32_t *src)
> +{
> + uint32_t acc;
> + int acc_len;
> +
> + acc = 0;
> + acc_len = 0;
> + while (len -- > 0) {
> + if (acc_len < 8) {
> + uint32_t w;
> +
> + w = *src ++;
> + dst[len] = (unsigned char)(acc | (w << acc_len));
> + acc = w >> (8 - acc_len);
> + acc_len += 22;
> + } else {
> + dst[len] = (unsigned char)acc;
> + acc >>= 8;
> + acc_len -= 8;
> + }
> + }
> +}
> +
> +/*
> + * Multiply two 31-bit integers, with a 62-bit result. This default
> + * implementation assumes that the basic multiplication operator
> + * yields constant-time code.
> + */
> +#define MUL31(x, y) ((uint64_t)(x) * (uint64_t)(y))
> +
> +/*
> + * Multiply two integers. Source integers are represented as arrays of
> + * nine 30-bit words, for values up to 2^270-1. Result is encoded over
> + * 18 words of 30 bits each.
> + */
> +static void
> +mul9(uint32_t *d, const uint32_t *a, const uint32_t *b)
> +{
> + /*
> + * Maximum intermediate result is no more than
> + * 10376293531797946367, which fits in 64 bits. Reason:
> + *
> + * 10376293531797946367 = 9 * (2^30-1)^2 + 9663676406
> + * 10376293531797946367 < 9663676407 * 2^30
> + *
> + * Thus, adding together 9 products of 30-bit integers, with
> + * a carry of at most 9663676406, yields an integer that fits
> + * on 64 bits and generates a carry of at most 9663676406.
> + */
> + uint64_t t[17];
> + uint64_t cc;
> + int i;
> +
> + t[ 0] = MUL31(a[0], b[0]);
> + t[ 1] = MUL31(a[0], b[1])
> + + MUL31(a[1], b[0]);
> + t[ 2] = MUL31(a[0], b[2])
> + + MUL31(a[1], b[1])
> + + MUL31(a[2], b[0]);
> + t[ 3] = MUL31(a[0], b[3])
> + + MUL31(a[1], b[2])
> + + MUL31(a[2], b[1])
> + + MUL31(a[3], b[0]);
> + t[ 4] = MUL31(a[0], b[4])
> + + MUL31(a[1], b[3])
> + + MUL31(a[2], b[2])
> + + MUL31(a[3], b[1])
> + + MUL31(a[4], b[0]);
> + t[ 5] = MUL31(a[0], b[5])
> + + MUL31(a[1], b[4])
> + + MUL31(a[2], b[3])
> + + MUL31(a[3], b[2])
> + + MUL31(a[4], b[1])
> + + MUL31(a[5], b[0]);
> + t[ 6] = MUL31(a[0], b[6])
> + + MUL31(a[1], b[5])
> + + MUL31(a[2], b[4])
> + + MUL31(a[3], b[3])
> + + MUL31(a[4], b[2])
> + + MUL31(a[5], b[1])
> + + MUL31(a[6], b[0]);
> + t[ 7] = MUL31(a[0], b[7])
> + + MUL31(a[1], b[6])
> + + MUL31(a[2], b[5])
> + + MUL31(a[3], b[4])
> + + MUL31(a[4], b[3])
> + + MUL31(a[5], b[2])
> + + MUL31(a[6], b[1])
> + + MUL31(a[7], b[0]);
> + t[ 8] = MUL31(a[0], b[8])
> + + MUL31(a[1], b[7])
> + + MUL31(a[2], b[6])
> + + MUL31(a[3], b[5])
> + + MUL31(a[4], b[4])
> + + MUL31(a[5], b[3])
> + + MUL31(a[6], b[2])
> + + MUL31(a[7], b[1])
> + + MUL31(a[8], b[0]);
> + t[ 9] = MUL31(a[1], b[8])
> + + MUL31(a[2], b[7])
> + + MUL31(a[3], b[6])
> + + MUL31(a[4], b[5])
> + + MUL31(a[5], b[4])
> + + MUL31(a[6], b[3])
> + + MUL31(a[7], b[2])
> + + MUL31(a[8], b[1]);
> + t[10] = MUL31(a[2], b[8])
> + + MUL31(a[3], b[7])
> + + MUL31(a[4], b[6])
> + + MUL31(a[5], b[5])
> + + MUL31(a[6], b[4])
> + + MUL31(a[7], b[3])
> + + MUL31(a[8], b[2]);
> + t[11] = MUL31(a[3], b[8])
> + + MUL31(a[4], b[7])
> + + MUL31(a[5], b[6])
> + + MUL31(a[6], b[5])
> + + MUL31(a[7], b[4])
> + + MUL31(a[8], b[3]);
> + t[12] = MUL31(a[4], b[8])
> + + MUL31(a[5], b[7])
> + + MUL31(a[6], b[6])
> + + MUL31(a[7], b[5])
> + + MUL31(a[8], b[4]);
> + t[13] = MUL31(a[5], b[8])
> + + MUL31(a[6], b[7])
> + + MUL31(a[7], b[6])
> + + MUL31(a[8], b[5]);
> + t[14] = MUL31(a[6], b[8])
> + + MUL31(a[7], b[7])
> + + MUL31(a[8], b[6]);
> + t[15] = MUL31(a[7], b[8])
> + + MUL31(a[8], b[7]);
> + t[16] = MUL31(a[8], b[8]);
> +
> + /*
> + * Propagate carries.
> + */
> + cc = 0;
> + for (i = 0; i < 17; i ++) {
> + uint64_t w;
> +
> + w = t[i] + cc;
> + d[i] = (uint32_t)w & 0x3FFFFFFF;
> + cc = w >> 30;
> + }
> + d[17] = (uint32_t)cc;
> +}
> +
> +/*
> + * Square a 270-bit integer, represented as an array of nine 30-bit words.
> + * Result uses 18 words of 30 bits each.
> + */
> +static void
> +square9(uint32_t *d, const uint32_t *a)
> +{
> + uint64_t t[17];
> + uint64_t cc;
> + int i;
> +
> + t[ 0] = MUL31(a[0], a[0]);
> + t[ 1] = ((MUL31(a[0], a[1])) << 1);
> + t[ 2] = MUL31(a[1], a[1])
> + + ((MUL31(a[0], a[2])) << 1);
> + t[ 3] = ((MUL31(a[0], a[3])
> + + MUL31(a[1], a[2])) << 1);
> + t[ 4] = MUL31(a[2], a[2])
> + + ((MUL31(a[0], a[4])
> + + MUL31(a[1], a[3])) << 1);
> + t[ 5] = ((MUL31(a[0], a[5])
> + + MUL31(a[1], a[4])
> + + MUL31(a[2], a[3])) << 1);
> + t[ 6] = MUL31(a[3], a[3])
> + + ((MUL31(a[0], a[6])
> + + MUL31(a[1], a[5])
> + + MUL31(a[2], a[4])) << 1);
> + t[ 7] = ((MUL31(a[0], a[7])
> + + MUL31(a[1], a[6])
> + + MUL31(a[2], a[5])
> + + MUL31(a[3], a[4])) << 1);
> + t[ 8] = MUL31(a[4], a[4])
> + + ((MUL31(a[0], a[8])
> + + MUL31(a[1], a[7])
> + + MUL31(a[2], a[6])
> + + MUL31(a[3], a[5])) << 1);
> + t[ 9] = ((MUL31(a[1], a[8])
> + + MUL31(a[2], a[7])
> + + MUL31(a[3], a[6])
> + + MUL31(a[4], a[5])) << 1);
> + t[10] = MUL31(a[5], a[5])
> + + ((MUL31(a[2], a[8])
> + + MUL31(a[3], a[7])
> + + MUL31(a[4], a[6])) << 1);
> + t[11] = ((MUL31(a[3], a[8])
> + + MUL31(a[4], a[7])
> + + MUL31(a[5], a[6])) << 1);
> + t[12] = MUL31(a[6], a[6])
> + + ((MUL31(a[4], a[8])
> + + MUL31(a[5], a[7])) << 1);
> + t[13] = ((MUL31(a[5], a[8])
> + + MUL31(a[6], a[7])) << 1);
> + t[14] = MUL31(a[7], a[7])
> + + ((MUL31(a[6], a[8])) << 1);
> + t[15] = ((MUL31(a[7], a[8])) << 1);
> + t[16] = MUL31(a[8], a[8]);
> +
> + /*
> + * Propagate carries.
> + */
> + cc = 0;
> + for (i = 0; i < 17; i ++) {
> + uint64_t w;
> +
> + w = t[i] + cc;
> + d[i] = (uint32_t)w & 0x3FFFFFFF;
> + cc = w >> 30;
> + }
> + d[17] = (uint32_t)cc;
> +}
> +
> +/*
> + * Base field modulus for P-256.
> + */
> +static const uint32_t F256[] = {
> +
> + 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x0000003F, 0x00000000,
> + 0x00000000, 0x00001000, 0x3FFFC000, 0x0000FFFF
> +};
> +
> +/*
> + * The 'b' curve equation coefficient for P-256.
> + */
> +static const uint32_t P256_B[] = {
> +
> + 0x27D2604B, 0x2F38F0F8, 0x053B0F63, 0x0741AC33, 0x1886BC65,
> + 0x2EF555DA, 0x293E7B3E, 0x0D762A8E, 0x00005AC6
> +};
> +
> +/*
> + * Addition in the field. Source operands shall fit on 257 bits; output
> + * will be lower than twice the modulus.
> + */
> +static void
> +add_f256(uint32_t *d, const uint32_t *a, const uint32_t *b)
> +{
> + uint32_t w, cc;
> + int i;
> +
> + cc = 0;
> + for (i = 0; i < 9; i ++) {
> + w = a[i] + b[i] + cc;
> + d[i] = w & 0x3FFFFFFF;
> + cc = w >> 30;
> + }
> + w >>= 16;
> + d[8] &= 0xFFFF;
> + d[3] -= w << 6;
> + d[6] -= w << 12;
> + d[7] += w << 14;
> + cc = w;
> + for (i = 0; i < 9; i ++) {
> + w = d[i] + cc;
> + d[i] = w & 0x3FFFFFFF;
> + cc = ARSH(w, 30);
> + }
> +}
> +
> +/*
> + * Subtraction in the field. Source operands shall be smaller than twice
> + * the modulus; the result will fulfil the same property.
> + */
> +static void
> +sub_f256(uint32_t *d, const uint32_t *a, const uint32_t *b)
> +{
> + uint32_t w, cc;
> + int i;
> +
> + /*
> + * We really compute a - b + 2*p to make sure that the result is
> + * positive.
> + */
> + w = a[0] - b[0] - 0x00002;
> + d[0] = w & 0x3FFFFFFF;
> + w = a[1] - b[1] + ARSH(w, 30);
> + d[1] = w & 0x3FFFFFFF;
> + w = a[2] - b[2] + ARSH(w, 30);
> + d[2] = w & 0x3FFFFFFF;
> + w = a[3] - b[3] + ARSH(w, 30) + 0x00080;
> + d[3] = w & 0x3FFFFFFF;
> + w = a[4] - b[4] + ARSH(w, 30);
> + d[4] = w & 0x3FFFFFFF;
> + w = a[5] - b[5] + ARSH(w, 30);
> + d[5] = w & 0x3FFFFFFF;
> + w = a[6] - b[6] + ARSH(w, 30) + 0x02000;
> + d[6] = w & 0x3FFFFFFF;
> + w = a[7] - b[7] + ARSH(w, 30) - 0x08000;
> + d[7] = w & 0x3FFFFFFF;
> + w = a[8] - b[8] + ARSH(w, 30) + 0x20000;
> + d[8] = w & 0xFFFF;
> + w >>= 16;
> + d[8] &= 0xFFFF;
> + d[3] -= w << 6;
> + d[6] -= w << 12;
> + d[7] += w << 14;
> + cc = w;
> + for (i = 0; i < 9; i ++) {
> + w = d[i] + cc;
> + d[i] = w & 0x3FFFFFFF;
> + cc = ARSH(w, 30);
> + }
> +}
> +
> +/*
> + * Compute a multiplication in F256. Source operands shall be less than
> + * twice the modulus.
> + */
> +static void
> +mul_f256(uint32_t *d, const uint32_t *a, const uint32_t *b)
> +{
> + uint32_t t[18];
> + uint64_t s[18];
> + uint64_t cc, x;
> + uint32_t z, c;
> + int i;
> +
> + mul9(t, a, b);
> +
> + /*
> + * Modular reduction: each high word in added/subtracted where
> + * necessary.
> + *
> + * The modulus is:
> + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1
> + * Therefore:
> + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p
> + *
> + * For a word x at bit offset n (n >= 256), we have:
> + * x*2^n = x*2^(n-32) - x*2^(n-64)
> + * - x*2^(n - 160) + x*2^(n-256) mod p
> + *
> + * Thus, we can nullify the high word if we reinject it at some
> + * proper emplacements.
> + *
> + * We use 64-bit intermediate words to allow for carries to
> + * accumulate easily, before performing the final propagation.
> + */
> + for (i = 0; i < 18; i ++) {
> + s[i] = t[i];
> + }
> +
> + for (i = 17; i >= 9; i --) {
> + uint64_t y;
> +
> + y = s[i];
> + s[i - 1] += ARSHW(y, 2);
> + s[i - 2] += (y << 28) & 0x3FFFFFFF;
> + s[i - 2] -= ARSHW(y, 4);
> + s[i - 3] -= (y << 26) & 0x3FFFFFFF;
> + s[i - 5] -= ARSHW(y, 10);
> + s[i - 6] -= (y << 20) & 0x3FFFFFFF;
> + s[i - 8] += ARSHW(y, 16);
> + s[i - 9] += (y << 14) & 0x3FFFFFFF;
> + }
> +
> + /*
> + * Carry propagation must be signed. Moreover, we may have overdone
> + * it a bit, and obtain a negative result.
> + *
> + * The loop above ran 9 times; each time, each word was augmented
> + * by at most one extra word (in absolute value). Thus, the top
> + * word must in fine fit in 39 bits, so the carry below will fit
> + * on 9 bits.
> + */
> + cc = 0;
> + for (i = 0; i < 9; i ++) {
> + x = s[i] + cc;
> + d[i] = (uint32_t)x & 0x3FFFFFFF;
> + cc = ARSHW(x, 30);
> + }
> +
> + /*
> + * All nine words fit on 30 bits, but there may be an extra
> + * carry for a few bits (at most 9), and that carry may be
> + * negative. Moreover, we want the result to fit on 257 bits.
> + * The two lines below ensure that the word in d[] has length
> + * 256 bits, and the (signed) carry (beyond 2^256) is in cc. The
> + * significant length of cc is less than 24 bits, so we will be
> + * able to switch to 32-bit operations.
> + */
> + cc = ARSHW(x, 16);
> + d[8] &= 0xFFFF;
> +
> + /*
> + * One extra round of reduction, for cc*2^256, which means
> + * adding cc*(2^224-2^192-2^96+1) to a 256-bit (nonnegative)
> + * value. If cc is negative, then it may happen (rarely, but
> + * not neglectibly so) that the result would be negative. In
> + * order to avoid that, if cc is negative, then we add the
> + * modulus once. Note that if cc is negative, then propagating
> + * that carry must yield a value lower than the modulus, so
> + * adding the modulus once will keep the final result under
> + * twice the modulus.
> + */
> + z = (uint32_t)cc;
> + d[3] -= z << 6;
> + d[6] -= (z << 12) & 0x3FFFFFFF;
> + d[7] -= ARSH(z, 18);
> + d[7] += (z << 14) & 0x3FFFFFFF;
> + d[8] += ARSH(z, 16);
> + c = z >> 31;
> + d[0] -= c;
> + d[3] += c << 6;
> + d[6] += c << 12;
> + d[7] -= c << 14;
> + d[8] += c << 16;
> + for (i = 0; i < 9; i ++) {
> + uint32_t w;
> +
> + w = d[i] + z;
> + d[i] = w & 0x3FFFFFFF;
> + z = ARSH(w, 30);
> + }
> +}
> +
> +/*
> + * Compute a square in F256. Source operand shall be less than
> + * twice the modulus.
> + */
> +static void
> +square_f256(uint32_t *d, const uint32_t *a)
> +{
> + uint32_t t[18];
> + uint64_t s[18];
> + uint64_t cc, x;
> + uint32_t z, c;
> + int i;
> +
> + square9(t, a);
> +
> + /*
> + * Modular reduction: each high word in added/subtracted where
> + * necessary.
> + *
> + * The modulus is:
> + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1
> + * Therefore:
> + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p
> + *
> + * For a word x at bit offset n (n >= 256), we have:
> + * x*2^n = x*2^(n-32) - x*2^(n-64)
> + * - x*2^(n - 160) + x*2^(n-256) mod p
> + *
> + * Thus, we can nullify the high word if we reinject it at some
> + * proper emplacements.
> + *
> + * We use 64-bit intermediate words to allow for carries to
> + * accumulate easily, before performing the final propagation.
> + */
> + for (i = 0; i < 18; i ++) {
> + s[i] = t[i];
> + }
> +
> + for (i = 17; i >= 9; i --) {
> + uint64_t y;
> +
> + y = s[i];
> + s[i - 1] += ARSHW(y, 2);
> + s[i - 2] += (y << 28) & 0x3FFFFFFF;
> + s[i - 2] -= ARSHW(y, 4);
> + s[i - 3] -= (y << 26) & 0x3FFFFFFF;
> + s[i - 5] -= ARSHW(y, 10);
> + s[i - 6] -= (y << 20) & 0x3FFFFFFF;
> + s[i - 8] += ARSHW(y, 16);
> + s[i - 9] += (y << 14) & 0x3FFFFFFF;
> + }
> +
> + /*
> + * Carry propagation must be signed. Moreover, we may have overdone
> + * it a bit, and obtain a negative result.
> + *
> + * The loop above ran 9 times; each time, each word was augmented
> + * by at most one extra word (in absolute value). Thus, the top
> + * word must in fine fit in 39 bits, so the carry below will fit
> + * on 9 bits.
> + */
> + cc = 0;
> + for (i = 0; i < 9; i ++) {
> + x = s[i] + cc;
> + d[i] = (uint32_t)x & 0x3FFFFFFF;
> + cc = ARSHW(x, 30);
> + }
> +
> + /*
> + * All nine words fit on 30 bits, but there may be an extra
> + * carry for a few bits (at most 9), and that carry may be
> + * negative. Moreover, we want the result to fit on 257 bits.
> + * The two lines below ensure that the word in d[] has length
> + * 256 bits, and the (signed) carry (beyond 2^256) is in cc. The
> + * significant length of cc is less than 24 bits, so we will be
> + * able to switch to 32-bit operations.
> + */
> + cc = ARSHW(x, 16);
> + d[8] &= 0xFFFF;
> +
> + /*
> + * One extra round of reduction, for cc*2^256, which means
> + * adding cc*(2^224-2^192-2^96+1) to a 256-bit (nonnegative)
> + * value. If cc is negative, then it may happen (rarely, but
> + * not neglectibly so) that the result would be negative. In
> + * order to avoid that, if cc is negative, then we add the
> + * modulus once. Note that if cc is negative, then propagating
> + * that carry must yield a value lower than the modulus, so
> + * adding the modulus once will keep the final result under
> + * twice the modulus.
> + */
> + z = (uint32_t)cc;
> + d[3] -= z << 6;
> + d[6] -= (z << 12) & 0x3FFFFFFF;
> + d[7] -= ARSH(z, 18);
> + d[7] += (z << 14) & 0x3FFFFFFF;
> + d[8] += ARSH(z, 16);
> + c = z >> 31;
> + d[0] -= c;
> + d[3] += c << 6;
> + d[6] += c << 12;
> + d[7] -= c << 14;
> + d[8] += c << 16;
> + for (i = 0; i < 9; i ++) {
> + uint32_t w;
> +
> + w = d[i] + z;
> + d[i] = w & 0x3FFFFFFF;
> + z = ARSH(w, 30);
> + }
> +}
> +
> +
> +/*
> + * Conditional copy: src[] is copied into dst[] if and only if ctl is 1.
> + * dst[] and src[] may overlap completely (but not partially).
> + */
> +static inline void
> +br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len)
> +{
> + unsigned char *d;
> + const unsigned char *s;
> +
> + d = dst;
> + s = src;
> + while (len -- > 0) {
> + uint32_t x, y;
> +
> + x = *s ++;
> + y = *d;
> + *d = MUX(ctl, x, y);
> + d ++;
> + }
> +}
> +
> +#define CCOPY br_ccopy
> +
> +/*
> + * Perform a "final reduction" in field F256 (field for curve P-256).
> + * The source value must be less than twice the modulus. If the value
> + * is not lower than the modulus, then the modulus is subtracted and
> + * this function returns 1; otherwise, it leaves it untouched and it
> + * returns 0.
> + */
> +static uint32_t
> +reduce_final_f256(uint32_t *d)
> +{
> + uint32_t t[9];
> + uint32_t cc;
> + int i;
> +
> + cc = 0;
> + for (i = 0; i < 9; i ++) {
> + uint32_t w;
> +
> + w = d[i] - F256[i] - cc;
> + cc = w >> 31;
> + t[i] = w & 0x3FFFFFFF;
> + }
> + cc ^= 1;
> + CCOPY(cc, d, t, sizeof t);
> + return cc;
> +}
> +
> +/*
> + * Jacobian coordinates for a point in P-256: affine coordinates (X,Y)
> + * are such that:
> + * X = x / z^2
> + * Y = y / z^3
> + * For the point at infinity, z = 0.
> + * Each point thus admits many possible representations.
> + *
> + * Coordinates are represented in arrays of 32-bit integers, each holding
> + * 30 bits of data. Values may also be slightly greater than the modulus,
> + * but they will always be lower than twice the modulus.
> + */
> +typedef struct {
> + uint32_t x[9];
> + uint32_t y[9];
> + uint32_t z[9];
> +} p256_jacobian;
> +
> +/*
> + * Convert a point to affine coordinates:
> + * - If the point is the point at infinity, then all three coordinates
> + * are set to 0.
> + * - Otherwise, the 'z' coordinate is set to 1, and the 'x' and 'y'
> + * coordinates are the 'X' and 'Y' affine coordinates.
> + * The coordinates are guaranteed to be lower than the modulus.
> + */
> +static void
> +p256_to_affine(p256_jacobian *P)
> +{
> + uint32_t t1[9], t2[9];
> + int i;
> +
> + /*
> + * Invert z with a modular exponentiation: the modulus is
> + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1, and the exponent is
> + * p-2. Exponent bit pattern (from high to low) is:
> + * - 32 bits of value 1
> + * - 31 bits of value 0
> + * - 1 bit of value 1
> + * - 96 bits of value 0
> + * - 94 bits of value 1
> + * - 1 bit of value 0
> + * - 1 bit of value 1
> + * Thus, we precompute z^(2^31-1) to speed things up.
> + *
> + * If z = 0 (point at infinity) then the modular exponentiation
> + * will yield 0, which leads to the expected result (all three
> + * coordinates set to 0).
> + */
> +
> + /*
> + * A simple square-and-multiply for z^(2^31-1). We could save about
> + * two dozen multiplications here with an addition chain, but
> + * this would require a bit more code, and extra stack buffers.
> + */
> + memcpy(t1, P->z, sizeof P->z);
> + for (i = 0; i < 30; i ++) {
> + square_f256(t1, t1);
> + mul_f256(t1, t1, P->z);
> + }
> +
> + /*
> + * Square-and-multiply. Apart from the squarings, we have a few
> + * multiplications to set bits to 1; we multiply by the original z
> + * for setting 1 bit, and by t1 for setting 31 bits.
> + */
> + memcpy(t2, P->z, sizeof P->z);
> + for (i = 1; i < 256; i ++) {
> + square_f256(t2, t2);
> + switch (i) {
> + case 31:
> + case 190:
> + case 221:
> + case 252:
> + mul_f256(t2, t2, t1);
> + break;
> + case 63:
> + case 253:
> + case 255:
> + mul_f256(t2, t2, P->z);
> + break;
> + }
> + }
> +
> + /*
> + * Now that we have 1/z, multiply x by 1/z^2 and y by 1/z^3.
> + */
> + mul_f256(t1, t2, t2);
> + mul_f256(P->x, t1, P->x);
> + mul_f256(t1, t1, t2);
> + mul_f256(P->y, t1, P->y);
> + reduce_final_f256(P->x);
> + reduce_final_f256(P->y);
> +
> + /*
> + * Multiply z by 1/z. If z = 0, then this will yield 0, otherwise
> + * this will set z to 1.
> + */
> + mul_f256(P->z, P->z, t2);
> + reduce_final_f256(P->z);
> +}
> +
> +/*
> + * Double a point in P-256. This function works for all valid points,
> + * including the point at infinity.
> + */
> +static void
> +p256_double(p256_jacobian *Q)
> +{
> + /*
> + * Doubling formulas are:
> + *
> + * s = 4*x*y^2
> + * m = 3*(x + z^2)*(x - z^2)
> + * x' = m^2 - 2*s
> + * y' = m*(s - x') - 8*y^4
> + * z' = 2*y*z
> + *
> + * These formulas work for all points, including points of order 2
> + * and points at infinity:
> + * - If y = 0 then z' = 0. But there is no such point in P-256
> + * anyway.
> + * - If z = 0 then z' = 0.
> + */
> + uint32_t t1[9], t2[9], t3[9], t4[9];
> +
> + /*
> + * Compute z^2 in t1.
> + */
> + square_f256(t1, Q->z);
> +
> + /*
> + * Compute x-z^2 in t2 and x+z^2 in t1.
> + */
> + add_f256(t2, Q->x, t1);
> + sub_f256(t1, Q->x, t1);
> +
> + /*
> + * Compute 3*(x+z^2)*(x-z^2) in t1.
> + */
> + mul_f256(t3, t1, t2);
> + add_f256(t1, t3, t3);
> + add_f256(t1, t3, t1);
> +
> + /*
> + * Compute 4*x*y^2 (in t2) and 2*y^2 (in t3).
> + */
> + square_f256(t3, Q->y);
> + add_f256(t3, t3, t3);
> + mul_f256(t2, Q->x, t3);
> + add_f256(t2, t2, t2);
> +
> + /*
> + * Compute x' = m^2 - 2*s.
> + */
> + square_f256(Q->x, t1);
> + sub_f256(Q->x, Q->x, t2);
> + sub_f256(Q->x, Q->x, t2);
> +
> + /*
> + * Compute z' = 2*y*z.
> + */
> + mul_f256(t4, Q->y, Q->z);
> + add_f256(Q->z, t4, t4);
> +
> + /*
> + * Compute y' = m*(s - x') - 8*y^4. Note that we already have
> + * 2*y^2 in t3.
> + */
> + sub_f256(t2, t2, Q->x);
> + mul_f256(Q->y, t1, t2);
> + square_f256(t4, t3);
> + add_f256(t4, t4, t4);
> + sub_f256(Q->y, Q->y, t4);
> +}
> +
> +/*
> + * Add point P2 to point P1.
> + *
> + * This function computes the wrong result in the following cases:
> + *
> + * - If P1 == 0 but P2 != 0
> + * - If P1 != 0 but P2 == 0
> + * - If P1 == P2
> + *
> + * In all three cases, P1 is set to the point at infinity.
> + *
> + * Returned value is 0 if one of the following occurs:
> + *
> + * - P1 and P2 have the same Y coordinate
> + * - P1 == 0 and P2 == 0
> + * - The Y coordinate of one of the points is 0 and the other point is
> + * the point at infinity.
> + *
> + * The third case cannot actually happen with valid points, since a point
> + * with Y == 0 is a point of order 2, and there is no point of order 2 on
> + * curve P-256.
> + *
> + * Therefore, assuming that P1 != 0 and P2 != 0 on input, then the caller
> + * can apply the following:
> + *
> + * - If the result is not the point at infinity, then it is correct.
> + * - Otherwise, if the returned value is 1, then this is a case of
> + * P1+P2 == 0, so the result is indeed the point at infinity.
> + * - Otherwise, P1 == P2, so a "double" operation should have been
> + * performed.
> + */
> +static uint32_t
> +p256_add(p256_jacobian *P1, const p256_jacobian *P2)
> +{
> + /*
> + * Addtions formulas are:
> + *
> + * u1 = x1 * z2^2
> + * u2 = x2 * z1^2
> + * s1 = y1 * z2^3
> + * s2 = y2 * z1^3
> + * h = u2 - u1
> + * r = s2 - s1
> + * x3 = r^2 - h^3 - 2 * u1 * h^2
> + * y3 = r * (u1 * h^2 - x3) - s1 * h^3
> + * z3 = h * z1 * z2
> + */
> + uint32_t t1[9], t2[9], t3[9], t4[9], t5[9], t6[9], t7[9];
> + uint32_t ret;
> + int i;
> +
> + /*
> + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3).
> + */
> + square_f256(t3, P2->z);
> + mul_f256(t1, P1->x, t3);
> + mul_f256(t4, P2->z, t3);
> + mul_f256(t3, P1->y, t4);
> +
> + /*
> + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).
> + */
> + square_f256(t4, P1->z);
> + mul_f256(t2, P2->x, t4);
> + mul_f256(t5, P1->z, t4);
> + mul_f256(t4, P2->y, t5);
> +
> + /*
> + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4).
> + * We need to test whether r is zero, so we will do some extra
> + * reduce.
> + */
> + sub_f256(t2, t2, t1);
> + sub_f256(t4, t4, t3);
> + reduce_final_f256(t4);
> + ret = 0;
> + for (i = 0; i < 9; i ++) {
> + ret |= t4[i];
> + }
> + ret = (ret | -ret) >> 31;
> +
> + /*
> + * Compute u1*h^2 (in t6) and h^3 (in t5);
> + */
> + square_f256(t7, t2);
> + mul_f256(t6, t1, t7);
> + mul_f256(t5, t7, t2);
> +
> + /*
> + * Compute x3 = r^2 - h^3 - 2*u1*h^2.
> + */
> + square_f256(P1->x, t4);
> + sub_f256(P1->x, P1->x, t5);
> + sub_f256(P1->x, P1->x, t6);
> + sub_f256(P1->x, P1->x, t6);
> +
> + /*
> + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3.
> + */
> + sub_f256(t6, t6, P1->x);
> + mul_f256(P1->y, t4, t6);
> + mul_f256(t1, t5, t3);
> + sub_f256(P1->y, P1->y, t1);
> +
> + /*
> + * Compute z3 = h*z1*z2.
> + */
> + mul_f256(t1, P1->z, P2->z);
> + mul_f256(P1->z, t1, t2);
> +
> + return ret;
> +}
> +
> +/*
> + * Add point P2 to point P1. This is a specialised function for the
> + * case when P2 is a non-zero point in affine coordinate.
> + *
> + * This function computes the wrong result in the following cases:
> + *
> + * - If P1 == 0
> + * - If P1 == P2
> + *
> + * In both cases, P1 is set to the point at infinity.
> + *
> + * Returned value is 0 if one of the following occurs:
> + *
> + * - P1 and P2 have the same Y coordinate
> + * - The Y coordinate of P2 is 0 and P1 is the point at infinity.
> + *
> + * The second case cannot actually happen with valid points, since a point
> + * with Y == 0 is a point of order 2, and there is no point of order 2 on
> + * curve P-256.
> + *
> + * Therefore, assuming that P1 != 0 on input, then the caller
> + * can apply the following:
> + *
> + * - If the result is not the point at infinity, then it is correct.
> + * - Otherwise, if the returned value is 1, then this is a case of
> + * P1+P2 == 0, so the result is indeed the point at infinity.
> + * - Otherwise, P1 == P2, so a "double" operation should have been
> + * performed.
> + */
> +static uint32_t
> +p256_add_mixed(p256_jacobian *P1, const p256_jacobian *P2)
> +{
> + /*
> + * Addtions formulas are:
> + *
> + * u1 = x1
> + * u2 = x2 * z1^2
> + * s1 = y1
> + * s2 = y2 * z1^3
> + * h = u2 - u1
> + * r = s2 - s1
> + * x3 = r^2 - h^3 - 2 * u1 * h^2
> + * y3 = r * (u1 * h^2 - x3) - s1 * h^3
> + * z3 = h * z1
> + */
> + uint32_t t1[9], t2[9], t3[9], t4[9], t5[9], t6[9], t7[9];
> + uint32_t ret;
> + int i;
> +
> + /*
> + * Compute u1 = x1 (in t1) and s1 = y1 (in t3).
> + */
> + memcpy(t1, P1->x, sizeof t1);
> + memcpy(t3, P1->y, sizeof t3);
> +
> + /*
> + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).
> + */
> + square_f256(t4, P1->z);
> + mul_f256(t2, P2->x, t4);
> + mul_f256(t5, P1->z, t4);
> + mul_f256(t4, P2->y, t5);
> +
> + /*
> + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4).
> + * We need to test whether r is zero, so we will do some extra
> + * reduce.
> + */
> + sub_f256(t2, t2, t1);
> + sub_f256(t4, t4, t3);
> + reduce_final_f256(t4);
> + ret = 0;
> + for (i = 0; i < 9; i ++) {
> + ret |= t4[i];
> + }
> + ret = (ret | -ret) >> 31;
> +
> + /*
> + * Compute u1*h^2 (in t6) and h^3 (in t5);
> + */
> + square_f256(t7, t2);
> + mul_f256(t6, t1, t7);
> + mul_f256(t5, t7, t2);
> +
> + /*
> + * Compute x3 = r^2 - h^3 - 2*u1*h^2.
> + */
> + square_f256(P1->x, t4);
> + sub_f256(P1->x, P1->x, t5);
> + sub_f256(P1->x, P1->x, t6);
> + sub_f256(P1->x, P1->x, t6);
> +
> + /*
> + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3.
> + */
> + sub_f256(t6, t6, P1->x);
> + mul_f256(P1->y, t4, t6);
> + mul_f256(t1, t5, t3);
> + sub_f256(P1->y, P1->y, t1);
> +
> + /*
> + * Compute z3 = h*z1*z2.
> + */
> + mul_f256(P1->z, P1->z, t2);
> +
> + return ret;
> +}
> +
> +/*
> + * Decode a P-256 point. This function does not support the point at
> + * infinity. Returned value is 0 if the point is invalid, 1 otherwise.
> + */
> +static uint32_t
> +p256_decode(p256_jacobian *P, const void *src, size_t len)
> +{
> + const unsigned char *buf;
> + uint32_t tx[9], ty[9], t1[9], t2[9];
> + uint32_t bad;
> + int i;
> +
> + if (len != 65) {
> + return 0;
> + }
> + buf = src;
> +
> + /*
> + * First byte must be 0x04 (uncompressed format). We could support
> + * "hybrid format" (first byte is 0x06 or 0x07, and encodes the
> + * least significant bit of the Y coordinate), but it is explicitly
> + * forbidden by RFC 5480 (section 2.2).
> + */
> + bad = NEQ(buf[0], 0x04);
> +
> + /*
> + * Decode the coordinates, and check that they are both lower
> + * than the modulus.
> + */
> + tx[8] = be8_to_le30(tx, buf + 1, 32);
> + ty[8] = be8_to_le30(ty, buf + 33, 32);
> + bad |= reduce_final_f256(tx);
> + bad |= reduce_final_f256(ty);
> +
> + /*
> + * Check curve equation.
> + */
> + square_f256(t1, tx);
> + mul_f256(t1, tx, t1);
> + square_f256(t2, ty);
> + sub_f256(t1, t1, tx);
> + sub_f256(t1, t1, tx);
> + sub_f256(t1, t1, tx);
> + add_f256(t1, t1, P256_B);
> + sub_f256(t1, t1, t2);
> + reduce_final_f256(t1);
> + for (i = 0; i < 9; i ++) {
> + bad |= t1[i];
> + }
> +
> + /*
> + * Copy coordinates to the point structure.
> + */
> + memcpy(P->x, tx, sizeof tx);
> + memcpy(P->y, ty, sizeof ty);
> + memset(P->z, 0, sizeof P->z);
> + P->z[0] = 1;
> + return EQ(bad, 0);
> +}
> +
> +/*
> + * Encode a point into a buffer. This function assumes that the point is
> + * valid, in affine coordinates, and not the point at infinity.
> + */
> +static void
> +p256_encode(void *dst, const p256_jacobian *P)
> +{
> + unsigned char *buf;
> +
> + buf = dst;
> + buf[0] = 0x04;
> + le30_to_be8(buf + 1, 32, P->x);
> + le30_to_be8(buf + 33, 32, P->y);
> +}
> +
> +/*
> + * Multiply a curve point by an integer. The integer is assumed to be
> + * lower than the curve order, and the base point must not be the point
> + * at infinity.
> + */
> +static void
> +p256_mul(p256_jacobian *P, const unsigned char *x, size_t xlen)
> +{
> + /*
> + * qz is a flag that is initially 1, and remains equal to 1
> + * as long as the point is the point at infinity.
> + *
> + * We use a 2-bit window to handle multiplier bits by pairs.
> + * The precomputed window really is the points P2 and P3.
> + */
> + uint32_t qz;
> + p256_jacobian P2, P3, Q, T, U;
> +
> + /*
> + * Compute window values.
> + */
> + P2 = *P;
> + p256_double(&P2);
> + P3 = *P;
> + p256_add(&P3, &P2);
> +
> + /*
> + * We start with Q = 0. We process multiplier bits 2 by 2.
> + */
> + memset(&Q, 0, sizeof Q);
> + qz = 1;
> + while (xlen -- > 0) {
> + int k;
> +
> + for (k = 6; k >= 0; k -= 2) {
> + uint32_t bits;
> + uint32_t bnz;
> +
> + p256_double(&Q);
> + p256_double(&Q);
> + T = *P;
> + U = Q;
> + bits = (*x >> k) & (uint32_t)3;
> + bnz = NEQ(bits, 0);
> + CCOPY(EQ(bits, 2), &T, &P2, sizeof T);
> + CCOPY(EQ(bits, 3), &T, &P3, sizeof T);
> + p256_add(&U, &T);
> + CCOPY(bnz & qz, &Q, &T, sizeof Q);
> + CCOPY(bnz & ~qz, &Q, &U, sizeof Q);
> + qz &= ~bnz;
> + }
> + x ++;
> + }
> + *P = Q;
> +}
> +
> +/*
> + * Precomputed window: k*G points, where G is the curve generator, and k
> + * is an integer from 1 to 15 (inclusive). The X and Y coordinates of
> + * the point are encoded as 9 words of 30 bits each (little-endian
> + * order).
> + */
> +static const uint32_t Gwin[15][18] = {
> +
> + { 0x1898C296, 0x1284E517, 0x1EB33A0F, 0x00DF604B,
> + 0x2440F277, 0x339B958E, 0x04247F8B, 0x347CB84B,
> + 0x00006B17, 0x37BF51F5, 0x2ED901A0, 0x3315ECEC,
> + 0x338CD5DA, 0x0F9E162B, 0x1FAD29F0, 0x27F9B8EE,
> + 0x10B8BF86, 0x00004FE3 },
> +
> + { 0x07669978, 0x182D23F1, 0x3F21B35A, 0x225A789D,
> + 0x351AC3C0, 0x08E00C12, 0x34F7E8A5, 0x1EC62340,
> + 0x00007CF2, 0x227873D1, 0x3812DE74, 0x0E982299,
> + 0x1F6B798F, 0x3430DBBA, 0x366B1A7D, 0x2D040293,
> + 0x154436E3, 0x00000777 },
> +
> + { 0x06E7FD6C, 0x2D05986F, 0x3ADA985F, 0x31ADC87B,
> + 0x0BF165E6, 0x1FBE5475, 0x30A44C8F, 0x3934698C,
> + 0x00005ECB, 0x227D5032, 0x29E6C49E, 0x04FB83D9,
> + 0x0AAC0D8E, 0x24A2ECD8, 0x2C1B3869, 0x0FF7E374,
> + 0x19031266, 0x00008734 },
> +
> + { 0x2B030852, 0x024C0911, 0x05596EF5, 0x07F8B6DE,
> + 0x262BD003, 0x3779967B, 0x08FBBA02, 0x128D4CB4,
> + 0x0000E253, 0x184ED8C6, 0x310B08FC, 0x30EE0055,
> + 0x3F25B0FC, 0x062D764E, 0x3FB97F6A, 0x33CC719D,
> + 0x15D69318, 0x0000E0F1 },
> +
> + { 0x03D033ED, 0x05552837, 0x35BE5242, 0x2320BF47,
> + 0x268FDFEF, 0x13215821, 0x140D2D78, 0x02DE9454,
> + 0x00005159, 0x3DA16DA4, 0x0742ED13, 0x0D80888D,
> + 0x004BC035, 0x0A79260D, 0x06FCDAFE, 0x2727D8AE,
> + 0x1F6A2412, 0x0000E0C1 },
> +
> + { 0x3C2291A9, 0x1AC2ABA4, 0x3B215B4C, 0x131D037A,
> + 0x17DDE302, 0x0C90B2E2, 0x0602C92D, 0x05CA9DA9,
> + 0x0000B01A, 0x0FC77FE2, 0x35F1214E, 0x07E16BDF,
> + 0x003DDC07, 0x2703791C, 0x3038B7EE, 0x3DAD56FE,
> + 0x041D0C8D, 0x0000E85C },
> +
> + { 0x3187B2A3, 0x0018A1C0, 0x00FEF5B3, 0x3E7E2E2A,
> + 0x01FB607E, 0x2CC199F0, 0x37B4625B, 0x0EDBE82F,
> + 0x00008E53, 0x01F400B4, 0x15786A1B, 0x3041B21C,
> + 0x31CD8CF2, 0x35900053, 0x1A7E0E9B, 0x318366D0,
> + 0x076F780C, 0x000073EB },
> +
> + { 0x1B6FB393, 0x13767707, 0x3CE97DBB, 0x348E2603,
> + 0x354CADC1, 0x09D0B4EA, 0x1B053404, 0x1DE76FBA,
> + 0x000062D9, 0x0F09957E, 0x295029A8, 0x3E76A78D,
> + 0x3B547DAE, 0x27CEE0A2, 0x0575DC45, 0x1D8244FF,
> + 0x332F647A, 0x0000AD5A },
> +
> + { 0x10949EE0, 0x1E7A292E, 0x06DF8B3D, 0x02B2E30B,
> + 0x31F8729E, 0x24E35475, 0x30B71878, 0x35EDBFB7,
> + 0x0000EA68, 0x0DD048FA, 0x21688929, 0x0DE823FE,
> + 0x1C53FAA9, 0x0EA0C84D, 0x052A592A, 0x1FCE7870,
> + 0x11325CB2, 0x00002A27 },
> +
> + { 0x04C5723F, 0x30D81A50, 0x048306E4, 0x329B11C7,
> + 0x223FB545, 0x085347A8, 0x2993E591, 0x1B5ACA8E,
> + 0x0000CEF6, 0x04AF0773, 0x28D2EEA9, 0x2751EEEC,
> + 0x037B4A7F, 0x3B4C1059, 0x08F37674, 0x2AE906E1,
> + 0x18A88A6A, 0x00008786 },
> +
> + { 0x34BC21D1, 0x0CCE474D, 0x15048BF4, 0x1D0BB409,
> + 0x021CDA16, 0x20DE76C3, 0x34C59063, 0x04EDE20E,
> + 0x00003ED1, 0x282A3740, 0x0BE3BBF3, 0x29889DAE,
> + 0x03413697, 0x34C68A09, 0x210EBE93, 0x0C8A224C,
> + 0x0826B331, 0x00009099 },
> +
> + { 0x0624E3C4, 0x140317BA, 0x2F82C99D, 0x260C0A2C,
> + 0x25D55179, 0x194DCC83, 0x3D95E462, 0x356F6A05,
> + 0x0000741D, 0x0D4481D3, 0x2657FC8B, 0x1BA5CA71,
> + 0x3AE44B0D, 0x07B1548E, 0x0E0D5522, 0x05FDC567,
> + 0x2D1AA70E, 0x00000770 },
> +
> + { 0x06072C01, 0x23857675, 0x1EAD58A9, 0x0B8A12D9,
> + 0x1EE2FC79, 0x0177CB61, 0x0495A618, 0x20DEB82B,
> + 0x0000177C, 0x2FC7BFD8, 0x310EEF8B, 0x1FB4DF39,
> + 0x3B8530E8, 0x0F4E7226, 0x0246B6D0, 0x2A558A24,
> + 0x163353AF, 0x000063BB },
> +
> + { 0x24D2920B, 0x1C249DCC, 0x2069C5E5, 0x09AB2F9E,
> + 0x36DF3CF1, 0x1991FD0C, 0x062B97A7, 0x1E80070E,
> + 0x000054E7, 0x20D0B375, 0x2E9F20BD, 0x35090081,
> + 0x1C7A9DDC, 0x22E7C371, 0x087E3016, 0x03175421,
> + 0x3C6ECA7D, 0x0000F599 },
> +
> + { 0x259B9D5F, 0x0D9A318F, 0x23A0EF16, 0x00EBE4B7,
> + 0x088265AE, 0x2CDE2666, 0x2BAE7ADF, 0x1371A5C6,
> + 0x0000F045, 0x0D034F36, 0x1F967378, 0x1B5FA3F4,
> + 0x0EC8739D, 0x1643E62A, 0x1653947E, 0x22D1F4E6,
> + 0x0FB8D64B, 0x0000B5B9 }
> +};
> +
> +/*
> + * Lookup one of the Gwin[] values, by index. This is constant-time.
> + */
> +static void
> +lookup_Gwin(p256_jacobian *T, uint32_t idx)
> +{
> + uint32_t xy[18];
> + uint32_t k;
> + size_t u;
> +
> + memset(xy, 0, sizeof xy);
> + for (k = 0; k < 15; k ++) {
> + uint32_t m;
> +
> + m = -EQ(idx, k + 1);
> + for (u = 0; u < 18; u ++) {
> + xy[u] |= m & Gwin[k][u];
> + }
> + }
> + memcpy(T->x, &xy[0], sizeof T->x);
> + memcpy(T->y, &xy[9], sizeof T->y);
> + memset(T->z, 0, sizeof T->z);
> + T->z[0] = 1;
> +}
> +
> +/*
> + * Multiply the generator by an integer. The integer is assumed non-zero
> + * and lower than the curve order.
> + */
> +static void
> +p256_mulgen(p256_jacobian *P, const unsigned char *x, size_t xlen)
> +{
> + /*
> + * qz is a flag that is initially 1, and remains equal to 1
> + * as long as the point is the point at infinity.
> + *
> + * We use a 4-bit window to handle multiplier bits by groups
> + * of 4. The precomputed window is constant static data, with
> + * points in affine coordinates; we use a constant-time lookup.
> + */
> + p256_jacobian Q;
> + uint32_t qz;
> +
> + memset(&Q, 0, sizeof Q);
> + qz = 1;
> + while (xlen -- > 0) {
> + int k;
> + unsigned bx;
> +
> + bx = *x ++;
> + for (k = 0; k < 2; k ++) {
> + uint32_t bits;
> + uint32_t bnz;
> + p256_jacobian T, U;
> +
> + p256_double(&Q);
> + p256_double(&Q);
> + p256_double(&Q);
> + p256_double(&Q);
> + bits = (bx >> 4) & 0x0F;
> + bnz = NEQ(bits, 0);
> + lookup_Gwin(&T, bits);
> + U = Q;
> + p256_add_mixed(&U, &T);
> + CCOPY(bnz & qz, &Q, &T, sizeof Q);
> + CCOPY(bnz & ~qz, &Q, &U, sizeof Q);
> + qz &= ~bnz;
> + bx <<= 4;
> + }
> + }
> + *P = Q;
> +}
> +
> +static const unsigned char P256_G[] = {
> + 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8,
> + 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D,
> + 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8,
> + 0x98, 0xC2, 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F,
> + 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B,
> + 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40,
> + 0x68, 0x37, 0xBF, 0x51, 0xF5
> +};
> +
> +static const unsigned char P256_N[] = {
> + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
> + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD,
> + 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63,
> + 0x25, 0x51
> +};
> +
> +static const unsigned char *
> +api_generator(int curve, size_t *len)
> +{
> + (void)curve;
> + *len = sizeof P256_G;
> + return P256_G;
> +}
> +
> +static const unsigned char *
> +api_order(int curve, size_t *len)
> +{
> + (void)curve;
> + *len = sizeof P256_N;
> + return P256_N;
> +}
> +
> +static size_t
> +api_xoff(int curve, size_t *len)
> +{
> + (void)curve;
> + *len = 32;
> + return 1;
> +}
> +
> +static int
> +api_prime(int curve, unsigned char *p, size_t plen)
> +{
> + unsigned char *buf;
> +
> + if (plen < 33)
> + return 0;
> +
> + buf = p;
> + buf[0] = 0x04;
> + le30_to_be8(buf + 1, 32, F256);
> + return 1;
> +}
> +
> +static int
> +api_invert(int curve, unsigned char *G, size_t Glen)
> +{
> + uint32_t r;
> + p256_jacobian P;
> +
> + (void)curve;
> + if (Glen != 65) {
> + return 0;
> + }
> +
> + r = p256_decode(&P, G, Glen);
> + sub_f256(P.y, F256, P.y);
> + p256_to_affine(&P);
> + p256_encode(G, &P);
> + return r;
> +}
> +
> +static uint32_t
> +api_mul(unsigned char *G, size_t Glen,
> + const unsigned char *x, size_t xlen, int curve)
> +{
> + uint32_t r;
> + p256_jacobian P;
> +
> + (void)curve;
> + if (Glen != 65) {
> + return 0;
> + }
> + r = p256_decode(&P, G, Glen);
> + p256_mul(&P, x, xlen);
> + p256_to_affine(&P);
> + p256_encode(G, &P);
> + return r;
> +}
> +
> +static size_t
> +api_mulgen(unsigned char *R,
> + const unsigned char *x, size_t xlen, int curve)
> +{
> + p256_jacobian P;
> +
> + (void)curve;
> + p256_mulgen(&P, x, xlen);
> + p256_to_affine(&P);
> + p256_encode(R, &P);
> + return 65;
> +}
> +
> +static uint32_t
> +api_muladd(unsigned char *A, const unsigned char *B, size_t len,
> + const unsigned char *x, size_t xlen,
> + const unsigned char *y, size_t ylen, int curve)
> +{
> + p256_jacobian P, Q;
> + uint32_t r, t, z;
> + int i;
> +
> + (void)curve;
> + if (len != 65) {
> + return 0;
> + }
> + r = p256_decode(&P, A, len);
> + p256_mul(&P, x, xlen);
> + if (B == NULL) {
> + p256_mulgen(&Q, y, ylen);
> + } else {
> + r &= p256_decode(&Q, B, len);
> + p256_mul(&Q, y, ylen);
> + }
> +
> + /*
> + * The final addition may fail in case both points are equal.
> + */
> + t = p256_add(&P, &Q);
> + reduce_final_f256(P.z);
> + z = 0;
> + for (i = 0; i < 9; i ++) {
> + z |= P.z[i];
> + }
> + z = EQ(z, 0);
> + p256_double(&Q);
> +
> + /*
> + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we
> + * have the following:
> + *
> + * z = 0, t = 0 return P (normal addition)
> + * z = 0, t = 1 return P (normal addition)
> + * z = 1, t = 0 return Q (a 'double' case)
> + * z = 1, t = 1 report an error (P+Q = 0)
> + */
> + CCOPY(z & ~t, &P, &Q, sizeof Q);
> + p256_to_affine(&P);
> + p256_encode(A, &P);
> + r &= ~(z & t);
> + return r;
> +}
> +
> +/* see bearssl_ec.h */
> +const br_ec_impl br_ec_p256_m31 = {
> + (uint32_t)0x00800000,
> + &api_generator,
> + &api_order,
> + &api_xoff,
> + &api_prime,
> + &api_invert,
> + &api_mul,
> + &api_mulgen,
> + &api_muladd
> +};
> blob - /dev/null
> blob + 0f7666bc2c5ce5ab3100548a2c29ed0f18ab9b6e (mode 644)
> --- /dev/null
> +++ sys/crypto/ec_p256_m31.h
> @@ -0,0 +1,301 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +/** \file bearssl_ec.h
> + *
> + * # Elliptic Curves
> + *
> + * This file documents the EC implementations provided with BearSSL.
> + *
> + * ## Elliptic Curve API
> + *
> + * Only "named curves" are supported. Each EC implementation supports
> + * one or several named curves, identified by symbolic identifiers.
> + * These identifiers are small integers, that correspond to the values
> + * registered by the
> + * [IANA](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8).
> + *
> + * Since all currently defined elliptic curve identifiers are in the 0..31
> + * range, it is convenient to encode support of some curves in a 32-bit
> + * word, such that bit x corresponds to curve of identifier x.
> + *
> + * An EC implementation is incarnated by a `br_ec_impl` instance, that
> + * offers the following fields:
> + *
> + * - `supported_curves`
> + *
> + * A 32-bit word that documents the identifiers of the curves supported
> + * by this implementation.
> + *
> + * - `generator()`
> + *
> + * Callback method that returns a pointer to the conventional generator
> + * point for that curve.
> + *
> + * - `order()`
> + *
> + * Callback method that returns a pointer to the subgroup order for
> + * that curve. That value uses unsigned big-endian encoding.
> + *
> + * - `xoff()`
> + *
> + * Callback method that returns the offset and length of the X
> + * coordinate in an encoded point.
> + *
> + * - `prime()`
> + *
> + * Callback method that returns the curve's prime number.
> + * (This is an API extension added by OpenBSD for use with net80211/SAE.)
> +
> + * - `invert()`
> + *
> + * Invert a curve point.
> + * (This is an API extension added by OpenBSD for use with net80211/SAE.)
> + *
> + * - `mul()`
> + *
> + * Multiply a curve point with an integer.
> + *
> + * - `mulgen()`
> + *
> + * Multiply the curve generator with an integer. This may be faster
> + * than the generic `mul()`.
> + *
> + * - `muladd()`
> + *
> + * Multiply two curve points by two integers, and return the sum of
> + * the two products.
> + *
> + * All curve points are represented in uncompressed format. The `mul()`
> + * and `muladd()` methods take care to validate that the provided points
> + * are really part of the relevant curve subgroup.
> + *
> + * For all point multiplication functions, the following holds:
> + *
> + * - Functions validate that the provided points are valid members
> + * of the relevant curve subgroup. An error is reported if that is
> + * not the case.
> + *
> + * - Processing is constant-time, even if the point operands are not
> + * valid. This holds for both the source and resulting points, and
> + * the multipliers (integers). Only the byte length of the provided
> + * multiplier arrays (not their actual value length in bits) may
> + * leak through timing-based side channels.
> + *
> + * - The multipliers (integers) MUST be lower than the subgroup order.
> + * If this property is not met, then the result is indeterminate,
> + * but an error value is not necessarily returned.
> + */
> +
> +/*
> + * Standard curve ID. These ID are equal to the assigned numerical
> + * identifiers assigned to these curves for TLS:
> + * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
> + */
> +
> +/** \brief Identifier for named curve secp192r1. */
> +#define BR_EC_secp192r1 19
> +
> +/**
> + * \brief Type for an EC implementation.
> + */
> +typedef struct {
> + /**
> + * \brief Supported curves.
> + *
> + * This word is a bitfield: bit `x` is set if the curve of ID `x`
> + * is supported. E.g. an implementation supporting both NIST P-256
> + * (secp256r1, ID 23) and NIST P-384 (secp384r1, ID 24) will have
> + * value `0x01800000` in this field.
> + */
> + uint32_t supported_curves;
> +
> + /**
> + * \brief Get the conventional generator.
> + *
> + * This function returns the conventional generator (encoded
> + * curve point) for the specified curve. This function MUST NOT
> + * be called if the curve is not supported.
> + *
> + * \param curve curve identifier.
> + * \param len receiver for the encoded generator length (in bytes).
> + * \return the encoded generator.
> + */
> + const unsigned char *(*generator)(int curve, size_t *len);
> +
> + /**
> + * \brief Get the subgroup order.
> + *
> + * This function returns the order of the subgroup generated by
> + * the conventional generator, for the specified curve. Unsigned
> + * big-endian encoding is used. This function MUST NOT be called
> + * if the curve is not supported.
> + *
> + * \param curve curve identifier.
> + * \param len receiver for the encoded order length (in bytes).
> + * \return the encoded order.
> + */
> + const unsigned char *(*order)(int curve, size_t *len);
> +
> + /**
> + * \brief Get the offset and length for the X coordinate.
> + *
> + * This function returns the offset and length (in bytes) of
> + * the X coordinate in an encoded non-zero point.
> + *
> + * \param curve curve identifier.
> + * \param len receiver for the X coordinate length (in bytes).
> + * \return the offset for the X coordinate (in bytes).
> + */
> + size_t (*xoff)(int curve, size_t *len);
> +
> + /**
> + * \brief Get the prime number of the curve as an integer in
> + * unsigned big-endian encoding.
> + * \param curve curve identifier.
> + * \param p output buffer
> + * \param plen output buffer size; must be at least as large as
> + * offset + length as returned by xoff().
> + * \return 1 on success, 0 on error.
> + */
> + int (*prime)(int curve, unsigned char *p, size_t plen);
> +
> + /**
> + * \brief Invert a curve point:
> + * The point's Y coordinate becomes equal to prime - Y.
> + *
> + * \param curve curve identifier.
> + * \param G point to invert.
> + * \param Glen length of the encoded point (in bytes).
> + * \return 1 on success, 0 on error.
> + */
> + int (*invert)(int curve, unsigned char *G, size_t Glen);
> +
> + /**
> + * \brief Multiply a curve point by an integer.
> + *
> + * The source point is provided in array `G` (of size `Glen` bytes);
> + * the multiplication result is written over it. The multiplier
> + * `x` (of size `xlen` bytes) uses unsigned big-endian encoding.
> + *
> + * Rules:
> + *
> + * - The specified curve MUST be supported.
> + *
> + * - The source point must be a valid point on the relevant curve
> + * subgroup (and not the "point at infinity" either). If this is
> + * not the case, then this function returns an error (0).
> + *
> + * - The multiplier integer MUST be non-zero and less than the
> + * curve subgroup order. If this property does not hold, then
> + * the result is indeterminate and an error code is not
> + * guaranteed.
> + *
> + * Returned value is 1 on success, 0 on error. On error, the
> + * contents of `G` are indeterminate.
> + *
> + * \param G point to multiply.
> + * \param Glen length of the encoded point (in bytes).
> + * \param x multiplier (unsigned big-endian).
> + * \param xlen multiplier length (in bytes).
> + * \param curve curve identifier.
> + * \return 1 on success, 0 on error.
> + */
> + uint32_t (*mul)(unsigned char *G, size_t Glen,
> + const unsigned char *x, size_t xlen, int curve);
> +
> + /**
> + * \brief Multiply the generator by an integer.
> + *
> + * The multiplier MUST be non-zero and less than the curve
> + * subgroup order. Results are indeterminate if this property
> + * does not hold.
> + *
> + * \param R output buffer for the point.
> + * \param x multiplier (unsigned big-endian).
> + * \param xlen multiplier length (in bytes).
> + * \param curve curve identifier.
> + * \return encoded result point length (in bytes).
> + */
> + size_t (*mulgen)(unsigned char *R,
> + const unsigned char *x, size_t xlen, int curve);
> +
> + /**
> + * \brief Multiply two points by two integers and add the
> + * results.
> + *
> + * The point `x*A + y*B` is computed and written back in the `A`
> + * array.
> + *
> + * Rules:
> + *
> + * - The specified curve MUST be supported.
> + *
> + * - The source points (`A` and `B`) must be valid points on
> + * the relevant curve subgroup (and not the "point at
> + * infinity" either). If this is not the case, then this
> + * function returns an error (0).
> + *
> + * - If the `B` pointer is `NULL`, then the conventional
> + * subgroup generator is used. With some implementations,
> + * this may be faster than providing a pointer to the
> + * generator.
> + *
> + * - The multiplier integers (`x` and `y`) MUST be non-zero
> + * and less than the curve subgroup order. If either integer
> + * is zero, then an error is reported, but if one of them is
> + * not lower than the subgroup order, then the result is
> + * indeterminate and an error code is not guaranteed.
> + *
> + * - If the final result is the point at infinity, then an
> + * error is returned.
> + *
> + * Returned value is 1 on success, 0 on error. On error, the
> + * contents of `A` are indeterminate.
> + *
> + * \param A first point to multiply.
> + * \param B second point to multiply (`NULL` for the generator).
> + * \param len common length of the encoded points (in bytes).
> + * \param x multiplier for `A` (unsigned big-endian).
> + * \param xlen length of multiplier for `A` (in bytes).
> + * \param y multiplier for `A` (unsigned big-endian).
> + * \param ylen length of multiplier for `A` (in bytes).
> + * \param curve curve identifier.
> + * \return 1 on success, 0 on error.
> + */
> + uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
> + const unsigned char *x, size_t xlen,
> + const unsigned char *y, size_t ylen, int curve);
> +} br_ec_impl;
> +
> +/**
> + * \brief EC implementation "m31" for P-256.
> + *
> + * This implementation uses specialised code for curve secp256r1 (also
> + * known as NIST P-256), relying on multiplications of 31-bit values
> + * (MUL31).
> + */
> +extern const br_ec_impl br_ec_p256_m31;
> blob - /dev/null
> blob + 58f9c41d0377301a3e0a9424e01a164dc5ebdc3f (mode 644)
> --- /dev/null
> +++ sys/crypto/i31.c
> @@ -0,0 +1,516 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include <sys/types.h>
> +#include <sys/systm.h>
> +
> +#include <crypto/i31.h>
> +
> +void
> +br_i31_decode(uint32_t *x, const void *src, size_t len)
> +{
> + const unsigned char *buf;
> + size_t u, v;
> + uint32_t acc;
> + int acc_len;
> +
> + buf = src;
> + u = len;
> + v = 1;
> + acc = 0;
> + acc_len = 0;
> + while (u -- > 0) {
> + uint32_t b;
> +
> + b = buf[u];
> + acc |= (b << acc_len);
> + acc_len += 8;
> + if (acc_len >= 31) {
> + x[v ++] = acc & (uint32_t)0x7FFFFFFF;
> + acc_len -= 31;
> + acc = b >> (8 - acc_len);
> + }
> + }
> + if (acc_len != 0) {
> + x[v ++] = acc;
> + }
> + x[0] = br_i31_bit_length(x + 1, v - 1);
> +}
> +
> +static inline void
> +br_enc32be(void *dst, uint32_t x)
> +{
> + unsigned char *buf;
> +
> + buf = dst;
> + buf[0] = (unsigned char)(x >> 24);
> + buf[1] = (unsigned char)(x >> 16);
> + buf[2] = (unsigned char)(x >> 8);
> + buf[3] = (unsigned char)x;
> +}
> +
> +void
> +br_i31_encode(void *dst, size_t len, const uint32_t *x)
> +{
> + unsigned char *buf;
> + size_t k, xlen;
> + uint32_t acc;
> + int acc_len;
> +
> + xlen = (x[0] + 31) >> 5;
> + if (xlen == 0) {
> + memset(dst, 0, len);
> + return;
> + }
> + buf = (unsigned char *)dst + len;
> + k = 1;
> + acc = 0;
> + acc_len = 0;
> + while (len != 0) {
> + uint32_t w;
> +
> + w = (k <= xlen) ? x[k] : 0;
> + k ++;
> + if (acc_len == 0) {
> + acc = w;
> + acc_len = 31;
> + } else {
> + uint32_t z;
> +
> + z = acc | (w << acc_len);
> + acc_len --;
> + acc = w >> (31 - acc_len);
> + if (len >= 4) {
> + buf -= 4;
> + len -= 4;
> + br_enc32be(buf, z);
> + } else {
> + switch (len) {
> + case 3:
> + buf[-3] = (unsigned char)(z >> 16);
> + /* fall through */
> + case 2:
> + buf[-2] = (unsigned char)(z >> 8);
> + /* fall through */
> + case 1:
> + buf[-1] = (unsigned char)z;
> + break;
> + }
> + return;
> + }
> + }
> + }
> +}
> +
> +uint32_t
> +br_i31_iszero(const uint32_t *x)
> +{
> + uint32_t z;
> + size_t u;
> +
> + z = 0;
> + for (u = (x[0] + 31) >> 5; u > 0; u --) {
> + z |= x[u];
> + }
> + return ~(z | -z) >> 31;
> +}
> +
> +uint32_t
> +br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl)
> +{
> + uint32_t cc;
> + size_t u, m;
> +
> + cc = 0;
> + m = (a[0] + 63) >> 5;
> + for (u = 1; u < m; u ++) {
> + uint32_t aw, bw, naw;
> +
> + aw = a[u];
> + bw = b[u];
> + naw = aw + bw + cc;
> + cc = naw >> 31;
> + a[u] = MUX(ctl, naw & (uint32_t)0x7FFFFFFF, aw);
> + }
> + return cc;
> +}
> +
> +uint32_t
> +br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl)
> +{
> + uint32_t cc;
> + size_t u, m;
> +
> + cc = 0;
> + m = (a[0] + 63) >> 5;
> + for (u = 1; u < m; u ++) {
> + uint32_t aw, bw, naw;
> +
> + aw = a[u];
> + bw = b[u];
> + naw = aw - bw - cc;
> + cc = naw >> 31;
> + a[u] = MUX(ctl, naw & 0x7FFFFFFF, aw);
> + }
> + return cc;
> +}
> +
> +void
> +br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m)
> +{
> + uint32_t m_bitlen, a_bitlen;
> + size_t mlen, alen, u;
> +
> + m_bitlen = m[0];
> + mlen = (m_bitlen + 31) >> 5;
> +
> + x[0] = m_bitlen;
> + if (m_bitlen == 0) {
> + return;
> + }
> +
> + /*
> + * If the source is shorter, then simply copy all words from a[]
> + * and zero out the upper words.
> + */
> + a_bitlen = a[0];
> + alen = (a_bitlen + 31) >> 5;
> + if (a_bitlen < m_bitlen) {
> + memcpy(x + 1, a + 1, alen * sizeof *a);
> + for (u = alen; u < mlen; u ++) {
> + x[u + 1] = 0;
> + }
> + return;
> + }
> +
> + /*
> + * The source length is at least equal to that of the modulus.
> + * We must thus copy N-1 words, and input the remaining words
> + * one by one.
> + */
> + memcpy(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a);
> + x[mlen] = 0;
> + for (u = 1 + alen - mlen; u > 0; u --) {
> + br_i31_muladd_small(x, a[u], m);
> + }
> +}
> +
> +/*
> + * Compute the bit length of a 32-bit integer. Returned value is between 0
> + * and 32 (inclusive).
> + */
> +static inline uint32_t
> +BIT_LENGTH(uint32_t x)
> +{
> + uint32_t k, c;
> +
> + k = NEQ(x, 0);
> + c = GT(x, 0xFFFF); x = MUX(c, x >> 16, x); k += c << 4;
> + c = GT(x, 0x00FF); x = MUX(c, x >> 8, x); k += c << 3;
> + c = GT(x, 0x000F); x = MUX(c, x >> 4, x); k += c << 2;
> + c = GT(x, 0x0003); x = MUX(c, x >> 2, x); k += c << 1;
> + k += GT(x, 0x0001);
> + return k;
> +}
> +
> +uint32_t
> +br_i31_bit_length(uint32_t *x, size_t xlen)
> +{
> + uint32_t tw, twk;
> +
> + tw = 0;
> + twk = 0;
> + while (xlen -- > 0) {
> + uint32_t w, c;
> +
> + c = EQ(tw, 0);
> + w = x[xlen];
> + tw = MUX(c, w, tw);
> + twk = MUX(c, (uint32_t)xlen, twk);
> + }
> + return (twk << 5) + BIT_LENGTH(tw);
> +}
> +
> +/*
> + * Constant-time division. The dividend hi:lo is divided by the
> + * divisor d; the quotient is returned and the remainder is written
> + * in *r. If hi == d, then the quotient does not fit on 32 bits;
> + * returned value is thus truncated. If hi > d, returned values are
> + * indeterminate.
> + */
> +static uint32_t
> +br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r)
> +{
> + /* TODO: optimize this */
> + uint32_t q;
> + uint32_t ch, cf;
> + int k;
> +
> + q = 0;
> + ch = EQ(hi, d);
> + hi = MUX(ch, 0, hi);
> + for (k = 31; k > 0; k --) {
> + int j;
> + uint32_t w, ctl, hi2, lo2;
> +
> + j = 32 - k;
> + w = (hi << j) | (lo >> k);
> + ctl = GE(w, d) | (hi >> k);
> + hi2 = (w - d) >> j;
> + lo2 = lo - (d << k);
> + hi = MUX(ctl, hi2, hi);
> + lo = MUX(ctl, lo2, lo);
> + q |= ctl << k;
> + }
> + cf = GE(lo, d) | hi;
> + q |= cf;
> + *r = MUX(cf, lo - d, lo);
> + return q;
> +}
> +
> +/*
> + * Wrapper for br_divrem(); the remainder is returned, and the quotient
> + * is discarded.
> + */
> +static inline uint32_t
> +br_rem(uint32_t hi, uint32_t lo, uint32_t d)
> +{
> + uint32_t r;
> +
> + br_divrem(hi, lo, d, &r);
> + return r;
> +}
> +
> +/*
> + * Wrapper for br_divrem(); the quotient is returned, and the remainder
> + * is discarded.
> + */
> +static inline uint32_t
> +br_div(uint32_t hi, uint32_t lo, uint32_t d)
> +{
> + uint32_t r;
> +
> + return br_divrem(hi, lo, d, &r);
> +}
> +
> +/*
> + * Multiply two 31-bit integers, with a 62-bit result. This default
> + * implementation assumes that the basic multiplication operator
> + * yields constant-time code.
> + */
> +#define MUL31(x, y) ((uint64_t)(x) * (uint64_t)(y))
> +
> +void
> +br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
> +{
> + uint32_t m_bitlen;
> + unsigned mblr;
> + size_t u, mlen;
> + uint32_t a0, a1, b0, hi, g, q, tb;
> + uint32_t under, over;
> + uint32_t cc;
> +
> + /*
> + * We can test on the modulus bit length since we accept to
> + * leak that length.
> + */
> + m_bitlen = m[0];
> + if (m_bitlen == 0) {
> + return;
> + }
> + if (m_bitlen <= 31) {
> + uint32_t lo;
> +
> + hi = x[1] >> 1;
> + lo = (x[1] << 31) | z;
> + x[1] = br_rem(hi, lo, m[1]);
> + return;
> + }
> + mlen = (m_bitlen + 31) >> 5;
> + mblr = (unsigned)m_bitlen & 31;
> +
> + /*
> + * Principle: we estimate the quotient (x*2^31+z)/m by
> + * doing a 64/32 division with the high words.
> + *
> + * Let:
> + * w = 2^31
> + * a = (w*a0 + a1) * w^N + a2
> + * b = b0 * w^N + b2
> + * such that:
> + * 0 <= a0 < w
> + * 0 <= a1 < w
> + * 0 <= a2 < w^N
> + * w/2 <= b0 < w
> + * 0 <= b2 < w^N
> + * a < w*b
> + * I.e. the two top words of a are a0:a1, the top word of b is
> + * b0, we ensured that b0 is "full" (high bit set), and a is
> + * such that the quotient q = a/b fits on one word (0 <= q < w).
> + *
> + * If a = b*q + r (with 0 <= r < q), we can estimate q by
> + * doing an Euclidean division on the top words:
> + * a0*w+a1 = b0*u + v (with 0 <= v < b0)
> + * Then the following holds:
> + * 0 <= u <= w
> + * u-2 <= q <= u
> + */
> + hi = x[mlen];
> + if (mblr == 0) {
> + a0 = x[mlen];
> + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
> + x[1] = z;
> + a1 = x[mlen];
> + b0 = m[mlen];
> + } else {
> + a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
> + & 0x7FFFFFFF;
> + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
> + x[1] = z;
> + a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
> + & 0x7FFFFFFF;
> + b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr))
> + & 0x7FFFFFFF;
> + }
> +
> + /*
> + * We estimate a divisor q. If the quotient returned by br_div()
> + * is g:
> + * -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF.
> + * -- Otherwise:
> + * -- if g == 0 then we set q = 0;
> + * -- otherwise, we set q = g - 1.
> + * The properties described above then ensure that the true
> + * quotient is q-1, q or q+1.
> + *
> + * Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We
> + * must adjust the parameters to br_div() accordingly.
> + */
> + g = br_div(a0 >> 1, a1 | (a0 << 31), b0);
> + q = MUX(EQ(a0, b0), 0x7FFFFFFF, MUX(EQ(g, 0), 0, g - 1));
> +
> + /*
> + * We subtract q*m from x (with the extra high word of value 'hi').
> + * Since q may be off by 1 (in either direction), we may have to
> + * add or subtract m afterwards.
> + *
> + * The 'tb' flag will be true (1) at the end of the loop if the
> + * result is greater than or equal to the modulus (not counting
> + * 'hi' or the carry).
> + */
> + cc = 0;
> + tb = 1;
> + for (u = 1; u <= mlen; u ++) {
> + uint32_t mw, zw, xw, nxw;
> + uint64_t zl;
> +
> + mw = m[u];
> + zl = MUL31(mw, q) + cc;
> + cc = (uint32_t)(zl >> 31);
> + zw = (uint32_t)zl & (uint32_t)0x7FFFFFFF;
> + xw = x[u];
> + nxw = xw - zw;
> + cc += nxw >> 31;
> + nxw &= 0x7FFFFFFF;
> + x[u] = nxw;
> + tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw));
> + }
> +
> + /*
> + * If we underestimated q, then either cc < hi (one extra bit
> + * beyond the top array word), or cc == hi and tb is true (no
> + * extra bit, but the result is not lower than the modulus). In
> + * these cases we must subtract m once.
> + *
> + * Otherwise, we may have overestimated, which will show as
> + * cc > hi (thus a negative result). Correction is adding m once.
> + */
> + over = GT(cc, hi);
> + under = ~over & (tb | LT(cc, hi));
> + br_i31_add(x, m, over);
> + br_i31_sub(x, m, under);
> +}
> +
> +int
> +br_i31_decode_cmp(const void *src, size_t len, const uint32_t *m)
> +{
> + const unsigned char *buf;
> + size_t mlen, tlen, u, v;
> + uint32_t r, acc;
> + int acc_len;
> +
> + buf = src;
> + mlen = (m[0] + 31) >> 5;
> + tlen = (mlen << 2);
> + if (tlen < len) {
> + tlen = len;
> + }
> + tlen += 4;
> + r = 0;
> +
> + v = 1;
> + acc = 0;
> + acc_len = 0;
> +
> + for (u = 0; u < tlen; u ++) {
> + uint32_t b;
> +
> + if (u < len) {
> + b = buf[len - 1 - u];
> + } else {
> + b = 0;
> + }
> + acc |= (b << acc_len);
> + acc_len += 8;
> + if (acc_len >= 31) {
> + uint32_t xw;
> +
> + xw = acc & (uint32_t)0x7FFFFFFF;
> + acc_len -= 31;
> + acc = b >> (8 - acc_len);
> + if (v <= mlen) {
> + uint32_t cc;
> +
> + cc = (uint32_t)CMP(xw, m[v]);
> + r = MUX(EQ(cc, 0), r, cc);
> + } else {
> + r = MUX(EQ(xw, 0), r, 1);
> + }
> + v ++;
> + }
> + }
> +
> + /*
> + * 'r' contains the comparison result:
> + * 0x00000000 value is equal to m
> + * 0x00000001 value is greater than m
> + * 0xFFFFFFFF value is lower than m
> + */
> + if (r == 0xFFFFFFFF)
> + return -1;
> + else
> + return r;
> +}
> blob - /dev/null
> blob + 11b16b4776535c3d3e2011de5fc6c3cdd8853995 (mode 644)
> --- /dev/null
> +++ sys/crypto/i31.h
> @@ -0,0 +1,258 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +/*
> + * Integers 'i31'
> + * --------------
> + *
> + * The 'i31' functions implement computations on big integers using
> + * an internal representation as an array of 32-bit integers. For
> + * an array x[]:
> + * -- x[0] encodes the array length and the "announced bit length"
> + * of the integer: namely, if the announced bit length is k,
> + * then x[0] = ((k / 31) << 5) + (k % 31).
> + * -- x[1], x[2]... contain the value in little-endian order, 31
> + * bits per word (x[1] contains the least significant 31 bits).
> + * The upper bit of each word is 0.
> + *
> + * Multiplications rely on the elementary 32x32->64 multiplication.
> + *
> + * The announced bit length specifies the number of bits that are
> + * significant in the subsequent 32-bit words. Unused bits in the
> + * last (most significant) word are set to 0; subsequent words are
> + * uninitialized and need not exist at all.
> + *
> + * The execution time and memory access patterns of all computations
> + * depend on the announced bit length, but not on the actual word
> + * values. For modular integers, the announced bit length of any integer
> + * modulo n is equal to the actual bit length of n; thus, computations
> + * on modular integers are "constant-time" (only the modulus length may
> + * leak).
> + */
> +
> +/*
> + * Test whether an integer is zero.
> + */
> +uint32_t br_i31_iszero(const uint32_t *x);
> +
> +/*
> + * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[]
> + * is unmodified, but the carry is still computed and returned. The
> + * arrays a[] and b[] MUST have the same announced bit length.
> + *
> + * a[] and b[] MAY be the same array, but partial overlap is not allowed.
> + */
> +uint32_t br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl);
> +
> +/*
> + * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0,
> + * then a[] is unmodified, but the carry is still computed and returned.
> + * The arrays a[] and b[] MUST have the same announced bit length.
> + *
> + * a[] and b[] MAY be the same array, but partial overlap is not allowed.
> + */
> +uint32_t br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl);
> +
> +/*
> + * Compute the ENCODED actual bit length of an integer. The argument x
> + * should point to the first (least significant) value word of the
> + * integer. The len 'xlen' contains the number of 32-bit words to
> + * access. The upper bit of each value word MUST be 0.
> + * Returned value is ((k / 31) << 5) + (k % 31) if the bit length is k.
> + *
> + * CT: value or length of x does not leak.
> + */
> +uint32_t br_i31_bit_length(uint32_t *x, size_t xlen);
> +
> +/*
> + * Decode an integer from its big-endian unsigned representation. The
> + * "true" bit length of the integer is computed and set in the encoded
> + * announced bit length (x[0]), but all words of x[] corresponding to
> + * the full 'len' bytes of the source are set.
> + *
> + * CT: value or length of x does not leak.
> + */
> +void br_i31_decode(uint32_t *x, const void *src, size_t len);
> +
> +/*
> + * Zeroize an integer. The announced bit length is set to the provided
> + * value, and the corresponding words are set to 0. The ENCODED bit length
> + * is expected here.
> + */
> +static inline void
> +br_i31_zero(uint32_t *x, uint32_t bit_len)
> +{
> + *x ++ = bit_len;
> + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
> +}
> +
> +/*
> + * Reduce an integer (a[]) modulo another (m[]). The result is written
> + * in x[] and its announced bit length is set to be equal to that of m[].
> + *
> + * x[] MUST be distinct from a[] and m[].
> + *
> + * CT: only announced bit lengths leak, not values of x, a or m.
> + */
> +void br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m);
> +
> +/*
> + * Multiply x[] by 2^31 and then add integer z, modulo m[]. This
> + * function assumes that x[] and m[] have the same announced bit
> + * length, the announced bit length of m[] matches its true
> + * bit length.
> + *
> + * x[] and m[] MUST be distinct arrays. z MUST fit in 31 bits (upper
> + * bit set to 0).
> + *
> + * CT: only the common announced bit length of x and m leaks, not
> + * the values of x, z or m.
> + */
> +void br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m);
> +
> +/*
> + * Encode an integer into its big-endian unsigned representation. The
> + * output length in bytes is provided (parameter 'len'); if the length
> + * is too short then the integer is appropriately truncated; if it is
> + * too long then the extra bytes are set to 0.
> + */
> +void br_i31_encode(void *dst, size_t len, const uint32_t *x);
> +
> +/*
> + * Decode an integer from its big-endian unsigned representation, and
> + * compare it to integer m. Return -1, 0, or 1, depending on whether
> + * the decoded value is less than, equal to, or greater than m.
> + * (This is an API extension added by OpenBSD.)
> + */
> +int br_i31_decode_cmp(const void *src, size_t len, const uint32_t *m);
> +
> +/*
> + * Constant-time primitives. These functions manipulate 32-bit values in
> + * order to provide constant-time comparisons and multiplexers.
> + *
> + * Boolean values (the "ctl" bits) MUST have value 0 or 1.
> + *
> + * Implementation notes:
> + * =====================
> + *
> + * The uintN_t types are unsigned and with width exactly N bits; the C
> + * standard guarantees that computations are performed modulo 2^N, and
> + * there can be no overflow. Negation (unary '-') works on unsigned types
> + * as well.
> + *
> + * The intN_t types are guaranteed to have width exactly N bits, with no
> + * padding bit, and using two's complement representation. Casting
> + * intN_t to uintN_t really is conversion modulo 2^N. Beware that intN_t
> + * types, being signed, trigger implementation-defined behaviour on
> + * overflow (including raising some signal): with GCC, while modular
> + * arithmetics are usually applied, the optimizer may assume that
> + * overflows don't occur (unless the -fwrapv command-line option is
> + * added); Clang has the additional -ftrapv option to explicitly trap on
> + * integer overflow or underflow.
> + */
> +
> +/*
> + * Negate a boolean.
> + */
> +static inline uint32_t
> +NOT(uint32_t ctl)
> +{
> + return ctl ^ 1;
> +}
> +
> +/*
> + * Multiplexer: returns x if ctl == 1, y if ctl == 0.
> + */
> +static inline uint32_t
> +MUX(uint32_t ctl, uint32_t x, uint32_t y)
> +{
> + return y ^ (-ctl & (x ^ y));
> +}
> +
> +/*
> + * Equality check: returns 1 if x == y, 0 otherwise.
> + */
> +static inline uint32_t
> +EQ(uint32_t x, uint32_t y)
> +{
> + uint32_t q;
> +
> + q = x ^ y;
> + return NOT((q | -q) >> 31);
> +}
> +
> +/*
> + * Inequality check: returns 1 if x != y, 0 otherwise.
> + */
> +static inline uint32_t
> +NEQ(uint32_t x, uint32_t y)
> +{
> + uint32_t q;
> +
> + q = x ^ y;
> + return (q | -q) >> 31;
> +}
> +
> +/*
> + * Comparison: returns 1 if x > y, 0 otherwise.
> + */
> +static inline uint32_t
> +GT(uint32_t x, uint32_t y)
> +{
> + /*
> + * If both x < 2^31 and x < 2^31, then y-x will have its high
> + * bit set if x > y, cleared otherwise.
> + *
> + * If either x >= 2^31 or y >= 2^31 (but not both), then the
> + * result is the high bit of x.
> + *
> + * If both x >= 2^31 and y >= 2^31, then we can virtually
> + * subtract 2^31 from both, and we are back to the first case.
> + * Since (y-2^31)-(x-2^31) = y-x, the subtraction is already
> + * fine.
> + */
> + uint32_t z;
> +
> + z = y - x;
> + return (z ^ ((x ^ y) & (x ^ z))) >> 31;
> +}
> +
> +/*
> + * Other comparisons (greater-or-equal, lower-than, lower-or-equal).
> + */
> +#define GE(x, y) NOT(GT(y, x))
> +#define LT(x, y) GT(y, x)
> +#define LE(x, y) NOT(GT(x, y))
> +
> +/*
> + * General comparison: returned value is -1, 0 or 1, depending on
> + * whether x is lower than, equal to, or greater than y.
> + */
> +static inline int32_t
> +CMP(uint32_t x, uint32_t y)
> +{
> + return (int32_t)GT(x, y) | -(int32_t)GT(y, x);
> +}
> blob - 71fded46c04ef6d17d08b0b616cfcf0508f3fe76
> blob + 536ca337a6406437927047c3d5422639685b1efc
> --- sys/net80211/ieee80211.h
> +++ sys/net80211/ieee80211.h
> @@ -470,7 +470,9 @@ enum {
> IEEE80211_ELEMID_OPMODE_NOTIF = 199, /* 11ac */
> /* 200-220 reserved */
> IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
> - /* 222-254 reserved */
> + /* 222 - 243 reserved or unused by us */
> + IEEE80211_ELEMID_RSNXE = 244,
> + /* 245-254 reserved */
> IEEE80211_ELEMID_EXTENSION = 255 /* Extension */
> };
> /*
> @@ -597,6 +599,13 @@ enum {
> #define IEEE80211_RSNCAP_EXTENDED_KEYID 0x2000
>
> /*
> + * Extended RSN (RSNXE) capabilities (see 802.11-2014 9.4.2.240).
> + */
> +#define IEEE80211_RSNXECAP_LENGTH_MASK 0x000f
> +#define IEEE80211_RSNXECAP_LENGTH_SHIFT 0
> +#define IEEE80211_RSNXECAP_H2E 0x0020
> +
> +/*
> * HT Capabilities Info (see 802.11-2012 8.4.2.58.2).
> */
> #define IEEE80211_HTCAP_LDPC 0x00000001
> @@ -930,6 +939,7 @@ enum ieee80211_edca_ac {
> */
> #define IEEE80211_AUTH_ALG_OPEN 0x0000
> #define IEEE80211_AUTH_ALG_SHARED 0x0001
> +#define IEEE80211_AUTH_ALG_SAE 0x0003
> #define IEEE80211_AUTH_ALG_LEAP 0x0080
>
> /*
> @@ -982,7 +992,7 @@ enum {
> };
>
> /*
> - * Status codes (see Table 23).
> + * Status codes (see 802.11-2024 Table 9-80).
> */
> enum {
> IEEE80211_STATUS_SUCCESS = 0,
> @@ -1016,7 +1026,10 @@ enum {
> IEEE80211_STATUS_BAD_AKMP = 43,
> IEEE80211_STATUS_RSN_IE_VER_UNSUP = 44,
>
> - IEEE80211_STATUS_CIPHER_REJ_POLICY = 46
> + IEEE80211_STATUS_CIPHER_REJ_POLICY = 46,
> +
> + IEEE80211_STATUS_BAD_FC_GROUP = 77,
> + IEEE80211_STATUS_H2E = 126,
> };
>
> #define IEEE80211_WEP_KEYLEN 5 /* 40bit */
> @@ -1140,6 +1153,7 @@ struct ieee80211_eapol_key {
>
> u_int8_t info[2];
> #define EAPOL_KEY_VERSION_MASK 0x7
> +#define EAPOL_KEY_DESC_USE_AKM 0
> #define EAPOL_KEY_DESC_V1 1
> #define EAPOL_KEY_DESC_V2 2
> #define EAPOL_KEY_DESC_V3 3 /* 11r */
> @@ -1201,4 +1215,16 @@ enum ieee80211_htprot {
> IEEE80211_HTPROT_NONHT_MIXED /* non-HT STA associated to our BSS */
> };
>
> +
> +/*
> + * SAE (see 802.11-2024 12.4)
> + */
> +
> +/* We only support the mandatory ECC curve IANA 19 (NIST p256). */
> +#define IEEE80211_SAE_CURVE_ID_P256 19
> +#define IEEE80211_SAE_MAX_ECC_PRIME_LEN 32
> +#define IEEE80211_SAE_COMMIT_MIN_LEN (2 + \
> + (3 * IEEE80211_SAE_MAX_ECC_PRIME_LEN))
> +#define IEEE80211_SAE_CONFIRM_MIN_LEN (2 + 32 /* SHA256_DIGEST_LENGTH */)
> +
> #endif /* _NET80211_IEEE80211_H_ */
> blob - 424f84a3c1f5b3deb8ef8b0fc3bc5eeaaedba238
> blob + e35bb97053603cafb15f537647c828ac9e24b97c
> --- sys/net80211/ieee80211_crypto.c
> +++ sys/net80211/ieee80211_crypto.c
> @@ -48,8 +48,6 @@
>
> void ieee80211_prf(const u_int8_t *, size_t, const u_int8_t *, size_t,
> const u_int8_t *, size_t, u_int8_t *, size_t);
> -void ieee80211_kdf(const u_int8_t *, size_t, const u_int8_t *, size_t,
> - const u_int8_t *, size_t, u_int8_t *, size_t);
> void ieee80211_derive_pmkid(enum ieee80211_akm, const u_int8_t *,
> const u_int8_t *, const u_int8_t *, u_int8_t *);
>
> @@ -493,6 +491,7 @@ ieee80211_eapol_key_mic(struct ieee80211_eapol_key *ke
> memcpy(key->mic, digest, EAPOL_KEY_MIC_LEN);
> break;
> case EAPOL_KEY_DESC_V3:
> + case EAPOL_KEY_DESC_USE_AKM: /* WPA3 */
> AES_CMAC_Init(&ctx.cmac);
> AES_CMAC_SetKey(&ctx.cmac, kck);
> AES_CMAC_Update(&ctx.cmac, (u_int8_t *)key, len);
> @@ -613,6 +612,7 @@ ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key
> return 0;
> case EAPOL_KEY_DESC_V2:
> case EAPOL_KEY_DESC_V3:
> + case EAPOL_KEY_DESC_USE_AKM: /* WPA3 */
> /* Key Data Length must be a multiple of 8 */
> if (len < 16 + 8 || (len & 7) != 0)
> return 1;
> blob - 0386f94ef340b604db85ec6ca50c5d014a50dd14
> blob + 68a7f6f8b1357d5128ca268c18982e14a311f153
> --- sys/net80211/ieee80211_crypto.h
> +++ sys/net80211/ieee80211_crypto.h
> @@ -58,6 +58,9 @@ enum ieee80211_akm {
>
> #ifdef _KERNEL
>
> +void ieee80211_kdf(const u_int8_t *, size_t, const u_int8_t *, size_t,
> + const u_int8_t *, size_t, u_int8_t *, size_t);
> +
> static __inline int
> ieee80211_is_8021x_akm(enum ieee80211_akm akm)
> {
> @@ -69,7 +72,8 @@ static __inline int
> ieee80211_is_sha256_akm(enum ieee80211_akm akm)
> {
> return akm == IEEE80211_AKM_SHA256_8021X ||
> - akm == IEEE80211_AKM_SHA256_PSK;
> + akm == IEEE80211_AKM_SHA256_PSK ||
> + akm == IEEE80211_AKM_SAE;
> }
>
> struct ieee80211_key {
> blob - 0cbebc4495f475cbbc84a7fe32b94c7014336782
> blob + 275ccd1b3a6f9a88f4be502fed65e8cc7b6ee26f
> --- sys/net80211/ieee80211_input.c
> +++ sys/net80211/ieee80211_input.c
> @@ -1609,6 +1609,22 @@ ieee80211_parse_wpa(struct ieee80211com *ic, const u_i
> return ieee80211_parse_rsn_body(ic, frm + 6, frm[1] - 4, rsn);
> }
>
> +void
> +ieee80211_parse_rsnxe(struct ieee80211com *ic, const u_int8_t *frm,
> + struct ieee80211_rsnparams *rsn)
> +{
> + if (frm[1] < 1) {
> + ic->ic_stats.is_rx_elem_toosmall++;
> + return;
> + }
> +
> + /*
> + * This is a variable length IE with up to 16 bytes. But we are
> + * only interested in the first byte for now.
> + */
> + rsn->rsnxe_caps = frm[2];
> +}
> +
> /*
> * Create (or update) a copy of an information element.
> */
> @@ -1653,7 +1669,8 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
> const struct ieee80211_frame *wh;
> const u_int8_t *frm, *efrm, *csa, *xcsa;
> const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
> - const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop, *hecaps, *heop;
> + const u_int8_t *rsnie, *rsnxeie, *wpaie;
> + const u_int8_t *htcaps, *htop, *vhtcaps, *vhtop, *hecaps, *heop;
> u_int16_t capinfo, bintval;
> u_int8_t chan, bchan, erp, wmm_qosinfo;
> int has_wmm_qosinfo = 0;
> @@ -1694,7 +1711,8 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
> bintval = LE_READ_2(frm); frm += 2;
> capinfo = LE_READ_2(frm); frm += 2;
>
> - ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
> + ssid = rates = xrates = edcaie = wmmie = NULL;
> + rsnie = rsnxeie = wpaie = tim = NULL;
> htcaps = htop = vhtcaps = vhtop = hecaps = heop = csa = xcsa = NULL;
> if (rxi->rxi_chan)
> bchan = rxi->rxi_chan;
> @@ -1748,6 +1766,9 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
> case IEEE80211_ELEMID_RSN:
> rsnie = frm;
> break;
> + case IEEE80211_ELEMID_RSNXE:
> + rsnxeie = frm;
> + break;
> case IEEE80211_ELEMID_EDCAPARMS:
> edcaie = frm;
> break;
> @@ -2011,6 +2032,9 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
> (ic->ic_flags & IEEE80211_F_BGSCAN)) {
> struct ieee80211_rsnparams rsn, wpa;
>
> + memset(&rsn, 0, sizeof(rsn));
> + memset(&wpa, 0, sizeof(wpa));
> +
> if (edcaie != NULL || wmmie != NULL)
> ni->ni_flags |= IEEE80211_NODE_QOS;
> else
> @@ -2035,12 +2059,14 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
> ni->ni_supported_rsnprotos |= IEEE80211_PROTO_WPA;
> ni->ni_supported_rsnakms |= wpa.rsn_akms;
> }
> + if (rsnie != NULL && rsnxeie != NULL)
> + ieee80211_parse_rsnxe(ic, rsnxeie, &rsn);
>
> ieee80211_setup_uapsd(ic, ni, has_wmm_qosinfo &&
> (wmm_qosinfo & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD));
>
> /*
> - * If the AP advertises both WPA and RSN IEs (WPA1+WPA2),
> + * If the AP advertises both WPA and RSN IEs (WPA1+WPA2/3),
> * we only use the highest protocol version we support.
> */
> if (rsnie != NULL &&
> @@ -2058,6 +2084,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
> ni->ni_rsngroupmgmtcipher =
> rsn.rsn_groupmgmtcipher;
> ni->ni_rsncaps = rsn.rsn_caps;
> + ni->ni_rsnxecaps = rsn.rsnxe_caps;
> }
> } else if (wpaie != NULL &&
> (ni->ni_supported_rsnprotos & IEEE80211_PROTO_WPA) &&
> @@ -2256,6 +2283,7 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mb
> const struct ieee80211_frame *wh;
> const u_int8_t *frm;
> u_int16_t algo, seq, status;
> + size_t sae_len = 0;
>
> /* make sure all mandatory fixed fields are present */
> if (m->m_len < sizeof(*wh) + 6) {
> @@ -2268,11 +2296,18 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mb
> algo = LE_READ_2(frm); frm += 2;
> seq = LE_READ_2(frm); frm += 2;
> status = LE_READ_2(frm); frm += 2;
> - DPRINTF(("auth %d seq %d from %s\n", algo, seq,
> - ether_sprintf((u_int8_t *)wh->i_addr2)));
> + DPRINTF(("%s: algo %d seq %d status %u from %s\n", __func__,
> + algo, seq, status, ether_sprintf((u_int8_t *)wh->i_addr2)));
>
> - /* only "open" auth mode is supported */
> - if (algo != IEEE80211_AUTH_ALG_OPEN) {
> + if (algo == IEEE80211_AUTH_ALG_SAE) {
> + if (m->m_len < sizeof(*wh) + 6) {
> + DPRINTF(("frame too short\n"));
> + return;
> + }
i suspect this should be a ; and not a , right here.
> + sae_len = m->m_len - sizeof(*wh) - 6,
> + ieee80211_auth_sae(ic, wh, frm, sae_len, ni, rxi, seq, status);
> + return;
> + } else if (algo != IEEE80211_AUTH_ALG_OPEN) {
> DPRINTF(("unsupported auth algorithm %d from %s\n",
> algo, ether_sprintf((u_int8_t *)wh->i_addr2)));
> ic->ic_stats.is_rx_auth_unsupported++;
> blob - eca62a34e8e49b8e438f7f562886c0ba94db3a75
> blob + e96b06dacaca2b28e27156f401874b7dcc9c2a45
> --- sys/net80211/ieee80211_ioctl.c
> +++ sys/net80211/ieee80211_ioctl.c
> @@ -126,8 +126,11 @@ ieee80211_node2req(struct ieee80211com *ic, const stru
> nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_8021X;
> if (ni->ni_supported_rsnakms & IEEE80211_AKM_SHA256_PSK)
> nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_PSK;
> - if (ni->ni_supported_rsnakms & IEEE80211_AKM_SAE)
> + if (ni->ni_supported_rsnakms & IEEE80211_AKM_SAE) {
> nr->nr_rsnakms |= IEEE80211_WPA_AKM_SAE;
> + if (ic->ic_caps & IEEE80211_C_MFP)
> + nr->nr_rsnprotos |= IEEE80211_WPA_PROTO_WPA3;
> + }
>
> /* Node flags */
> nr->nr_flags = 0;
> @@ -199,9 +202,13 @@ ieee80211_disable_wep(struct ieee80211com *ic)
> void
> ieee80211_disable_rsn(struct ieee80211com *ic)
> {
> - ic->ic_flags &= ~(IEEE80211_F_PSK | IEEE80211_F_RSNON);
> + ic->ic_flags &= ~(IEEE80211_F_PSK | IEEE80211_F_RSNON |
> + IEEE80211_F_SAE_PT);
> + ic->ic_xflags &= ~IEEE80211_F_SAE_PWE;
> ic->ic_flags &= ~IEEE80211_F_MFPR;
> explicit_bzero(ic->ic_psk, sizeof(ic->ic_psk));
> + explicit_bzero(ic->ic_sae_pt, sizeof(ic->ic_sae_pt));
> + explicit_bzero(ic->ic_sae_pwe, sizeof(ic->ic_sae_pwe));
> ic->ic_rsnprotos = 0;
> ic->ic_rsnakms = 0;
> ic->ic_rsngroupcipher = 0;
> @@ -311,7 +318,8 @@ ieee80211_ioctl_setwpaparms(struct ieee80211com *ic,
> ic->ic_rsnprotos = 0;
> if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA1)
> ic->ic_rsnprotos |= IEEE80211_PROTO_WPA;
> - if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA2)
> + if (wpa->i_protos & (IEEE80211_WPA_PROTO_WPA2 |
> + IEEE80211_WPA_PROTO_WPA3))
> ic->ic_rsnprotos |= IEEE80211_PROTO_RSN;
> if (ic->ic_rsnprotos == 0) /* set to default (RSN) */
> ic->ic_rsnprotos = IEEE80211_PROTO_RSN;
> @@ -321,14 +329,18 @@ ieee80211_ioctl_setwpaparms(struct ieee80211com *ic,
> ic->ic_rsnakms |= IEEE80211_AKM_PSK;
> if (wpa->i_akms & IEEE80211_WPA_AKM_SHA256_PSK)
> ic->ic_rsnakms |= IEEE80211_AKM_SHA256_PSK;
> + if (wpa->i_akms & IEEE80211_WPA_AKM_SAE)
> + ic->ic_rsnakms |= IEEE80211_AKM_SAE;
> if (wpa->i_akms & IEEE80211_WPA_AKM_8021X)
> ic->ic_rsnakms |= IEEE80211_AKM_8021X;
> if (wpa->i_akms & IEEE80211_WPA_AKM_SHA256_8021X)
> ic->ic_rsnakms |= IEEE80211_AKM_SHA256_8021X;
> - if (ic->ic_rsnakms == 0) { /* set to default (PSK) */
> + if (ic->ic_rsnakms == 0) { /* set to default (PSK/SAE) */
> ic->ic_rsnakms = IEEE80211_AKM_PSK;
> - if (ic->ic_caps & IEEE80211_C_MFP)
> + if (ic->ic_caps & IEEE80211_C_MFP) {
> ic->ic_rsnakms |= IEEE80211_AKM_SHA256_PSK;
> + ic->ic_rsnakms |= IEEE80211_AKM_SAE;
> + }
> }
>
> if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_WEP40)
> @@ -385,6 +397,11 @@ ieee80211_ioctl_getwpaparms(struct ieee80211com *ic,
> wpa->i_akms |= IEEE80211_WPA_AKM_8021X;
> if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_8021X)
> wpa->i_akms |= IEEE80211_WPA_AKM_SHA256_8021X;
> + if (ic->ic_rsnakms & IEEE80211_AKM_SAE) {
> + wpa->i_akms |= IEEE80211_WPA_AKM_SAE;
> + if (ic->ic_caps & IEEE80211_C_MFP)
> + wpa->i_protos |= IEEE80211_WPA_PROTO_WPA3;
> + }
>
> if (ic->ic_rsngroupcipher == IEEE80211_CIPHER_WEP40)
> wpa->i_groupcipher = IEEE80211_WPA_CIPHER_WEP40;
> @@ -429,6 +446,10 @@ ieee80211_ess_getwpaparms(struct ieee80211_ess *ess,
> wpa->i_akms |= IEEE80211_WPA_AKM_8021X;
> if (ess->rsnakms & IEEE80211_AKM_SHA256_8021X)
> wpa->i_akms |= IEEE80211_WPA_AKM_SHA256_8021X;
> + if (ess->rsnakms & IEEE80211_AKM_SAE) {
> + wpa->i_akms |= IEEE80211_WPA_AKM_SAE;
> + wpa->i_protos |= IEEE80211_WPA_PROTO_WPA3;
> + }
>
> if (ess->rsngroupcipher == IEEE80211_CIPHER_WEP40)
> wpa->i_groupcipher = IEEE80211_WPA_CIPHER_WEP40;
> @@ -462,6 +483,7 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
> struct ieee80211_joinreq_all *ja;
> struct ieee80211_ess *ess;
> struct ieee80211_wpapsk *psk;
> + struct ieee80211_wpasae *sae;
> struct ieee80211_keyavail *ka;
> struct ieee80211_keyrun *kr;
> struct ieee80211_power *power;
> @@ -607,6 +629,8 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
> join.i_flags |= IEEE80211_JOIN_WPA;
> if (ess->flags & IEEE80211_F_PSK)
> join.i_flags |= IEEE80211_JOIN_WPAPSK;
> + if (ess->flags & IEEE80211_F_SAE_PT)
> + join.i_flags |= IEEE80211_JOIN_WPASAE;
> if (ess->flags & IEEE80211_JOIN_8021X)
> join.i_flags |= IEEE80211_JOIN_8021X;
> if (ess->flags & IEEE80211_F_WEPON)
> @@ -663,6 +687,31 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
> } else
> psk->i_enabled = 0;
> break;
> + case SIOCS80211WPASAE:
> + if ((error = suser(curproc)) != 0)
> + break;
> + sae = (struct ieee80211_wpasae *)data;
> + if (sae->i_enabled) {
> + ic->ic_flags |= IEEE80211_F_SAE_PT;
> + memcpy(ic->ic_sae_pt, sae->i_pt, sizeof(ic->ic_sae_pt));
> + if (ic->ic_flags & IEEE80211_F_WEPON)
> + ieee80211_disable_wep(ic);
> + } else {
> + ic->ic_flags &= ~IEEE80211_F_SAE_PT;
> + memset(ic->ic_sae_pt, 0, sizeof(ic->ic_sae_pt));
> + }
> + error = ENETRESET;
> + break;
> + case SIOCG80211WPASAE:
> + sae = (struct ieee80211_wpasae *)data;
> + if (ic->ic_flags & IEEE80211_F_SAE_PT) {
> + /* do not show any keys to userland */
> + sae->i_enabled = 2;
> + memset(sae->i_pt, 0, sizeof(sae->i_pt));
> + break; /* return ok but w/o key */
> + } else
> + sae->i_enabled = 0;
> + break;
> case SIOCS80211KEYAVAIL:
> if ((error = suser(curproc)) != 0)
> break;
> blob - 420751935b437316c9ea27fc72f33167bf4f91bd
> blob + 04b7550d352322dae141434d51aa6fa9b3d1a8c1
> --- sys/net80211/ieee80211_ioctl.h
> +++ sys/net80211/ieee80211_ioctl.h
> @@ -222,8 +222,18 @@ struct ieee80211_wpapsk {
> #define SIOCS80211WPAPSK _IOW('i', 245, struct ieee80211_wpapsk)
> #define SIOCG80211WPAPSK _IOWR('i', 246, struct ieee80211_wpapsk)
>
> +struct ieee80211_wpasae {
> + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
> + int i_enabled;
> + u_int8_t i_pt[IEEE80211_SAE_MAX_ECC_PRIME_LEN * 2];
> +};
> +
> +#define SIOCS80211WPASAE _IOW('i', 249, struct ieee80211_wpasae)
> +#define SIOCG80211WPASAE _IOWR('i', 250, struct ieee80211_wpasae)
> +
> #define IEEE80211_WPA_PROTO_WPA1 0x01
> #define IEEE80211_WPA_PROTO_WPA2 0x02
> +#define IEEE80211_WPA_PROTO_WPA3 0x04
>
> #define IEEE80211_WPA_CIPHER_NONE 0x00
> #define IEEE80211_WPA_CIPHER_USEGROUP 0x01
> @@ -284,6 +294,7 @@ struct ieee80211_join {
> struct ieee80211_wpaparams i_wpaparams;
> struct ieee80211_wpapsk i_wpapsk;
> struct ieee80211_nwkey i_nwkey;
> + struct ieee80211_wpasae i_wpasae;
> };
>
> struct ieee80211_joinreq_all {
> @@ -303,6 +314,7 @@ struct ieee80211_joinreq_all {
> #define IEEE80211_JOIN_8021X 0x40
> #define IEEE80211_JOIN_ANY 0x80
> #define IEEE80211_JOIN_DEL_ALL 0x100
> +#define IEEE80211_JOIN_WPASAE 0x200
>
> /* node and requests */
> struct ieee80211_nodereq {
> blob - 5038e611ddcc80496bae94fe6008a7f2a5945949
> blob + 10c16abf261eda7a3d39746533eb090da3c1b4a8
> --- sys/net80211/ieee80211_node.c
> +++ sys/net80211/ieee80211_node.c
> @@ -151,6 +151,8 @@ ieee80211_print_ess(struct ieee80211_ess *ess)
> printf(",psk");
> if (ess->rsnakms & IEEE80211_AKM_SHA256_PSK)
> printf(",sha256-psk");
> + if (ess->rsnakms & IEEE80211_AKM_SAE)
> + printf(",sae");
>
> if (ess->rsnakms & IEEE80211_AKM_8021X ||
> ess->rsnakms & IEEE80211_AKM_SHA256_8021X)
> @@ -290,7 +292,8 @@ ieee80211_ess_setwpaparms(struct ieee80211com *ic, str
> ess->rsnprotos = 0;
> if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA1)
> ess->rsnprotos |= IEEE80211_PROTO_WPA;
> - if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA2)
> + if (wpa->i_protos & (IEEE80211_WPA_PROTO_WPA2 |
> + IEEE80211_WPA_PROTO_WPA3))
> ess->rsnprotos |= IEEE80211_PROTO_RSN;
> if (ess->rsnprotos == 0) /* set to default (RSN) */
> ess->rsnprotos = IEEE80211_PROTO_RSN;
> @@ -300,16 +303,18 @@ ieee80211_ess_setwpaparms(struct ieee80211com *ic, str
> ess->rsnakms |= IEEE80211_AKM_PSK;
> if (wpa->i_akms & IEEE80211_WPA_AKM_SHA256_PSK)
> ess->rsnakms |= IEEE80211_AKM_SHA256_PSK;
> + if (wpa->i_akms & IEEE80211_WPA_AKM_SAE)
> + ess->rsnakms |= IEEE80211_AKM_SAE;
> if (wpa->i_akms & IEEE80211_WPA_AKM_8021X)
> ess->rsnakms |= IEEE80211_AKM_8021X;
> if (wpa->i_akms & IEEE80211_WPA_AKM_SHA256_8021X)
> ess->rsnakms |= IEEE80211_AKM_SHA256_8021X;
> - if (wpa->i_akms & IEEE80211_WPA_AKM_SAE)
> - ess->rsnakms |= IEEE80211_AKM_SAE;
> - if (ess->rsnakms == 0) { /* set to default (PSK) */
> + if (ess->rsnakms == 0) { /* set to default (PSK/SAE) */
> ess->rsnakms |= IEEE80211_AKM_PSK;
> - if (ic->ic_caps & IEEE80211_C_MFP)
> + if (ic->ic_caps & IEEE80211_C_MFP) {
> ess->rsnakms |= IEEE80211_AKM_SHA256_PSK;
> + ess->rsnakms |= IEEE80211_AKM_SAE;
> + }
> }
>
> if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_WEP40)
> @@ -369,7 +374,9 @@ ieee80211_ess_clear_wpa(struct ieee80211_ess *ess)
> ess->rsnprotos = ess->rsnakms = ess->rsngroupcipher =
> ess->rsnciphers = 0;
> explicit_bzero(ess->psk, sizeof(ess->psk));
> - ess->flags &= ~(IEEE80211_F_PSK | IEEE80211_F_RSNON);
> + explicit_bzero(ess->sae_pt, sizeof(ess->sae_pt));
> + ess->flags &= ~(IEEE80211_F_PSK | IEEE80211_F_SAE_PT |
> + IEEE80211_F_RSNON);
> }
>
> int
> @@ -420,6 +427,13 @@ ieee80211_add_ess(struct ieee80211com *ic, struct ieee
> memcpy(ess->psk, &join->i_wpapsk.i_psk,
> sizeof(ess->psk));
> }
> + if (join->i_flags & IEEE80211_JOIN_WPASAE) {
> + ess->flags |= IEEE80211_F_SAE_PT;
> + explicit_bzero(ess->sae_pt,
> + sizeof(ess->sae_pt));
> + memcpy(ess->sae_pt, &join->i_wpasae.i_pt,
> + sizeof(ess->sae_pt));
> + }
> ieee80211_ess_clear_wep(ess);
> } else {
> ieee80211_ess_clear_wpa(ess);
> @@ -559,7 +573,8 @@ ieee80211_match_ess(struct ieee80211_ess *ess, struct
> return 0;
> }
>
> - if (ess->flags & (IEEE80211_F_PSK | IEEE80211_F_RSNON)) {
> + if (ess->flags & (IEEE80211_F_PSK | IEEE80211_F_SAE_PT |
> + IEEE80211_F_RSNON)) {
> /* Ensure same WPA version. */
> if ((ni->ni_rsnprotos & IEEE80211_PROTO_RSN) &&
> (ess->rsnprotos & IEEE80211_PROTO_RSN) == 0) {
> @@ -678,6 +693,9 @@ ieee80211_set_ess(struct ieee80211com *ic, struct ieee
> explicit_bzero(ic->ic_psk, sizeof(ic->ic_psk));
> memcpy(ic->ic_psk, ess->psk, sizeof(ic->ic_psk));
>
> + explicit_bzero(ic->ic_sae_pt, sizeof(ic->ic_sae_pt));
> + explicit_bzero(ic->ic_sae_pwe, sizeof(ic->ic_sae_pwe));
> +
> ic->ic_rsnprotos = ess->rsnprotos;
> ic->ic_rsnakms = ess->rsnakms;
> ic->ic_rsngroupcipher = ess->rsngroupcipher;
> @@ -685,6 +703,11 @@ ieee80211_set_ess(struct ieee80211com *ic, struct ieee
> ic->ic_flags |= IEEE80211_F_RSNON;
> if (ess->flags & IEEE80211_F_PSK)
> ic->ic_flags |= IEEE80211_F_PSK;
> + if (ess->flags & IEEE80211_F_SAE_PT) {
> + memcpy(ic->ic_sae_pt, ess->sae_pt,
> + sizeof(ic->ic_sae_pt));
> + ic->ic_flags |= IEEE80211_F_SAE_PT;
> + }
> } else if (ess->flags & IEEE80211_F_WEPON) {
> struct ieee80211_key *k;
> int i;
> @@ -1156,6 +1179,12 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ie
> if (!(ic->ic_flags & IEEE80211_F_PSK))
> fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> }
> + if ((ni->ni_rsnakms & ic->ic_rsnakms &
> + (~IEEE80211_AKM_SAE)) == 0) {
> + /* AP only supports SAE */
> + if (!(ic->ic_flags & IEEE80211_F_SAE_PT))
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> + }
> if (ni->ni_rsngroupcipher != IEEE80211_CIPHER_WEP40 &&
> ni->ni_rsngroupcipher != IEEE80211_CIPHER_TKIP &&
> ni->ni_rsngroupcipher != IEEE80211_CIPHER_CCMP &&
> @@ -1412,6 +1441,21 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struc
> else if (auth_next)
> mgt = IEEE80211_FC0_SUBTYPE_AUTH;
>
> + /* Prepare WPA3 SAE handshake if needed. */
> + if (ieee80211_node_allow_wpa3(ic, ni)) {
> + if (ieee80211_sae_derive_password_elem(ic->ic_sae_pwe,
> + ic->ic_sae_pt, ic->ic_myaddr, ni->ni_macaddr)) {
> + /* We don't have the PWE. Disable WPA3. */
> + ic->ic_flags &= ~IEEE80211_F_SAE_PT;
> + ic->ic_xflags &= ~IEEE80211_F_SAE_PWE;
> + explicit_bzero(ic->ic_sae_pt,
> + sizeof(ic->ic_sae_pt));
> + explicit_bzero(ic->ic_sae_pwe,
> + sizeof(ic->ic_sae_pwe));
> + } else
> + ic->ic_xflags |= IEEE80211_F_SAE_PWE;
> + }
> +
> ieee80211_new_state(ic, IEEE80211_S_AUTH, mgt);
> }
> }
> @@ -1650,6 +1694,39 @@ ieee80211_end_scan(struct ifnet *ifp)
> ieee80211_node_join_bss(ic, selbs);
> }
>
> +int
> +ieee80211_node_allow_wpa3(struct ieee80211com *ic,
> + const struct ieee80211_node *ni)
> +{
> + /* If RSN is disabled then we cannot use WPA3. */
> + if ((ic->ic_flags & IEEE80211_F_RSNON) == 0 ||
> + (ni->ni_rsnprotos & IEEE80211_PROTO_RSN) == 0)
> + return 0;
> +
> + /* SAE PT must be set (from userland). */
> + if ((ic->ic_flags & IEEE80211_F_SAE_PT) == 0)
> + return 0;
> +
> + /* PMF is a requirement for WPA3. */
> + if ((ic->ic_caps & IEEE80211_C_MFP) == 0 ||
> + (ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC) == 0)
> + return 0;
> +
> + /* The peer must support SAE. TODO: 802.1x? */
> + if ((ni->ni_rsnakms & IEEE80211_AKM_SAE) == 0)
> + return 0;
> +
> + /*
> + * We require the peer to support hash-to-element.
> + * Hunting-and-pecking key derivation is not implemented to
> + * avoid the side-channel leaks associated with this method.
> + */
> + if ((ni->ni_rsnxecaps & IEEE80211_RSNXECAP_H2E) == 0)
> + return 0;
> +
> + return 1;
> +}
> +
> /*
> * Autoselect the best RSN parameters (protocol, AKMP, pairwise cipher...)
> * that are supported by both peers (STA mode only).
> @@ -1670,11 +1747,14 @@ ieee80211_choose_rsnparams(struct ieee80211com *ic)
>
> /* filter out unsupported AKMPs */
> ni->ni_rsnakms &= ic->ic_rsnakms;
> - /* prefer SHA-256 based AKMPs */
> - if ((ic->ic_flags & IEEE80211_F_PSK) && (ni->ni_rsnakms &
> - (IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK))) {
> - /* AP supports PSK AKMP and a PSK is configured */
> - if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)
> + /* prefer SAE and SHA-256 based AKMPs */
> + if (((ic->ic_flags & IEEE80211_F_PSK) && (ni->ni_rsnakms &
> + (IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK))) ||
> + ((ic->ic_flags & IEEE80211_F_SAE_PT) &&
> + (ni->ni_rsnakms & IEEE80211_AKM_SAE))) {
> + if (ni->ni_rsnakms & IEEE80211_AKM_SAE)
> + ni->ni_rsnakms = IEEE80211_AKM_SAE;
> + else if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)
> ni->ni_rsnakms = IEEE80211_AKM_SHA256_PSK;
> else
> ni->ni_rsnakms = IEEE80211_AKM_PSK;
> blob - dcb9e40e8345da7918b9793f1428156d60ac0e07
> blob + f449e6f9e83ff226afaf7cf4e1b772339f771b7e
> --- sys/net80211/ieee80211_node.h
> +++ sys/net80211/ieee80211_node.h
> @@ -188,6 +188,53 @@ enum {
> RSNA_SUPP_PTKDONE /* got message 3 and authenticated AP */
> };
>
> +/* Initial SAE state. */
> +#define SAE_STATE_NOTHING 0x00
> +
> +/* State flags for SAE-related events which have occured. */
> +#define SAE_EVENT_COMMIT_SENT 0x01
> +#define SAE_EVENT_PEER_COMMIT_RECEIVED 0x02
> +#define SAE_EVENT_CONFIRM_SENT 0x04
> +#define SAE_EVENT_PEER_CONFIRM_RECEIVED 0x08
> +
> +/*
> + * SAE COMMITTED state.
> + * Sent COMMIT message to peer. Awaiting COMMIT and CONFIRM from peer.
> + */
> +#define SAE_STATE_COMMITTED SAE_EVENT_COMMIT_SENT
> +
> +/*
> + * SAE CONFIRMED srtate.
> + * Sent COMMIT and CONFIRMED message to peer. Have received COMMIT from peer.
> + */
> +#define SAE_STATE_CONFIRMED \
> + (SAE_EVENT_COMMIT_SENT | SAE_EVENT_CONFIRM_SENT | \
> + SAE_EVENT_PEER_COMMIT_RECEIVED)
> +
> +/*
> + * SAE ACCEPTED state.
> + * Sent COMMIT and CONFIRMED message to peer.
> + * Have received COMMIT and CONFIRMED from peer.
> + */
> +#define SAE_STATE_ACCEPTED \
> + (SAE_EVENT_COMMIT_SENT | SAE_EVENT_CONFIRM_SENT | \
> + SAE_EVENT_PEER_COMMIT_RECEIVED | SAE_EVENT_PEER_CONFIRM_RECEIVED)
> +
> +struct ieee80211_sae {
> + uint8_t sae_scalar[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_element_x[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_element_y[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_rand[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_peer_scalar[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_peer_element_x[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_peer_element_y[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_kck[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t sae_pmk[IEEE80211_PMK_LEN];
> + int sae_state;
> + uint16_t sae_send_confirm;
> + uint16_t sae_peer_send_confirm;
> +};
> +
> struct ieee80211_rxinfo {
> u_int32_t rxi_flags;
> u_int32_t rxi_tstamp;
> @@ -323,6 +370,7 @@ struct ieee80211_node {
> enum ieee80211_cipher ni_rsngroupcipher;
> enum ieee80211_cipher ni_rsngroupmgmtcipher;
> u_int16_t ni_rsncaps;
> + u_int8_t ni_rsnxecaps;
> enum ieee80211_cipher ni_rsncipher;
> u_int8_t ni_nonce[EAPOL_KEY_NONCE_LEN];
> u_int8_t ni_pmk[IEEE80211_PMK_LEN];
> @@ -334,6 +382,7 @@ struct ieee80211_node {
> u_int8_t *ni_rsnie;
> struct ieee80211_key ni_pairwise_key;
> struct ieee80211_ptk ni_ptk;
> + struct ieee80211_sae ni_sae;
> u_int8_t ni_key_count;
> int ni_port_valid;
>
> @@ -661,6 +710,8 @@ struct ieee80211_node *ieee80211_dup_bss(struct ieee80
> const u_int8_t *);
> struct ieee80211_node *ieee80211_find_node(struct ieee80211com *,
> const u_int8_t *);
> +int ieee80211_node_allow_wpa3(struct ieee80211com *,
> + const struct ieee80211_node *);
> void ieee80211_node_tx_ba_clear(struct ieee80211_node *, int);
> void ieee80211_ba_del(struct ieee80211_node *);
> struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *,
> blob - e2e5a8298674f75b26447f67d01666096714ba20
> blob + b3042bd9fdef763800197c4d9c1342c0ebf9ae9a
> --- sys/net80211/ieee80211_output.c
> +++ sys/net80211/ieee80211_output.c
> @@ -63,6 +63,11 @@
> #include <net80211/ieee80211_var.h>
> #include <net80211/ieee80211_priv.h>
>
> +#include <crypto/md5.h>
> +#include <crypto/sha1.h>
> +#include <crypto/sha2.h>
> +#include <crypto/hmac.h>
> +
> int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *,
> struct mbuf *, int);
> int ieee80211_can_use_ampdu(struct ieee80211com *,
> @@ -75,6 +80,11 @@ struct mbuf *ieee80211_get_probe_req(struct ieee80211c
> #ifndef IEEE80211_STA_ONLY
> struct mbuf *ieee80211_get_probe_resp(struct ieee80211com *);
> #endif
> +u_int8_t *ieee80211_add_sae_commit(u_int8_t *, struct ieee80211com *,
> + const struct ieee80211_node *, const uint8_t *, size_t,
> + const uint8_t *, size_t, const uint8_t *, size_t);
> +u_int8_t *ieee80211_add_sae_confirm(u_int8_t *, struct ieee80211com *,
> + const struct ieee80211_node *);
> struct mbuf *ieee80211_get_auth(struct ieee80211com *,
> struct ieee80211_node *, u_int16_t, u_int16_t);
> struct mbuf *ieee80211_get_deauth(struct ieee80211com *,
> @@ -1140,6 +1150,11 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211
> *frm++ = 6;
> count++;
> }
> + if (!wpa && ieee80211_node_allow_wpa3(ic, ni)) {
> + memcpy(frm, oui, 3); frm += 3;
> + *frm++ = 8; /* SAE */
> + count++;
> + }
> /* write AKM Suite List Count field */
> LE_WRITE_2(pcount, count);
>
> @@ -1235,6 +1250,17 @@ ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *
> return frm;
> }
>
> +u_int8_t *
> +ieee80211_add_rsnxe(u_int8_t *frm, struct ieee80211com *ic,
> + const struct ieee80211_node *ni)
> +{
> + *frm++ = IEEE80211_ELEMID_RSNXE;
> + *frm++ = 1;
> + *frm++ = IEEE80211_RSNXECAP_H2E;
> +
> + return frm;
> +}
> +
> /*
> * Add an extended supported rates element to a frame (see 7.3.2.14).
> */
> @@ -1525,11 +1551,70 @@ ieee80211_get_probe_resp(struct ieee80211com *ic)
> }
> #endif /* IEEE80211_STA_ONLY */
>
> +u_int8_t *
> +ieee80211_add_sae_commit(u_int8_t *frm, struct ieee80211com *ic,
> + const struct ieee80211_node *ni, const uint8_t *commit_scalar,
> + size_t scalar_len,
> + const uint8_t *commit_element_x, size_t element_x_len,
> + const uint8_t *commit_element_y, size_t element_y_len)
> +{
> + LE_WRITE_2(frm, IEEE80211_SAE_CURVE_ID_P256);
> + frm += 2;
> +
> + memcpy(frm, commit_scalar, scalar_len);
> + frm += scalar_len;
> +
> + memcpy(frm, commit_element_x, element_x_len);
> + frm += element_x_len;
> +
> + memcpy(frm, commit_element_y, element_y_len);
> + frm += element_y_len;
> +
> + return frm;
> +}
> +
> +u_int8_t *
> +ieee80211_add_sae_confirm(u_int8_t *frm, struct ieee80211com *ic,
> + const struct ieee80211_node *ni)
> +{
> + HMAC_SHA256_CTX ctx;
> +
> + /*
> + * confirm = CN(SAE-KCK, send-confirm, commit-scalar, commit-element,
> + * peer-commit-scalar, peer-commit-element)
> + */
> + HMAC_SHA256_Init(&ctx, ni->ni_sae.sae_kck, sizeof(ni->ni_sae.sae_kck));
> +
> + LE_WRITE_2(frm, ni->ni_sae.sae_send_confirm);
> + HMAC_SHA256_Update(&ctx, frm, 2);
> + frm += 2;
> +
> + HMAC_SHA256_Update(&ctx, ni->ni_sae.sae_scalar,
> + sizeof(ni->ni_sae.sae_scalar));
> + HMAC_SHA256_Update(&ctx, ni->ni_sae.sae_element_x,
> + sizeof(ni->ni_sae.sae_element_x));
> + HMAC_SHA256_Update(&ctx, ni->ni_sae.sae_element_y,
> + sizeof(ni->ni_sae.sae_element_y));
> +
> + HMAC_SHA256_Update(&ctx, ni->ni_sae.sae_peer_scalar,
> + sizeof(ni->ni_sae.sae_peer_scalar));
> + HMAC_SHA256_Update(&ctx, ni->ni_sae.sae_peer_element_x,
> + sizeof(ni->ni_sae.sae_peer_element_x));
> + HMAC_SHA256_Update(&ctx, ni->ni_sae.sae_peer_element_y,
> + sizeof(ni->ni_sae.sae_peer_element_y));
> +
> + HMAC_SHA256_Final(frm, &ctx);
> + frm += SHA256_DIGEST_LENGTH;
> +
> + return frm;
> +}
> +
> /*-
> * Authentication frame format:
> * [2] Authentication algorithm number
> * [2] Authentication transaction sequence number
> * [2] Status code
> + * [tlv] SAE information elements, if algorithm is SAE
> */
> struct mbuf *
> ieee80211_get_auth(struct ieee80211com *ic, struct ieee80211_node *ni,
> @@ -1537,18 +1622,90 @@ ieee80211_get_auth(struct ieee80211com *ic, struct iee
> {
> struct mbuf *m;
> u_int8_t *frm;
> + uint16_t alg;
> + int sae_commit = 0, sae_confirm = 0;
> + size_t sae_len = 0;
> + uint8_t commit_scalar[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t commit_element_x[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t commit_element_y[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t rand[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t k[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t kck[IEEE80211_SAE_MAX_ECC_PRIME_LEN];
> + uint8_t pmk[IEEE80211_PMK_LEN];
>
> + if (ieee80211_node_allow_wpa3(ic, ni) &&
> + (ic->ic_xflags & IEEE80211_F_SAE_PWE) &&
> + status == IEEE80211_STATUS_SUCCESS) {
> + if (seq == IEEE80211_AUTH_OPEN_REQUEST) {
> + if (ni->ni_sae.sae_state == SAE_STATE_NOTHING &&
> + ieee80211_sae_derive_commit_elem(commit_scalar,
> + commit_element_x, commit_element_y, rand,
> + ic->ic_sae_pwe) == 0)
> + sae_commit = 1;
> + } else if (seq == IEEE80211_AUTH_OPEN_RESPONSE) {
> + if (ni->ni_sae.sae_state == (SAE_EVENT_COMMIT_SENT |
> + SAE_EVENT_PEER_COMMIT_RECEIVED) &&
> + ieee80211_sae_derive_shared_secret(k,
> + ni->ni_sae.sae_peer_scalar,
> + ni->ni_sae.sae_peer_element_x,
> + ni->ni_sae.sae_peer_element_y, ic->ic_sae_pwe,
> + ni->ni_sae.sae_rand) == IEEE80211_STATUS_SUCCESS &&
> + ieee80211_sae_derive_secret_keys(kck, pmk, k,
> + ni->ni_sae.sae_scalar,
> + ni->ni_sae.sae_peer_scalar) == 0) {
> + sae_confirm = 1;
> + }
> +
> + explicit_bzero(k, sizeof(k));
> + }
> + }
> +
> + if (sae_commit || sae_confirm)
> + alg = IEEE80211_AUTH_ALG_SAE;
> + else
> + alg = IEEE80211_AUTH_ALG_OPEN;
> +
> + if (sae_commit) {
> + status = IEEE80211_STATUS_H2E;
> + sae_len = 2 + sizeof(commit_scalar) +
> + sizeof(commit_element_x) + sizeof(commit_element_y);
> + } else if (sae_confirm)
> + sae_len = 2 + SHA256_DIGEST_LENGTH;
> +
> MGETHDR(m, M_DONTWAIT, MT_DATA);
> if (m == NULL)
> return NULL;
> - m_align(m, 2 * 3);
> - m->m_pkthdr.len = m->m_len = 2 * 3;
> + if (sae_len == 0)
> + m_align(m, 2 * 3);
> + m->m_pkthdr.len = m->m_len = 2 * 3 + sae_len;
>
> frm = mtod(m, u_int8_t *);
> - LE_WRITE_2(frm, IEEE80211_AUTH_ALG_OPEN); frm += 2;
> + LE_WRITE_2(frm, alg); frm += 2;
> LE_WRITE_2(frm, seq); frm += 2;
> - LE_WRITE_2(frm, status);
> + LE_WRITE_2(frm, status); frm += 2;
>
> + if (sae_commit) {
> + memcpy(ni->ni_sae.sae_scalar, commit_scalar,
> + sizeof(ni->ni_sae.sae_scalar));
> + memcpy(ni->ni_sae.sae_element_x, commit_element_x,
> + sizeof(ni->ni_sae.sae_element_x));
> + memcpy(ni->ni_sae.sae_element_y, commit_element_y,
> + sizeof(ni->ni_sae.sae_element_y));
> + memcpy(ni->ni_sae.sae_rand, rand, sizeof(ni->ni_sae.sae_rand));
> + ni->ni_sae.sae_state |= SAE_EVENT_COMMIT_SENT;
> + frm = ieee80211_add_sae_commit(frm, ic, ni,
> + commit_scalar, sizeof(commit_scalar),
> + commit_element_x, sizeof(commit_element_x),
> + commit_element_y, sizeof(commit_element_y));
> + } else if (sae_confirm) {
> + memcpy(ni->ni_sae.sae_kck, kck, sizeof(ni->ni_sae.sae_kck));
> + memcpy(ni->ni_sae.sae_pmk, pmk, sizeof(ni->ni_sae.sae_pmk));
> + ni->ni_sae.sae_state |= SAE_EVENT_CONFIRM_SENT;
> + if (ni->ni_sae.sae_send_confirm < 0xffff)
> + ni->ni_sae.sae_send_confirm++;
> + frm = ieee80211_add_sae_confirm(frm, ic, ni);
> + }
> +
> return m;
> }
>
> @@ -1633,7 +1790,9 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struc
> ((ic->ic_flags & IEEE80211_F_HTON) ? 28 : 0) +
> (addwme ? 9 : 0) +
> (addvht ? 14 : 0) +
> - hecapslen);
> + hecapslen +
> + (ieee80211_node_allow_wpa3(ic, ni) ?
> + 2 + IEEE80211_RSNXEIE_MAXLEN : 0));
> if (m == NULL)
> return NULL;
>
> @@ -1672,6 +1831,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struc
> frm = ieee80211_add_vhtcaps(frm, ic);
> if (hecapslen)
> frm = ieee80211_add_hecaps(frm, ic);
> + if (ieee80211_node_allow_wpa3(ic, ni))
> + frm = ieee80211_add_rsnxe(frm, ic, ni);
>
> m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
>
> @@ -1993,7 +2154,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ie
> if (m == NULL)
> senderr(ENOMEM, is_tx_nombuf);
>
> - if (ic->ic_opmode == IEEE80211_M_STA)
> + if (ic->ic_opmode == IEEE80211_M_STA &&
> + (arg1 & 0xffff) != IEEE80211_AUTH_OPEN_RESPONSE /* SAE */)
> timer = IEEE80211_TRANS_WAIT;
> break;
>
> blob - c370f62a269e95813a90a1f5711d3e346a337337
> blob + 9d20d76e7513ce9b40099bcbaf80c7f96c6c0ab5
> --- sys/net80211/ieee80211_pae_input.c
> +++ sys/net80211/ieee80211_pae_input.c
> @@ -126,11 +126,19 @@ ieee80211_eapol_key_input(struct ieee80211com *ic, str
>
> /* discard EAPOL-Key frames with an unknown descriptor version */
> desc = info & EAPOL_KEY_VERSION_MASK;
> - if (desc < EAPOL_KEY_DESC_V1 || desc > EAPOL_KEY_DESC_V3)
> + if (ieee80211_node_allow_wpa3(ic, ni) &&
> + ni->ni_sae.sae_state == SAE_STATE_ACCEPTED) {
> + if (desc != EAPOL_KEY_DESC_USE_AKM)
> + goto done;
> + } else if (desc < EAPOL_KEY_DESC_V1 || desc > EAPOL_KEY_DESC_V3)
> goto done;
>
> if (ieee80211_is_sha256_akm(ni->ni_rsnakms)) {
> - if (desc != EAPOL_KEY_DESC_V3)
> + if (ieee80211_node_allow_wpa3(ic, ni) &&
> + ni->ni_sae.sae_state == SAE_STATE_ACCEPTED) {
> + if (desc != EAPOL_KEY_DESC_USE_AKM)
> + goto done;
> + } else if (desc != EAPOL_KEY_DESC_V3)
> goto done;
> } else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
> ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) {
> @@ -250,6 +258,9 @@ ieee80211_recv_4way_msg1(struct ieee80211com *ic,
> return;
> }
> memcpy(ni->ni_pmk, pmk->pmk_key, IEEE80211_PMK_LEN);
> + } else if (ieee80211_node_allow_wpa3(ic, ni) &&
> + ni->ni_sae.sae_state == SAE_STATE_ACCEPTED) {
> + memcpy(ni->ni_pmk, ni->ni_sae.sae_pmk, IEEE80211_PMK_LEN);
> } else /* use pre-shared key */
> memcpy(ni->ni_pmk, ic->ic_psk, IEEE80211_PMK_LEN);
> ni->ni_flags |= IEEE80211_NODE_PMK;
> blob - fa24a1db640358ef41680d37b86681033549137c
> blob + e4b0fcb2e9cac258f260ffbef7851c6f534356ee
> --- sys/net80211/ieee80211_pae_output.c
> +++ sys/net80211/ieee80211_pae_output.c
> @@ -84,10 +84,15 @@ ieee80211_send_eapol_key(struct ieee80211com *ic, stru
> EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA;
>
> info = BE_READ_2(key->info);
> - /* use V3 descriptor if KDF is SHA256-based */
> - if (ieee80211_is_sha256_akm(ni->ni_rsnakms))
> - info |= EAPOL_KEY_DESC_V3;
> - /* use V2 descriptor if pairwise or group cipher is CCMP */
> + /* use V3 or use-AKM descriptor if KDF is SHA256-based */
> + if (ieee80211_is_sha256_akm(ni->ni_rsnakms)) {
> + if (ieee80211_node_allow_wpa3(ic, ni) &&
> + ni->ni_sae.sae_state == SAE_STATE_ACCEPTED)
> + info |= EAPOL_KEY_DESC_USE_AKM;
> + else
> + info |= EAPOL_KEY_DESC_V3;
> + }
> + /* use V2 descriptor if WPA2 pairwise or group cipher is CCMP */
> else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
> ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP)
> info |= EAPOL_KEY_DESC_V2;
> @@ -314,12 +319,21 @@ ieee80211_send_4way_msg2(struct ieee80211com *ic, stru
> struct mbuf *m;
> u_int16_t info;
> u_int8_t *frm;
> + u_int pktlen = 0;
>
> ni->ni_rsn_supp_state = RSNA_SUPP_PTKNEGOTIATING;
> - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
> - (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ?
> - 2 + IEEE80211_WPAIE_MAXLEN :
> - 2 + IEEE80211_RSNIE_MAXLEN);
> +
> + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA)
> + pktlen += 2 + IEEE80211_WPAIE_MAXLEN;
> + else if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) {
> + pktlen += 2 + IEEE80211_RSNIE_MAXLEN;
> +
> + if (ieee80211_node_allow_wpa3(ic, ni) &&
> + ni->ni_sae.sae_state == SAE_STATE_ACCEPTED)
> + pktlen += 2 + IEEE80211_RSNXEIE_MAXLEN;
> + }
> +
> + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, pktlen);
> if (m == NULL)
> return ENOMEM;
> key = mtod(m, struct ieee80211_eapol_key *);
> @@ -342,9 +356,14 @@ ieee80211_send_4way_msg2(struct ieee80211com *ic, stru
> /* WPA sets the key length field here */
> keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
> BE_WRITE_2(key->keylen, keylen);
> - } else /* RSN */
> + } else { /* RSN */
> frm = ieee80211_add_rsn(frm, ic, ni);
>
> + if (ieee80211_node_allow_wpa3(ic, ni) &&
> + ni->ni_sae.sae_state == SAE_STATE_ACCEPTED)
> + frm = ieee80211_add_rsnxe(frm, ic, ni);
> + }
> +
> m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
>
> if (ic->ic_if.if_flags & IFF_DEBUG)
> blob - d1775b11a08a12770417ec195255142c4ce21b48
> blob + 6b6781c7734894feb506c38075639d230df89d7a
> --- sys/net80211/ieee80211_priv.h
> +++ sys/net80211/ieee80211_priv.h
> @@ -62,6 +62,9 @@ extern int ieee80211_debug;
> 2 + /* AKM Suite List Count */ \
> 4 * 2) /* AKM Suite List (max 2) */
>
> +#define IEEE80211_RSNXEIE_MAXLEN \
> + 1 /* First byte only, for now */
> +
> struct ieee80211_rsnparams {
> u_int16_t rsn_nakms;
> u_int32_t rsn_akms;
> @@ -70,10 +73,28 @@ struct ieee80211_rsnparams {
> enum ieee80211_cipher rsn_groupcipher;
> enum ieee80211_cipher rsn_groupmgmtcipher;
> u_int16_t rsn_caps;
> + u_int8_t rsnxe_caps;
> u_int8_t rsn_npmkids;
> const u_int8_t *rsn_pmkids;
> };
>
> +/* WPA3/SAE related functions */
> +int ieee80211_sae_derive_password_elem(uint8_t *, const uint8_t *,
> + const uint8_t *, const uint8_t *);
> +int ieee80211_sae_derive_commit_elem(uint8_t *, uint8_t *, uint8_t *, uint8_t *,
> + const uint8_t *);
> +uint16_t ieee80211_sae_verify_commit_elem(const uint8_t **, const uint8_t **,
> + const uint8_t **, const uint8_t *, size_t,
> + const unsigned char *, const unsigned char *, const unsigned char *);
> +uint16_t ieee80211_sae_derive_shared_secret(uint8_t *, const uint8_t *,
> + const uint8_t *, const uint8_t *, const uint8_t *, const uint8_t *);
> +int ieee80211_sae_derive_secret_keys(uint8_t *, uint8_t *, const uint8_t *,
> + const uint8_t *, const uint8_t *);
> +int ieee80211_sae_verify_confirm(const uint8_t *, size_t,
> + const uint8_t *, const uint8_t *, const uint8_t *,
> + const uint8_t *, const uint8_t *, const uint8_t *,
> + const uint8_t *);
> +
> /* unaligned big endian access */
> #define BE_READ_2(p) \
> ((u_int16_t) \
> blob - 1539cd1a7e2687d8bd1657e1dd2059a16fa26cc4
> blob + cbd14e6c5c240174fbce06413dd83f236c253c9a
> --- sys/net80211/ieee80211_proto.c
> +++ sys/net80211/ieee80211_proto.c
> @@ -1039,6 +1039,155 @@ ieee80211_auth_open(struct ieee80211com *ic, const str
> }
>
> void
> +ieee80211_auth_sae_failure(struct ieee80211com *ic, struct ieee80211_node *ni,
> + uint16_t status, const struct ieee80211_frame *wh)
> +{
> + struct ifnet *ifp = &ic->ic_if;
> +
> + memset(&ni->ni_sae, 0, sizeof(ni->ni_sae));
> + ni->ni_sae.sae_state = SAE_STATE_NOTHING;
> +
> + if ((ifp->if_flags & IFF_DEBUG) && wh != NULL) {
> + printf("%s: SAE authentication failed "
> + "(status %d) for %s\n", ifp->if_xname, status,
> + ether_sprintf((u_int8_t *)wh->i_addr3));
> + }
> +
> + if (ni != ic->ic_bss)
> + ni->ni_fails++;
> + else
> + ieee80211_try_another_bss(ic);
> +
> + ic->ic_stats.is_rx_auth_fail++;
> +
> + /*
> + * While ieee80211_auth_open can depend on the management
> + * frame timer in order to leave AUTH state, SAE verification
> + * occurs on received AUTH frames. We could get stuck in AUTH
> + * state here without switching back to SCAN state manually.
> + */
> + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
> +}
> +
> +void
> +ieee80211_auth_sae(struct ieee80211com *ic, const struct ieee80211_frame *wh,
> + const uint8_t *sae_data, size_t sae_len, struct ieee80211_node *ni,
> + struct ieee80211_rxinfo *rxi, u_int16_t seq, u_int16_t status)
> +{
> + struct ifnet *ifp = &ic->ic_if;
> +
> + switch (ic->ic_opmode) {
> + case IEEE80211_M_STA:
> + if (ic->ic_state != IEEE80211_S_AUTH || ni != ic->ic_bss ||
> + !ieee80211_node_allow_wpa3(ic, ni))
> + goto discard;
> +
> + if (seq == IEEE80211_AUTH_OPEN_REQUEST) {
> + const uint8_t *scalar, *element_x, *element_y;
> +
> + /*
> + * In station mode, we always send our COMMIT message
> + * to the AP before it will respond with COMMIT and
> + * CONFIRM. So we should already be in COMMITTED state.
> + */
> + if (ni->ni_sae.sae_state != SAE_STATE_COMMITTED)
> + goto discard;
> +
> + if (status != IEEE80211_STATUS_H2E ||
> + sae_len < IEEE80211_SAE_COMMIT_MIN_LEN ||
> + ieee80211_sae_verify_commit_elem(&scalar,
> + &element_x, &element_y, sae_data, sae_len,
> + ni->ni_sae.sae_scalar,
> + ni->ni_sae.sae_element_x,
> + ni->ni_sae.sae_element_y) !=
> + IEEE80211_STATUS_SUCCESS) {
> + ieee80211_auth_sae_failure(ic, ni, status, wh);
> + return;
> + }
> +
> + memcpy(ni->ni_sae.sae_peer_scalar, scalar,
> + sizeof(ni->ni_sae.sae_peer_scalar));
> + memcpy(ni->ni_sae.sae_peer_element_x,
> + element_x,
> + sizeof(ni->ni_sae.sae_peer_element_x));
> + memcpy(ni->ni_sae.sae_peer_element_y,
> + element_y,
> + sizeof(ni->ni_sae.sae_peer_element_y));
> +
> + ni->ni_sae.sae_state |=
> + SAE_EVENT_PEER_COMMIT_RECEIVED;
> +
> + ic->ic_mgt_timer = 0;
> + IEEE80211_SEND_MGMT(ic, ni,
> + IEEE80211_FC0_SUBTYPE_AUTH,
> + IEEE80211_AUTH_OPEN_RESPONSE);
> + } else if (seq == IEEE80211_AUTH_OPEN_RESPONSE) {
> + uint16_t send_confirm = LE_READ_2(sae_data);
> +
> + /*
> + * In station mode, we should already have received a
> + * COMMIT from the AP and have sent our own CONFIRM.
> + */
> + if (ni->ni_sae.sae_state != SAE_STATE_CONFIRMED)
> + goto discard;
> +
> + if (status != IEEE80211_STATUS_SUCCESS ||
> + sae_len < IEEE80211_SAE_CONFIRM_MIN_LEN ||
> + ieee80211_sae_verify_confirm(sae_data, sae_len,
> + ni->ni_sae.sae_kck, ni->ni_sae.sae_scalar,
> + ni->ni_sae.sae_element_x,
> + ni->ni_sae.sae_element_y,
> + ni->ni_sae.sae_peer_scalar,
> + ni->ni_sae.sae_peer_element_x,
> + ni->ni_sae.sae_peer_element_y) != 0) {
> + ieee80211_auth_sae_failure(ic, ni, status, wh);
> + return;
> + }
> +
> + ni->ni_sae.sae_peer_send_confirm = send_confirm;
> + ni->ni_sae.sae_state |=
> + SAE_EVENT_PEER_CONFIRM_RECEIVED;
> +
> + /* SAE protocol should have sucessfully completed. */
> + if (ni->ni_sae.sae_state != SAE_STATE_ACCEPTED) {
> + ieee80211_auth_sae_failure(ic, ni, status, wh);
> + return;
> + }
> +
> + if ((ifp->if_flags & IFF_DEBUG) && wh != NULL) {
> + printf("%s: SAE authentication success "
> + "(status %d) for %s\n",
> + ifp->if_xname, status,
> + ether_sprintf((u_int8_t *)wh->i_addr3));
> + }
> +
> + /* XXX not here! */
> + ic->ic_bss->ni_flags &= ~IEEE80211_NODE_TXRXPROT;
> + ic->ic_bss->ni_flags &= ~IEEE80211_NODE_RXMGMTPROT;
> + ic->ic_bss->ni_flags &= ~IEEE80211_NODE_TXMGMTPROT;
> + ic->ic_bss->ni_port_valid = 0;
> + ic->ic_bss->ni_replaycnt_ok = 0;
> + (*ic->ic_delete_key)(ic, ic->ic_bss,
> + &ic->ic_bss->ni_pairwise_key);
> +
> + ic->ic_mgt_timer = 0;
> + ieee80211_new_state(ic, IEEE80211_S_ASSOC,
> + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
> + }
> + break;
> + /* TODO: HOSTAP/IBSS modes? */
> + default:
> +discard:
> + DPRINTF(("discard SAE auth from %s; state %u, "
> + "seq %u, status %u\n",
> + ether_sprintf((u_int8_t *)wh->i_addr2),
> + ic->ic_state, seq, status));
> + ic->ic_stats.is_rx_bad_auth++;
> + return;
> + }
> +}
> +
> +void
> ieee80211_set_beacon_miss_threshold(struct ieee80211com *ic)
> {
> struct ifnet *ifp = &ic->ic_if;
> blob - a86affa800afcb52ff65b8fafdaee3c8096690e0
> blob + e11ed34d0d38cd29477a3e04af67fcb8a612e42d
> --- sys/net80211/ieee80211_proto.h
> +++ sys/net80211/ieee80211_proto.h
> @@ -132,6 +132,8 @@ extern u_int8_t *ieee80211_add_rsn(u_int8_t *, struct
> const struct ieee80211_node *);
> extern u_int8_t *ieee80211_add_wpa(u_int8_t *, struct ieee80211com *,
> const struct ieee80211_node *);
> +extern u_int8_t *ieee80211_add_rsnxe(u_int8_t *, struct ieee80211com *,
> + const struct ieee80211_node *);
> extern u_int8_t *ieee80211_add_xrates(u_int8_t *,
> const struct ieee80211_rateset *);
> extern u_int8_t *ieee80211_add_htcaps(u_int8_t *, struct ieee80211com *);
> @@ -157,6 +159,10 @@ extern void ieee80211_auth_open_confirm(struct ieee802
> extern void ieee80211_auth_open(struct ieee80211com *,
> const struct ieee80211_frame *, struct ieee80211_node *,
> struct ieee80211_rxinfo *rs, u_int16_t, u_int16_t);
> +extern void ieee80211_auth_sae(struct ieee80211com *,
> + const struct ieee80211_frame *, const uint8_t *, size_t,
> + struct ieee80211_node *, struct ieee80211_rxinfo *rs,
> + u_int16_t, u_int16_t);
> extern void ieee80211_stop_ampdu_tx(struct ieee80211com *,
> struct ieee80211_node *, int);
> extern void ieee80211_gtk_rekey_timeout(void *);
> blob - e7cedb0f71cb831b680520e012ad78f569080e3e
> blob + a5c4e063304d724a9ea4be2198092535e99e56bd
> --- sys/net80211/ieee80211_var.h
> +++ sys/net80211/ieee80211_var.h
> @@ -355,6 +355,8 @@ struct ieee80211com {
> #ifndef IEEE80211_STA_ONLY
> struct timeout ic_tkip_micfail_timeout;
> #endif
> + u_int8_t ic_sae_pt[IEEE80211_SAE_MAX_ECC_PRIME_LEN * 2];
> + u_int8_t ic_sae_pwe[IEEE80211_SAE_MAX_ECC_PRIME_LEN * 2];
>
> TAILQ_HEAD(, ieee80211_pmk) ic_pmksa; /* PMKSA cache */
> u_int ic_rsnprotos;
> @@ -421,6 +423,7 @@ struct ieee80211_ess {
>
> /* wpakey */
> u_int8_t psk[IEEE80211_PMK_LEN];
> + u_int8_t sae_pt[IEEE80211_SAE_MAX_ECC_PRIME_LEN * 2];
> u_int rsnprotos;
> u_int rsnakms;
> u_int rsnciphers;
> @@ -458,9 +461,11 @@ struct ieee80211_ess {
> #define IEEE80211_F_AUTO_JOIN 0x10000000 /* CONF: auto-join active */
> #define IEEE80211_F_VHTON 0x20000000 /* CONF: VHT enabled */
> #define IEEE80211_F_HEON 0x40000000 /* CONF: HE enabled */
> +#define IEEE80211_F_SAE_PT 0x80000000 /* CONF: SAE PT set */
>
> /* ic_xflags */
> #define IEEE80211_F_TX_MGMT_ONLY 0x00000001 /* leave data frames on ifq */
> +#define IEEE80211_F_SAE_PWE 0x00000002 /* STATUS: SAE PWE set */
>
> /* ic_caps */
> #define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
> blob - /dev/null
> blob + f81e603b03c93369d26bd71f77a61c5226d58767 (mode 644)
> --- /dev/null
> +++ sys/net80211/ieee80211_sae.c
> @@ -0,0 +1,590 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2026 Stefan Sperling <stsp@openbsd.org>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/mbuf.h>
> +#include <sys/types.h>
> +
> +#include <crypto/i31.h>
> +#include <crypto/ec_p256_m31.h>
> +
> +#include <net/if.h>
> +
> +#include <net80211/ieee80211.h>
> +#include <net80211/ieee80211_crypto.h>
> +#include <net80211/ieee80211_priv.h>
> +
> +#include <crypto/md5.h>
> +#include <crypto/sha1.h>
> +#include <crypto/sha2.h>
> +#include <crypto/hmac.h>
> +
> +#define SAE_P256_PRIME_LEN IEEE80211_SAE_MAX_ECC_PRIME_LEN
> +
> +/*
> + * Expected number of 4-byte words in an i31 integer array.
> + * There is one 4-byte length word, followed by enough data
> + * words to store the group's prime number.
> + */
> + #define SAE_I31INT_WORDS (1 + (SAE_P256_PRIME_LEN / 4) + 1)
> +
> +static inline void
> +hexdump(const char *label, const uint8_t *s, size_t len)
> +{
> +#ifdef IEEE80211_DEBUG
> + size_t i;
> +
> + printf("%s: len=%zd:", label, len);
> + for (i = 0; i < len; i++)
> + printf(" %.2x", s[i]);
> + printf("\n");
> +#endif
> +}
> +
> +static inline void
> +pwe_hash(const uint8_t *salt, size_t salt_len,
> + const uint8_t *addr_max, const uint8_t *addr_min, uint8_t *hash)
> +{
> + HMAC_SHA256_CTX ctx;
> +
> + HMAC_SHA256_Init(&ctx, salt, salt_len);
> +
> + HMAC_SHA256_Update(&ctx, addr_max, IEEE80211_ADDR_LEN);
> + HMAC_SHA256_Update(&ctx, addr_min, IEEE80211_ADDR_LEN);
> +
> + HMAC_SHA256_Final(hash, &ctx);
> +}
> +
> +/* Derive the SAE password element (PWE) from PT and two MAC addresses. */
> +int
> +ieee80211_sae_derive_password_elem(uint8_t *pwe, const uint8_t *pt,
> + const uint8_t *own_macaddr, const uint8_t *bssid)
> +{
> + const uint8_t *addr_max, *addr_min;
> + uint8_t salt[SAE_P256_PRIME_LEN];
> + uint8_t hash[SAE_P256_PRIME_LEN];
> + uint8_t bin[SAE_P256_PRIME_LEN];
> + const unsigned char *generator, *order;
> + size_t xoff, prime_len, generator_len, order_len;
> + const br_ec_impl *group = &br_ec_p256_m31;
> + int curve = BR_EC_secp192r1; /* Group 19, P-256 */
> + uint32_t one[SAE_I31INT_WORDS];
> + uint32_t val[SAE_I31INT_WORDS];
> + uint32_t r[SAE_I31INT_WORDS];
> + uint32_t t[SAE_I31INT_WORDS];
> + unsigned char point[1 + SAE_P256_PRIME_LEN * 2];
> +
> + generator = group->generator(curve, &generator_len);
> + hexdump("generator", generator, generator_len);
> + order = group->order(curve, &order_len);
> + hexdump("order", order, order_len);
> + xoff = group->xoff(curve, &prime_len);
> + DPRINTF(("%s: xoff=%zu prime_len=%zu\n", __func__, xoff, prime_len));
> +
> + if (prime_len != SAE_P256_PRIME_LEN)
> + return -1;
> +
> + memset(salt, 0, sizeof(salt));
> + memset(hash, 0, sizeof(hash));
> +
> + if (memcmp(own_macaddr, bssid, IEEE80211_ADDR_LEN) > 0) {
> + addr_max = own_macaddr;
> + addr_min = bssid;
> + } else {
> + addr_max = bssid;
> + addr_min = own_macaddr;
> + }
> +
> + hexdump("MAC max", addr_max, IEEE80211_ADDR_LEN);
> + hexdump("MAC min", addr_min, IEEE80211_ADDR_LEN);
> +
> + /*
> + * val = H(0^n, MAX(STA-A-MAC,STA-B-MAC) || MIN(STA-A-MAC,STA-B-MAC))
> + */
> + pwe_hash(salt, sizeof(salt), addr_max, addr_min, hash);
> + hexdump("hash", hash, sizeof(hash));
> +
> + /* val as i31 integer. */
> + br_i31_decode(val, hash, sizeof(hash));
> +
> + /* The number 1 as i31 integer. */
> + br_i31_zero(one, prime_len * 8);
> + one[1] = 0x1;
> + br_i31_encode(bin, sizeof(bin), one);
> + hexdump("one", bin, sizeof(bin));
> +
> + /* The order ("r") of the group as i31 integer. */
> + br_i31_decode(r, order, order_len);
> +
> + /* val = val modulo (r - 1) + 1 */
> + br_i31_sub(r, one, 1); /* r - 1 */
> + br_i31_reduce(t, val, r); /* val modulo (r - 1) */
> + br_i31_add(t, one, 1); /* + 1 */
> + memcpy(val, t, sizeof(val)); /* val = t */
> +
> + br_i31_encode(bin, sizeof(bin), val);
> + hexdump("val", bin, sizeof(bin));
> +
> + hexdump("PT.x", pt, SAE_P256_PRIME_LEN);
> + hexdump("PT.y", pt + SAE_P256_PRIME_LEN, SAE_P256_PRIME_LEN);
> +
> + /* represent PT as a curve point */
> + point[0] = 0x04; /* "uncompressed" format (RFC 5480, 2.2) */
> + memcpy(&point[1], pt, SAE_P256_PRIME_LEN);
> + memcpy(&point[SAE_P256_PRIME_LEN + 1], pt + SAE_P256_PRIME_LEN,
> + SAE_P256_PRIME_LEN);
> +
> + /* PWE = scalar-op(val, PT) */
> + if (!group->mul(point, sizeof(point), bin, prime_len, curve))
> + return -1;
> +
> + hexdump("PWE.x", &point[1], SAE_P256_PRIME_LEN);
> + hexdump("PWE.y", &point[33], SAE_P256_PRIME_LEN);
> +
> + memcpy(pwe, &point[1], SAE_P256_PRIME_LEN * 2);
> +
> + return 0;
> +}
> +
> +int
> +ieee80211_sae_derive_commit_elem(uint8_t *scalar, uint8_t *element_x,
> + uint8_t *element_y, uint8_t *sae_rand, const uint8_t *pwe)
> +{
> + const br_ec_impl *group = &br_ec_p256_m31;
> + int curve = BR_EC_secp192r1; /* Group 19, P-256 */
> + const unsigned char *order;
> + unsigned char prime[1 + SAE_P256_PRIME_LEN];
> + uint32_t one[SAE_I31INT_WORDS];
> + uint32_t r[SAE_I31INT_WORDS];
> + uint32_t rand[SAE_I31INT_WORDS];
> + uint32_t mask[SAE_I31INT_WORDS];
> + uint32_t t[SAE_I31INT_WORDS];
> + uint32_t val[SAE_I31INT_WORDS];
> + uint32_t p[SAE_I31INT_WORDS];
> + size_t xoff, prime_len, order_len;
> + int ret = -1, tries;
> + unsigned char point[1 + SAE_P256_PRIME_LEN * 2];
> + uint8_t bin[SAE_P256_PRIME_LEN];
> +
> + order = group->order(curve, &order_len);
> + hexdump("order", order, order_len);
> + xoff = group->xoff(curve, &prime_len);
> + DPRINTF(("%s: xoff=%zu prime_len=%zu\n", __func__, xoff, prime_len));
> +
> + if (prime_len != SAE_P256_PRIME_LEN) {
> + DPRINTF(("%s: wrong prime length %zu\n", __func__, prime_len));
> + return -1;
> + }
> +
> + if (group->prime(curve, prime, sizeof(prime)) == 0) {
> + DPRINTF(("%s: no prime\n", __func__));
> + return -1;
> + }
> +
> + /* The group's prime number as an i31 integer. */
> + br_i31_decode(p, prime, prime_len);
> +
> + /* The number 1 as i31 integer. */
> + br_i31_zero(one, prime_len * 8);
> + one[1] = 0x1;
> + br_i31_encode(bin, sizeof(bin), one);
> + hexdump("one", bin, sizeof(bin));
> +
> + /* The order ("r") of the group as i31 integer. */
> + br_i31_decode(r, order, order_len);
> + DPRINTF(("order_len=%zu\n", order_len));
> + DPRINTF(("r bit length=%u\n", r[0]));
> +
> + /* Choose a random value 'rand' such that: 1 < rand < r */
> + for (tries = 0; tries < 100; tries++) {
> + arc4random_buf(bin, sizeof(bin));
> + br_i31_decode(rand, bin, sizeof(bin));
> + explicit_bzero(bin, sizeof(bin));
> +
> + if (br_i31_iszero(rand))
> + continue;
> +
> + br_i31_encode(bin, sizeof(bin), rand);
> + hexdump("rand initial", bin, SAE_P256_PRIME_LEN);
> +
> + /* Ensure that rand < r holds. */
> + memcpy(t, r, sizeof(t)); /* t = r */
> + br_i31_encode(bin, sizeof(bin), t);
> + hexdump("t before -1", bin, SAE_P256_PRIME_LEN);
> + DPRINTF(("t bit length=%u\n", t[0]));
> + DPRINTF(("one bit length=%u\n", one[0]));
> + br_i31_sub(t, one, 1); /* t = r - 1 */
> + br_i31_encode(bin, sizeof(bin), t);
> + hexdump("t before reduce", bin, SAE_P256_PRIME_LEN);
> + br_i31_reduce(val, rand, t); /* val = rand modulo (r - 1) */
> + br_i31_encode(bin, sizeof(bin), val);
> + hexdump("t after reduce", bin, SAE_P256_PRIME_LEN);
> + memcpy(rand, val, sizeof(rand)); /* rand = val */
> +
> + /* Ensure that 1 < rand holds. */
> + memcpy(t, rand, sizeof(t)); /* t = rand */
> + br_i31_sub(t, one, 1); /* t = rand - 1 */
> + if (br_i31_iszero(t))
> + continue; /* --> rand == 1 */
> +
> + break;
> + }
> + if (tries >= 100) /* should not happen */
> + goto done;
> +
> + br_i31_encode(sae_rand, SAE_P256_PRIME_LEN, rand);
> + hexdump("rand final", sae_rand, SAE_P256_PRIME_LEN);
> +
> + /*
> + * Choose a random value 'mask' such that:
> + * 1 < mask < r && ((rand + mask) % r) > 1
> + */
> + for (tries = 0; tries < 100; tries++) {
> + arc4random_buf(bin, sizeof(bin));
> + br_i31_decode(mask, bin, sizeof(bin));
> + explicit_bzero(bin, sizeof(bin));
> +
> + if (br_i31_iszero(mask))
> + continue;
> +
> + /* Ensure that mask < r holds. */
> + memcpy(t, r, sizeof(t)); /* t = r */
> + br_i31_sub(t, one, 1); /* t = r - 1 */
> + br_i31_reduce(val, mask, t); /* val = mask modulo (r - 1) */
> + memcpy(mask, val, sizeof(mask)); /* mask = val */
> +
> + /* Ensure that 1 < mask holds. */
> + memcpy(t, mask, sizeof(t)); /* t = mask */
> + br_i31_sub(t, one, 1); /* t = mask - 1 */
> + if (br_i31_iszero(t))
> + continue; /* --> mask == 1 */
> +
> + memcpy(t, rand, sizeof(t)); /* t = rand */
> + br_i31_add(t, mask, 1); /* t = rand + mask */
> + br_i31_reduce(val, t, r); /* val = ((rand + mask) % r) */
> +
> + memcpy(t, val, sizeof(t)); /* t = val */
> + br_i31_sub(t, one, 1); /* t = val - 1 */
> + if (br_i31_iszero(t))
> + continue; /* --> ((rand + mask) % r) == 1 */
> + break;
> + }
> + if (tries >= 100) /* should not happen */
> + goto done;
> +
> + br_i31_encode(scalar, SAE_P256_PRIME_LEN, val);
> + hexdump("commit-scalar", scalar, SAE_P256_PRIME_LEN);
> +
> + br_i31_encode(bin, sizeof(bin), mask);
> + hexdump("mask", bin, sizeof(bin));
> +
> + /* represent PWE as a curve point */
> + point[0] = 0x04; /* "uncompressed" format (RFC 5480, 2.2) */
> + memcpy(&point[1], pwe, SAE_P256_PRIME_LEN);
> + memcpy(&point[SAE_P256_PRIME_LEN + 1], pwe + SAE_P256_PRIME_LEN,
> + SAE_P256_PRIME_LEN);
> +
> + /* point = scalar-op(mask, PWE) */
> + if (!group->mul(point, sizeof(point), bin, prime_len, curve))
> + goto done;
> +
> + /* inverse-op(point(X, Y)) -> point(X, p - Y) */
> + if (!group->invert(curve, point, sizeof(point)))
> + goto done;
> +
> + /* COMMIT-ELEMENT = inverse-op(scalar-op(mask, PWE)) */
> + memcpy(element_x, &point[1], SAE_P256_PRIME_LEN);
> + hexdump("commit-element(x)", element_x, SAE_P256_PRIME_LEN);
> + memcpy(element_y, &point[1 + SAE_P256_PRIME_LEN], SAE_P256_PRIME_LEN);
> + hexdump("commit-element(y)", element_y, SAE_P256_PRIME_LEN);
> +
> + ret = 0;
> +done:
> + if (ret != 0)
> + DPRINTF(("%s: error %d\n", __func__, ret));
> + return ret;
> +}
> +
> +uint16_t
> +ieee80211_sae_verify_commit_elem(const uint8_t **scalar,
> + const uint8_t **element_x, const uint8_t **element_y,
> + const uint8_t *frm, size_t remain, const unsigned char *own_scalar,
> + const unsigned char *own_element_x, const unsigned char *own_element_y)
> +{
> + const br_ec_impl *group = &br_ec_p256_m31;
> + int curve = BR_EC_secp192r1; /* Group 19, P-256 */
> + const unsigned char *order;
> + uint32_t one[SAE_I31INT_WORDS];
> + uint32_t r[SAE_I31INT_WORDS];
> + size_t xoff, prime_len, order_len;
> + uint16_t fc_group;
> + unsigned char point[1 + SAE_P256_PRIME_LEN * 2];
> + uint8_t bin[SAE_P256_PRIME_LEN];
> + uint16_t ret = IEEE80211_STATUS_UNSPECIFIED;
> +
> + *scalar = NULL;
> + *element_x = NULL;
> + *element_y = NULL;
> +
> + /* We do not support any of the optional elements yet. */
> + if (remain != IEEE80211_SAE_COMMIT_MIN_LEN)
> + return IEEE80211_STATUS_UNSPECIFIED;
> +
> + fc_group = LE_READ_2(frm);
> + if (fc_group != curve)
> + return IEEE80211_STATUS_BAD_FC_GROUP;
> + frm += 2;
> +
> + *scalar = frm;
> + frm += SAE_P256_PRIME_LEN;
> +
> + *element_x = frm;
> + frm += SAE_P256_PRIME_LEN;
> +
> + *element_y = frm;
> + frm += SAE_P256_PRIME_LEN;
> +
> + order = group->order(curve, &order_len);
> + xoff = group->xoff(curve, &prime_len);
> + if (prime_len != SAE_P256_PRIME_LEN)
> + return IEEE80211_STATUS_UNSPECIFIED;
> +
> + /* The order ("r") of the group as i31 integer. */
> + br_i31_decode(r, order, order_len);
> +
> + /* The number 1 as i31 integer. */
> + br_i31_zero(one, prime_len * 8);
> + one[1] = 0x1;
> + br_i31_encode(bin, sizeof(bin), one);
> +
> + /* Represent the peer's element as a curve point. */
> + point[0] = 0x04; /* "uncompressed" format (RFC 5480, 2.2) */
> + memcpy(&point[1], *element_x, prime_len);
> + memcpy(&point[prime_len + 1], *element_y, prime_len);
> +
> + ret = IEEE80211_STATUS_SUCCESS;
> +
> + /* Ensure our own scalar or element weren't replayed to us. */
> + ret |= !!(timingsafe_bcmp(own_scalar, *scalar, prime_len) == 0 ||
> + timingsafe_bcmp(own_element_x, *element_x, prime_len) == 0 ||
> + timingsafe_bcmp(own_element_y, *element_y, prime_len) == 0);
> +
> + /* Ensure that 1 < scalar < r holds. */
> + ret |= !!(br_i31_decode_cmp(*scalar, prime_len, one) != 1 ||
> + br_i31_decode_cmp(*scalar, prime_len, r) != -1 ||
> + br_i31_decode_cmp(*element_x, prime_len, r) != -1 ||
> + br_i31_decode_cmp(*element_y, prime_len, r) != -1);
> +
> + /* Ensure that the provided group element is valid. */
> + ret |= !!(group->mul(point, sizeof(point), bin, prime_len, curve) == 0);
> +
> + KASSERT(ret == IEEE80211_STATUS_SUCCESS /* 0x0 */ ||
> + ret == IEEE80211_STATUS_UNSPECIFIED /* 0x1 */);
> +
> + return ret;
> +}
> +
> +uint16_t
> +ieee80211_sae_derive_shared_secret(uint8_t *k, const uint8_t *scalar,
> + const uint8_t *element_x, const uint8_t *element_y, const uint8_t *pwe,
> + const uint8_t *rand)
> +{
> + const br_ec_impl *group = &br_ec_p256_m31;
> + int curve = BR_EC_secp192r1; /* Group 19, P-256 */
> + const unsigned char *order;
> + size_t xoff, prime_len, order_len;
> + unsigned char pointA[1 + SAE_P256_PRIME_LEN * 2];
> + unsigned char pointB[1 + SAE_P256_PRIME_LEN * 2];
> + uint8_t one_bin[SAE_P256_PRIME_LEN];
> + uint32_t one[SAE_I31INT_WORDS];
> + uint32_t t[SAE_I31INT_WORDS];
> + uint16_t ret = IEEE80211_STATUS_UNSPECIFIED;
> +
> + order = group->order(curve, &order_len);
> + xoff = group->xoff(curve, &prime_len);
> + if (prime_len != SAE_P256_PRIME_LEN)
> + return IEEE80211_STATUS_UNSPECIFIED;
> +
> + /* The number 1 as i31 integer. */
> + br_i31_zero(one, prime_len * 8);
> + one[1] = 0x1;
> +
> + /* The number 1 in unsigned big-endian. */
> + br_i31_encode(one_bin, sizeof(one_bin), one);
> +
> + /*
> + * K = scalar-op(rand,
> + * (elem-op(scalar-op(peer-commit-scalar, PWE),
> + * PEER-COMMIT-ELEMENT)))
> + */
> +
> + /* represent PWE as a curve point */
> + pointA[0] = 0x04; /* "uncompressed" format (RFC 5480, 2.2) */
> + memcpy(&pointA[1], pwe, prime_len * 2);
> +
> + hexdump("peer-commit-scalar", scalar, SAE_P256_PRIME_LEN);
> +
> + ret = IEEE80211_STATUS_SUCCESS;
> +
> + /* pointA = scalar-op(peer-commit-scalar, PWE) */
> + ret |= !!(group->mul(pointA, sizeof(pointA), scalar, prime_len,
> + curve) == 0);
> +
> + /* Represent the peer's element as a curve point. */
> + pointB[0] = 0x04; /* "uncompressed" format (RFC 5480, 2.2) */
> + memcpy(&pointB[1], element_x, prime_len);
> + memcpy(&pointB[prime_len + 1], element_y, prime_len);
> +
> + /* pointA = (elem-op(pointA, PEER-COMMIT-ELEMENT)) */
> + ret |= !!(group->muladd(pointA, pointB, sizeof(pointA),
> + one_bin, sizeof(one_bin), one_bin, sizeof(one_bin), curve) == 0);
> +
> + /* K = pointA = scalar-op(rand, pointA) */
> + ret |= !!(group->mul(pointA, sizeof(pointA), rand, prime_len,
> + curve) == 0);
> +
> + /* K must not be the identity element (point-at-infinity) */
> + br_i31_decode(t, pointA, 1 + SAE_P256_PRIME_LEN);
> + ret |= !!br_i31_iszero(t);
> + br_i31_decode(t, &pointA[1 + SAE_P256_PRIME_LEN], SAE_P256_PRIME_LEN);
> + ret |= !!br_i31_iszero(t);
> +
> + /* k = F(K) -> X coordinate */
> + memcpy(k, &pointA[1], SAE_P256_PRIME_LEN);
> +
> + KASSERT(ret == IEEE80211_STATUS_SUCCESS /* 0x0 */ ||
> + ret == IEEE80211_STATUS_UNSPECIFIED /* 0x1 */);
> +
> + return ret;
> +}
> +
> +int
> +ieee80211_sae_derive_secret_keys(uint8_t *sae_kck, uint8_t *pmk,
> + const uint8_t *k, const uint8_t *own_scalar, const uint8_t *peer_scalar)
> +{
> + const br_ec_impl *group = &br_ec_p256_m31;
> + int curve = BR_EC_secp192r1; /* Group 19, P-256 */
> + const unsigned char *order;
> + size_t xoff, prime_len, order_len;
> + uint32_t r[SAE_I31INT_WORDS];
> + uint32_t os[SAE_I31INT_WORDS];
> + uint32_t ps[SAE_I31INT_WORDS];
> + uint32_t context[SAE_I31INT_WORDS];
> + uint32_t t[SAE_I31INT_WORDS];
> + uint8_t salt[SHA256_DIGEST_LENGTH];
> + uint8_t keyseed[SHA256_DIGEST_LENGTH];
> + uint8_t bin[SAE_P256_PRIME_LEN];
> + uint8_t keys[SHA256_DIGEST_LENGTH + IEEE80211_PMK_LEN];
> + HMAC_SHA256_CTX ctx;
> +
> + order = group->order(curve, &order_len);
> + xoff = group->xoff(curve, &prime_len);
> + if (prime_len != SAE_P256_PRIME_LEN)
> + return -1;
> +
> + /* The order ("r") of the group as i31 integer. */
> + br_i31_decode(r, order, order_len);
> +
> + /* Own SAE commit scalar as i31 integer. */
> + br_i31_decode(os, own_scalar, SAE_P256_PRIME_LEN);
> +
> + /* Peer SAE commit scalar as i31 integer. */
> + br_i31_decode(ps, peer_scalar, SAE_P256_PRIME_LEN);
> +
> + /* context = (commit-scalar + peer-commit-scalar) mod r */
> + memcpy(t, os, sizeof(t)); /* t = commit-scalar */
> + br_i31_add(t, ps, 1); /* t = commit-scalar + peer-scalar */
> + br_i31_reduce(context, t, r); /* context = t modulo r */
> +
> + br_i31_encode(bin, order_len, context);
> +
> + /* PMKID = ExtractBits(context, 0, 128) */
> + hexdump("pmkid", bin, IEEE80211_PMKID_LEN);
> +
> + /*
> + * The salt value is either a list of rejected groups or zero.
> + * We only support mandatory group 19, which cannot be rejected.
> + */
> + memset(salt, 0, sizeof(salt));
> +
> + /* keyseed = H(salt, k) */
> + HMAC_SHA256_Init(&ctx, salt, sizeof(salt));
> + HMAC_SHA256_Update(&ctx, k, SAE_P256_PRIME_LEN);
> + HMAC_SHA256_Final(keyseed, &ctx);
> + hexdump("keyseed", keyseed, sizeof(keyseed));
> +
> + /* SAE-KCK and PMK = KDF(keyseed, "SAE KCK and PMK", context) */
> + ieee80211_kdf(keyseed, sizeof(keyseed),
> + "SAE KCK and PMK", 15 /* KDF omits \0 */,
> + bin, order_len, keys, sizeof(keys));
> +
> + memcpy(sae_kck, &keys[0], SHA256_DIGEST_LENGTH);
> + hexdump("kck", sae_kck, SHA256_DIGEST_LENGTH);
> + memcpy(pmk, &keys[SHA256_DIGEST_LENGTH], IEEE80211_PMK_LEN);
> + hexdump("pmk", pmk, IEEE80211_PMK_LEN);
> +
> + return 0;
> +}
> +
> +int
> +ieee80211_sae_verify_confirm(const uint8_t *frm, size_t remain,
> + const uint8_t *kck, const uint8_t *own_scalar,
> + const uint8_t *own_element_x, const uint8_t *own_element_y,
> + const uint8_t *peer_scalar,
> + const uint8_t *peer_element_x, const uint8_t *peer_element_y)
> +{
> + HMAC_SHA256_CTX ctx;
> + u_int8_t digest[SHA256_DIGEST_LENGTH];
> + const uint8_t *verifier;
> +
> + /*
> + * confirm = CN(SAE-KCK, peer-send-confirm, peer-commit-scalar,
> + * peer-commit-element, commit-scalar, commit-element)
> + */
> + HMAC_SHA256_Init(&ctx, kck, SHA256_DIGEST_LENGTH);
> +
> + HMAC_SHA256_Update(&ctx, frm, 2); /* peer-send-confirm */
> +
> + HMAC_SHA256_Update(&ctx, peer_scalar,
> + IEEE80211_SAE_MAX_ECC_PRIME_LEN);
> + HMAC_SHA256_Update(&ctx, peer_element_x,
> + IEEE80211_SAE_MAX_ECC_PRIME_LEN);
> + HMAC_SHA256_Update(&ctx, peer_element_y,
> + IEEE80211_SAE_MAX_ECC_PRIME_LEN);
> +
> + HMAC_SHA256_Update(&ctx, own_scalar,
> + IEEE80211_SAE_MAX_ECC_PRIME_LEN);
> + HMAC_SHA256_Update(&ctx, own_element_x,
> + IEEE80211_SAE_MAX_ECC_PRIME_LEN);
> + HMAC_SHA256_Update(&ctx, own_element_y,
> + IEEE80211_SAE_MAX_ECC_PRIME_LEN);
> +
> + HMAC_SHA256_Final(digest, &ctx);
> +
> + verifier = frm + 2;
> +
> + hexdump("verifier", verifier, SHA256_DIGEST_LENGTH);
> + hexdump("confirm", digest, SHA256_DIGEST_LENGTH);
> +
> + if (timingsafe_bcmp(verifier, digest, SHA256_DIGEST_LENGTH) != 0)
> + return -1;
> +
> + return 0;
> +}
>
add WPA3 support