SAE: Hash algorithm selection for H2E KCK/CN()
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
parent
aeb022f8e5
commit
efd4285299
2 changed files with 112 additions and 33 deletions
138
src/common/sae.c
138
src/common/sae.c
|
@ -1484,32 +1484,99 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label,
|
||||||
|
const u8 *context, size_t context_len,
|
||||||
|
u8 *out, size_t out_len)
|
||||||
|
{
|
||||||
|
if (hash_len == 32)
|
||||||
|
return sha256_prf(k, hash_len, label,
|
||||||
|
context, context_len, out, out_len);
|
||||||
|
#ifdef CONFIG_SHA384
|
||||||
|
if (hash_len == 48)
|
||||||
|
return sha384_prf(k, hash_len, label,
|
||||||
|
context, context_len, out, out_len);
|
||||||
|
#endif /* CONFIG_SHA384 */
|
||||||
|
#ifdef CONFIG_SHA512
|
||||||
|
if (hash_len == 64)
|
||||||
|
return sha512_prf(k, hash_len, label,
|
||||||
|
context, context_len, out, out_len);
|
||||||
|
#endif /* CONFIG_SHA512 */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int sae_derive_keys(struct sae_data *sae, const u8 *k)
|
static int sae_derive_keys(struct sae_data *sae, const u8 *k)
|
||||||
{
|
{
|
||||||
u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
|
u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN];
|
||||||
u8 keyseed[SHA256_MAC_LEN];
|
const u8 *salt;
|
||||||
u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
|
struct wpabuf *rejected_groups = NULL;
|
||||||
|
u8 keyseed[SAE_MAX_HASH_LEN];
|
||||||
|
u8 keys[SAE_MAX_HASH_LEN + SAE_PMK_LEN];
|
||||||
struct crypto_bignum *tmp;
|
struct crypto_bignum *tmp;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
|
||||||
|
const u8 *addr[1];
|
||||||
|
size_t len[1];
|
||||||
|
|
||||||
tmp = crypto_bignum_init();
|
tmp = crypto_bignum_init();
|
||||||
if (tmp == NULL)
|
if (tmp == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/* keyseed = H(<0>32, k)
|
/* keyseed = H(salt, k)
|
||||||
* KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
|
* KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
|
||||||
* (commit-scalar + peer-commit-scalar) modulo r)
|
* (commit-scalar + peer-commit-scalar) modulo r)
|
||||||
* PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
|
* PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
|
||||||
*/
|
*/
|
||||||
|
if (!sae->tmp->h2e)
|
||||||
|
hash_len = SHA256_MAC_LEN;
|
||||||
|
else if (sae->tmp->dh)
|
||||||
|
hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
|
||||||
|
else
|
||||||
|
hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
|
||||||
|
if (sae->tmp->h2e && (sae->tmp->own_rejected_groups ||
|
||||||
|
sae->tmp->peer_rejected_groups)) {
|
||||||
|
struct wpabuf *own, *peer;
|
||||||
|
|
||||||
os_memset(null_key, 0, sizeof(null_key));
|
own = sae->tmp->own_rejected_groups;
|
||||||
hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
|
peer = sae->tmp->peer_rejected_groups;
|
||||||
keyseed);
|
salt_len = 0;
|
||||||
wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
|
if (own)
|
||||||
|
salt_len += wpabuf_len(own);
|
||||||
|
if (peer)
|
||||||
|
salt_len += wpabuf_len(peer);
|
||||||
|
rejected_groups = wpabuf_alloc(salt_len);
|
||||||
|
if (!rejected_groups)
|
||||||
|
goto fail;
|
||||||
|
if (sae->tmp->own_addr_higher) {
|
||||||
|
if (own)
|
||||||
|
wpabuf_put_buf(rejected_groups, own);
|
||||||
|
if (peer)
|
||||||
|
wpabuf_put_buf(rejected_groups, peer);
|
||||||
|
} else {
|
||||||
|
if (peer)
|
||||||
|
wpabuf_put_buf(rejected_groups, peer);
|
||||||
|
if (own)
|
||||||
|
wpabuf_put_buf(rejected_groups, own);
|
||||||
|
}
|
||||||
|
salt = wpabuf_head(rejected_groups);
|
||||||
|
salt_len = wpabuf_len(rejected_groups);
|
||||||
|
} else {
|
||||||
|
os_memset(zero, 0, hash_len);
|
||||||
|
salt = zero;
|
||||||
|
salt_len = hash_len;
|
||||||
|
}
|
||||||
|
wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation",
|
||||||
|
salt, salt_len);
|
||||||
|
addr[0] = k;
|
||||||
|
len[0] = prime_len;
|
||||||
|
if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0)
|
||||||
|
goto fail;
|
||||||
|
wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len);
|
||||||
|
|
||||||
crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
|
if (crypto_bignum_add(sae->tmp->own_commit_scalar,
|
||||||
tmp);
|
sae->peer_commit_scalar, tmp) < 0 ||
|
||||||
crypto_bignum_mod(tmp, sae->tmp->order, tmp);
|
crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0)
|
||||||
|
goto fail;
|
||||||
/* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit
|
/* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit
|
||||||
* string that is needed for KCK, PMK, and PMKID derivation, but it
|
* string that is needed for KCK, PMK, and PMKID derivation, but it
|
||||||
* seems to make most sense to encode the
|
* seems to make most sense to encode the
|
||||||
|
@ -1518,19 +1585,23 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k)
|
||||||
* octets). */
|
* octets). */
|
||||||
crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
|
crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
|
||||||
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
|
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
|
||||||
if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
|
if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
|
||||||
val, sae->tmp->order_len, keys, sizeof(keys)) < 0)
|
val, sae->tmp->order_len,
|
||||||
|
keys, hash_len + SAE_PMK_LEN) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
os_memset(keyseed, 0, sizeof(keyseed));
|
forced_memzero(keyseed, sizeof(keyseed));
|
||||||
os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
|
os_memcpy(sae->tmp->kck, keys, hash_len);
|
||||||
os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
|
sae->tmp->kck_len = hash_len;
|
||||||
|
os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
|
||||||
os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
|
os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
|
||||||
os_memset(keys, 0, sizeof(keys));
|
forced_memzero(keys, sizeof(keys));
|
||||||
wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
|
wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
|
||||||
|
sae->tmp->kck, sae->tmp->kck_len);
|
||||||
wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
|
wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
|
wpabuf_free(rejected_groups);
|
||||||
crypto_bignum_deinit(tmp, 0);
|
crypto_bignum_deinit(tmp, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -2044,8 +2115,8 @@ static int sae_cn_confirm(struct sae_data *sae, const u8 *sc,
|
||||||
len[3] = sae->tmp->prime_len;
|
len[3] = sae->tmp->prime_len;
|
||||||
addr[4] = element2;
|
addr[4] = element2;
|
||||||
len[4] = element2_len;
|
len[4] = element2_len;
|
||||||
return hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck),
|
return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len,
|
||||||
5, addr, len, confirm);
|
5, addr, len, confirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2097,10 +2168,13 @@ static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
|
||||||
void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
|
void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
|
||||||
{
|
{
|
||||||
const u8 *sc;
|
const u8 *sc;
|
||||||
|
size_t hash_len;
|
||||||
|
|
||||||
if (sae->tmp == NULL)
|
if (sae->tmp == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
hash_len = sae->tmp->kck_len;
|
||||||
|
|
||||||
/* Send-Confirm */
|
/* Send-Confirm */
|
||||||
sc = wpabuf_put(buf, 0);
|
sc = wpabuf_put(buf, 0);
|
||||||
wpabuf_put_le16(buf, sae->send_confirm);
|
wpabuf_put_le16(buf, sae->send_confirm);
|
||||||
|
@ -2112,29 +2186,33 @@ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
|
||||||
sae->tmp->own_commit_element_ecc,
|
sae->tmp->own_commit_element_ecc,
|
||||||
sae->peer_commit_scalar,
|
sae->peer_commit_scalar,
|
||||||
sae->tmp->peer_commit_element_ecc,
|
sae->tmp->peer_commit_element_ecc,
|
||||||
wpabuf_put(buf, SHA256_MAC_LEN));
|
wpabuf_put(buf, hash_len));
|
||||||
else
|
else
|
||||||
sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
|
sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
|
||||||
sae->tmp->own_commit_element_ffc,
|
sae->tmp->own_commit_element_ffc,
|
||||||
sae->peer_commit_scalar,
|
sae->peer_commit_scalar,
|
||||||
sae->tmp->peer_commit_element_ffc,
|
sae->tmp->peer_commit_element_ffc,
|
||||||
wpabuf_put(buf, SHA256_MAC_LEN));
|
wpabuf_put(buf, hash_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
|
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
|
||||||
{
|
{
|
||||||
u8 verifier[SHA256_MAC_LEN];
|
u8 verifier[SAE_MAX_HASH_LEN];
|
||||||
|
size_t hash_len;
|
||||||
|
|
||||||
if (len < 2 + SHA256_MAC_LEN) {
|
if (!sae->tmp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
hash_len = sae->tmp->kck_len;
|
||||||
|
if (len < 2 + hash_len) {
|
||||||
wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
|
wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
|
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
|
||||||
|
|
||||||
if (!sae->tmp || !sae->peer_commit_scalar ||
|
if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) {
|
||||||
!sae->tmp->own_commit_scalar) {
|
|
||||||
wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
|
wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -2159,12 +2237,12 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
|
if (os_memcmp_const(verifier, data + 2, hash_len) != 0) {
|
||||||
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
|
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
|
||||||
wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
|
wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
|
||||||
data + 2, SHA256_MAC_LEN);
|
data + 2, hash_len);
|
||||||
wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
|
wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
|
||||||
verifier, SHA256_MAC_LEN);
|
verifier, hash_len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,18 @@
|
||||||
#define SAE_KCK_LEN 32
|
#define SAE_KCK_LEN 32
|
||||||
#define SAE_PMK_LEN 32
|
#define SAE_PMK_LEN 32
|
||||||
#define SAE_PMKID_LEN 16
|
#define SAE_PMKID_LEN 16
|
||||||
#define SAE_KEYSEED_KEY_LEN 32
|
|
||||||
#define SAE_MAX_PRIME_LEN 512
|
#define SAE_MAX_PRIME_LEN 512
|
||||||
#define SAE_MAX_ECC_PRIME_LEN 66
|
#define SAE_MAX_ECC_PRIME_LEN 66
|
||||||
|
#define SAE_MAX_HASH_LEN 64
|
||||||
#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
|
#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
|
||||||
#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
|
#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN)
|
||||||
|
|
||||||
/* Special value returned by sae_parse_commit() */
|
/* Special value returned by sae_parse_commit() */
|
||||||
#define SAE_SILENTLY_DISCARD 65535
|
#define SAE_SILENTLY_DISCARD 65535
|
||||||
|
|
||||||
struct sae_temporary_data {
|
struct sae_temporary_data {
|
||||||
u8 kck[SAE_KCK_LEN];
|
u8 kck[SAE_MAX_HASH_LEN];
|
||||||
|
size_t kck_len;
|
||||||
struct crypto_bignum *own_commit_scalar;
|
struct crypto_bignum *own_commit_scalar;
|
||||||
struct crypto_bignum *own_commit_element_ffc;
|
struct crypto_bignum *own_commit_element_ffc;
|
||||||
struct crypto_ec_point *own_commit_element_ecc;
|
struct crypto_ec_point *own_commit_element_ecc;
|
||||||
|
|
Loading…
Reference in a new issue