359 lines
11 KiB
C
359 lines
11 KiB
C
|
/*
|
||
|
* Copyright 2019-2021 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
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* RSA low level APIs are deprecated for public use, but still ok for
|
||
|
* internal use.
|
||
|
*/
|
||
|
#include "internal/deprecated.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <openssl/bio.h>
|
||
|
#include <openssl/bn.h>
|
||
|
#include <openssl/rsa.h>
|
||
|
#include <openssl/evp.h>
|
||
|
#include <openssl/pem.h>
|
||
|
#include <openssl/provider.h>
|
||
|
#include <openssl/core_names.h>
|
||
|
#include "internal/core.h"
|
||
|
#include "internal/nelem.h"
|
||
|
#include "crypto/evp.h" /* For the internal API */
|
||
|
#include "testutil.h"
|
||
|
|
||
|
typedef struct {
|
||
|
OSSL_LIB_CTX *ctx1;
|
||
|
OSSL_PROVIDER *prov1;
|
||
|
OSSL_LIB_CTX *ctx2;
|
||
|
OSSL_PROVIDER *prov2;
|
||
|
} FIXTURE;
|
||
|
|
||
|
/* Collected arguments */
|
||
|
static const char *cert_filename = NULL;
|
||
|
|
||
|
static void tear_down(FIXTURE *fixture)
|
||
|
{
|
||
|
if (fixture != NULL) {
|
||
|
OSSL_PROVIDER_unload(fixture->prov1);
|
||
|
OSSL_PROVIDER_unload(fixture->prov2);
|
||
|
OSSL_LIB_CTX_free(fixture->ctx1);
|
||
|
OSSL_LIB_CTX_free(fixture->ctx2);
|
||
|
OPENSSL_free(fixture);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static FIXTURE *set_up(const char *testcase_name)
|
||
|
{
|
||
|
FIXTURE *fixture;
|
||
|
|
||
|
if (!TEST_ptr(fixture = OPENSSL_zalloc(sizeof(*fixture)))
|
||
|
|| !TEST_ptr(fixture->ctx1 = OSSL_LIB_CTX_new())
|
||
|
|| !TEST_ptr(fixture->prov1 = OSSL_PROVIDER_load(fixture->ctx1,
|
||
|
"default"))
|
||
|
|| !TEST_ptr(fixture->ctx2 = OSSL_LIB_CTX_new())
|
||
|
|| !TEST_ptr(fixture->prov2 = OSSL_PROVIDER_load(fixture->ctx2,
|
||
|
"default"))) {
|
||
|
tear_down(fixture);
|
||
|
return NULL;
|
||
|
}
|
||
|
return fixture;
|
||
|
}
|
||
|
|
||
|
/* Array indexes */
|
||
|
#define N 0
|
||
|
#define E 1
|
||
|
#define D 2
|
||
|
#define P 3
|
||
|
#define Q 4
|
||
|
#define F3 5 /* Extra factor */
|
||
|
#define DP 6
|
||
|
#define DQ 7
|
||
|
#define E3 8 /* Extra exponent */
|
||
|
#define QINV 9
|
||
|
#define C2 10 /* Extra coefficient */
|
||
|
|
||
|
/*
|
||
|
* We have to do this because OSSL_PARAM_get_ulong() can't handle params
|
||
|
* holding data that isn't exactly sizeof(uint32_t) or sizeof(uint64_t),
|
||
|
* and because the other end deals with BIGNUM, the resulting param might
|
||
|
* be any size. In this particular test, we know that the expected data
|
||
|
* fits within an unsigned long, and we want to get the data in that form
|
||
|
* to make testing of values easier.
|
||
|
*/
|
||
|
static int get_ulong_via_BN(const OSSL_PARAM *p, unsigned long *goal)
|
||
|
{
|
||
|
BIGNUM *n = NULL;
|
||
|
int ret = 1; /* Ever so hopeful */
|
||
|
|
||
|
if (!TEST_true(OSSL_PARAM_get_BN(p, &n))
|
||
|
|| !TEST_int_ge(BN_bn2nativepad(n, (unsigned char *)goal, sizeof(*goal)), 0))
|
||
|
ret = 0;
|
||
|
BN_free(n);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int export_cb(const OSSL_PARAM *params, void *arg)
|
||
|
{
|
||
|
unsigned long *keydata = arg;
|
||
|
const OSSL_PARAM *p = NULL;
|
||
|
|
||
|
if (keydata == NULL)
|
||
|
return 0;
|
||
|
|
||
|
if (!TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[N]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[E]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[D])))
|
||
|
return 0;
|
||
|
|
||
|
if (!TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR1))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[P]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR2))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[Q]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR3))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[F3])))
|
||
|
return 0;
|
||
|
|
||
|
if (!TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_EXPONENT1))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[DP]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_EXPONENT2))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[DQ]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_EXPONENT3))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[E3])))
|
||
|
return 0;
|
||
|
|
||
|
if (!TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_COEFFICIENT1))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[QINV]))
|
||
|
|| !TEST_ptr(p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_COEFFICIENT2))
|
||
|
|| !TEST_true(get_ulong_via_BN(p, &keydata[C2])))
|
||
|
return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int test_pass_rsa(FIXTURE *fixture)
|
||
|
{
|
||
|
size_t i;
|
||
|
int ret = 0;
|
||
|
RSA *rsa = NULL;
|
||
|
BIGNUM *bn1 = NULL, *bn2 = NULL, *bn3 = NULL;
|
||
|
EVP_PKEY *pk = NULL, *dup_pk = NULL;
|
||
|
EVP_KEYMGMT *km = NULL, *km1 = NULL, *km2 = NULL, *km3 = NULL;
|
||
|
void *provkey = NULL, *provkey2 = NULL;
|
||
|
BIGNUM *bn_primes[1] = { NULL };
|
||
|
BIGNUM *bn_exps[1] = { NULL };
|
||
|
BIGNUM *bn_coeffs[1] = { NULL };
|
||
|
/*
|
||
|
* 32-bit RSA key, extracted from this command,
|
||
|
* executed with OpenSSL 1.0.2:
|
||
|
* An extra factor was added just for testing purposes.
|
||
|
*
|
||
|
* openssl genrsa 32 | openssl rsa -text
|
||
|
*/
|
||
|
static BN_ULONG expected[] = {
|
||
|
0xbc747fc5, /* N */
|
||
|
0x10001, /* E */
|
||
|
0x7b133399, /* D */
|
||
|
0xe963, /* P */
|
||
|
0xceb7, /* Q */
|
||
|
1, /* F3 */
|
||
|
0x8599, /* DP */
|
||
|
0xbd87, /* DQ */
|
||
|
2, /* E3 */
|
||
|
0xcc3b, /* QINV */
|
||
|
3, /* C3 */
|
||
|
0 /* Extra, should remain zero */
|
||
|
};
|
||
|
static unsigned long keydata[OSSL_NELEM(expected)] = { 0, };
|
||
|
|
||
|
if (!TEST_ptr(rsa = RSA_new()))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_ptr(bn1 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn1, expected[N]))
|
||
|
|| !TEST_ptr(bn2 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn2, expected[E]))
|
||
|
|| !TEST_ptr(bn3 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn3, expected[D]))
|
||
|
|| !TEST_true(RSA_set0_key(rsa, bn1, bn2, bn3)))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_ptr(bn1 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn1, expected[P]))
|
||
|
|| !TEST_ptr(bn2 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn2, expected[Q]))
|
||
|
|| !TEST_true(RSA_set0_factors(rsa, bn1, bn2)))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_ptr(bn1 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn1, expected[DP]))
|
||
|
|| !TEST_ptr(bn2 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn2, expected[DQ]))
|
||
|
|| !TEST_ptr(bn3 = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn3, expected[QINV]))
|
||
|
|| !TEST_true(RSA_set0_crt_params(rsa, bn1, bn2, bn3)))
|
||
|
goto err;
|
||
|
bn1 = bn2 = bn3 = NULL;
|
||
|
|
||
|
if (!TEST_ptr(bn_primes[0] = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn_primes[0], expected[F3]))
|
||
|
|| !TEST_ptr(bn_exps[0] = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn_exps[0], expected[E3]))
|
||
|
|| !TEST_ptr(bn_coeffs[0] = BN_new())
|
||
|
|| !TEST_true(BN_set_word(bn_coeffs[0], expected[C2]))
|
||
|
|| !TEST_true(RSA_set0_multi_prime_params(rsa, bn_primes, bn_exps,
|
||
|
bn_coeffs, 1)))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_ptr(pk = EVP_PKEY_new())
|
||
|
|| !TEST_true(EVP_PKEY_assign_RSA(pk, rsa)))
|
||
|
goto err;
|
||
|
rsa = NULL;
|
||
|
|
||
|
if (!TEST_ptr(km1 = EVP_KEYMGMT_fetch(fixture->ctx1, "RSA", NULL))
|
||
|
|| !TEST_ptr(km2 = EVP_KEYMGMT_fetch(fixture->ctx2, "RSA", NULL))
|
||
|
|| !TEST_ptr(km3 = EVP_KEYMGMT_fetch(fixture->ctx1, "RSA-PSS", NULL))
|
||
|
|| !TEST_ptr_ne(km1, km2))
|
||
|
goto err;
|
||
|
|
||
|
while (dup_pk == NULL) {
|
||
|
ret = 0;
|
||
|
km = km3;
|
||
|
/* Check that we can't export an RSA key into an RSA-PSS keymanager */
|
||
|
if (!TEST_ptr_null(provkey2 = evp_pkey_export_to_provider(pk, NULL,
|
||
|
&km,
|
||
|
NULL)))
|
||
|
goto err;
|
||
|
|
||
|
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;
|
||
|
|
||
|
/*
|
||
|
* At this point, the hope is that keydata will have all the numbers
|
||
|
* from the key.
|
||
|
*/
|
||
|
|
||
|
for (i = 0; i < OSSL_NELEM(expected); i++) {
|
||
|
int rv = TEST_int_eq(expected[i], keydata[i]);
|
||
|
|
||
|
if (!rv)
|
||
|
TEST_info("i = %zu", i);
|
||
|
else
|
||
|
ret++;
|
||
|
}
|
||
|
|
||
|
ret = (ret == OSSL_NELEM(expected));
|
||
|
if (!ret || !TEST_ptr(dup_pk = EVP_PKEY_dup(pk)))
|
||
|
goto err;
|
||
|
|
||
|
ret = TEST_int_eq(EVP_PKEY_eq(pk, dup_pk), 1);
|
||
|
EVP_PKEY_free(pk);
|
||
|
pk = dup_pk;
|
||
|
if (!ret)
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
err:
|
||
|
RSA_free(rsa);
|
||
|
BN_free(bn1);
|
||
|
BN_free(bn2);
|
||
|
BN_free(bn3);
|
||
|
EVP_PKEY_free(pk);
|
||
|
EVP_KEYMGMT_free(km1);
|
||
|
EVP_KEYMGMT_free(km2);
|
||
|
EVP_KEYMGMT_free(km3);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int (*tests[])(FIXTURE *) = {
|
||
|
test_pass_rsa
|
||
|
};
|
||
|
|
||
|
static int test_pass_key(int n)
|
||
|
{
|
||
|
SETUP_TEST_FIXTURE(FIXTURE, set_up);
|
||
|
EXECUTE_TEST(tests[n], tear_down);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int test_evp_pkey_export_to_provider(int n)
|
||
|
{
|
||
|
OSSL_LIB_CTX *libctx = NULL;
|
||
|
OSSL_PROVIDER *prov = NULL;
|
||
|
X509 *cert = NULL;
|
||
|
BIO *bio = NULL;
|
||
|
X509_PUBKEY *pubkey = NULL;
|
||
|
EVP_KEYMGMT *keymgmt = NULL;
|
||
|
EVP_PKEY *pkey = NULL;
|
||
|
void *keydata = NULL;
|
||
|
int ret = 0;
|
||
|
|
||
|
if (!TEST_ptr(libctx = OSSL_LIB_CTX_new())
|
||
|
|| !TEST_ptr(prov = OSSL_PROVIDER_load(libctx, "default")))
|
||
|
goto end;
|
||
|
|
||
|
if ((bio = BIO_new_file(cert_filename, "r")) == NULL) {
|
||
|
TEST_error("Couldn't open '%s' for reading\n", cert_filename);
|
||
|
TEST_openssl_errors();
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL) {
|
||
|
TEST_error("'%s' doesn't appear to be a X.509 certificate in PEM format\n",
|
||
|
cert_filename);
|
||
|
TEST_openssl_errors();
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
pubkey = X509_get_X509_PUBKEY(cert);
|
||
|
pkey = X509_PUBKEY_get0(pubkey);
|
||
|
|
||
|
if (n == 0) {
|
||
|
if (!TEST_ptr(keydata = evp_pkey_export_to_provider(pkey, NULL,
|
||
|
NULL, NULL)))
|
||
|
goto end;
|
||
|
} else if (n == 1) {
|
||
|
if (!TEST_ptr(keydata = evp_pkey_export_to_provider(pkey, NULL,
|
||
|
&keymgmt, NULL)))
|
||
|
goto end;
|
||
|
} else {
|
||
|
keymgmt = EVP_KEYMGMT_fetch(libctx, "RSA", NULL);
|
||
|
|
||
|
if (!TEST_ptr(keydata = evp_pkey_export_to_provider(pkey, NULL,
|
||
|
&keymgmt, NULL)))
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
ret = 1;
|
||
|
end:
|
||
|
BIO_free(bio);
|
||
|
X509_free(cert);
|
||
|
EVP_KEYMGMT_free(keymgmt);
|
||
|
OSSL_PROVIDER_unload(prov);
|
||
|
OSSL_LIB_CTX_free(libctx);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int setup_tests(void)
|
||
|
{
|
||
|
if (!TEST_ptr(cert_filename = test_get_argument(0)))
|
||
|
return 0;
|
||
|
|
||
|
ADD_ALL_TESTS(test_pass_key, 1);
|
||
|
ADD_ALL_TESTS(test_evp_pkey_export_to_provider, 3);
|
||
|
return 1;
|
||
|
}
|