diff --git a/src/common/sae.c b/src/common/sae.c index fcca157f2..d9d6467e0 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -63,15 +63,27 @@ static int val_zero(const u8 *val, size_t len) } -static int sae_get_rand(const u8 *order, size_t prime_len, u8 *val) +static void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + +static int sae_get_rand(const u8 *order, size_t prime_len_bits, u8 *val) { int iter = 0; + size_t prime_len = (prime_len_bits + 7) / 8; do { - if (random_get_bytes(val, prime_len) < 0) - return -1; if (iter++ > 100) return -1; + if (random_get_bytes(val, prime_len) < 0) + return -1; + if (prime_len_bits % 8) + buf_shift_right(val, prime_len, 8 - prime_len_bits % 8); } while (os_memcmp(val, order, prime_len) >= 0 || val_zero_or_one(val, prime_len)); @@ -83,13 +95,14 @@ static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) { u8 mask[SAE_MAX_PRIME_LEN], order[SAE_MAX_PRIME_LEN]; struct crypto_bignum *bn; + size_t prime_len_bits = crypto_ec_prime_len_bits(sae->ec); if (crypto_bignum_to_bin(crypto_ec_get_order(sae->ec), order, sizeof(order), sae->prime_len) < 0) return NULL; - if (sae_get_rand(order, sae->prime_len, sae->sae_rand) < 0 || - sae_get_rand(order, sae->prime_len, mask) < 0) + if (sae_get_rand(order, prime_len_bits, sae->sae_rand) < 0 || + sae_get_rand(order, prime_len_bits, mask) < 0) return NULL; wpa_hexdump_key(MSG_DEBUG, "SAE: rand", sae->sae_rand, sae->prime_len); @@ -120,6 +133,7 @@ static int sae_test_pwd_seed(struct sae_data *sae, const u8 *pwd_seed, u8 pwd_value[SAE_MAX_PRIME_LEN], prime[SAE_MAX_PRIME_LEN]; struct crypto_bignum *x; int y_bit; + size_t bits; if (crypto_bignum_to_bin(crypto_ec_get_prime(sae->ec), prime, sizeof(prime), sae->prime_len) < 0) @@ -128,8 +142,11 @@ static int sae_test_pwd_seed(struct sae_data *sae, const u8 *pwd_seed, wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ - sha256_prf(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", - prime, sae->prime_len, pwd_value, sae->prime_len); + bits = crypto_ec_prime_len_bits(sae->ec); + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + prime, sae->prime_len, pwd_value, bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->prime_len); @@ -241,8 +258,10 @@ static int sae_derive_commit(struct sae_data *sae, struct crypto_ec_point *pwe) int ret = -1; mask = sae_get_rand_and_mask(sae); - if (mask == NULL) + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); return -1; + } x = crypto_bignum_init(); bn_rand = crypto_bignum_init_set(sae->sae_rand, sae->prime_len); @@ -263,8 +282,10 @@ static int sae_derive_commit(struct sae_data *sae, struct crypto_ec_point *pwe) crypto_ec_point_invert(sae->ec, elem) < 0 || crypto_ec_point_to_bin(sae->ec, elem, sae->own_commit_element, sae->own_commit_element + sae->prime_len) < - 0) + 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); goto fail; + } wpa_hexdump(MSG_DEBUG, "SAE: commit-element x", sae->own_commit_element, sae->prime_len); diff --git a/src/common/sae.h b/src/common/sae.h index 0a9881b93..a30fb625a 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -1,6 +1,6 @@ /* * Simultaneous authentication of equals - * Copyright (c) 2012, Jouni Malinen + * Copyright (c) 2012-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,7 +13,7 @@ #define SAE_PMK_LEN 32 #define SAE_PMKID_LEN 16 #define SAE_KEYSEED_KEY_LEN 32 -#define SAE_MAX_PRIME_LEN 48 +#define SAE_MAX_PRIME_LEN 66 #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index d33ccb196..950cd9e43 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -549,6 +549,13 @@ void crypto_ec_deinit(struct crypto_ec *e); */ size_t crypto_ec_prime_len(struct crypto_ec *e); +/** + * crypto_ec_prime_len_bits - Get length of the prime in bits + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group in bits + */ +size_t crypto_ec_prime_len_bits(struct crypto_ec *e); + /** * crypto_ec_get_prime - Get prime defining an EC group * @e: EC context from crypto_ec_init() diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index a2b5c5f0e..593cf6f2d 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -917,6 +917,9 @@ struct crypto_ec * crypto_ec_init(int group) case 20: nid = NID_secp384r1; break; + case 21: + nid = NID_secp521r1; + break; case 25: nid = NID_X9_62_prime192v1; break; @@ -972,6 +975,12 @@ size_t crypto_ec_prime_len(struct crypto_ec *e) } +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return BN_num_bits(e->prime); +} + + const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) { return (const struct crypto_bignum *) e->prime; diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c index 0da6d130a..9a11208f5 100644 --- a/src/crypto/sha256-prf.c +++ b/src/crypto/sha256-prf.c @@ -1,6 +1,6 @@ /* * SHA256-based PRF (IEEE 802.11r) - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -28,6 +28,29 @@ */ void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -35,6 +58,7 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *addr[4]; size_t len[4]; u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; addr[0] = counter_le; len[0] = 2; @@ -45,7 +69,7 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, addr[3] = length_le; len[3] = sizeof(length_le); - WPA_PUT_LE16(length_le, buf_len * 8); + WPA_PUT_LE16(length_le, buf_len_bits); pos = 0; while (pos < buf_len) { plen = buf_len - pos; @@ -57,8 +81,18 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, } else { hmac_sha256_vector(key, key_len, 4, addr, len, hash); os_memcpy(&buf[pos], hash, plen); + pos += plen; break; } counter++; } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } } diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index fcac8004c..7596a5221 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -1,6 +1,6 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2011, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,6 +17,9 @@ int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen);