SAE-PK: Update design for fingerprint encoding into password
Update the SAE-PK implementation to match the changes in the protocol design: - allow only Sec values 3 and 5 and encode this as a single bit field with multiple copies - add a checksum character Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
parent
02f4946172
commit
0a9d7b169e
7 changed files with 428 additions and 213 deletions
|
@ -551,19 +551,14 @@ fail:
|
|||
static int sae_pk_tests(void)
|
||||
{
|
||||
#ifdef CONFIG_SAE_PK
|
||||
const char *invalid[] = { "a2bc-de3f-ghi4-", "a2bcde3fghi4", "", NULL };
|
||||
const char *invalid[] = { "a2bc-de3f-ghim-", "a2bcde3fghim", "", NULL };
|
||||
struct {
|
||||
const char *pw;
|
||||
const u8 *val;
|
||||
} valid[] = {
|
||||
{ "a2bc-de3f-ghi4", (u8 *) "\x06\x82\x21\x93\x65\x31\xd1\xc0" },
|
||||
{ "aaaa-aaaa-aaaa-a",
|
||||
(u8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
|
||||
{ "aaaa-aaaa-aaaa", (u8 *) "\x00\x00\x00\x00\x00\x00\x00\x00" },
|
||||
{ "7777-7777-777", (u8 *) "\xff\xff\xff\xff\xff\xff\xfe" },
|
||||
{ "7777-7777-7777", (u8 *) "\xff\xff\xff\xff\xff\xff\xff\xf0" },
|
||||
{ "7777-7777-7777-7",
|
||||
(u8 *) "\xff\xff\xff\xff\xff\xff\xff\xff\x80" },
|
||||
{ "a2bc-de3f-ghim", (u8 *) "\x06\x82\x21\x93\x65\x31\xd0\xc0" },
|
||||
{ "aaaa-aaaa-aaaj", (u8 *) "\x00\x00\x00\x00\x00\x00\x00\x90" },
|
||||
{ "7777-7777-777f", (u8 *) "\xff\xff\xff\xff\xff\xff\xfe\x50" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
int i;
|
||||
|
@ -617,7 +612,7 @@ static int sae_pk_tests(void)
|
|||
}
|
||||
os_free(res);
|
||||
|
||||
b32 = sae_pk_base32_encode(val, bits);
|
||||
b32 = sae_pk_base32_encode(val, bits - 5);
|
||||
if (!b32) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"SAE-PK: Failed to encode password '%s'",
|
||||
|
|
|
@ -112,9 +112,6 @@ void sae_clear_temp_data(struct sae_data *sae)
|
|||
wpabuf_free(tmp->own_rejected_groups);
|
||||
wpabuf_free(tmp->peer_rejected_groups);
|
||||
os_free(tmp->pw_id);
|
||||
#ifdef CONFIG_SAE_PK
|
||||
bin_clear_free(tmp->pw, tmp->pw_len);
|
||||
#endif /* CONFIG_SAE_PK */
|
||||
bin_clear_free(tmp, sizeof(*tmp));
|
||||
sae->tmp = NULL;
|
||||
}
|
||||
|
|
|
@ -70,9 +70,11 @@ struct sae_temporary_data {
|
|||
const struct sae_pk *ap_pk;
|
||||
u8 own_addr[ETH_ALEN];
|
||||
u8 peer_addr[ETH_ALEN];
|
||||
u8 *pw;
|
||||
size_t pw_len;
|
||||
u8 fingerprint[SAE_MAX_HASH_LEN];
|
||||
size_t fingerprint_bytes;
|
||||
size_t fingerprint_bits;
|
||||
size_t lambda;
|
||||
unsigned int sec;
|
||||
u8 ssid[32];
|
||||
size_t ssid_len;
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
|
@ -164,5 +166,7 @@ struct sae_pk * sae_parse_pk(const char *val);
|
|||
int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf);
|
||||
int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len);
|
||||
int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash);
|
||||
u32 sae_pk_get_be19(const u8 *buf);
|
||||
void sae_pk_buf_shift_left_19(u8 *buf, size_t len);
|
||||
|
||||
#endif /* SAE_H */
|
||||
|
|
|
@ -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);
|
||||
int pos;
|
||||
size_t i, pw_len = os_strlen(pw);
|
||||
u8 sec_1b;
|
||||
u8 dtable[256];
|
||||
|
||||
/* 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)
|
||||
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;
|
||||
/* Fetch Sec from the two MSBs */
|
||||
idx = os_strchr(sae_pk_base32_table, pw[0]);
|
||||
if (!idx)
|
||||
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;
|
||||
|
||||
len = os_strlen(password);
|
||||
if (!tmp || len < 1)
|
||||
if (!tmp)
|
||||
return -1;
|
||||
|
||||
bin_clear_free(tmp->pw, tmp->pw_len);
|
||||
tmp->pw = sae_pk_base32_decode(password, len, &tmp->pw_len);
|
||||
os_memset(tmp->fingerprint, 0, sizeof(tmp->fingerprint));
|
||||
tmp->fingerprint_bytes = tmp->fingerprint_bits = 0;
|
||||
|
||||
len = os_strlen(password);
|
||||
if (len < 1 || !sae_pk_valid_password(password))
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue