mirror of
https://github.com/openssl/openssl.git
synced 2024-11-27 05:21:51 +08:00
DOCS: Update the internal documentation on EVP_PKEY.
Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/14059)
This commit is contained in:
parent
c5689319eb
commit
1695e10e40
@ -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".
|
||||
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:
|
||||
|
||||
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().
|
||||
=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)>
|
||||
|
Loading…
Reference in New Issue
Block a user