]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Add support for ECC group 21 (521-bit random ECP group)
authorJouni Malinen <j@w1.fi>
Tue, 1 Jan 2013 12:00:40 +0000 (14:00 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 12 Jan 2013 15:51:53 +0000 (17:51 +0200)
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>

src/common/sae.c
src/common/sae.h
src/crypto/crypto.h
src/crypto/crypto_openssl.c
src/crypto/sha256-prf.c
src/crypto/sha256.h

index fcca157f20f951307bf561fb1b872e5a68d02166..d9d6467e0c36c16840e245c049a288ac3c3933ab 100644 (file)
@@ -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);
index 0a9881b93b90f678b7733caa8028a1a681de753f..a30fb625abb5bd877532d6059e372dabfbe4d310 100644 (file)
@@ -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)
 
index d33ccb196c14ba52ac08ddcc05c4728b73c66242..950cd9e43099ff1636c53eade739cef89d7959d3 100644 (file)
@@ -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()
index a2b5c5f0e9730b1225cb0d8be53dd2512c904ea2..593cf6f2de3fa953fabf7ee81be52c641d75ceab 100644 (file)
@@ -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;
index 0da6d130a88e8eaecaa22a283b6d806a651cc9be..9a11208f5af3ffeafc074be193b4b2896d29ffd2 100644 (file)
@@ -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.
  */
 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;
+       }
 }
index fcac8004ca4f5a6cbae56b32ff17f188a0f8de0c..7596a52219fee786775f640032ea2fafbaa9ab99 100644 (file)
@@ -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);