diff options
author | Richard Levitte <levitte@openssl.org> | 2021-02-03 20:40:37 +0100 |
---|---|---|
committer | Richard Levitte <levitte@openssl.org> | 2021-02-12 15:51:52 +0100 |
commit | 1695e10e402a2d25e57df2ac709d6265f3a2533f (patch) | |
tree | cb7d98ed89afd73eb82ca434f988939e8a16d76a /doc | |
parent | c5689319ebcb5356a28c297779094f3208f925f8 (diff) |
DOCS: Update the internal documentation on EVP_PKEY.
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14059)
Diffstat (limited to 'doc')
-rw-r--r-- | doc/internal/man7/EVP_PKEY.pod | 197 |
1 files 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 --- a/doc/internal/man7/EVP_PKEY.pod +++ b/doc/internal/man7/EVP_PKEY.pod @@ -8,37 +8,198 @@ EVP_PKEY - an internal description #include "crypto/evp.h" - struct evp_pkey_st; + typedef struct evp_pkey_st EVP_PKEY; =head1 DESCRIPTION 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: + +=over 4 + +=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 +an L<EVP_PKEY_ASN1_METHOD(3)>. + +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 +methods. + +=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)>. + +=back + +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: + +=over 4 + +=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)>. + +=item EC: + +It's possible to flip the ECDH cofactor mode. + +=back + +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 +function. + +=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 +dirty_cnt(). +For a provider native origin, this requires that the associated +L<EVP_KEYMGMT(3)> implements the OSSL_FUNC_keymgmt_export() function +(see L<provider-keymgmt(7)>). +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 keys). +=head3 Exporting implementation details + + +Exporting a key to the operation cache involves the following: + +=over 4 + +=item 1. + +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. + +=item 2. + +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. + +=item 3. + +Export the internal origin key to the provider, using the appropriate +method. + +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 +OSSL_FUNC_keymgmt_import() function. + +=back + +=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 +all. + =head1 SEE ALSO L<provider-keymgmt(7)> |