From c96399e296d9c280115d2ed9c129399c61b8edfc Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Wed, 25 Sep 2019 08:56:14 +0200 Subject: [PATCH] Adapt EVP_CIPHER_{param_to_asn1,asn1_to_param} for use with provider. So far, these two funtions have depended on legacy EVP_CIPHER implementations to be able to do their work. This change adapts them to work with provided implementations as well, in one of two possible ways: 1. If the implementation's set_asn1_parameters or get_asn1_parameters function pointers are non-NULL, this is a legacy implementation, and that function is called. 2. Otherwise, if the cipher doesn't have EVP_CIPH_FLAG_CUSTOM_ASN1 set, the default AlgorithmIdentifier parameter code in libcrypto is executed. 3. Otherwise, if the cipher is a provided implementation, the ASN1 type structure is converted to a DER blob which is then passed to the implementation as a parameter (param_to_asn1) or the DER blob is retrieved from the implementation as a parameter and converted locally to a ASN1_TYPE (asn1_to_param). With this, the old flag EVP_CIPH_FLAG_DEFAULT_ASN1 has become irrelevant and is simply ignored. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/10008) --- crypto/evp/evp_lib.c | 128 +++++++++++++++++++++++++---------- doc/man7/provider-cipher.pod | 7 ++ include/openssl/core_names.h | 2 + include/openssl/evp.h | 7 +- 4 files changed, 105 insertions(+), 39 deletions(-) diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c index 6a3ad8553b..5c935075dc 100644 --- a/crypto/evp/evp_lib.c +++ b/crypto/evp/evp_lib.c @@ -21,29 +21,31 @@ #if !defined(FIPS_MODE) int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type) { - int ret; + int ret = -1; /* Assume the worst */ const EVP_CIPHER *cipher = c->cipher; - if (cipher->prov != NULL) { - /* - * The cipher has come from a provider and won't have the default flags. - * Find the implicit form so we can check the flags. - * TODO(3.0): This won't work for 3rd party ciphers we know nothing about - * We'll need to think of something else for those. - */ - cipher = EVP_get_cipherbynid(cipher->nid); - if (cipher == NULL) { - EVPerr(EVP_F_EVP_CIPHER_PARAM_TO_ASN1, ASN1_R_UNSUPPORTED_CIPHER); - return -1; - } - } - - if (cipher->set_asn1_parameters != NULL) + /* + * For legacy implementations, we detect custom AlgorithmIdentifier + * parameter handling by checking if the function pointer + * cipher->set_asn1_parameters is set. We know that this pointer + * is NULL for provided implementations. + * + * Otherwise, for any implementation, we check the flag + * EVP_CIPH_FLAG_CUSTOM_ASN1. If it isn't set, we apply + * default AI parameter extraction. + * + * Otherwise, for provided implementations, we convert |type| to + * a DER encoded blob and pass to the implementation in OSSL_PARAM + * form. + * + * If none of the above applies, this operation is unsupported. + */ + if (cipher->set_asn1_parameters != NULL) { ret = cipher->set_asn1_parameters(c, type); - else if (cipher->flags & EVP_CIPH_FLAG_DEFAULT_ASN1) { + } else if ((EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_CUSTOM_ASN1) == 0) { switch (EVP_CIPHER_mode(cipher)) { case EVP_CIPH_WRAP_MODE: - if (EVP_CIPHER_nid(cipher) == NID_id_smime_alg_CMS3DESwrap) + if (EVP_CIPHER_is_a(cipher, SN_id_smime_alg_CMS3DESwrap)) ASN1_TYPE_set(type, V_ASN1_NULL, NULL); ret = 1; break; @@ -58,8 +60,40 @@ int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type) default: ret = EVP_CIPHER_set_asn1_iv(c, type); } - } else - ret = -1; + } else if (cipher->prov != NULL) { + OSSL_PARAM params[3], *p = params; + unsigned char *der = NULL, *derp; + + /* + * We make two passes, the first to get the appropriate buffer size, + * and the second to get the actual value. + */ + *p++ = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_ALG_ID, + NULL, 0); + *p = OSSL_PARAM_construct_end(); + + if (!EVP_CIPHER_CTX_get_params(c, params)) + goto err; + + /* ... but, we should get a return size too! */ + if (params[0].return_size != 0 + && (der = OPENSSL_malloc(params[0].return_size)) != NULL) { + params[0].data = der; + params[0].data_size = params[0].return_size; + params[0].return_size = 0; + derp = der; + if (EVP_CIPHER_CTX_get_params(c, params) + && d2i_ASN1_TYPE(&type, (const unsigned char **)&derp, + params[0].return_size) != NULL) { + ret = 1; + } + OPENSSL_free(der); + } + } else { + ret = -2; + } + + err: if (ret == -2) EVPerr(EVP_F_EVP_CIPHER_PARAM_TO_ASN1, ASN1_R_UNSUPPORTED_CIPHER); else if (ret <= 0) @@ -71,24 +105,29 @@ int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type) int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type) { - int ret; + int ret = -1; /* Assume the worst */ const EVP_CIPHER *cipher = c->cipher; - if (cipher->prov != NULL) { - /* - * The cipher has come from a provider and won't have the default flags. - * Find the implicit form so we can check the flags. - */ - cipher = EVP_get_cipherbynid(cipher->nid); - if (cipher == NULL) - return -1; - } - - if (cipher->get_asn1_parameters != NULL) + /* + * For legacy implementations, we detect custom AlgorithmIdentifier + * parameter handling by checking if there the function pointer + * cipher->get_asn1_parameters is set. We know that this pointer + * is NULL for provided implementations. + * + * Otherwise, for any implementation, we check the flag + * EVP_CIPH_FLAG_CUSTOM_ASN1. If it isn't set, we apply + * default AI parameter creation. + * + * Otherwise, for provided implementations, we get the AI parameter + * in DER encoded form from the implementation by requesting the + * appropriate OSSL_PARAM and converting the result to a ASN1_TYPE. + * + * If none of the above applies, this operation is unsupported. + */ + if (cipher->get_asn1_parameters != NULL) { ret = cipher->get_asn1_parameters(c, type); - else if (cipher->flags & EVP_CIPH_FLAG_DEFAULT_ASN1) { + } else if ((EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_CUSTOM_ASN1) == 0) { switch (EVP_CIPHER_mode(cipher)) { - case EVP_CIPH_WRAP_MODE: ret = 1; break; @@ -102,10 +141,25 @@ int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type) default: ret = EVP_CIPHER_get_asn1_iv(c, type); - break; } - } else - ret = -1; + } else if (cipher->prov != NULL) { + OSSL_PARAM params[3], *p = params; + unsigned char *der = NULL; + int derl = -1; + + if ((derl = i2d_ASN1_TYPE(type, &der)) >= 0) { + *p++ = + OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_ALG_ID, + der, (size_t)derl); + *p = OSSL_PARAM_construct_end(); + if (EVP_CIPHER_CTX_set_params(c, params)) + ret = 1; + OPENSSL_free(der); + } + } else { + ret = -2; + } + if (ret == -2) EVPerr(EVP_F_EVP_CIPHER_ASN1_TO_PARAM, EVP_R_UNSUPPORTED_CIPHER); else if (ret <= 0) diff --git a/doc/man7/provider-cipher.pod b/doc/man7/provider-cipher.pod index c8377d6835..d5766f47a5 100644 --- a/doc/man7/provider-cipher.pod +++ b/doc/man7/provider-cipher.pod @@ -321,6 +321,13 @@ Gets a implementation specific randomly generated key for the associated cipher ctx. This is currently only supported by 3DES (which sets the key to odd parity). +=item "alg_id_param" (B) (octet string) + +Used to pass the DER encoded AlgorithmIdentifier parameter to or from +the cipher implementation. Functions like L +and L use this parameter for any implementation +that has the flag B set. + =back =head1 RETURN VALUES diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h index 6656a7fb43..4659d1f08d 100644 --- a/include/openssl/core_names.h +++ b/include/openssl/core_names.h @@ -66,6 +66,8 @@ extern "C" { #define OSSL_CIPHER_PARAM_AEAD_IVLEN OSSL_CIPHER_PARAM_IVLEN #define OSSL_CIPHER_PARAM_AEAD_TAGLEN "taglen" /* size_t */ #define OSSL_CIPHER_PARAM_RANDOM_KEY "randkey" /* octet_string */ +/* For passing the AlgorithmIdentifier parameter in DER form */ +#define OSSL_CIPHER_PARAM_ALG_ID "alg_id_param" /* octet_string */ /* digest parameters */ #define OSSL_DIGEST_PARAM_XOFLEN "xoflen" /* size_t */ diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 407162f3f0..fd9855d380 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -275,8 +275,9 @@ int (*EVP_CIPHER_meth_get_ctrl(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *, # define EVP_CIPH_CUSTOM_COPY 0x400 /* Don't use standard iv length function */ # define EVP_CIPH_CUSTOM_IV_LENGTH 0x800 -/* Allow use default ASN1 get/set iv */ -# define EVP_CIPH_FLAG_DEFAULT_ASN1 0x1000 +/* Legacy and no longer relevant: Allow use default ASN1 get/set iv */ +# define EVP_CIPH_FLAG_DEFAULT_ASN1 0 +/* Free: 0x1000 */ /* Buffer length in bits not bytes: CFB1 mode only */ # define EVP_CIPH_FLAG_LENGTH_BITS 0x2000 /* Note if suitable for use in FIPS mode */ @@ -291,6 +292,8 @@ int (*EVP_CIPHER_meth_get_ctrl(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *, # define EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK 0x400000 /* Cipher can handle pipeline operations */ # define EVP_CIPH_FLAG_PIPELINE 0X800000 +/* For provider implementations that handle ASN1 get/set param themselves */ +# define EVP_CIPH_FLAG_CUSTOM_ASN1 0x1000000 /* * Cipher context flag to indicate we can handle wrap mode: if allowed in -- 2.25.1