From fb09ec87f206f25be82349dde334ab0dc61b7630 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 2 Jun 2020 22:44:42 +0300 Subject: [PATCH] SAE-PK: A tool for generating SAE-PK Modifier and password sae_pk_gen can be used to generate Modifier (M) and password for SAE-PK based on a previously generated EC private key, Sec value (2..5), and SSID. For example, these commands can be used to generate the private key and the needed hostapd configuration parameter options: make sae_pk_gen openssl ecparam -genkey -outform DER -out saepk.der -name prime256v1 ./sae_pk_gen saepk.der 3 "SAE-PK test" Signed-off-by: Jouni Malinen --- hostapd/Makefile | 34 +++++++++ hostapd/sae_pk_gen.c | 160 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 hostapd/sae_pk_gen.c diff --git a/hostapd/Makefile b/hostapd/Makefile index 3cc64c965..1400002a9 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -19,6 +19,9 @@ endif ifndef LIBS_n LIBS_n := $(LIBS) endif +ifndef LIBS_s +LIBS_s := $(LIBS) +endif endif CFLAGS += $(EXTRA_CFLAGS) @@ -122,6 +125,7 @@ LIBS += -lbfd -ldl -liberty -lz LIBS_c += -lbfd -ldl -liberty -lz LIBS_h += -lbfd -ldl -liberty -lz LIBS_n += -lbfd -ldl -liberty -lz +LIBS_s += -lbfd -ldl -liberty -lz endif endif @@ -704,6 +708,7 @@ LIBS += -lssl endif OBJS += ../src/crypto/crypto_openssl.o HOBJS += ../src/crypto/crypto_openssl.o +SOBJS += ../src/crypto/crypto_openssl.o ifdef NEED_FIPS186_2_PRF OBJS += ../src/crypto/fips_prf_openssl.o endif @@ -711,9 +716,11 @@ NEED_TLS_PRF_SHA256=y LIBS += -lcrypto LIBS_h += -lcrypto LIBS_n += -lcrypto +LIBS_s += -lcrypto ifdef CONFIG_TLS_ADD_DL LIBS += -ldl LIBS_h += -ldl +LIBS_s += -ldl endif ifndef CONFIG_TLS_DEFAULT_CIPHERS CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW" @@ -1345,6 +1352,28 @@ ifeq ($(CONFIG_TLS), linux) HOBJS += ../src/crypto/crypto_linux.o endif +SOBJS += sae_pk_gen.o +SOBJS += ../src/utils/common.o +SOBJS += ../src/utils/os_$(CONFIG_OS).o +SOBJS += ../src/utils/base64.o +SOBJS += ../src/utils/wpa_debug.o +SOBJS += ../src/utils/wpabuf.o +ifdef CONFIG_WPA_TRACE +SOBJS += ../src/utils/trace.o +endif +SOBJS += ../src/common/ieee802_11_common.o +SOBJS += ../src/common/sae.o +SOBJS += ../src/common/sae_pk.o +SOBJS += ../src/common/dragonfly.o +SOBJS += $(AESOBJS) +SOBJS += ../src/crypto/sha256-prf.o +SOBJS += ../src/crypto/sha384-prf.o +SOBJS += ../src/crypto/sha512-prf.o +SOBJS += ../src/crypto/dh_groups.o +SOBJS += ../src/crypto/sha256-kdf.o +SOBJS += ../src/crypto/sha384-kdf.o +SOBJS += ../src/crypto/sha512-kdf.o + nt_password_hash: $(NOBJS) $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n) @$(E) " LD " $@ @@ -1353,6 +1382,10 @@ hlr_auc_gw: $(HOBJS) $(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h) @$(E) " LD " $@ +sae_pk_gen: $(SOBJS) + $(Q)$(CC) $(LDFLAGS) -o sae_pk_gen $(SOBJS) $(LIBS_s) + @$(E) " LD " $@ + lcov-html: lcov -c -d .. > lcov.info genhtml lcov.info --output-directory lcov-html @@ -1360,6 +1393,7 @@ lcov-html: clean: $(MAKE) -C ../src clean rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw + rm -f sae_pk_gen rm -f *.d *.gcno *.gcda *.gcov rm -f lcov.info rm -rf lcov-html diff --git a/hostapd/sae_pk_gen.c b/hostapd/sae_pk_gen.c new file mode 100644 index 000000000..6570e9ed9 --- /dev/null +++ b/hostapd/sae_pk_gen.c @@ -0,0 +1,160 @@ +/* + * SAE-PK password/modifier generator + * Copyright (c) 2020, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/base64.h" +#include "crypto/crypto.h" +#include "common/sae.h" + + +int main(int argc, char *argv[]) +{ + char *der = NULL; + size_t der_len; + struct crypto_ec_key *key = NULL; + struct wpabuf *pub = NULL; + u8 *data = NULL, *m; + size_t data_len; + char *b64 = NULL, *pw = NULL, *pos, *src; + int sec, j; + int ret = -1; + u8 hash[SAE_MAX_HASH_LEN], fingerprint[SAE_MAX_HASH_LEN]; + int group; + size_t hash_len; + unsigned long long i, expected; + char m_hex[2 * SAE_PK_M_LEN + 1]; + + wpa_debug_level = MSG_INFO; + if (os_program_init() < 0) + goto fail; + + if (argc != 4) { + fprintf(stderr, + "usage: sae_pk_gen \n"); + goto fail; + } + + sec = atoi(argv[2]); + if (sec < 2 || sec > 5) { + fprintf(stderr, "Invalid Sec value (allowed range: 2..5)\n"); + goto fail; + } + expected = 1; + for (j = 0; j < sec; j++) + expected *= 256; + + der = os_readfile(argv[1], &der_len); + if (!der) { + fprintf(stderr, "Could not read %s: %s\n", + argv[1], strerror(errno)); + goto fail; + } + + key = crypto_ec_key_parse_priv((u8 *) der, der_len); + if (!key) { + fprintf(stderr, "Could not parse ECPrivateKey\n"); + goto fail; + } + + pub = crypto_ec_key_get_subject_public_key(key); + if (!pub) { + fprintf(stderr, "Failed to build SubjectPublicKey\n"); + goto fail; + } + + group = crypto_ec_key_group(key); + switch (group) { + case 19: + hash_len = 32; + break; + case 20: + hash_len = 48; + break; + case 21: + hash_len = 64; + break; + default: + fprintf(stderr, "Unsupported private key group\n"); + goto fail; + } + + data_len = os_strlen(argv[3]) + SAE_PK_M_LEN + wpabuf_len(pub); + data = os_malloc(data_len); + if (!data) { + fprintf(stderr, "No memory for data buffer\n"); + goto fail; + } + os_memcpy(data, argv[3], os_strlen(argv[3])); + m = data + os_strlen(argv[3]); + if (os_get_random(m, SAE_PK_M_LEN) < 0) { + fprintf(stderr, "Could not generate random Modifier M\n"); + goto fail; + } + os_memcpy(m + SAE_PK_M_LEN, wpabuf_head(pub), wpabuf_len(pub)); + + fprintf(stderr, "Searching for a suitable Modifier M value\n"); + for (i = 0;; i++) { + if (sae_hash(hash_len, data, data_len, hash) < 0) { + fprintf(stderr, "Hash failed\n"); + goto fail; + } + if (hash[0] == 0 && hash[1] == 0) { + if (sec == 2 || (hash[2] & 0xf0) == 0) + fprintf(stderr, "\r%3.2f%%", + 100.0 * (double) i / (double) expected); + for (j = 2; j < sec; j++) { + if (hash[j]) + break; + } + if (j == sec) + break; + } + inc_byte_array(m, SAE_PK_M_LEN); + } + + fprintf(stderr, "\nFound a valid hash in %llu iterations\n", i); + wpa_hexdump(MSG_DEBUG, "Valid hash", hash, hash_len); + fingerprint[0] = (sec - 2) << 6 | hash[sec] >> 2; + for (i = 1; i < hash_len - sec; i++) + fingerprint[i] = hash[sec + i - 1] << 6 | hash[sec + i] >> 2; + wpa_hexdump(MSG_DEBUG, "Fingerprint part for password", + fingerprint, hash_len - sec); + + b64 = base64_encode(der, der_len, NULL); + pw = sae_pk_base32_encode(fingerprint, (hash_len - sec) * 8 - 2); + if (!b64 || !pw || + wpa_snprintf_hex(m_hex, sizeof(m_hex), m, SAE_PK_M_LEN) < 0) + goto fail; + src = pos = b64; + while (*src) { + if (*src != '\n') + *pos++ = *src; + src++; + } + *pos = '\0'; + + printf("# SAE-PK password/M/private key for Sec=%d.\n", sec); + printf("# The password can be truncated from right to improve\n"); + printf("# usability at the cost of security.\n"); + printf("sae_password=%s|pk=%s:%s\n", pw, m_hex, b64); + + ret = 0; +fail: + os_free(der); + wpabuf_free(pub); + crypto_ec_key_deinit(key); + os_free(data); + os_free(b64); + os_free(pw); + + os_program_deinit(); + + return ret; +}