[PATCH] DPP: Allow loading bootstrap keys using an OpenSSL engine

Andrew Beltrano anbeltra at microsoft.com
Tue Apr 6 17:03:48 BST 2021


Add ability to load a DPP bootstrap key-pair using an arbitrary OpenSSL
engine instead of requiring the private key to be specified explicitly.
The engine name, so path, and key identifier must be specified to
enable loading a key using an OpenSSL engine. The key identifier is
an engine-specific field used to identify the key to load.

Explicit private keys, if specified, take precedence over OpenSSL
engine-based keys.

Signed-off-by: Andrew Beltrano <anbeltra at microsoft.com>
---
Note that this patch depends on the patch-set titled 'Expose OpenSSL dynamic
engine loading globally'.

 hostapd/hostapd_cli.c    |   2 +-
 src/common/dpp.c         |  19 ++++++++
 src/common/dpp.h         |   9 ++++
 src/common/dpp_crypto.c  | 101 +++++++++++++++++++++++++++++++++++++++
 src/common/dpp_i.h       |   7 +++
 wpa_supplicant/wpa_cli.c |   2 +-
 6 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index eaa628ad0..62d948680 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1690,7 +1690,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
 	  "report a scanned DPP URI from a QR Code" },
 	{ "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
-	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] [key_id=..] [engine=..] [engine_path=..] = generate DPP bootstrap information" },
 	{ "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
 	  "*|<id> = remove DPP bootstrap information" },
 	{ "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 3c8c7682d..a7468ca91 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -180,6 +180,13 @@ void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
 	os_free(info->info);
 	os_free(info->chan);
 	os_free(info->pk);
+#ifndef OPENSSL_NO_ENGINE
+	os_free(info->key_id);
+	os_free(info->engine_id);
+	os_free(info->engine_path);
+	if (info->engine)
+		ENGINE_finish(info->engine);
+#endif /* OPENSSL_NO_ENGINE */
 	EVP_PKEY_free(info->pubkey);
 	str_clear_free(info->configurator_params);
 	os_free(info);
@@ -3893,6 +3900,11 @@ int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
 	info = get_param(cmd, " info=");
 	curve = get_param(cmd, " curve=");
 	key = get_param(cmd, " key=");
+#ifndef OPENSSL_NO_ENGINE
+	bi->key_id = get_param(cmd, " key_id=");
+	bi->engine_id = get_param(cmd, " engine=");
+	bi->engine_path = get_param(cmd, " engine_path=");
+#endif /* OPENSSL_NO_ENGINE */
 
 	if (key) {
 		privkey_len = os_strlen(key) / 2;
@@ -3901,6 +3913,13 @@ int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
 		    hexstr2bin(key, privkey, privkey_len) < 0)
 			goto fail;
 	}
+#ifndef OPENSSL_NO_ENGINE
+	else if (bi->key_id) {
+		bi->engine = dpp_load_engine(bi->engine_id, bi->engine_path);
+		if (!bi->engine)
+			goto fail;
+	}
+#endif /* OPENSSL_NO_ENGINE */
 
 	if (dpp_keygen(bi, curve, privkey, privkey_len) < 0 ||
 	    dpp_parse_uri_chan_list(bi, bi->chan) < 0 ||
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 65ee905a7..d5c062f82 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -12,6 +12,9 @@
 
 #ifdef CONFIG_DPP
 #include <openssl/x509.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */ 
 
 #include "utils/list.h"
 #include "common/wpa_common.h"
@@ -166,6 +169,12 @@ struct dpp_bootstrap_info {
 	int nfc_negotiated; /* whether this has been used in NFC negotiated
 			     * connection handover */
 	char *configurator_params;
+#ifndef OPENSSL_NO_ENGINE
+	char *key_id;
+	char *engine_id;
+	char *engine_path;
+	ENGINE *engine;
+#endif /* OPENSSL_NO_ENGINE */
 };
 
 #define PKEX_COUNTER_T_LIMIT 5
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index c75fc7871..f04ee9e0e 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -19,6 +19,9 @@
 #include "utils/json.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/crypto.h"
+#ifndef OPENSSL_NO_ENGINE
+#include "crypto/openssl_engine.h"
+#endif
 #include "crypto/random.h"
 #include "crypto/sha384.h"
 #include "crypto/sha512.h"
@@ -363,6 +366,100 @@ int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
 #endif /* CONFIG_DPP2 */
 
 
+#ifndef OPENSSL_NO_ENGINE
+static EVP_PKEY * dpp_load_keypair(const struct dpp_curve_params **curve,
+				  ENGINE *engine, const char *key_id)
+{
+	EVP_PKEY *pkey;
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	pkey = ENGINE_load_private_key(engine, key_id, NULL, NULL);
+	if (!pkey) {
+		wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id '%s' [%s]",
+			key_id, ERR_error_string(ERR_get_error(), NULL));
+		return NULL;
+	}
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey) {
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	group = EC_KEY_get0_group(eckey);
+	if (!group) {
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	nid = EC_GROUP_get_curve_name(group);
+	*curve = dpp_get_curve_nid(nid);
+	if (!*curve) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+			   nid);
+		EC_KEY_free(eckey);
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	EC_KEY_free(eckey);
+	return pkey;
+}
+
+
+static int dpp_openssl_engine_load_dynamic(const char *engine_id,
+			const char *engine_path)
+{
+	const char *pre_cmd[] = {
+		"SO_PATH", engine_path,
+		"ID", engine_id,
+		"LIST_ADD", "1",
+		"LOAD", NULL,
+		NULL, NULL
+	};
+	const char *post_cmd[] = {
+		NULL, NULL
+	};
+
+	if (!engine_id || !engine_path)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading %s Engine from %s",
+		   engine_id, engine_path);
+
+	return openssl_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
+}
+
+
+ENGINE * dpp_load_engine(const char *engine_id, const char *engine_path)
+{
+	if (dpp_openssl_engine_load_dynamic(engine_id, engine_path) < 0)
+		return NULL;
+
+	ENGINE *engine = ENGINE_by_id(engine_id);
+	if (!engine) {
+		wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
+			engine_id, ERR_error_string(ERR_get_error(), NULL));
+		return NULL;
+	}
+
+	if (ENGINE_init(engine) != 1) {
+		wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
+			"(engine: %s) [%s]", engine_id,
+			ERR_error_string(ERR_get_error(), NULL));
+		ENGINE_free(engine);
+		return NULL;
+	}
+
+	return engine;
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
 int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
 {
 	int num_bytes, offset;
@@ -730,6 +827,10 @@ int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 
 	if (privkey)
 		bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+#ifndef OPENSSL_NO_ENGINE
+	else if (bi->engine)
+		bi->pubkey = dpp_load_keypair(&bi->curve, bi->engine, bi->key_id);
+#endif /* OPENSSL_NO_ENGINE */
 	else
 		bi->pubkey = dpp_gen_keypair(bi->curve);
 	if (!bi->pubkey)
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index af12467a5..3e42b1a11 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -12,6 +12,10 @@
 
 #ifdef CONFIG_DPP
 
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
+
 struct dpp_global {
 	void *msg_ctx;
 	struct dl_list bootstrap; /* struct dpp_bootstrap_info */
@@ -139,6 +143,9 @@ char * dpp_sign_connector(struct dpp_configurator *conf,
 			  const struct wpabuf *dppcon);
 int dpp_test_gen_invalid_key(struct wpabuf *msg,
 			     const struct dpp_curve_params *curve);
+#ifndef OPENSSL_NO_ENGINE
+ENGINE * dpp_load_engine(const char *engine_id, const char *engine_path);
+#endif /* OPENSSL_NO_ENGINE */
 
 struct dpp_reconfig_id {
 	const EC_GROUP *group;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index fea7b85e0..11bb63dc3 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -3855,7 +3855,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
 	  "report a scanned DPP URI from a QR Code" },
 	{ "dpp_bootstrap_gen", wpa_cli_cmd_dpp_bootstrap_gen, NULL,
 	  cli_cmd_flag_sensitive,
-	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+	  "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] [key_id=..] [engine=..] [engine_path=..] = generate DPP bootstrap information" },
 	{ "dpp_bootstrap_remove", wpa_cli_cmd_dpp_bootstrap_remove, NULL,
 	  cli_cmd_flag_none,
 	  "*|<id> = remove DPP bootstrap information" },
-- 
2.23.3




More information about the Hostap mailing list