From: Nick Owens Subject: Re: add WPA3 support To: tech@openbsd.org Date: Sat, 18 Jul 2026 05:53:14 -0700 hi, On Sat, Jul 18, 2026 at 4:42 AM Stefan Sperling 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 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 > 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 > + * > + * 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 > + * > + * Wrapper functions for OpenSSL libcrypto > + * Copyright (c) 2004-2024, Jouni Malinen > + * > + * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) > + * Copyright (c) 2014-2017, Jouni Malinen > + * > + * 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 > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#include > + > +#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 > + * > + * 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 > +#include > + > +#include > +#include > + > +/* > + * 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 > + * > + * 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 > + * > + * 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 > +#include > + > +#include > + > +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 > + * > + * 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 > #include > > +#include > +#include > +#include > +#include > + > 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 > + * > + * 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 > +#include > +#include > +#include > + > +#include > +#include > + > +#include > + > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#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; > +} >