EVP: Downgrade keys rather than upgrade
authorRichard Levitte <levitte@openssl.org>
Sat, 21 Mar 2020 05:21:26 +0000 (06:21 +0100)
committerRichard Levitte <levitte@openssl.org>
Wed, 25 Mar 2020 16:01:10 +0000 (17:01 +0100)
Upgrading EVP_PKEYs from containing legacy keys to containing provider
side keys proved to be risky, with a number of unpleasant corner
cases, and with functions like EVP_PKEY_get0_DSA() failing
unexpectedly.

We therefore change course, and instead of upgrading legacy internal
keys to provider side internal keys, we downgrade provider side
internal keys to legacy ones.  To be able to do this, we add
|import_from| and make it a callback function designed for
evp_keymgmt_export().

This means that evp_pkey_upgrade_to_provider() is replaced with
evp_pkey_downgrade().

EVP_PKEY_copy_parameters() is the most deeply affected function of
this change.

Fixes #11366

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/11375)

CHANGES.md
crypto/err/openssl.txt
crypto/evp/evp_err.c
crypto/evp/p_lib.c
crypto/evp/pmeth_gn.c
include/crypto/asn1.h
include/crypto/evp.h
include/openssl/evperr.h
test/keymgmt_internal_test.c

index ba2569b..82c186a 100644 (file)
@@ -24,6 +24,12 @@ OpenSSL 3.0
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx] ###
 
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx] ###
 
+ * EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(), EVP_PKEY_get0_DH(), and
+   EVP_PKEY_get0_EC_KEY() can now handle EVP_PKEYs with provider side
+   internal keys, if they correspond to one of those built in types.
+
+   *Richard Levitte*
+
  * Added EVP_PKEY_set_type_by_keymgmt(), to initialise an EVP_PKEY to
    contain a provider side internal key.
 
  * Added EVP_PKEY_set_type_by_keymgmt(), to initialise an EVP_PKEY to
    contain a provider side internal key.
 
index cb106e2..1a21faa 100644 (file)
@@ -2525,6 +2525,8 @@ EVP_R_FINAL_ERROR:188:final error
 EVP_R_FIPS_MODE_NOT_SUPPORTED:167:fips mode not supported
 EVP_R_GET_RAW_KEY_FAILED:182:get raw key failed
 EVP_R_ILLEGAL_SCRYPT_PARAMETERS:171:illegal scrypt parameters
 EVP_R_FIPS_MODE_NOT_SUPPORTED:167:fips mode not supported
 EVP_R_GET_RAW_KEY_FAILED:182:get raw key failed
 EVP_R_ILLEGAL_SCRYPT_PARAMETERS:171:illegal scrypt parameters
+EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS:204:inaccessible domain parameters
+EVP_R_INACCESSIBLE_KEY:203:inaccessible key
 EVP_R_INITIALIZATION_ERROR:134:initialization error
 EVP_R_INPUT_NOT_INITIALIZED:111:input not initialized
 EVP_R_INVALID_CUSTOM_LENGTH:185:invalid custom length
 EVP_R_INITIALIZATION_ERROR:134:initialization error
 EVP_R_INPUT_NOT_INITIALIZED:111:input not initialized
 EVP_R_INVALID_CUSTOM_LENGTH:185:invalid custom length
@@ -2537,6 +2539,7 @@ EVP_R_INVALID_OPERATION:148:invalid operation
 EVP_R_INVALID_PROVIDER_FUNCTIONS:193:invalid provider functions
 EVP_R_INVALID_SALT_LENGTH:186:invalid salt length
 EVP_R_KEYGEN_FAILURE:120:keygen failure
 EVP_R_INVALID_PROVIDER_FUNCTIONS:193:invalid provider functions
 EVP_R_INVALID_SALT_LENGTH:186:invalid salt length
 EVP_R_KEYGEN_FAILURE:120:keygen failure
+EVP_R_KEYMGMT_EXPORT_FAILURE:205:keymgmt export failure
 EVP_R_KEY_SETUP_FAILED:180:key setup failed
 EVP_R_MEMORY_LIMIT_EXCEEDED:172:memory limit exceeded
 EVP_R_MESSAGE_DIGEST_IS_NULL:159:message digest is null
 EVP_R_KEY_SETUP_FAILED:180:key setup failed
 EVP_R_MEMORY_LIMIT_EXCEEDED:172:memory limit exceeded
 EVP_R_MESSAGE_DIGEST_IS_NULL:159:message digest is null
@@ -2547,6 +2550,7 @@ EVP_R_NOT_XOF_OR_INVALID_LENGTH:178:not XOF or invalid length
 EVP_R_NO_CIPHER_SET:131:no cipher set
 EVP_R_NO_DEFAULT_DIGEST:158:no default digest
 EVP_R_NO_DIGEST_SET:139:no digest set
 EVP_R_NO_CIPHER_SET:131:no cipher set
 EVP_R_NO_DEFAULT_DIGEST:158:no default digest
 EVP_R_NO_DIGEST_SET:139:no digest set
+EVP_R_NO_IMPORT_FUNCTION:206:no import function
 EVP_R_NO_KEYMGMT_AVAILABLE:199:no keymgmt available
 EVP_R_NO_KEYMGMT_PRESENT:196:no keymgmt present
 EVP_R_NO_KEY_SET:154:no key set
 EVP_R_NO_KEYMGMT_AVAILABLE:199:no keymgmt available
 EVP_R_NO_KEYMGMT_PRESENT:196:no keymgmt present
 EVP_R_NO_KEY_SET:154:no key set
@@ -2566,6 +2570,7 @@ EVP_R_PUBLIC_KEY_NOT_RSA:106:public key not rsa
 EVP_R_TOO_MANY_RECORDS:183:too many records
 EVP_R_UNKNOWN_CIPHER:160:unknown cipher
 EVP_R_UNKNOWN_DIGEST:161:unknown digest
 EVP_R_TOO_MANY_RECORDS:183:too many records
 EVP_R_UNKNOWN_CIPHER:160:unknown cipher
 EVP_R_UNKNOWN_DIGEST:161:unknown digest
+EVP_R_UNKNOWN_KEY_TYPE:207:unknown key type
 EVP_R_UNKNOWN_OPTION:169:unknown option
 EVP_R_UNKNOWN_PBE_ALGORITHM:121:unknown pbe algorithm
 EVP_R_UNSUPPORTED_ALGORITHM:156:unsupported algorithm
 EVP_R_UNKNOWN_OPTION:169:unknown option
 EVP_R_UNKNOWN_PBE_ALGORITHM:121:unknown pbe algorithm
 EVP_R_UNSUPPORTED_ALGORITHM:156:unsupported algorithm
index 2092171..3f2b814 100644 (file)
@@ -71,6 +71,9 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_GET_RAW_KEY_FAILED), "get raw key failed"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_ILLEGAL_SCRYPT_PARAMETERS),
     "illegal scrypt parameters"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_GET_RAW_KEY_FAILED), "get raw key failed"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_ILLEGAL_SCRYPT_PARAMETERS),
     "illegal scrypt parameters"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS),
+    "inaccessible domain parameters"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INACCESSIBLE_KEY), "inaccessible key"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INITIALIZATION_ERROR),
     "initialization error"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INPUT_NOT_INITIALIZED),
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INITIALIZATION_ERROR),
     "initialization error"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INPUT_NOT_INITIALIZED),
@@ -88,6 +91,8 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_SALT_LENGTH),
     "invalid salt length"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYGEN_FAILURE), "keygen failure"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_SALT_LENGTH),
     "invalid salt length"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYGEN_FAILURE), "keygen failure"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYMGMT_EXPORT_FAILURE),
+    "keymgmt export failure"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEY_SETUP_FAILED), "key setup failed"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_MEMORY_LIMIT_EXCEEDED),
     "memory limit exceeded"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEY_SETUP_FAILED), "key setup failed"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_MEMORY_LIMIT_EXCEEDED),
     "memory limit exceeded"},
@@ -103,6 +108,7 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_CIPHER_SET), "no cipher set"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DEFAULT_DIGEST), "no default digest"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DIGEST_SET), "no digest set"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_CIPHER_SET), "no cipher set"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DEFAULT_DIGEST), "no default digest"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DIGEST_SET), "no digest set"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_IMPORT_FUNCTION), "no import function"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_AVAILABLE),
     "no keymgmt available"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_PRESENT), "no keymgmt present"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_AVAILABLE),
     "no keymgmt available"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_PRESENT), "no keymgmt present"},
@@ -129,6 +135,7 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_TOO_MANY_RECORDS), "too many records"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_CIPHER), "unknown cipher"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_DIGEST), "unknown digest"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_TOO_MANY_RECORDS), "too many records"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_CIPHER), "unknown cipher"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_DIGEST), "unknown digest"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_KEY_TYPE), "unknown key type"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_OPTION), "unknown option"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_PBE_ALGORITHM),
     "unknown pbe algorithm"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_OPTION), "unknown option"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_PBE_ALGORITHM),
     "unknown pbe algorithm"},
index 9c11da6..92d65d9 100644 (file)
@@ -94,16 +94,35 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
      */
 
     /*
      */
 
     /*
-     * Only check that type match this early when both keys are legacy.
-     * If either of them is provided, we let evp_keymgmt_util_copy()
-     * do this check, after having exported either of them that isn't
-     * provided.
+     * If |to| is a legacy key and |from| isn't, we must downgrade |from|.
+     * If that fails, this function fails.
      */
      */
-    if (to->keymgmt == NULL && from->keymgmt == NULL) {
-        if (to->type == EVP_PKEY_NONE) {
+    if (to->type != EVP_PKEY_NONE && from->keymgmt != NULL)
+        if (!evp_pkey_downgrade((EVP_PKEY *)from))
+            return 0;
+
+    /*
+     * Make sure |to| is typed.  Content is less important at this early
+     * stage.
+     *
+     * 1.  If |to| is untyped, assign |from|'s key type to it.
+     * 2.  If |to| contains a legacy key, compare its |type| to |from|'s.
+     *     (|from| was already downgraded above)
+     *
+     * If |to| is a provided key, there's nothing more to do here, functions
+     * like evp_keymgmt_util_copy() and evp_pkey_export_to_provider() called
+     * further down help us find out if they are the same or not.
+     */
+    if (to->type == EVP_PKEY_NONE && to->keymgmt == NULL) {
+        if (from->type != EVP_PKEY_NONE) {
             if (EVP_PKEY_set_type(to, from->type) == 0)
                 return 0;
             if (EVP_PKEY_set_type(to, from->type) == 0)
                 return 0;
-        } else if (to->type != from->type) {
+        } else {
+            if (EVP_PKEY_set_type_by_keymgmt(to, from->keymgmt) == 0)
+                return 0;
+        }
+    } else if (to->type != EVP_PKEY_NONE) {
+        if (to->type != from->type) {
             EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES);
             goto err;
         }
             EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES);
             goto err;
         }
@@ -121,30 +140,6 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
         return 0;
     }
 
         return 0;
     }
 
-    /*
-     * If |from| is provided, we upgrade |to| to be provided as well.
-     * This drops the legacy key from |to|.
-     * evp_pkey_upgrade_to_provider() checks if |to| is already provided,
-     * we don't need to do that here.
-     *
-     * TODO(3.0) We should investigate if that's too aggressive and make
-     * this scenario unsupported instead.
-     */
-    if (from->keymgmt != NULL) {
-        EVP_KEYMGMT *tmp_keymgmt = from->keymgmt;
-
-        /*
-         * The returned pointer is known to be cached, so we don't have to
-         * save it.  However, if it's NULL, something went wrong and we can't
-         * copy.
-         */
-        if (evp_pkey_upgrade_to_provider(to, NULL,
-                                         &tmp_keymgmt, NULL) == NULL) {
-            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
-            return 0;
-        }
-    }
-
     /* For purely provided keys, we just call the keymgmt utility */
     if (to->keymgmt != NULL && from->keymgmt != NULL)
         return evp_keymgmt_util_copy(to, (EVP_PKEY *)from,
     /* For purely provided keys, we just call the keymgmt utility */
     if (to->keymgmt != NULL && from->keymgmt != NULL)
         return evp_keymgmt_util_copy(to, (EVP_PKEY *)from,
@@ -161,8 +156,12 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
             evp_pkey_export_to_provider((EVP_PKEY *)from, NULL, &to_keymgmt,
                                         NULL);
 
             evp_pkey_export_to_provider((EVP_PKEY *)from, NULL, &to_keymgmt,
                                         NULL);
 
+        /*
+         * If we get a NULL, it could be an internal error, or it could be
+         * that there's a key mismatch.  We're pretending the latter...
+         */
         if (from_keydata == NULL) {
         if (from_keydata == NULL) {
-            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
             return 0;
         }
         return evp_keymgmt_copy(to->keymgmt, to->keydata, from_keydata,
             return 0;
         }
         return evp_keymgmt_copy(to->keymgmt, to->keydata, from_keydata,
@@ -208,20 +207,25 @@ static int evp_pkey_cmp_any(const EVP_PKEY *a, const EVP_PKEY *b,
         return evp_keymgmt_util_match((EVP_PKEY *)a, (EVP_PKEY *)b, selection);
 
     /*
         return evp_keymgmt_util_match((EVP_PKEY *)a, (EVP_PKEY *)b, selection);
 
     /*
-     * Here, we know that we have a mixture of legacy and provided keys.
-     * Try cross export and compare the resulting key data.
+     * At this point, one of them is provided, the other not.  This allows
+     * us to compare types using legacy NIDs.
+     */
+    if ((a->type != EVP_PKEY_NONE
+         && !EVP_KEYMGMT_is_a(b->keymgmt, OBJ_nid2sn(a->type)))
+        || (b->type != EVP_PKEY_NONE
+            && !EVP_KEYMGMT_is_a(a->keymgmt, OBJ_nid2sn(b->type))))
+        return -1;               /* not the same key type */
+
+    /*
+     * We've determined that they both are the same keytype, so the next
+     * step is to do a bit of cross export to ensure we have keydata for
+     * both keys in the same keymgmt.
      */
     keymgmt1 = a->keymgmt;
     keydata1 = a->keydata;
     keymgmt2 = b->keymgmt;
     keydata2 = b->keydata;
 
      */
     keymgmt1 = a->keymgmt;
     keydata1 = a->keydata;
     keymgmt2 = b->keymgmt;
     keydata2 = b->keydata;
 
-    if ((keymgmt1 == NULL
-         && !EVP_KEYMGMT_is_a(keymgmt2, OBJ_nid2sn(a->type)))
-        || (keymgmt2 == NULL
-            && !EVP_KEYMGMT_is_a(keymgmt1, OBJ_nid2sn(b->type))))
-        return -1;               /* not the same key type */
-
     if (keymgmt2 != NULL && keymgmt2->match != NULL) {
         tmp_keydata =
             evp_pkey_export_to_provider((EVP_PKEY *)a, NULL, &keymgmt2, NULL);
     if (keymgmt2 != NULL && keymgmt2->match != NULL) {
         tmp_keydata =
             evp_pkey_export_to_provider((EVP_PKEY *)a, NULL, &keymgmt2, NULL);
@@ -359,6 +363,7 @@ EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e,
 int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
                                  size_t *len)
 {
 int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
                                  size_t *len)
 {
+    /* TODO(3.0) Do we need to do anything about provider side keys? */
      if (pkey->ameth->get_priv_key == NULL) {
         EVPerr(EVP_F_EVP_PKEY_GET_RAW_PRIVATE_KEY,
                EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
      if (pkey->ameth->get_priv_key == NULL) {
         EVPerr(EVP_F_EVP_PKEY_GET_RAW_PRIVATE_KEY,
                EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -376,6 +381,7 @@ int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
 int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
                                 size_t *len)
 {
 int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
                                 size_t *len)
 {
+    /* TODO(3.0) Do we need to do anything about provider side keys? */
      if (pkey->ameth->get_pub_key == NULL) {
         EVPerr(EVP_F_EVP_PKEY_GET_RAW_PUBLIC_KEY,
                EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
      if (pkey->ameth->get_pub_key == NULL) {
         EVPerr(EVP_F_EVP_PKEY_GET_RAW_PUBLIC_KEY,
                EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -524,6 +530,10 @@ int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key)
 
 void *EVP_PKEY_get0(const EVP_PKEY *pkey)
 {
 
 void *EVP_PKEY_get0(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     return pkey->pkey.ptr;
 }
 
     return pkey->pkey.ptr;
 }
 
@@ -579,6 +589,10 @@ int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key)
 
 RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey)
 {
 
 RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_RSA_PSS) {
         EVPerr(EVP_F_EVP_PKEY_GET0_RSA, EVP_R_EXPECTING_AN_RSA_KEY);
         return NULL;
     if (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_RSA_PSS) {
         EVPerr(EVP_F_EVP_PKEY_GET0_RSA, EVP_R_EXPECTING_AN_RSA_KEY);
         return NULL;
@@ -606,6 +620,10 @@ int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key)
 
 DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey)
 {
 
 DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (pkey->type != EVP_PKEY_DSA) {
         EVPerr(EVP_F_EVP_PKEY_GET0_DSA, EVP_R_EXPECTING_A_DSA_KEY);
         return NULL;
     if (pkey->type != EVP_PKEY_DSA) {
         EVPerr(EVP_F_EVP_PKEY_GET0_DSA, EVP_R_EXPECTING_A_DSA_KEY);
         return NULL;
@@ -634,6 +652,10 @@ int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key)
 
 EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey)
 {
 
 EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
         EVPerr(EVP_F_EVP_PKEY_GET0_EC_KEY, EVP_R_EXPECTING_A_EC_KEY);
         return NULL;
     if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
         EVPerr(EVP_F_EVP_PKEY_GET0_EC_KEY, EVP_R_EXPECTING_A_EC_KEY);
         return NULL;
@@ -664,6 +686,10 @@ int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key)
 
 DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey)
 {
 
 DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (pkey->type != EVP_PKEY_DH && pkey->type != EVP_PKEY_DHX) {
         EVPerr(EVP_F_EVP_PKEY_GET0_DH, EVP_R_EXPECTING_A_DH_KEY);
         return NULL;
     if (pkey->type != EVP_PKEY_DH && pkey->type != EVP_PKEY_DHX) {
         EVPerr(EVP_F_EVP_PKEY_GET0_DH, EVP_R_EXPECTING_A_DH_KEY);
         return NULL;
@@ -1349,101 +1375,87 @@ void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
 }
 
 #ifndef FIPS_MODE
 }
 
 #ifndef FIPS_MODE
-/*
- * This differs from exporting in that it releases the legacy key and assigns
- * the export keymgmt and keydata to the "origin" provider side key instead
- * of the operation cache.
- */
-void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
-                                   EVP_KEYMGMT **keymgmt,
-                                   const char *propquery)
+int evp_pkey_downgrade(EVP_PKEY *pk)
 {
 {
-    EVP_KEYMGMT *allocated_keymgmt = NULL;
-    EVP_KEYMGMT *tmp_keymgmt = NULL;
-    void *keydata = NULL;
+    EVP_KEYMGMT *keymgmt = pk->keymgmt;
+    void *keydata = pk->keydata;
+    int type = pk->save_type;
+    const char *keytype = NULL;
 
 
-    if (pk == NULL)
-        return NULL;
+    /* If this isn't a provider side key, we're done */
+    if (keymgmt == NULL)
+        return 1;
+
+    /* Get the key type name for error reporting */
+    if (type != EVP_PKEY_NONE)
+        keytype = OBJ_nid2sn(type);
+    else
+        keytype =
+            evp_first_name(EVP_KEYMGMT_provider(keymgmt), keymgmt->name_id);
 
     /*
 
     /*
-     * If this key is already "upgraded", this function shouldn't have been
-     * called.
+     * |save_type| was set when any of the EVP_PKEY_set_type functions
+     * was called.  It was set to EVP_PKEY_NONE if the key type wasn't
+     * recognised to be any of the legacy key types, and the downgrade
+     * isn't possible.
      */
      */
-    if (!ossl_assert(pk->keymgmt == NULL))
-        return NULL;
-
-    if (keymgmt != NULL) {
-        tmp_keymgmt = *keymgmt;
-        *keymgmt = NULL;
+    if (type == EVP_PKEY_NONE) {
+        ERR_raise_data(ERR_LIB_EVP, EVP_R_UNKNOWN_KEY_TYPE,
+                       "key type = %s, can't downgrade", keytype);
+        return 0;
     }
 
     }
 
-    /* If the key isn't a legacy one, bail out, but with proper values */
-    if (pk->pkey.ptr == NULL) {
-        tmp_keymgmt = pk->keymgmt;
-        keydata = pk->keydata;
-    } else {
-        /* If the legacy key doesn't have an export function, give up */
-        if (pk->ameth->export_to == NULL)
-            return NULL;
-
-        /*
-         * If no keymgmt was given or found, get a default keymgmt.  We do
-         * so by letting EVP_PKEY_CTX_new_from_pkey() do it for us, then we
-         * steal it.
-         */
-        if (tmp_keymgmt == NULL) {
-            EVP_PKEY_CTX *ctx =
-                EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery);
-
-            tmp_keymgmt = ctx->keymgmt;
-            ctx->keymgmt = NULL;
-            EVP_PKEY_CTX_free(ctx);
-        }
-
-        /* If we still don't have a keymgmt, give up */
-        if (tmp_keymgmt == NULL)
-            goto end;
-
-        /* Make sure that the keymgmt key type matches the legacy NID */
-        if (!ossl_assert(EVP_KEYMGMT_is_a(tmp_keymgmt, OBJ_nid2sn(pk->type))))
-            goto end;
+    /*
+     * To be able to downgrade, we steal the provider side "origin" keymgmt
+     * and keydata.  We've already grabbed the pointers, so all we need to
+     * do is clear those pointers in |pk| and then call evp_pkey_free_it().
+     * That way, we can restore |pk| if we need to.
+     */
+    pk->keymgmt = NULL;
+    pk->keydata = NULL;
+    evp_pkey_free_it(pk);
+    if (EVP_PKEY_set_type(pk, type)) {
+        /* If the key is typed but empty, we're done */
+        if (keydata == NULL)
+            return 1;
 
 
-        if ((keydata = evp_keymgmt_newdata(tmp_keymgmt)) == NULL)
-            goto end;
+        if (pk->ameth->import_from == NULL) {
+            ERR_raise_data(ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION,
+                           "key type = %s", keytype);
+        } else if (evp_keymgmt_export(keymgmt, keydata,
+                                      OSSL_KEYMGMT_SELECT_ALL,
+                                      pk->ameth->import_from, pk)) {
+            /*
+             * Save the provider side data in the operation cache, so they'll
+             * find it again.  evp_pkey_free_it() cleared the cache, so it's
+             * safe to assume slot zero is free.
+             * Note that evp_keymgmt_util_cache_keydata() increments keymgmt's
+             * reference count.
+             */
+            evp_keymgmt_util_cache_keydata(pk, 0, keymgmt, keydata);
 
 
-        if (!pk->ameth->export_to(pk, keydata, tmp_keymgmt)
-            || !EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
-            evp_keymgmt_freedata(tmp_keymgmt, keydata);
-            keydata = NULL;
-            goto end;
+            /* Synchronize the dirty count */
+            pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+            return 1;
         }
 
         }
 
-        /*
-         * Clear the operation cache, all the legacy data, as well as the
-         * dirty counters
-         */
-        evp_pkey_free_legacy(pk);
-        pk->dirty_cnt_copy = 0;
-
-        evp_keymgmt_util_clear_operation_cache(pk);
-        pk->keymgmt = tmp_keymgmt;
-        pk->keydata = keydata;
-        evp_keymgmt_util_cache_keyinfo(pk);
+        ERR_raise_data(ERR_LIB_EVP, EVP_R_KEYMGMT_EXPORT_FAILURE,
+                       "key type = %s", keytype);
     }
 
     }
 
- end:
     /*
     /*
-     * If nothing was upgraded, |tmp_keymgmt| might point at a freed
-     * EVP_KEYMGMT, so we clear it to be safe.  It shouldn't be useful for
-     * the caller either way in that case.
+     * Something went wrong.  This could for example happen if the keymgmt
+     * turns out to be an HSM implementation that refuses to let go of some
+     * of the key data, typically the private bits.  In this case, we restore
+     * the provider side internal "origin" and leave it at that.
      */
      */
-    if (keydata == NULL)
-        tmp_keymgmt = NULL;
-
-    if (keymgmt != NULL)
-        *keymgmt = tmp_keymgmt;
-
-    EVP_KEYMGMT_free(allocated_keymgmt);
-    return keydata;
+    if (!ossl_assert(EVP_PKEY_set_type_by_keymgmt(pk, keymgmt))) {
+        /* This should not be impossible */
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    pk->keydata = keydata;
+    evp_keymgmt_util_cache_keyinfo(pk);
+    return 0;     /* No downgrade, but at least the key is restored */
 }
 #endif  /* FIPS_MODE */
 }
 #endif  /* FIPS_MODE */
index 1bf95af..6780028 100644 (file)
@@ -180,6 +180,8 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
 #ifdef FIPS_MODE
     goto not_supported;
 #else
 #ifdef FIPS_MODE
     goto not_supported;
 #else
+    if (ctx->pkey && !evp_pkey_downgrade(ctx->pkey))
+        goto not_accessible;
     switch (ctx->operation) {
     case EVP_PKEY_OP_PARAMGEN:
         ret = ctx->pmeth->paramgen(ctx, *ppkey);
     switch (ctx->operation) {
     case EVP_PKEY_OP_PARAMGEN:
         ret = ctx->pmeth->paramgen(ctx, *ppkey);
@@ -208,6 +210,12 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
     ERR_raise(ERR_LIB_EVP, EVP_R_OPERATON_NOT_INITIALIZED);
     ret = -1;
     goto end;
     ERR_raise(ERR_LIB_EVP, EVP_R_OPERATON_NOT_INITIALIZED);
     ret = -1;
     goto end;
+#ifndef FIPS_MODE
+ not_accessible:
+    ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS);
+    ret = -1;
+    goto end;
+#endif
 }
 
 int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
 }
 
 int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
index 0e7e9ba..20732c2 100644 (file)
@@ -11,6 +11,8 @@
 
 /* ASN1 public key method structure */
 
 
 /* ASN1 public key method structure */
 
+#include <openssl/core.h>
+
 struct evp_pkey_asn1_method_st {
     int pkey_id;
     int pkey_base_id;
 struct evp_pkey_asn1_method_st {
     int pkey_id;
     int pkey_base_id;
@@ -68,10 +70,11 @@ struct evp_pkey_asn1_method_st {
      * TODO: Make sure these functions are defined for key types that are
      * implemented in providers.
      */
      * TODO: Make sure these functions are defined for key types that are
      * implemented in providers.
      */
-    /* Exports to providers */
+    /* Exports and imports to / from providers */
     size_t (*dirty_cnt) (const EVP_PKEY *pk);
     int (*export_to) (const EVP_PKEY *pk, void *to_keydata,
                       EVP_KEYMGMT *to_keymgmt);
     size_t (*dirty_cnt) (const EVP_PKEY *pk);
     int (*export_to) (const EVP_PKEY *pk, void *to_keydata,
                       EVP_KEYMGMT *to_keymgmt);
+    OSSL_CALLBACK *import_from;
 } /* EVP_PKEY_ASN1_METHOD */ ;
 
 DEFINE_STACK_OF_CONST(EVP_PKEY_ASN1_METHOD)
 } /* EVP_PKEY_ASN1_METHOD */ ;
 
 DEFINE_STACK_OF_CONST(EVP_PKEY_ASN1_METHOD)
index e5f9aad..63b6dad 100644 (file)
@@ -630,10 +630,8 @@ void evp_app_cleanup_int(void);
 void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
                                   EVP_KEYMGMT **keymgmt,
                                   const char *propquery);
 void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
                                   EVP_KEYMGMT **keymgmt,
                                   const char *propquery);
-void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
-                                   EVP_KEYMGMT **keymgmt,
-                                   const char *propquery);
 #ifndef FIPS_MODE
 #ifndef FIPS_MODE
+int evp_pkey_downgrade(EVP_PKEY *pk);
 void evp_pkey_free_legacy(EVP_PKEY *x);
 #endif
 
 void evp_pkey_free_legacy(EVP_PKEY *x);
 #endif
 
index 994268a..9290cff 100644 (file)
 
 #ifndef OPENSSL_EVPERR_H
 # define OPENSSL_EVPERR_H
 
 #ifndef OPENSSL_EVPERR_H
 # define OPENSSL_EVPERR_H
-# pragma once
-
-# include <openssl/macros.h>
-# ifndef OPENSSL_NO_DEPRECATED_3_0
-#  define HEADER_EVPERR_H
-# endif
 
 # include <openssl/opensslconf.h>
 # include <openssl/symhacks.h>
 
 # include <openssl/opensslconf.h>
 # include <openssl/symhacks.h>
@@ -199,6 +193,8 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_FIPS_MODE_NOT_SUPPORTED                    167
 # define EVP_R_GET_RAW_KEY_FAILED                         182
 # define EVP_R_ILLEGAL_SCRYPT_PARAMETERS                  171
 # define EVP_R_FIPS_MODE_NOT_SUPPORTED                    167
 # define EVP_R_GET_RAW_KEY_FAILED                         182
 # define EVP_R_ILLEGAL_SCRYPT_PARAMETERS                  171
+# define EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS             204
+# define EVP_R_INACCESSIBLE_KEY                           203
 # define EVP_R_INITIALIZATION_ERROR                       134
 # define EVP_R_INPUT_NOT_INITIALIZED                      111
 # define EVP_R_INVALID_CUSTOM_LENGTH                      185
 # define EVP_R_INITIALIZATION_ERROR                       134
 # define EVP_R_INPUT_NOT_INITIALIZED                      111
 # define EVP_R_INVALID_CUSTOM_LENGTH                      185
@@ -211,6 +207,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_INVALID_PROVIDER_FUNCTIONS                 193
 # define EVP_R_INVALID_SALT_LENGTH                        186
 # define EVP_R_KEYGEN_FAILURE                             120
 # define EVP_R_INVALID_PROVIDER_FUNCTIONS                 193
 # define EVP_R_INVALID_SALT_LENGTH                        186
 # define EVP_R_KEYGEN_FAILURE                             120
+# define EVP_R_KEYMGMT_EXPORT_FAILURE                     205
 # define EVP_R_KEY_SETUP_FAILED                           180
 # define EVP_R_MEMORY_LIMIT_EXCEEDED                      172
 # define EVP_R_MESSAGE_DIGEST_IS_NULL                     159
 # define EVP_R_KEY_SETUP_FAILED                           180
 # define EVP_R_MEMORY_LIMIT_EXCEEDED                      172
 # define EVP_R_MESSAGE_DIGEST_IS_NULL                     159
@@ -221,6 +218,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_NO_CIPHER_SET                              131
 # define EVP_R_NO_DEFAULT_DIGEST                          158
 # define EVP_R_NO_DIGEST_SET                              139
 # define EVP_R_NO_CIPHER_SET                              131
 # define EVP_R_NO_DEFAULT_DIGEST                          158
 # define EVP_R_NO_DIGEST_SET                              139
+# define EVP_R_NO_IMPORT_FUNCTION                         206
 # define EVP_R_NO_KEYMGMT_AVAILABLE                       199
 # define EVP_R_NO_KEYMGMT_PRESENT                         196
 # define EVP_R_NO_KEY_SET                                 154
 # define EVP_R_NO_KEYMGMT_AVAILABLE                       199
 # define EVP_R_NO_KEYMGMT_PRESENT                         196
 # define EVP_R_NO_KEY_SET                                 154
@@ -238,6 +236,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_TOO_MANY_RECORDS                           183
 # define EVP_R_UNKNOWN_CIPHER                             160
 # define EVP_R_UNKNOWN_DIGEST                             161
 # define EVP_R_TOO_MANY_RECORDS                           183
 # define EVP_R_UNKNOWN_CIPHER                             160
 # define EVP_R_UNKNOWN_DIGEST                             161
+# define EVP_R_UNKNOWN_KEY_TYPE                           207
 # define EVP_R_UNKNOWN_OPTION                             169
 # define EVP_R_UNKNOWN_PBE_ALGORITHM                      121
 # define EVP_R_UNSUPPORTED_ALGORITHM                      156
 # define EVP_R_UNKNOWN_OPTION                             169
 # define EVP_R_UNKNOWN_PBE_ALGORITHM                      121
 # define EVP_R_UNSUPPORTED_ALGORITHM                      156
index 5ef238c..fd60893 100644 (file)
@@ -207,14 +207,10 @@ static int test_pass_rsa(FIXTURE *fixture)
         || !TEST_ptr_ne(km1, km2))
         goto err;
 
         || !TEST_ptr_ne(km1, km2))
         goto err;
 
-    if (!TEST_ptr(evp_pkey_export_to_provider(pk, NULL, &km1, NULL))
-        || !TEST_ptr(evp_pkey_upgrade_to_provider(pk, NULL, &km1, NULL))
-        || !TEST_ptr(provkey = evp_keymgmt_util_export_to_provider(pk, km2)))
-        goto err;
-
-    if (!TEST_true(evp_keymgmt_export(km2, provkey,
-                                      OSSL_KEYMGMT_SELECT_KEYPAIR,
-                                      &export_cb, keydata)))
+    if (!TEST_ptr(provkey = evp_pkey_export_to_provider(pk, NULL, &km1, NULL))
+        || !TEST_true(evp_keymgmt_export(km2, provkey,
+                                         OSSL_KEYMGMT_SELECT_KEYPAIR,
+                                         &export_cb, keydata)))
         goto err;
 
     /*
         goto err;
 
     /*