From 77f273c82cafb54ba2a141ecd9b2e34a7e4b5bf0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 15 Jun 2017 21:17:50 +0300 Subject: [PATCH] Extend SHA-384 and SHA-512 support to match SHA-256 The additional SHA-384 and SHA-512 functionality is needed to support DPP with various ECC curves. Signed-off-by: Jouni Malinen --- hostapd/Android.mk | 18 ++++++ hostapd/Makefile | 15 +++++ src/crypto/crypto_openssl.c | 29 ++++++++++ src/crypto/sha384-kdf.c | 87 +++++++++++++++++++++++++++++ src/crypto/sha384.h | 3 + src/crypto/sha512-kdf.c | 87 +++++++++++++++++++++++++++++ src/crypto/sha512-prf.c | 108 ++++++++++++++++++++++++++++++++++++ src/crypto/sha512.h | 27 +++++++++ wpa_supplicant/Android.mk | 15 +++++ wpa_supplicant/Makefile | 17 ++++++ 10 files changed, 406 insertions(+) create mode 100644 src/crypto/sha384-kdf.c create mode 100644 src/crypto/sha512-kdf.c create mode 100644 src/crypto/sha512-prf.c create mode 100644 src/crypto/sha512.h diff --git a/hostapd/Android.mk b/hostapd/Android.mk index eafc5df03..50b1e2c5d 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -859,6 +859,15 @@ endif ifdef NEED_TLS_PRF_SHA256 OBJS += src/crypto/sha256-tlsprf.c endif +ifdef NEED_HMAC_SHA256_KDF +OBJS += src/crypto/sha256-kdf.c +endif +ifdef NEED_HMAC_SHA384_KDF +OBJS += src/crypto/sha384-kdf.c +endif +ifdef NEED_HMAC_SHA512_KDF +OBJS += src/crypto/sha512-kdf.c +endif endif ifdef NEED_SHA384 L_CFLAGS += -DCONFIG_SHA384 @@ -867,6 +876,15 @@ OBJS += src/crypto/sha384.c endif OBJS += src/crypto/sha384-prf.c endif +ifdef NEED_SHA512 +L_CFLAGS += -DCONFIG_SHA512 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +OBJS += src/crypto/sha512.c +endif +endif +OBJS += src/crypto/sha512-prf.c +endif ifdef CONFIG_INTERNAL_SHA384 L_CFLAGS += -DCONFIG_INTERNAL_SHA384 diff --git a/hostapd/Makefile b/hostapd/Makefile index ab144e2fd..b9e89240a 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -952,6 +952,12 @@ endif ifdef NEED_HMAC_SHA256_KDF OBJS += ../src/crypto/sha256-kdf.o endif +ifdef NEED_HMAC_SHA384_KDF +OBJS += ../src/crypto/sha384-kdf.o +endif +ifdef NEED_HMAC_SHA512_KDF +OBJS += ../src/crypto/sha512-kdf.o +endif endif ifdef NEED_SHA384 CFLAGS += -DCONFIG_SHA384 @@ -962,6 +968,15 @@ endif endif OBJS += ../src/crypto/sha384-prf.o endif +ifdef NEED_SHA512 +CFLAGS += -DCONFIG_SHA512 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +OBJS += ../src/crypto/sha512.o +endif +endif +OBJS += ../src/crypto/sha512-prf.o +endif ifdef CONFIG_INTERNAL_SHA384 CFLAGS += -DCONFIG_INTERNAL_SHA384 diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index d535c7c9c..6bff2027f 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -246,6 +246,7 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, } #endif /* NO_SHA256_WRAPPER */ + #ifndef NO_SHA384_WRAPPER int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -255,6 +256,15 @@ int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, #endif /* NO_SHA384_WRAPPER */ +#ifndef NO_SHA512_WRAPPER +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac); +} +#endif /* NO_SHA512_WRAPPER */ + + static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) { switch (keylen) { @@ -1049,6 +1059,25 @@ int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr, + len, mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + int crypto_get_random(void *buf, size_t len) { if (RAND_bytes(buf, len) != 1) diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c new file mode 100644 index 000000000..1d1962790 --- /dev/null +++ b/src/crypto/sha384-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" + + +/** + * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA384_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA384_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA384_MAC_LEN) + clen = SHA384_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA384_MAC_LEN); + return 0; +} diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h index 74d0da3d7..224142538 100644 --- a/src/crypto/sha384.h +++ b/src/crypto/sha384.h @@ -20,5 +20,8 @@ int sha384_prf(const u8 *key, size_t key_len, const char *label, int sha384_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); +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA384_H */ diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c new file mode 100644 index 000000000..8b71f9b0e --- /dev/null +++ b/src/crypto/sha512-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" + + +/** + * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA512_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA512_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA512_MAC_LEN) + clen = SHA512_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA512_MAC_LEN); + return 0; +} diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c new file mode 100644 index 000000000..3b2ad889d --- /dev/null +++ b/src/crypto/sha512-prf.c @@ -0,0 +1,108 @@ +/* + * SHA512-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2) + * @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 bytes of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + return sha512_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); +} + + +/** + * sha512_prf_bits - IEEE Std 802.11ac-2013, 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 + * Returns: 0 on success, -1 on failure + * + * 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. + */ +int sha512_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[SHA512_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; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA512_MAC_LEN) { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; + pos += SHA512_MAC_LEN; + } else { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; + 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; + } + + os_memset(hash, 0, sizeof(hash)); + + return 0; +} diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h new file mode 100644 index 000000000..8e64c8b11 --- /dev/null +++ b/src/crypto/sha512.h @@ -0,0 +1,27 @@ +/* + * SHA512 hash implementation and interface functions + * Copyright (c) 2015-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_H +#define SHA512_H + +#define SHA512_MAC_LEN 64 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha512_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); +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); + +#endif /* SHA512_H */ diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index f7ec113b4..1eb092753 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -1306,6 +1306,14 @@ ifdef NEED_HMAC_SHA256_KDF L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF SHA256OBJS += src/crypto/sha256-kdf.c endif +ifdef NEED_HMAC_SHA384_KDF +L_CFLAGS += -DCONFIG_HMAC_SHA384_KDF +SHA256OBJS += src/crypto/sha384-kdf.c +endif +ifdef NEED_HMAC_SHA512_KDF +L_CFLAGS += -DCONFIG_HMAC_SHA512_KDF +SHA256OBJS += src/crypto/sha512-kdf.c +endif OBJS += $(SHA256OBJS) endif ifdef NEED_SHA384 @@ -1315,6 +1323,13 @@ OBJS += src/crypto/sha384.c endif OBJS += src/crypto/sha384-prf.c endif +ifdef NEED_SHA512 +L_CFLAGS += -DCONFIG_SHA512 +ifneq ($(CONFIG_TLS), openssl) +OBJS += src/crypto/sha512.c +endif +OBJS += src/crypto/sha512-prf.c +endif ifdef NEED_DH_GROUPS OBJS += src/crypto/dh_groups.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 28ef52ad3..f410d925e 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1403,6 +1403,14 @@ ifdef NEED_HMAC_SHA256_KDF CFLAGS += -DCONFIG_HMAC_SHA256_KDF OBJS += ../src/crypto/sha256-kdf.o endif +ifdef NEED_HMAC_SHA384_KDF +CFLAGS += -DCONFIG_HMAC_SHA384_KDF +OBJS += ../src/crypto/sha384-kdf.o +endif +ifdef NEED_HMAC_SHA512_KDF +CFLAGS += -DCONFIG_HMAC_SHA512_KDF +OBJS += ../src/crypto/sha512-kdf.o +endif OBJS += $(SHA256OBJS) endif ifdef NEED_SHA384 @@ -1414,6 +1422,15 @@ endif CFLAGS += -DCONFIG_SHA384 OBJS += ../src/crypto/sha384-prf.o endif +ifdef NEED_SHA512 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +OBJS += ../src/crypto/sha512.o +endif +endif +CFLAGS += -DCONFIG_SHA512 +OBJS += ../src/crypto/sha512-prf.o +endif ifdef NEED_DH_GROUPS OBJS += ../src/crypto/dh_groups.o