[openssl] master update
levitte at openssl.org
Fri Feb 12 14:52:18 UTC 2021
The branch master has been updated
via 1695e10e402a2d25e57df2ac709d6265f3a2533f (commit)
from c5689319ebcb5356a28c297779094f3208f925f8 (commit)
- Log -----------------------------------------------------------------
Author: Richard Levitte <levitte at openssl.org>
Date: Wed Feb 3 20:40:37 2021 +0100
DOCS: Update the internal documentation on EVP_PKEY.
Reviewed-by: Tomas Mraz <tomas at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14059)
Summary of changes:
doc/internal/man7/EVP_PKEY.pod | 197 +++++++++++++++++++++++++++++++++++++----
1 file changed, 179 insertions(+), 18 deletions(-)
diff --git a/doc/internal/man7/EVP_PKEY.pod b/doc/internal/man7/EVP_PKEY.pod
index 00d4df57f5..7e7c292f85 100644
@@ -8,37 +8,198 @@ EVP_PKEY - an internal description
- struct evp_pkey_st;
+ typedef struct evp_pkey_st EVP_PKEY;
I<This is not a complete description yet>
B<EVP_PKEY> is a complex type that's essentially a container for
-private/public key key pairs, but has had other uses as well.
+private/public key pairs, but has had other uses as well.
=for comment "uses" could as well be "abuses"...
-It can contain the legacy form of keys -- i.e. pointers to the low-level key types, such as B<RSA>, B<DSA> and B<EC> --, but also the
-provided form of keys -- i.e. pointers to provider side key data.
-Those two forms are mutually exclusive; an B<EVP_PKEY> instance can't
-contain both a key in legacy form and in provided form. Regardless of
-form, this key is commonly referred to as the "origin".
-An B<EVP_PKEY> also contains a cache of provider side copies of the
-key, each adapted for the provider that is going to use that copy to
-perform some operation.
-For a legacy "origin", the B<EVP_PKEY_ASN1_METHOD>'s functions
-export_to() and dirty_cnt() must be implemented for such caching to be
-possible. For a provider side "origin", the B<EVP_KEYMGMT>'s function
-OP_keymgmt_export() must be implemented. In all cases, the receiving
-B<EVP_KEYMGMT> must have an implemented OP_keygmt_import().
+The private/public key pair that an B<EVP_PKEY> contains is refered to
+as its "internal key" or "origin" (the reason for "origin" is
+explained further down, in L</Export cache for provider operations>),
+and it can take one of the following forms:
+=item legacy origin
+This is the form that an B<EVP_PKEY> in OpenSSL prior to 3.0 had. The
+internal key in the B<EVP_PKEY> is a pointer to the low-level key
+types, such as B<RSA>, B<DSA> and B<EC>, or an engine driven
+structure, and is governed by an associated L<EVP_PKEY_METHOD(3)> and
+The functions available through those two method structures get full
+access to the B<EVP_PKEY> and therefore have a lot of freedom to
+modify whatever they want. This also means that an B<EVP_PKEY> is a
+shared structure between libcrypto and any ENGINE that serves such
+=item provider-native origin
+This is a new form in OpenSSL 3.0, which permits providers to hold the
+key data (see L<provider-keymgmt(7)>). The internal key in the
+B<EVP_PKEY> is a pointer to that key data held by the provider, and
+is governed by an associated L<EVP_KEYMGMT(3)> method structure.
+The functions available through the L<EVP_KEYMGMT(3)> have no access
+to the B<EVP_PKEY>, and can therefore not make any direct changes.
+Similarly, the key data that the B<EVP_PKEY> points at is only known
+to the functions pointed at in the L<EVP_KEYMGMT(3)>.
+These two forms can never co-exist in the same B<EVP_PKEY>, the main
+reason being that having both at the same time will create problems
+with synchronising between the two forms, and potentially make it
+confusing which one of the two is the origin.
+=head2 Key mutability
+The B<EVP_PKEY> internal keys are mutable.
+This is especially visible with internal legacy keys, since they can
+be extracted with functions like L<EVP_PKEY_get0_RSA(3)> and then
+modified at will with functions like L<RSA_set0_key(3)>.
+Internal provider native keys are also possible to be modified, if the
+associated L<EVP_KEYMGMT(3)> implementation allows it. This is done
+with L<EVP_PKEY_set_params(3)> and its specialised derivatives. The
+OpenSSL providers allow it for the following:
+=item DH, EC, X25519, X448:
+It's possible to set the encoded public key. This is supported in
+particular through L<EVP_PKEY_set1_encoded_public_key(3)>.
+It's possible to flip the ECDH cofactor mode.
+Every time the B<EVP_PKEY> internal key mutates, an internal dirty
+count is incremented. The need for a dirty count is explained further
+in L</Export cache for provider operations>.
+For provider native origin keys, this doesn't require any help from
+the L<EVP_KEYMGMT(3)>, the dirty count is maintained in the B<EVP_PKEY>
+itself, and is incremented every time L<EVP_PKEY_set_params(3)> or its
+specialised derivatives are called.
+For legacy origin keys, this requires the associated
+L<EVP_PKEY_ASN1_METHOD(3)> to implement the dirty_cnt() function. All
+of OpenSSL's built-in L<EVP_PKEY_ASN1_METHOD(3)> implement this
+=head2 Export cache for provider operations
+OpenSSL 3.0 can handle operations such as signing, encrypting, etc in
+diverse providers, potentially others than the provider of the
+L<EVP_KEYMGMT(3)>. Two providers, possibly from different vendors,
+can't be expected to share internal key structures. There are
+therefore instances where key data will need to be exported to the
+provider that is going to perform the operation (this also implies
+that every provider that implements a key pair based operation must
+also implement an L<EVP_KEYMGMT(3)>).
+For performance reasons, libcrypto tries to minimize the need to
+perform such an export, so it maintains a cache of such exports in the
+B<EVP_PKEY>. Each cache entry has two items, a pointer to the
+provider side key data and the associated L<EVP_KEYMGMT(3)>.
+I<This cache is often referred to as the "operation key cache", and
+the key data that the cached keys came from is the "origin", and since
+there are two forms of the latter, we have the "legacy origin" and the
+"provider native origin".>
+The export to the operation key cache can be performed independent of
+what form the origin has.
+For a legacy origin, this requires that the associated
+L<EVP_PKEY_ASN1_METHOD(3)> implements the functions export_to() and
+For a provider native origin, this requires that the associated
+L<EVP_KEYMGMT(3)> implements the OSSL_FUNC_keymgmt_export() function
+In all cases, the receiving L<EVP_KEYMGMT(3)> (the one associated with
+the exported key data) must implement OSSL_FUNC_keymgmt_import().
If such caching isn't supported, the operations that can be performed
-with that key are limited to the same backend as the "origin" key
-(ENGINE for legacy "origin" keys, provider for provider side "origin"
+with that key are limited to the same backend as the origin key
+(ENGINE for legacy origin keys, provider for provider side origin
+=head3 Exporting implementation details
+Exporting a key to the operation cache involves the following:
+Check if the dirty count for the internal origin key has changed since
+the previous time. This is done by comparing it with a copy of the
+dirty count, which is maintained by the export function.
+If the dirty count has changed, the export cache is cleared.
+Check if there's an entry in the export cache with the same
+L<EVP_KEYMGMT(3)> that's the same provider that an export is to be
+made to (which is the provider that's going to perform an operation
+for which the current B<EVP_PKEY> is going to be used).
+If such an entry is found, nothing more is done, the key data and
+L<EVP_KEYMGMT(3)> found in that export cache entry will be used for
+the operation to be performed.
+Export the internal origin key to the provider, using the appropriate
+For legacy origin keys, that's done with the help of the
+L<EVP_PKEY_ASN1_METHOD(3)> export_to() function.
+For provider native origin keys, that's done by retrieving the key
+data in L<OSSL_PARAM(3)> form from the origin keys, using the
+OSSL_FUNC_keymgmt_export() functions of the associated
+L<EVP_KEYMGMT(3)>, and sending that data to the L<EVP_KEYMGMT(3)> of
+the provider that's to perform the operation, using its
+=head2 Upgrading and downgrading a key
+An B<EVP_PKEY> with a legacy origin will I<never> be upgraded to
+become an B<EVP_PKEY> with a provider native origin. Instead, we have
+the operation cache as described above, that takes care of the needs
+of the diverse operation the application may want to perform.
+An B<EVP_PKEY> with a provider native origin, I<may> be downgraded to
+be I<transformed> into an B<EVP_PKEY> with a legacy origin. Because
+an B<EVP_PKEY> can't have two origins, it means that it stops having a
+provider native origin. The previous provider native key data is
+moved to the operation cache. Downgrading is performed with the
+internal function L<evp_pkey_downgrade(3)>.
+I<Downgrading a key is understandably fragile>, and possibly surprising,
+and should therefore be done I<as little as possible>, but is needed
+to be able to support functions like L<EVP_PKEY_get0_RSA(3)>.
+The general recommendation is to use L<evp_pkey_copy_downgraded(3)>
+whenever possible, which it should be if the need for a legacy origin
+is only internal, or better yet, to remove the need for downgrade at
=head1 SEE ALSO
More information about the openssl-commits