SAE: Hash algorithm selection for H2E KCK/CN()

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2019-08-27 16:33:15 +03:00 committed by Jouni Malinen
parent aeb022f8e5
commit efd4285299
2 changed files with 112 additions and 33 deletions

View file

@ -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;
} }

View file

@ -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;