From patchwork Mon May 16 00:48:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 2462 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director11.mail.ord1d.rsapps.net ([172.30.191.6]) by backend41.mail.ord1d.rsapps.net with LMTP id ZYNdIy4sgmL0BgAAqwncew (envelope-from ) for ; Mon, 16 May 2022 06:49:18 -0400 Received: from proxy19.mail.ord1d.rsapps.net ([172.30.191.6]) by director11.mail.ord1d.rsapps.net with LMTP id oBVwAi8sgmKlawAAvGGmqA (envelope-from ) for ; Mon, 16 May 2022 06:49:19 -0400 Received: from smtp33.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy19.mail.ord1d.rsapps.net with LMTPS id IOVDAi8sgmIQIQAAyH2SIw (envelope-from ) for ; Mon, 16 May 2022 06:49:19 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp33.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: d5ba2900-d505-11ec-9884-525400041ef2-1-1 Received: from [216.105.38.7] ([216.105.38.7:58430] helo=lists.sourceforge.net) by smtp33.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 01/F7-00840-E2C22826; Mon, 16 May 2022 06:49:18 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.94.2) (envelope-from ) id 1nqYGz-0003cU-P4; Mon, 16 May 2022 10:48:20 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1nqYGy-0003cO-Jp for openvpn-devel@lists.sourceforge.net; Mon, 16 May 2022 10:48:19 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=HpNeDqwl5UJX85KVWVXV/0KDzpgINWiLauS6PQ1ot3U=; b=Nw0wlovO9qq+oi+/jnpMpzn66L G9w/9kl7BslPar/A/DEVH2KKjuiAGQXxqVDqApcLVqm9HlEZ9xqMqbNKDQMQGDtpzdYVsDc8ITXGM F9i1PQ+/6UaMRPwaCpt38xQzWjXLDgnmyVRvs/LgiCD7TfktJyNuTBSGzRNv7L0uTxZs=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=HpNeDqwl5UJX85KVWVXV/0KDzpgINWiLauS6PQ1ot3U=; b=kp/JXkippEpom1blFxZm6hxsD0 S+BBi3PR7RW4Q2snD7Gz1ktZvZxpT5A7JD0AaPG/x93FjMs99oBUsumLxPfSWR9DSGSs0SCdopcDr zGaxX3STfyQluJ6Qbua9sBYUJI5vPzDf14TlXj5qi/YEOS3rx/RFDdMG8vi/U8LrVXAg=; Received: from mail.blinkt.de ([192.26.174.232]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1nqYGv-00Gwkr-Ig for openvpn-devel@lists.sourceforge.net; Mon, 16 May 2022 10:48:19 +0000 Received: from kamera.blinkt.de ([2001:638:502:390:20c:29ff:fec8:535c]) by mail.blinkt.de with smtp (Exim 4.95 (FreeBSD)) (envelope-from ) id 1nqYGl-0004OM-I8 for openvpn-devel@lists.sourceforge.net; Mon, 16 May 2022 12:48:07 +0200 Received: (nullmailer pid 2568988 invoked by uid 10006); Mon, 16 May 2022 10:48:07 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Mon, 16 May 2022 12:48:07 +0200 Message-Id: <20220516104807.2568937-1-arne@rfc2549.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220510113055.1829260-1-arne@rfc2549.org> References: <20220510113055.1829260-1-arne@rfc2549.org> MIME-Version: 1.0 X-Spam-Report: Spam detection software, running on the system "util-spamd-2.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: OpenSSL's implementation of ED448 and ED25519 has a few idiosyncrasies. Instead of belonging to the elliptic curve type or to a common Edwards curve type, ED448 and ED25519 have each their own type. Also, OpenSSL expects signatures using these curves to be done with the EVP_DigestSign API instead of the EVP_Sign API but using md=NULL. Content analysis details: (0.2 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 SPF_NONE SPF: sender does not publish an SPF Record 0.2 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail domains are different -0.0 T_SCC_BODY_TEXT_LINE No description available. X-Headers-End: 1nqYGv-00Gwkr-Ig Subject: [Openvpn-devel] [PATCH v3] Implement ED448 and ED25519 support in xkey_provider X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox OpenSSL's implementation of ED448 and ED25519 has a few idiosyncrasies. Instead of belonging to the elliptic curve type or to a common Edwards curve type, ED448 and ED25519 have each their own type. Also, OpenSSL expects signatures using these curves to be done with the EVP_DigestSign API instead of the EVP_Sign API but using md=NULL. This has been tested using a "fake" external key that used a normal software key instead of a hardware implementation but that makes no difference from the perspective of xkey_provider/management interface. Patch v2: remove name functions from ed448/ed25519, ensure md is NULL for ed448/ed25519 and handle NULL/none better in general. Patch v3: do not pass NULL as string for the OSSL params. Acked-by: Selva Nair --- src/openvpn/xkey_common.h | 2 +- src/openvpn/xkey_helper.c | 7 +- src/openvpn/xkey_provider.c | 97 ++++++++++++++++++++++-- tests/unit_tests/openvpn/test_provider.c | 36 +++++++-- 4 files changed, 126 insertions(+), 16 deletions(-) diff --git a/src/openvpn/xkey_common.h b/src/openvpn/xkey_common.h index 35cbcf576..e0e5ed5b2 100644 --- a/src/openvpn/xkey_common.h +++ b/src/openvpn/xkey_common.h @@ -43,7 +43,7 @@ OSSL_provider_init_fn xkey_provider_init; #define XKEY_PROV_PROPS "provider=ovpn.xkey" /** - * Stuct to encapsulate signature algorithm parameters to pass + * Struct to encapsulate signature algorithm parameters to pass * to sign operation. */ typedef struct { diff --git a/src/openvpn/xkey_helper.c b/src/openvpn/xkey_helper.c index ecc7b1204..c1b1dfdbf 100644 --- a/src/openvpn/xkey_helper.c +++ b/src/openvpn/xkey_helper.c @@ -179,7 +179,8 @@ xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen, bool is_message = !strcmp(alg.op, "DigestSign"); /* tbs is message, not digest */ /* if management client cannot do digest -- we do it here */ - if (!strcmp(alg.op, "DigestSign") && !(flags & MF_EXTERNAL_KEY_DIGEST)) + if (!strcmp(alg.op, "DigestSign") && !(flags & MF_EXTERNAL_KEY_DIGEST) + && strcmp(alg.mdname, "none")) { dmsg(D_XKEY, "xkey_management_sign: computing digest"); if (xkey_digest(tbs, tbslen, buf, &buflen, alg.mdname)) @@ -206,6 +207,10 @@ xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen, openvpn_snprintf(alg_str, sizeof(alg_str), "ECDSA,hashalg=%s", alg.mdname); } } + else if (!strcmp(alg.keytype, "ED448") || !strcmp(alg.keytype, "ED25519")) + { + strncpynt(alg_str, alg.keytype, sizeof(alg_str)); + } /* else assume RSA key */ else if (!strcmp(alg.padmode, "pkcs1") && (flags & MF_EXTERNAL_KEY_PKCS1PAD)) { diff --git a/src/openvpn/xkey_provider.c b/src/openvpn/xkey_provider.c index 46e57e0fe..67000004e 100644 --- a/src/openvpn/xkey_provider.c +++ b/src/openvpn/xkey_provider.c @@ -99,12 +99,28 @@ typedef struct int refcount; /**< reference count */ } XKEY_KEYDATA; -static int -KEYTYPE(const XKEY_KEYDATA *key) +static inline const char * +get_keytype(const XKEY_KEYDATA *key) { - return key->pubkey ? EVP_PKEY_get_id(key->pubkey) : 0; + int keytype = key->pubkey ? EVP_PKEY_get_id(key->pubkey) : 0; + + switch (keytype) + { + case EVP_PKEY_RSA: + return "RSA"; + + case EVP_PKEY_ED448: + return "ED448"; + + case EVP_PKEY_ED25519: + return "ED25519"; + + default: + return "EC"; + } } + static int KEYSIZE(const XKEY_KEYDATA *key) { @@ -310,6 +326,22 @@ ec_keymgmt_import(void *keydata, int selection, const OSSL_PARAM params[]) return keymgmt_import(keydata, selection, params, "EC"); } +static int +ed448_keymgmt_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + xkey_dmsg(D_XKEY, "entry"); + + return keymgmt_import(keydata, selection, params, "ED448"); +} + +static int +ed25519_keymgmt_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + xkey_dmsg(D_XKEY, "entry"); + + return keymgmt_import(keydata, selection, params, "ED25519"); +} + /* This function has to exist for key import to work * though we do not support import of individual params * like n or e. We simply return an empty list here for @@ -449,7 +481,7 @@ keymgmt_import_helper(XKEY_KEYDATA *key, const OSSL_PARAM *params) ASSERT(pkey); int id = EVP_PKEY_get_id(pkey); - if (id != EVP_PKEY_RSA && id != EVP_PKEY_EC) + if (id != EVP_PKEY_RSA && id != EVP_PKEY_EC && id != EVP_PKEY_ED25519 && id != EVP_PKEY_ED448) { msg(M_WARN, "Error: xkey keymgmt_import: unknown key type (%d)", id); return 0; @@ -588,10 +620,43 @@ static const OSSL_DISPATCH ec_keymgmt_functions[] = { {0, NULL } }; +static const OSSL_DISPATCH ed448_keymgmt_functions[] = { + {OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))keymgmt_new}, + {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))keymgmt_free}, + {OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))keymgmt_load}, + {OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))keymgmt_has}, + {OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))keymgmt_match}, + {OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ed448_keymgmt_import}, + {OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))keymgmt_import_types}, + {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, + {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))keymgmt_get_params}, + {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))keymgmt_set_params}, + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ + {0, NULL } +}; + +static const OSSL_DISPATCH ed25519_keymgmt_functions[] = { + {OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))keymgmt_new}, + {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))keymgmt_free}, + {OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))keymgmt_load}, + {OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))keymgmt_has}, + {OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))keymgmt_match}, + {OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ed25519_keymgmt_import}, + {OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))keymgmt_import_types}, + {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, + {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))keymgmt_get_params}, + {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))keymgmt_set_params}, + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ + {0, NULL } +}; + + const OSSL_ALGORITHM keymgmts[] = { {"RSA:rsaEncryption", XKEY_PROV_PROPS, rsa_keymgmt_functions, "OpenVPN xkey RSA Key Manager"}, {"RSA-PSS:RSASSA-PSS", XKEY_PROV_PROPS, rsa_keymgmt_functions, "OpenVPN xkey RSA-PSS Key Manager"}, {"EC:id-ecPublicKey", XKEY_PROV_PROPS, ec_keymgmt_functions, "OpenVPN xkey EC Key Manager"}, + {"ED448", XKEY_PROV_PROPS, ed448_keymgmt_functions, "OpenVPN xkey ED448 Key Manager"}, + {"ED25519", XKEY_PROV_PROPS, ed25519_keymgmt_functions, "OpenVPN xkey ED25519 Key Manager"}, {NULL, NULL, NULL, NULL} }; @@ -649,6 +714,11 @@ static const char *saltlen_names[] = {"digest", "max", "auto", NULL}; static const char * xkey_mdname(const char *name) { + if (name == NULL) + { + return "none"; + } + int i = 0; int nid = EVP_MD_get_type(EVP_get_digestbyname(name)); @@ -835,7 +905,7 @@ signature_sign_init(void *ctx, void *provkey, const OSSL_PARAM params[]) } sctx->keydata = provkey; sctx->keydata->refcount++; /* we are keeping a copy */ - sctx->sigalg.keytype = KEYTYPE(sctx->keydata) == EVP_PKEY_RSA ? "RSA" : "EC"; + sctx->sigalg.keytype = get_keytype(sctx->keydata); signature_set_ctx_params(sctx, params); @@ -929,10 +999,21 @@ signature_digest_sign_init(void *ctx, const char *mdname, } sctx->keydata = provkey; /* used by digest_sign */ sctx->keydata->refcount++; - sctx->sigalg.keytype = KEYTYPE(sctx->keydata) == EVP_PKEY_RSA ? "RSA" : "EC"; + sctx->sigalg.keytype = get_keytype(sctx->keydata); signature_set_ctx_params(ctx, params); - if (mdname) + if (!strcmp(sctx->sigalg.keytype, "ED448") || !strcmp(sctx->sigalg.keytype, "ED25519")) + { + /* EdDSA requires NULL as digest for the DigestSign API instead + * of using the normal Sign API. Ensure it is actually NULL too */ + if (mdname != NULL) + { + msg(M_WARN, "xkey digest_sign_init: mdname must be NULL for ED448/ED25519."); + return 0; + } + sctx->sigalg.mdname = "none"; + } + else if (mdname) { sctx->sigalg.mdname = xkey_mdname(mdname); /* get a string literal pointer */ } @@ -1073,6 +1154,8 @@ static const OSSL_DISPATCH signature_functions[] = { const OSSL_ALGORITHM signatures[] = { {"RSA:rsaEncryption", XKEY_PROV_PROPS, signature_functions, "OpenVPN xkey RSA Signature"}, {"ECDSA", XKEY_PROV_PROPS, signature_functions, "OpenVPN xkey ECDSA Signature"}, + {"ED448", XKEY_PROV_PROPS, signature_functions, "OpenVPN xkey Ed448 Signature"}, + {"ED25519", XKEY_PROV_PROPS, signature_functions, "OpenVPN xkey Ed25519 Signature"}, {NULL, NULL, NULL, NULL} }; diff --git a/tests/unit_tests/openvpn/test_provider.c b/tests/unit_tests/openvpn/test_provider.c index 0b0952ee2..0045b61a6 100644 --- a/tests/unit_tests/openvpn/test_provider.c +++ b/tests/unit_tests/openvpn/test_provider.c @@ -66,7 +66,11 @@ static const char *const pubkey2 = "-----BEGIN PUBLIC KEY-----\n" "u95ff1JiUaJIkYNIkZA+hwIPFVH5aJcSCv3SPIeDS2VUAESNKHZJBQ==\n" "-----END PUBLIC KEY-----\n"; -static const char *pubkeys[] = {pubkey1, pubkey2}; +static const char *const pubkey3 = "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEA+q5xjF5hGyyqYZidJdz/0saEQabL3N4wIZJBxNGbgJE=\n" + "-----END PUBLIC KEY-----"; + +static const char *pubkeys[] = {pubkey1, pubkey2, pubkey3}; static const char *prov_name = "ovpn.xkey"; @@ -158,12 +162,17 @@ management_query_pk_sig(struct management *man, const char *b64_data, if (strstr(algorithm, "data=message")) { expected_tbs = test_msg_b64; - assert_non_null(strstr(algorithm, "hashalg=SHA256")); + /* ED25519 does not have a hash algorithm even though it goes via + * the DigestSign path (data=message) */ + if (!strstr(algorithm, "ED25519")) + { + assert_non_null(strstr(algorithm, "hashalg=SHA256")); + } } assert_string_equal(b64_data, expected_tbs); - /* We test using ECDSA or PSS with saltlen = digest */ - if (!strstr(algorithm, "ECDSA")) + /* We test using ED25519, ECDSA or PSS with saltlen = digest */ + if (!strstr(algorithm, "ECDSA") && !strstr(algorithm, "ED25519")) { assert_non_null(strstr(algorithm, "RSA_PKCS1_PSS_PADDING,hashalg=SHA256,saltlen=digest")); } @@ -228,6 +237,12 @@ digest_sign(EVP_PKEY *pkey) params[3] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, (char *)saltlen, 0); params[4] = OSSL_PARAM_construct_end(); } + else if (EVP_PKEY_get_id(pkey) == EVP_PKEY_ED25519) + { + mdname = NULL; + params[0] = OSSL_PARAM_construct_end(); + } + EVP_PKEY_CTX *pctx = NULL; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); @@ -328,13 +343,20 @@ xkey_sign(void *handle, unsigned char *sig, size_t *siglen, assert_memory_equal(tbs, test_digest, sizeof(test_digest)); } - /* For the test use sha256 and PSS padding for RSA */ - assert_int_equal(OBJ_sn2nid(s.mdname), NID_sha256); + /* For the test use sha256 and PSS padding for RSA and none for EDDSA */ + if (!strcmp(s.keytype,"ED25519")) + { + assert_string_equal(s.mdname, "none"); + } + else + { + assert_int_equal(OBJ_sn2nid(s.mdname), NID_sha256); + } if (!strcmp(s.keytype, "RSA")) { assert_string_equal(s.padmode, "pss"); /* we use PSS for the test */ } - else if (strcmp(s.keytype, "EC")) + else if (strcmp(s.keytype, "EC") && strcmp(s.keytype,"ED25519")) { fail_msg("Unknown keytype: %s", s.keytype); }