Add evp_keymgmt_export_to_provider(), for key transfer between providers

This function is used to transport private key materia from whatever
is already attached to the EVP_PKEY to the new provider, using key
data export and import functionality.

If a legacy lower level key has been assigned to the EVP_PKEY, we use
its data to create a provider side key, and thereby have a bridge
between old style public key types and the EVP_PKEY on providers.

If successful, this function returns a reference to the appropriate
provider side data for the key.

This can be used by any operation that wants to use this key.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9312)
This commit is contained in:
Richard Levitte 2019-07-06 21:57:15 +02:00
parent a94a3e0d91
commit 70a1f7b4d7
6 changed files with 266 additions and 2 deletions

View file

@ -1,6 +1,6 @@
LIBS=../../libcrypto
$COMMON=digest.c evp_enc.c evp_lib.c evp_fetch.c cmeth_lib.c keymgmt_meth.c \
evp_utils.c
$COMMON=digest.c evp_enc.c evp_lib.c evp_fetch.c cmeth_lib.c evp_utils.c \
keymgmt_meth.c keymgmt_lib.c
SOURCE[../../libcrypto]=$COMMON\
encode.c evp_key.c evp_cnf.c \
e_des.c e_bf.c e_idea.c e_des3.c e_camellia.c\

188
crypto/evp/keymgmt_lib.c Normal file
View file

@ -0,0 +1,188 @@
/*
* Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/cryptlib.h"
#include "internal/nelem.h"
#include "internal/evp_int.h"
#include "internal/asn1_int.h"
#include "internal/provider.h"
#include "evp_locl.h"
static OSSL_PARAM *paramdefs_to_params(const OSSL_PARAM *paramdefs)
{
size_t cnt;
const OSSL_PARAM *p;
OSSL_PARAM *params, *q;
for (cnt = 1, p = paramdefs; p->key != NULL; p++, cnt++)
continue;
params = OPENSSL_zalloc(cnt * sizeof(*params));
for (p = paramdefs, q = params; ; p++, q++) {
*q = *p;
if (p->key == NULL)
break;
q->data = NULL; /* In case the provider used it */
q->return_size = 0;
}
return params;
}
typedef union align_block_un {
OSSL_UNION_ALIGN;
} ALIGN_BLOCK;
#define ALIGN_SIZE sizeof(ALIGN_BLOCK)
static void *allocate_params_space(OSSL_PARAM *params)
{
unsigned char *data = NULL;
size_t space;
OSSL_PARAM *p;
for (space = 0, p = params; p->key != NULL; p++)
space += ((p->return_size + ALIGN_SIZE - 1) / ALIGN_SIZE) * ALIGN_SIZE;
data = OPENSSL_zalloc(space);
for (space = 0, p = params; p->key != NULL; p++) {
p->data = data + space;
space += ((p->return_size + ALIGN_SIZE - 1) / ALIGN_SIZE) * ALIGN_SIZE;
}
return data;
}
void *evp_keymgmt_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
{
void *provkey = NULL;
size_t i, j;
/*
* If there is an underlying legacy key and it has changed, invalidate
* the cache of provider keys.
*/
if (pk->pkey.ptr != NULL) {
/*
* If there is no dirty counter, this key can't be used with
* providers.
*/
if (pk->ameth->dirty_cnt == NULL)
return NULL;
if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy)
for (i = 0;
i < OSSL_NELEM(pk->pkeys) && pk->pkeys[i].keymgmt != NULL;
i++) {
pk->pkeys[i].keymgmt->freekey(pk->pkeys[i].provkey);
pk->pkeys[i].keymgmt = NULL;
pk->pkeys[i].provkey = NULL;
}
}
/*
* See if we have exported to this provider already.
* If we have, return immediately.
*/
for (i = 0;
i < OSSL_NELEM(pk->pkeys) && pk->pkeys[i].keymgmt != NULL;
i++) {
if (keymgmt == pk->pkeys[i].keymgmt)
return pk->pkeys[i].provkey;
}
if (pk->pkey.ptr != NULL) {
/* There is a legacy key, try to export that one to the provider */
/* If the legacy key doesn't have an export function, give up */
if (pk->ameth->export_to == NULL)
return NULL;
/* Otherwise, simply use it */
provkey = pk->ameth->export_to(pk, keymgmt);
/* Synchronize the dirty count, but only if we exported successfully */
if (provkey != NULL)
pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
} else {
/*
* Here, there is no legacy key, so we look at the already cached
* provider keys, and import from the first that supports it
* (i.e. use its export function), and export the imported data to
* the new provider.
*/
/*
* If the given keymgmt doesn't have an import function, give up
*/
if (keymgmt->importkey == NULL)
return NULL;
for (j = 0; j < i && pk->pkeys[j].keymgmt != NULL; j++) {
if (pk->pkeys[j].keymgmt->exportkey != NULL) {
const OSSL_PARAM *paramdefs = NULL;
OSSL_PARAM *params = NULL;
void *data = NULL;
void *provctx =
ossl_provider_ctx(EVP_KEYMGMT_provider(keymgmt));
paramdefs = pk->pkeys[j].keymgmt->exportkey_types();
/*
* All params have 'data' set to NULL. In that case,
* the exportkey call should just fill in 'return_size'
* in all applicable params.
*/
params = paramdefs_to_params(paramdefs);
/* Get 'return_size' filled */
pk->pkeys[j].keymgmt->exportkey(pk->pkeys[j].provkey, params);
/*
* Allocate space and assign 'data' to point into the
* data block
*/
data = allocate_params_space(params);
/*
* Call the exportkey function a second time, to get
* the data filled
*/
pk->pkeys[j].keymgmt->exportkey(pk->pkeys[j].provkey, params);
/*
* We should have all the data at this point, so import
* into the new provider and hope to get a key back.
*/
provkey = keymgmt->importkey(provctx, params);
OPENSSL_free(params);
OPENSSL_free(data);
if (provkey != NULL)
break;
}
}
}
/*
* TODO(3.0) Right now, we assume we have ample space. We will
* have to think about a cache aging scheme, though, if |i| indexes
* outside the array.
*/
j = ossl_assert(i < OSSL_NELEM(pk->pkeys));
if (provkey != NULL) {
EVP_KEYMGMT_up_ref(keymgmt);
pk->pkeys[i].keymgmt = keymgmt;
pk->pkeys[i].provkey = provkey;
}
return provkey;
}

View file

@ -63,6 +63,14 @@ struct evp_pkey_asn1_method_st {
int (*set_pub_key) (EVP_PKEY *pk, const unsigned char *pub, size_t len);
int (*get_priv_key) (const EVP_PKEY *pk, unsigned char *priv, size_t *len);
int (*get_pub_key) (const EVP_PKEY *pk, unsigned char *pub, size_t *len);
/*
* TODO: Make sure these functions are defined for key types that are
* implemented in providers.
*/
/* Exports to providers */
size_t (*dirty_cnt) (const EVP_PKEY *pk);
void *(*export_to) (const EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
} /* EVP_PKEY_ASN1_METHOD */ ;
DEFINE_STACK_OF_CONST(EVP_PKEY_ASN1_METHOD)

View file

@ -545,6 +545,11 @@ struct evp_pkey_st {
EVP_KEYMGMT *keymgmt;
void *provkey;
} pkeys[10];
/*
* If there is a legacy key assigned to this structure, we keep
* a copy of that key's dirty count.
*/
size_t dirty_cnt_copy;
} /* EVP_PKEY */ ;
@ -555,6 +560,9 @@ void openssl_add_all_kdfs_int(void);
void evp_cleanup_int(void);
void evp_app_cleanup_int(void);
void *evp_keymgmt_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
/* Pulling defines out of C source files */
#define EVP_RC4_KEY_SIZE 16

View file

@ -0,0 +1,50 @@
=pod
=head1 NAME
evp_keymgmt_export_to_provider - key material exporter to providers for EVP
=head1 SYNOPSIS
#include "internal/evp_int.h"
void *evp_keymgmt_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
=head1 DESCRIPTION
evp_keymgmt_export_to_provider() exports the key material from the
given key I<pk> to a provider via a B<EVP_KEYMGMT> interface, if this
hasn't already been done.
It maintains a cache of provider key references in I<pk> to keep track
of all such exports.
If I<pk> has an assigned legacy key, a check is done to see if any of
its key material has changed since last export, i.e. the legacy key's
is_dirty() method returns 1.
If it has, the cache of already exported keys is cleared, and a new
export is made with the new key material.
=head1 RETURN VALUES
evp_keymgmt_export_to_provider() returns a pointer to the newly
created provider side key, or NULL on error.
=head1 NOTES
"Legacy key" is the term used for any key that has been assigned to an
B<EVP_PKEY> with EVP_PKEY_assign_RSA() and similar functions.
=head1 SEE ALSO
L<EVP_PKEY_ASN1_METHOD(3)>, L<EVP_PKEY_assign_RSA(3)>
=head1 COPYRIGHT
Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

View file

@ -361,6 +361,16 @@ public key data for an EVP_PKEY. They MUST return 0 on error, or 1 on success.
They are called by L<EVP_PKEY_new_raw_private_key(3)>, and
L<EVP_PKEY_new_raw_public_key(3)> respectively.
size_t (*dirty) (const EVP_PKEY *pk);
void *(*export_to) (const EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
dirty_cnt() returns the internal key's dirty count.
This can be used to synchronise different copies of the same keys.
The export_to() method exports the key material from the given key to
a provider, through the L<EVP_KEYMGMT(3)> interface, if that provider
supports importing key material.
=head2 Functions
EVP_PKEY_asn1_new() creates and returns a new B<EVP_PKEY_ASN1_METHOD>