From cdf05183b960efc79bd1025994fd6a48b8d15818 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 6 Dec 2021 13:03:53 -0800 Subject: [PATCH] dpp-util: Introduce dpp-util, and add crypto operations --- Makefile.am | 1 + src/dpp-util.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dpp-util.h | 46 +++++++++ 3 files changed, 307 insertions(+) create mode 100644 src/dpp-util.c create mode 100644 src/dpp-util.h diff --git a/Makefile.am b/Makefile.am index d93dd2c9..25269a0d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -248,6 +248,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \ src/band.h src/band.c \ src/sysfs.h src/sysfs.c \ src/offchannel.h src/offchannel.c \ + src/dpp-util.h src/dpp-util.c \ $(eap_sources) \ $(builtin_sources) diff --git a/src/dpp-util.c b/src/dpp-util.c new file mode 100644 index 00000000..8482b1ff --- /dev/null +++ b/src/dpp-util.c @@ -0,0 +1,260 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include + +#include "src/dpp-util.h" +#include "src/util.h" +#include "src/crypto.h" + +/* + * EasyConnect 2.0 Table 3. Key and Nonce Length Dependency on Prime Length + */ +static enum l_checksum_type dpp_sha_from_key_len(size_t len) +{ + if (len == 32) + return L_CHECKSUM_SHA256; + else if (len == 48) + return L_CHECKSUM_SHA384; + else if (len == 64) + return L_CHECKSUM_SHA512; + else + return L_CHECKSUM_NONE; + +} + +size_t dpp_nonce_len_from_key_len(size_t len) +{ + if (len == 32) + return 16; + else if (len == 48) + return 24; + else if (len == 64) + return 32; + else + return 0; +} + +/* + * 3.2.2 + * H() + */ +bool dpp_hash(enum l_checksum_type type, uint8_t *out, unsigned int num, ...) +{ + struct l_checksum *sha = l_checksum_new(type); + size_t hsize = l_checksum_digest_length(type); + unsigned int i; + + va_list va; + + va_start(va, num); + + for (i = 0; i < num; i++) { + void *data = va_arg(va, void *); + size_t len = va_arg(va, size_t); + + l_checksum_update(sha, data, len); + } + + va_end(va); + + l_checksum_get_digest(sha, out, hsize); + l_checksum_free(sha); + + return true; +} + +/* + * 3.2.2 + * + * HKDF is defined as: + * + * key = HKDF(salt, info, ikm) + * = HKDF-Expand(HKDF-Extract(salt, ikm), info, len) + * + * Note: A NULL 'salt' means a zero'ed buffer and 'salt_len' should still be + * set for this zero'ed buffer length. + */ +static bool dpp_hkdf(enum l_checksum_type sha, const void *salt, + size_t salt_len, const char *info, const void *ikm, + size_t ikm_len, void *out, size_t out_len) +{ + uint8_t tmp[64]; + uint8_t zero_salt[64] = { 0 }; + size_t hash_len = l_checksum_digest_length(sha); + + if (!salt) + salt = zero_salt; + + if (!hkdf_extract(sha, salt, salt_len, 1, tmp, + ikm, ikm_len)) + return false; + + return hkdf_expand(sha, tmp, hash_len, info, out, out_len); +} + +bool dpp_derive_r_auth(const void *i_nonce, const void *r_nonce, + size_t nonce_len, struct l_ecc_point *i_proto, + struct l_ecc_point *r_proto, + struct l_ecc_point *r_boot, + void *r_auth) +{ + uint64_t pix[L_ECC_MAX_DIGITS]; + uint64_t prx[L_ECC_MAX_DIGITS]; + uint64_t brx[L_ECC_MAX_DIGITS]; + size_t keys_len; + uint8_t zero = 0; + enum l_checksum_type type; + + keys_len = l_ecc_point_get_x(i_proto, pix, sizeof(pix)); + l_ecc_point_get_x(r_proto, prx, sizeof(prx)); + l_ecc_point_get_x(r_boot, brx, sizeof(brx)); + + type = dpp_sha_from_key_len(keys_len); + + /* + * R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [ BI.x | ] BR.x | 0) + */ + return dpp_hash(type, r_auth, 6, i_nonce, nonce_len, r_nonce, nonce_len, + pix, keys_len, prx, keys_len, brx, keys_len, &zero, 1); +} + +bool dpp_derive_i_auth(const void *r_nonce, const void *i_nonce, + size_t nonce_len, struct l_ecc_point *r_proto, + struct l_ecc_point *i_proto, + struct l_ecc_point *r_boot, void *i_auth) +{ + uint64_t prx[L_ECC_MAX_DIGITS]; + uint64_t pix[L_ECC_MAX_DIGITS]; + uint64_t brx[L_ECC_MAX_DIGITS]; + size_t keys_len; + uint8_t one = 1; + enum l_checksum_type type; + + keys_len = l_ecc_point_get_x(r_proto, prx, sizeof(prx)); + l_ecc_point_get_x(i_proto, pix, sizeof(pix)); + l_ecc_point_get_x(r_boot, brx, sizeof(brx)); + + type = dpp_sha_from_key_len(keys_len); + + /* + * I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [ BI.x | ] 1) + */ + return dpp_hash(type, i_auth, 6, r_nonce, nonce_len, i_nonce, nonce_len, + prx, keys_len, pix, keys_len, brx, keys_len, &one, 1); +} + +/* + * Derives key k1. This returns the intermediate secret M.x used in deriving + * key ke. + */ +struct l_ecc_scalar *dpp_derive_k1(const struct l_ecc_point *i_proto_public, + const struct l_ecc_scalar *boot_private, + void *k1) +{ + struct l_ecc_scalar *m; + uint64_t mx_bytes[L_ECC_MAX_DIGITS]; + ssize_t key_len; + enum l_checksum_type sha; + + if (!l_ecdh_generate_shared_secret(boot_private, i_proto_public, &m)) + return NULL; + + key_len = l_ecc_scalar_get_data(m, mx_bytes, sizeof(mx_bytes)); + + sha = dpp_sha_from_key_len(key_len); + + if (!dpp_hkdf(sha, NULL, key_len, "first intermediate key", mx_bytes, + key_len, k1, key_len)) { + l_ecc_scalar_free(m); + return NULL; + } + + return m; +} + +/* + * Derives key k2. This returns the intermediate secret N.x used in deriving + * key ke. + */ +struct l_ecc_scalar *dpp_derive_k2(const struct l_ecc_point *i_proto_public, + const struct l_ecc_scalar *proto_private, + void *k2) +{ + struct l_ecc_scalar *n; + uint64_t nx_bytes[L_ECC_MAX_DIGITS]; + ssize_t key_len; + enum l_checksum_type sha; + + if (!l_ecdh_generate_shared_secret(proto_private, i_proto_public, &n)) + return NULL; + + key_len = l_ecc_scalar_get_data(n, nx_bytes, sizeof(nx_bytes)); + + sha = dpp_sha_from_key_len(key_len); + + if (!dpp_hkdf(sha, NULL, key_len, "second intermediate key", nx_bytes, + key_len, k2, key_len)) { + l_ecc_scalar_free(n); + return NULL; + } + + return n; +} + +bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce, + struct l_ecc_scalar *m, struct l_ecc_scalar *n, + void *ke) +{ + uint8_t nonces[32 + 32]; + size_t nonce_len; + uint64_t mx_bytes[L_ECC_MAX_DIGITS]; + uint64_t nx_bytes[L_ECC_MAX_DIGITS]; + uint64_t bk[L_ECC_MAX_DIGITS]; + ssize_t key_len; + enum l_checksum_type sha; + + key_len = l_ecc_scalar_get_data(m, mx_bytes, sizeof(mx_bytes)); + l_ecc_scalar_get_data(n, nx_bytes, sizeof(nx_bytes)); + + nonce_len = dpp_nonce_len_from_key_len(key_len); + sha = dpp_sha_from_key_len(key_len); + + memcpy(nonces, i_nonce, nonce_len); + memcpy(nonces + nonce_len, r_nonce, nonce_len); + + /* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [ | L.x]) */ + if (!hkdf_extract(sha, nonces, nonce_len * 2, 2, bk, mx_bytes, + key_len, nx_bytes, key_len)) + return false; + + /* ke = HKDF-Expand(bk, "DPP Key", length) */ + return hkdf_expand(sha, bk, key_len, "DPP Key", ke, key_len); +} diff --git a/src/dpp-util.h b/src/dpp-util.h new file mode 100644 index 00000000..92fc38e3 --- /dev/null +++ b/src/dpp-util.h @@ -0,0 +1,46 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +struct l_ecc_point; +struct l_ecc_scalar; + +size_t dpp_nonce_len_from_key_len(size_t len); + +bool dpp_hash(enum l_checksum_type type, uint8_t *out, unsigned int num, ...); + +bool dpp_derive_r_auth(const void *i_nonce, const void *r_nonce, + size_t nonce_len, struct l_ecc_point *i_proto, + struct l_ecc_point *r_proto, + struct l_ecc_point *r_boot, + void *r_auth); +bool dpp_derive_i_auth(const void *r_nonce, const void *i_nonce, + size_t nonce_len, struct l_ecc_point *r_proto, + struct l_ecc_point *i_proto, + struct l_ecc_point *r_boot, void *i_auth); +struct l_ecc_scalar *dpp_derive_k1(const struct l_ecc_point *i_proto_public, + const struct l_ecc_scalar *boot_private, + void *k1); +struct l_ecc_scalar *dpp_derive_k2(const struct l_ecc_point *i_proto_public, + const struct l_ecc_scalar *proto_private, + void *k2); +bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce, + struct l_ecc_scalar *m, struct l_ecc_scalar *n, + void *ke);