diff options
author | James Muir <muir.james.a@gmail.com> | 2022-10-15 22:23:39 -0400 |
---|---|---|
committer | Hugo Landau <hlandau@openssl.org> | 2023-01-13 07:09:09 +0000 |
commit | 836080a89a1f5e45dac4e0df76b9270587f65d5b (patch) | |
tree | 72abd378cbb77d89d85c3d1dcb14c7e92d0653a0 /providers/implementations | |
parent | 9fa553247874728cee8ca0ece9aaed476eb0f303 (diff) |
Support all five EdDSA instances from RFC 8032
Fixes #6277
Description:
Make each of the five EdDSA instances defined in RFC 8032 -- Ed25519,
Ed25519ctx, Ed25519ph, Ed448, Ed448ph -- available via the EVP APIs.
The desired EdDSA instance is specified via an OSSL_PARAM.
All instances, except for Ed25519, allow context strings as input.
Context strings are passed via an OSSL_PARAM. For Ed25519ctx, the
context string must be nonempty.
Ed25519, Ed25519ctx, Ed448 are PureEdDSA instances, which means that
the full message (not a digest) must be passed to sign and verify
operations.
Ed25519ph, Ed448ph are HashEdDSA instances, which means that the input
message is hashed before sign and verify.
Testing:
All 21 test vectors from RFC 8032 have been added to evppkey_ecx.txt
(thanks to Shane Lontis for showing how to do that). Those 21 test
vectors are exercised by evp_test.c and cover all five instances.
Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/19705)
Diffstat (limited to 'providers/implementations')
-rw-r--r-- | providers/implementations/signature/eddsa_sig.c | 311 |
1 files changed, 289 insertions, 22 deletions
diff --git a/providers/implementations/signature/eddsa_sig.c b/providers/implementations/signature/eddsa_sig.c index f678e64cf8..e3d5c5a7c8 100644 --- a/providers/implementations/signature/eddsa_sig.c +++ b/providers/implementations/signature/eddsa_sig.c @@ -43,6 +43,24 @@ static int s390x_ed448_digestverify(const ECX_KEY *edkey, #endif /* S390X_EC_ASM */ +enum ID_EdDSA_INSTANCE { + ID_NOT_SET = 0, + ID_Ed25519, + ID_Ed25519ctx, + ID_Ed25519ph, + ID_Ed448, + ID_Ed448ph +}; + +#define SN_Ed25519 "Ed25519" +#define SN_Ed25519ph "Ed25519ph" +#define SN_Ed25519ctx "Ed25519ctx" +#define SN_Ed448 "Ed448" +#define SN_Ed448ph "Ed448ph" + +#define EDDSA_MAX_CONTEXT_STRING_LEN 255 +#define EDDSA_PREHASH_OUTPUT_LEN 64 + static OSSL_FUNC_signature_newctx_fn eddsa_newctx; static OSSL_FUNC_signature_digest_sign_init_fn eddsa_digest_signverify_init; static OSSL_FUNC_signature_digest_sign_fn ed25519_digest_sign; @@ -53,6 +71,55 @@ static OSSL_FUNC_signature_freectx_fn eddsa_freectx; static OSSL_FUNC_signature_dupctx_fn eddsa_dupctx; static OSSL_FUNC_signature_get_ctx_params_fn eddsa_get_ctx_params; static OSSL_FUNC_signature_gettable_ctx_params_fn eddsa_gettable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn eddsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn eddsa_settable_ctx_params; + +/* there are five EdDSA instances: + + Ed25519 + Ed25519ph + Ed25519ctx + Ed448 + Ed448ph + + Quoting from RFC 8032, Section 5.1: + + For Ed25519, dom2(f,c) is the empty string. The phflag value is + irrelevant. The context (if present at all) MUST be empty. This + causes the scheme to be one and the same with the Ed25519 scheme + published earlier. + + For Ed25519ctx, phflag=0. The context input SHOULD NOT be empty. + + For Ed25519ph, phflag=1 and PH is SHA512 instead. That is, the input + is hashed using SHA-512 before signing with Ed25519. + + Quoting from RFC 8032, Section 5.2: + + Ed448ph is the same but with PH being SHAKE256(x, 64) and phflag + being 1, i.e., the input is hashed before signing with Ed448 with a + hash constant modified. + + Value of context is set by signer and verifier (maximum of 255 + octets; the default is empty string) and has to match octet by octet + for verification to be successful. + + Quoting from RFC 8032, Section 2: + + dom2(x, y) The blank octet string when signing or verifying + Ed25519. Otherwise, the octet string: "SigEd25519 no + Ed25519 collisions" || octet(x) || octet(OLEN(y)) || + y, where x is in range 0-255 and y is an octet string + of at most 255 octets. "SigEd25519 no Ed25519 + collisions" is in ASCII (32 octets). + + dom4(x, y) The octet string "SigEd448" || octet(x) || + octet(OLEN(y)) || y, where x is in range 0-255 and y + is an octet string of at most 255 octets. "SigEd448" + is in ASCII (8 octets). + + Note above that x is the pre-hash flag, and y is the context string. +*/ typedef struct { OSSL_LIB_CTX *libctx; @@ -62,6 +129,19 @@ typedef struct { unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; unsigned char *aid; size_t aid_len; + + /* id indicating the EdDSA instance */ + int instance_id; + + unsigned int dom2_flag : 1; + unsigned int prehash_flag : 1; + + /* indicates that a non-empty context string is required, as in Ed25519ctx */ + unsigned int context_string_flag : 1; + + unsigned char context_string[EDDSA_MAX_CONTEXT_STRING_LEN]; + size_t context_string_len; + } PROV_EDDSA_CTX; static void *eddsa_newctx(void *provctx, const char *propq_unused) @@ -82,7 +162,7 @@ static void *eddsa_newctx(void *provctx, const char *propq_unused) static int eddsa_digest_signverify_init(void *vpeddsactx, const char *mdname, void *vedkey, - ossl_unused const OSSL_PARAM params[]) + const OSSL_PARAM params[]) { PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; ECX_KEY *edkey = (ECX_KEY *)vedkey; @@ -99,8 +179,7 @@ static int eddsa_digest_signverify_init(void *vpeddsactx, const char *mdname, if (edkey == NULL) { if (peddsactx->key != NULL) - /* there is nothing to do on reinit */ - return 1; + return eddsa_set_ctx_params(peddsactx, params); ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); return 0; } @@ -110,6 +189,11 @@ static int eddsa_digest_signverify_init(void *vpeddsactx, const char *mdname, return 0; } + peddsactx->dom2_flag = 0; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 0; + peddsactx->context_string_len = 0; + /* * We do not care about DER writing errors. * All it really means is that for some reason, there's no @@ -122,9 +206,11 @@ static int eddsa_digest_signverify_init(void *vpeddsactx, const char *mdname, switch (edkey->type) { case ECX_KEY_TYPE_ED25519: ret = ret && ossl_DER_w_algorithmIdentifier_ED25519(&pkt, -1, edkey); + peddsactx->instance_id = ID_Ed25519; break; case ECX_KEY_TYPE_ED448: ret = ret && ossl_DER_w_algorithmIdentifier_ED448(&pkt, -1, edkey); + peddsactx->instance_id = ID_Ed448; break; default: /* Should never happen */ @@ -140,6 +226,9 @@ static int eddsa_digest_signverify_init(void *vpeddsactx, const char *mdname, peddsactx->key = edkey; + if (!eddsa_set_ctx_params(peddsactx, params)) + return 0; + return 1; } @@ -149,6 +238,8 @@ int ed25519_digest_sign(void *vpeddsactx, unsigned char *sigret, { PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EVP_MAX_MD_SIZE]; + size_t mdlen; if (!ossl_prov_is_running()) return 0; @@ -166,17 +257,33 @@ int ed25519_digest_sign(void *vpeddsactx, unsigned char *sigret, return 0; } #ifdef S390X_EC_ASM - if (S390X_CAN_SIGN(ED25519)) { - if (s390x_ed25519_digestsign(edkey, sigret, tbs, tbslen) == 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); - return 0; - } - *siglen = ED25519_SIGSIZE; - return 1; + /* s390x_ed25519_digestsign() does not yet support dom2 or context-strings. + fall back to non-accelerated sign if those options are set. */ + if (S390X_CAN_SIGN(ED25519) + && !peddsactx->dom2_flag + && !peddsactx->context_string_flag + && peddsactx->context_string_len == 0) { + if (s390x_ed25519_digestsign(edkey, sigret, tbs, tbslen) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); + return 0; + } + *siglen = ED25519_SIGSIZE; + return 1; } #endif /* S390X_EC_ASM */ + + if (peddsactx->prehash_flag) { + if (!EVP_Q_digest(peddsactx->libctx, SN_sha512, NULL, tbs, tbslen, md, &mdlen) + || mdlen != EDDSA_PREHASH_OUTPUT_LEN) + return 0; + tbs = md; + tbslen = mdlen; + } + if (ossl_ed25519_sign(sigret, tbs, tbslen, edkey->pubkey, edkey->privkey, - peddsactx->libctx, NULL) == 0) { + peddsactx->dom2_flag, peddsactx->prehash_flag, peddsactx->context_string_flag, + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->libctx, NULL) == 0) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); return 0; } @@ -184,12 +291,41 @@ int ed25519_digest_sign(void *vpeddsactx, unsigned char *sigret, return 1; } +/* EVP_Q_digest() does not allow variable output length for XOFs, + so we use this function */ +static int ed448_shake256(OSSL_LIB_CTX *libctx, + const char *propq, + const uint8_t *in, size_t inlen, + uint8_t *out, size_t outlen) +{ + int ret = 0; + EVP_MD_CTX *hash_ctx = EVP_MD_CTX_new(); + EVP_MD *shake256 = EVP_MD_fetch(libctx, SN_shake256, propq); + + if (hash_ctx == NULL || shake256 == NULL) + goto err; + + if (!EVP_DigestInit_ex(hash_ctx, shake256, NULL) + || !EVP_DigestUpdate(hash_ctx, in, inlen) + || !EVP_DigestFinalXOF(hash_ctx, out, outlen)) + goto err; + + ret = 1; + + err: + EVP_MD_CTX_free(hash_ctx); + EVP_MD_free(shake256); + return ret; +} + int ed448_digest_sign(void *vpeddsactx, unsigned char *sigret, size_t *siglen, size_t sigsize, const unsigned char *tbs, size_t tbslen) { PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EDDSA_PREHASH_OUTPUT_LEN]; + size_t mdlen = sizeof(md); if (!ossl_prov_is_running()) return 0; @@ -207,17 +343,30 @@ int ed448_digest_sign(void *vpeddsactx, unsigned char *sigret, return 0; } #ifdef S390X_EC_ASM - if (S390X_CAN_SIGN(ED448)) { + /* s390x_ed448_digestsign() does not yet support context-strings. + fall back to non-accelerated sign if a context-string is provided. */ + if (S390X_CAN_SIGN(ED448) + && peddsactx->context_string_len == 0) { if (s390x_ed448_digestsign(edkey, sigret, tbs, tbslen) == 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); - return 0; - } - *siglen = ED448_SIGSIZE; - return 1; + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); + return 0; + } + *siglen = ED448_SIGSIZE; + return 1; } #endif /* S390X_EC_ASM */ - if (ossl_ed448_sign(peddsactx->libctx, sigret, tbs, tbslen, edkey->pubkey, - edkey->privkey, NULL, 0, edkey->propq) == 0) { + + if (peddsactx->prehash_flag) { + if (!ed448_shake256(peddsactx->libctx, NULL, tbs, tbslen, md, mdlen)) + return 0; + tbs = md; + tbslen = mdlen; + } + + if (ossl_ed448_sign(peddsactx->libctx, sigret, tbs, tbslen, + edkey->pubkey, edkey->privkey, + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->prehash_flag, edkey->propq) == 0) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); return 0; } @@ -231,16 +380,34 @@ int ed25519_digest_verify(void *vpeddsactx, const unsigned char *sig, { PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EVP_MAX_MD_SIZE]; + size_t mdlen; if (!ossl_prov_is_running() || siglen != ED25519_SIGSIZE) return 0; #ifdef S390X_EC_ASM - if (S390X_CAN_SIGN(ED25519)) + /* s390x_ed25519_digestverify() does not yet support dom2 or context-strings. + fall back to non-accelerated verify if those options are set. */ + if (S390X_CAN_SIGN(ED25519) + && !peddsactx->dom2_flag + && !peddsactx->context_string_flag + && peddsactx->context_string_len == 0) { return s390x_ed25519_digestverify(edkey, sig, tbs, tbslen); + } #endif /* S390X_EC_ASM */ + if (peddsactx->prehash_flag) { + if (!EVP_Q_digest(peddsactx->libctx, SN_sha512, NULL, tbs, tbslen, md, &mdlen) + || mdlen != EDDSA_PREHASH_OUTPUT_LEN) + return 0; + tbs = md; + tbslen = mdlen; + } + return ossl_ed25519_verify(tbs, tbslen, sig, edkey->pubkey, + peddsactx->dom2_flag, peddsactx->prehash_flag, peddsactx->context_string_flag, + peddsactx->context_string, peddsactx->context_string_len, peddsactx->libctx, edkey->propq); } @@ -250,17 +417,31 @@ int ed448_digest_verify(void *vpeddsactx, const unsigned char *sig, { PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EDDSA_PREHASH_OUTPUT_LEN]; + size_t mdlen = sizeof(md); if (!ossl_prov_is_running() || siglen != ED448_SIGSIZE) return 0; #ifdef S390X_EC_ASM - if (S390X_CAN_SIGN(ED448)) + /* s390x_ed448_digestverify() does not yet support context-strings. + fall back to non-accelerated verify if a context-string is provided. */ + if (S390X_CAN_SIGN(ED448) + && peddsactx->context_string_len == 0) { return s390x_ed448_digestverify(edkey, sig, tbs, tbslen); + } #endif /* S390X_EC_ASM */ + if (peddsactx->prehash_flag) { + if (!ed448_shake256(peddsactx->libctx, NULL, tbs, tbslen, md, mdlen)) + return 0; + tbs = md; + tbslen = mdlen; + } + return ossl_ed448_verify(peddsactx->libctx, tbs, tbslen, sig, edkey->pubkey, - NULL, 0, edkey->propq); + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->prehash_flag, edkey->propq); } static void eddsa_freectx(void *vpeddsactx) @@ -317,6 +498,8 @@ static int eddsa_get_ctx_params(void *vpeddsactx, OSSL_PARAM *params) static const OSSL_PARAM known_gettable_ctx_params[] = { OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), OSSL_PARAM_END }; @@ -326,6 +509,84 @@ static const OSSL_PARAM *eddsa_gettable_ctx_params(ossl_unused void *vpeddsactx, return known_gettable_ctx_params; } +static int eddsa_set_ctx_params(void *vpeddsactx, const OSSL_PARAM params[]) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + const OSSL_PARAM *p; + + if (peddsactx == NULL) + return 0; + if (params == NULL) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_INSTANCE); + if (p != NULL) { + char instance_name[OSSL_MAX_NAME_SIZE] = ""; + char *pinstance_name = instance_name; + + if (!OSSL_PARAM_get_utf8_string(p, &pinstance_name, sizeof(instance_name))) + return 0; + + if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519) == 0) { + peddsactx->instance_id = ID_Ed25519; + if (peddsactx->key->type != ECX_KEY_TYPE_ED25519) return 0; + peddsactx->dom2_flag = 0; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 0; + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519ctx) == 0) { + peddsactx->instance_id = ID_Ed25519ctx; + if (peddsactx->key->type != ECX_KEY_TYPE_ED25519) return 0; + peddsactx->dom2_flag = 1; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 1; + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519ph) == 0) { + peddsactx->instance_id = ID_Ed25519ph; + if (peddsactx->key->type != ECX_KEY_TYPE_ED25519) return 0; + peddsactx->dom2_flag = 1; + peddsactx->prehash_flag = 1; + peddsactx->context_string_flag = 0; + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed448) == 0) { + peddsactx->instance_id = ID_Ed448; + if (peddsactx->key->type != ECX_KEY_TYPE_ED448) return 0; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 0; + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed448ph) == 0) { + peddsactx->instance_id = ID_Ed448ph; + if (peddsactx->key->type != ECX_KEY_TYPE_ED448) return 0; + peddsactx->prehash_flag = 1; + peddsactx->context_string_flag = 0; + } else { + /* we did not recognize the instance */ + return 0; + } + + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p != NULL) { + void *vp_context_string = peddsactx->context_string; + + if (!OSSL_PARAM_get_octet_string(p, &vp_context_string, sizeof(peddsactx->context_string), &(peddsactx->context_string_len))) { + peddsactx->context_string_len = 0; + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *eddsa_settable_ctx_params(ossl_unused void *vpeddsactx, + ossl_unused void *provctx) +{ + return settable_ctx_params; +} + const OSSL_DISPATCH ossl_ed25519_signature_functions[] = { { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))eddsa_newctx }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, @@ -341,6 +602,9 @@ const OSSL_DISPATCH ossl_ed25519_signature_functions[] = { { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))eddsa_get_ctx_params }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void (*)(void))eddsa_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))eddsa_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))eddsa_settable_ctx_params }, { 0, NULL } }; @@ -359,6 +623,9 @@ const OSSL_DISPATCH ossl_ed448_signature_functions[] = { { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))eddsa_get_ctx_params }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void (*)(void))eddsa_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))eddsa_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))eddsa_settable_ctx_params }, { 0, NULL } }; |