@ -23,40 +23,173 @@
static const char * sae_pk_base32_table = " abcdefghijklmnopqrstuvwxyz234567 " ;
static const u8 d_mult_table [ ] = {
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 ,
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 ,
17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 ,
2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 ,
18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 ,
3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 ,
19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 ,
4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 ,
20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 ,
5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 ,
21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 ,
6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 ,
22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 ,
7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 ,
23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 ,
8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ,
24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 ,
9 , 10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,
25 , 26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ,
10 , 11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ,
26 , 27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 ,
11 , 12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
27 , 28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 ,
12 , 13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 ,
28 , 29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 ,
13 , 14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ,
29 , 30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 ,
14 , 15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 ,
30 , 31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 ,
15 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 ,
31 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 ,
16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 ,
0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 ,
17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 ,
1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 ,
18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 ,
2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 ,
19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 ,
3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 ,
20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 ,
4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 ,
21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 ,
5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 ,
22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 ,
6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 ,
23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 ,
7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 ,
24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 , 25 ,
8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 , 9 ,
25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 , 26 ,
9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 , 10 ,
26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 , 27 ,
10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 , 11 ,
27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 , 28 ,
11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 , 12 ,
28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 , 29 ,
12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 , 13 ,
29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 , 30 ,
13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 , 14 ,
30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 , 31 ,
14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 , 15 ,
31 , 30 , 29 , 28 , 27 , 26 , 25 , 24 , 23 , 22 , 21 , 20 , 19 , 18 , 17 , 16 ,
15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0
} ;
static const u8 d_perm_table [ ] = {
7 , 2 , 1 , 30 , 16 , 20 , 27 , 11 , 31 , 6 , 8 , 13 , 29 , 5 , 10 , 21 ,
22 , 3 , 24 , 0 , 23 , 25 , 12 , 9 , 28 , 14 , 4 , 15 , 17 , 18 , 19 , 26
} ;
static u8 d_permute ( u8 val , unsigned int iter )
{
if ( iter = = 0 )
return val ;
return d_permute ( d_perm_table [ val ] , iter - 1 ) ;
}
static u8 d_invert ( u8 val )
{
if ( val > 0 & & val < 16 )
return 16 - val ;
return val ;
}
static char d_check_char ( const char * str , size_t len )
{
size_t i ;
u8 val = 0 ;
u8 dtable [ 256 ] ;
unsigned int iter = 1 ;
int j ;
os_memset ( dtable , 0x80 , 256 ) ;
for ( i = 0 ; sae_pk_base32_table [ i ] ; i + + )
dtable [ ( u8 ) sae_pk_base32_table [ i ] ] = i ;
for ( j = len - 1 ; j > = 0 ; j - - ) {
u8 c , p ;
c = dtable [ ( u8 ) str [ j ] ] ;
if ( c = = 0x80 )
continue ;
p = d_permute ( c , iter ) ;
iter + + ;
val = d_mult_table [ val * 32 + p ] ;
}
return sae_pk_base32_table [ d_invert ( val ) ] ;
}
bool sae_pk_valid_password ( const char * pw )
{
int pos , sec ;
const char * idx ;
size_t pw_len = os_strlen ( pw ) ;
/* Check whether the password is long enough to meet the minimum
* required resistance to preimage attacks . This makes it less likely to
* recognize non - SAE - PK passwords as suitable for SAE - PK . */
if ( pw_len < 1 )
return false ;
/* Fetch Sec from the two MSBs */
idx = os_strchr ( sae_pk_base32_table , pw [ 0 ] ) ;
if ( ! idx )
int pos ;
size_t i , pw_len = os_strlen ( pw ) ;
u8 sec_1b ;
u8 dtable [ 256 ] ;
os_memset ( dtable , 0x80 , 256 ) ;
for ( i = 0 ; sae_pk_base32_table [ i ] ; i + + )
dtable [ ( u8 ) sae_pk_base32_table [ i ] ] = i ;
/* SAE-PK password has at least three four character components
* separated by hyphens . */
if ( pw_len < 14 | | pw_len % 5 ! = 4 ) {
wpa_printf ( MSG_DEBUG , " SAE-PK: Not a valid password (length) " ) ;
return false ;
sec = ( ( ( u8 ) ( ( idx - sae_pk_base32_table ) & 0x1f ) ) > > 3 ) + 2 ;
if ( ( sec = = 2 & & pw_len < 14 ) | |
( sec = = 3 & & pw_len < 13 ) | |
( sec = = 4 & & pw_len < 11 ) | |
( sec = = 5 & & pw_len < 9 ) )
return false ; /* too short password */
}
for ( pos = 0 ; pw [ pos ] ; pos + + ) {
if ( pos & & pos % 5 = = 4 ) {
if ( pw [ pos ] ! = ' - ' )
if ( pw [ pos ] ! = ' - ' ) {
wpa_printf ( MSG_DEBUG ,
" SAE-PK: Not a valid password (separator) " ) ;
return false ;
}
continue ;
}
if ( ! os_strchr ( sae_pk_base32_table , pw [ pos ] ) )
if ( dtable [ ( u8 ) pw [ pos ] ] = = 0x80 ) {
wpa_printf ( MSG_DEBUG ,
" SAE-PK: Not a valid password (character) " ) ;
return false ;
}
}
if ( pos = = 0 )
/* Verify that the checksum character is valid */
if ( pw [ pw_len - 1 ] ! = d_check_char ( pw , pw_len - 1 ) ) {
wpa_printf ( MSG_DEBUG ,
" SAE-PK: Not a valid password (checksum) " ) ;
return false ;
return pw [ pos - 1 ] ! = ' - ' ;
}
/* Verify that Sec_1b bits match */
sec_1b = dtable [ ( u8 ) pw [ 0 ] ] & BIT ( 4 ) ;
for ( i = 5 ; i < pw_len ; i + = 5 ) {
if ( sec_1b ! = ( dtable [ ( u8 ) pw [ i ] ] & BIT ( 4 ) ) ) {
wpa_printf ( MSG_DEBUG ,
" SAE-PK: Not a valid password (Sec_1b) " ) ;
return false ;
}
}
return true ;
}
@ -76,6 +209,7 @@ static char * add_char(const char *start, char *pos, u8 idx, size_t *bits)
}
/* Base32 encode a password and add hyper separators and checksum */
char * sae_pk_base32_encode ( const u8 * src , size_t len_bits )
{
char * out , * pos ;
@ -90,7 +224,7 @@ char * sae_pk_base32_encode(const u8 *src, size_t len_bits)
return NULL ;
olen = len * 8 / 5 + 1 ;
olen + = olen / 4 ; /* hyphen separators */
pos = out = os_zalloc ( olen + 1) ;
pos = out = os_zalloc ( olen + 2) ; /* include room for ChkSum and nul */
if ( ! out )
return NULL ;
@ -107,6 +241,8 @@ char * sae_pk_base32_encode(const u8 *src, size_t len_bits)
}
}
* pos = d_check_char ( out , os_strlen ( out ) ) ;
return out ;
}
@ -178,19 +314,107 @@ u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len)
}
u32 sae_pk_get_be19 ( const u8 * buf )
{
return ( buf [ 0 ] < < 11 ) | ( buf [ 1 ] < < 3 ) | ( buf [ 2 ] > > 5 ) ;
}
/* shift left by two octets and three bits; fill in zeros from right;
* len must be at least three */
void sae_pk_buf_shift_left_19 ( u8 * buf , size_t len )
{
u8 * dst , * src , * end ;
dst = buf ;
src = buf + 2 ;
end = buf + len ;
while ( src + 1 < end ) {
* dst + + = ( src [ 0 ] < < 3 ) | ( src [ 1 ] > > 5 ) ;
src + + ;
}
* dst + + = * src < < 3 ;
* dst + + = 0 ;
* dst + + = 0 ;
}
static void sae_pk_buf_shift_left_1 ( u8 * buf , size_t len )
{
u8 * dst , * src , * end ;
dst = buf ;
src = buf ;
end = buf + len ;
while ( src + 1 < end ) {
* dst + + = ( src [ 0 ] < < 1 ) | ( src [ 1 ] > > 7 ) ;
src + + ;
}
* dst + + = * src < < 1 ;
}
int sae_pk_set_password ( struct sae_data * sae , const char * password )
{
struct sae_temporary_data * tmp = sae - > tmp ;
size_t len ;
size_t len , pw_len ;
u8 * pw , * pos ;
int bits ;
u32 val = 0 , val19 ;
unsigned int val_bits = 0 ;
if ( ! tmp )
return - 1 ;
os_memset ( tmp - > fingerprint , 0 , sizeof ( tmp - > fingerprint ) ) ;
tmp - > fingerprint_bytes = tmp - > fingerprint_bits = 0 ;
len = os_strlen ( password ) ;
if ( ! tmp | | len < 1 )
if ( len < 1 | | ! sae_pk_valid_password ( password ) )
return - 1 ;
bin_clear_free ( tmp - > pw , tmp - > pw_len ) ;
tmp - > pw = sae_pk_base32_decode ( password , len , & tmp - > pw_len ) ;
pw = sae_pk_base32_decode ( password , len , & pw_len ) ;
if ( ! pw )
return - 1 ;
tmp - > sec = ( pw [ 0 ] & BIT ( 7 ) ) ? 3 : 5 ;
tmp - > lambda = len - len / 5 ;
return tmp - > pw ? 0 : - 1 ;
tmp - > fingerprint_bits = 8 * tmp - > sec + 19 * tmp - > lambda / 4 - 5 ;
wpa_printf ( MSG_DEBUG , " SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu " ,
tmp - > sec , tmp - > lambda , tmp - > fingerprint_bits ) ;
/* Construct Fingerprint from PasswordBase by prefixing with Sec zero
* octets and skipping the Sec_1b bits */
pos = & tmp - > fingerprint [ tmp - > sec ] ;
bits = tmp - > fingerprint_bits - 8 * tmp - > sec ;
wpa_hexdump_key ( MSG_DEBUG , " SAE-PK: PasswordBase " , pw , pw_len ) ;
while ( bits > 0 ) {
if ( val_bits < 8 ) {
sae_pk_buf_shift_left_1 ( pw , pw_len ) ; /* Sec_1b */
val19 = sae_pk_get_be19 ( pw ) ;
sae_pk_buf_shift_left_19 ( pw , pw_len ) ;
val = ( val < < 19 ) | val19 ;
val_bits + = 19 ;
}
if ( val_bits > = 8 ) {
if ( bits < 8 )
break ;
* pos + + = ( val > > ( val_bits - 8 ) ) & 0xff ;
val_bits - = 8 ;
bits - = 8 ;
}
}
if ( bits > 0 ) {
val > > = val_bits - bits ;
* pos + + = val < < ( 8 - bits ) ;
}
tmp - > fingerprint_bytes = pos - tmp - > fingerprint ;
wpa_hexdump_key ( MSG_DEBUG , " SAE-PK: Fingerprint " ,
tmp - > fingerprint , tmp - > fingerprint_bytes ) ;
bin_clear_free ( pw , pw_len ) ;
return 0 ;
}
@ -481,19 +705,19 @@ static bool sae_pk_valid_fingerprint(struct sae_data *sae,
const u8 * k_ap , size_t k_ap_len , int group )
{
struct sae_temporary_data * tmp = sae - > tmp ;
size_t sec , i ;
u8 * fingerprint_exp , * hash_data , * pos ;
size_t hash_len , hash_data_len , fingerprint_bits , fingerprint_bytes ;
u8 * hash_data , * pos ;
size_t hash_len , hash_data_len ;
u8 hash [ SAE_MAX_HASH_LEN ] ;
int res ;
if ( ! tmp - > pw | | tmp - > pw_len < 1 ) {
if ( ! tmp - > fingerprint_bytes ) {
wpa_printf ( MSG_DEBUG ,
" SAE-PK: No PW available for K_AP fingerprint check " ) ;
return false ;
}
/* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 5*Lambda - 2) */
/* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 19*Lambda/4 - 5)
*/
hash_len = sae_group_2_hash_len ( group ) ;
hash_data_len = tmp - > ssid_len + m_len + k_ap_len ;
@ -516,44 +740,26 @@ static bool sae_pk_valid_fingerprint(struct sae_data *sae,
wpa_hexdump ( MSG_DEBUG , " SAE-PK: Hash(SSID || M || K_AP) " ,
hash , hash_len ) ;
wpa_hexdump_key ( MSG_DEBUG , " SAE-PK: PW " , tmp - > pw , tmp - > pw_len ) ;
sec = ( tmp - > pw [ 0 ] > > 6 ) + 2 ;
fingerprint_bits = 8 * sec + 5 * tmp - > lambda - 2 ;
wpa_printf ( MSG_DEBUG , " SAE-PK: Sec=%zu Lambda=%zu fingerprint_bits=%zu " ,
sec , tmp - > lambda , fingerprint_bits ) ;
if ( fingerprint_bits > hash_len * 8 ) {
if ( tmp - > fingerprint_bits > hash_len * 8 ) {
wpa_printf ( MSG_INFO ,
" SAE-PK: Not enough hash output bits for the fingerprint " ) ;
return false ;
}
fingerprint_bytes = ( fingerprint_bits + 7 ) / 8 ;
if ( fingerprint_bits % 8 ) {
if ( tmp - > fingerprint_bits % 8 ) {
size_t extra ;
/* Zero out the extra bits in the last octet */
extra = 8 - fingerprint_bits % 8 ;
pos = & hash [ fingerprint_bits / 8 ] ;
extra = 8 - tmp- > fingerprint_bits % 8 ;
pos = & hash [ tmp- > fingerprint_bits / 8 ] ;
* pos = ( * pos > > extra ) < < extra ;
}
wpa_hexdump ( MSG_DEBUG , " SAE-PK: Fingerprint " , hash , fingerprint_bytes ) ;
fingerprint_exp = os_zalloc ( sec + tmp - > pw_len ) ;
if ( ! fingerprint_exp )
return false ;
pos = fingerprint_exp + sec ;
for ( i = 0 ; i < tmp - > pw_len ; i + + ) {
u8 next = i + 1 < tmp - > pw_len ? tmp - > pw [ i + 1 ] : 0 ;
* pos + + = tmp - > pw [ i ] < < 2 | next > > 6 ;
}
wpa_hexdump ( MSG_DEBUG , " SAE-PK: Fingerprint_Expected " ,
fingerprint_exp , fingerprint_bytes ) ;
res = os_memcmp_const ( hash , fingerprint_exp , fingerprint_bytes ) ;
bin_clear_free ( fingerprint_exp , tmp - > pw_len ) ;
wpa_hexdump ( MSG_DEBUG , " SAE-PK: Fingerprint " , hash ,
tmp - > fingerprint_bytes ) ;
res = os_memcmp_const ( hash , tmp - > fingerprint , tmp - > fingerprint_bytes ) ;
if ( res ) {
wpa_printf ( MSG_DEBUG , " SAE-PK: K_AP fingerprint mismatch " ) ;
wpa_hexdump ( MSG_DEBUG , " SAE-PK: Expected fingerprint " ,
tmp - > fingerprint , tmp - > fingerprint_bytes ) ;
return false ;
}