aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2013-01-01 12:00:40 (GMT)
committerJouni Malinen <j@w1.fi>2013-01-12 15:51:53 (GMT)
commitcd9c2714e795a0eb2d9cdad3edb9146b27ff261c (patch)
tree05fb522959eda3bb75ffeff26485d8da9234d07d
parentbf14657b9ffd8f46a46879708619b91a160f9967 (diff)
downloadhostap-cd9c2714e795a0eb2d9cdad3edb9146b27ff261c.zip
hostap-cd9c2714e795a0eb2d9cdad3edb9146b27ff261c.tar.gz
hostap-cd9c2714e795a0eb2d9cdad3edb9146b27ff261c.tar.bz2
SAE: Add support for ECC group 21 (521-bit random ECP group)
In addition to the trivial change in adding the new group ientifier, this required changes to KDF and random number generation to support cases where the length of the prime in bits is not a multiple of eight. The binary presentation of the value needs to be shifted so that the unused most significant bits are the zero padding rather than the extra bits in the end of the array. Signed-hostap: Jouni Malinen <j@w1.fi>
-rw-r--r--src/common/sae.c39
-rw-r--r--src/common/sae.h4
-rw-r--r--src/crypto/crypto.h7
-rw-r--r--src/crypto/crypto_openssl.c9
-rw-r--r--src/crypto/sha256-prf.c38
-rw-r--r--src/crypto/sha256.h5
6 files changed, 88 insertions, 14 deletions
diff --git a/src/common/sae.c b/src/common/sae.c
index fcca157..d9d6467 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 0a9881b..a30fb62 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -1,6 +1,6 @@
/*
* Simultaneous authentication of equals
- * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
*
* 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 d33ccb1..950cd9e 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -550,6 +550,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()
* Returns: Prime (bignum) defining the group
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index a2b5c5f..593cf6f 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 0da6d13..9a11208 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 <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,12 +29,36 @@
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;
u8 hash[SHA256_MAC_LEN];
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 fcac800..7596a52 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 <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* 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);