From edfbe5c30f374db4e52d0ef262f980a10caba4c2 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 22 Mar 2022 16:16:57 +1100 Subject: [PATCH] Check that we can verify a signature at initialisation time Fedora 33 doesn't support RSASHA1 in future mode. There is no easy check for this other than by attempting to perform a verification using known good signatures. We don't attempt to sign with RSASHA1 as that would not work in FIPS mode. RSASHA1 is verify only. The test vectors were generated using OpenSSL 3.0 and util/gen-rsa-sha-vectors.c. Rerunning will generate a new set of test vectors as the private key is not preserved. e.g. cc util/gen-rsa-sha-vectors.c -I /opt/local/include \ -L /opt/local/lib -lcrypto --- lib/dns/opensslrsa_link.c | 245 ++++++++++++++++++++++++++++++++++++- util/gen-rsa-sha-vectors.c | 129 +++++++++++++++++++ 2 files changed, 370 insertions(+), 4 deletions(-) create mode 100644 util/gen-rsa-sha-vectors.c diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index 26c1182805..2e03a2f022 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -1562,14 +1562,251 @@ static dst_func_t opensslrsa_functions = { NULL, /*%< restore */ }; +/* + * An RSA public key with 2048 bits + */ +static const unsigned char e_bytes[] = "\x01\x00\x01"; +static const unsigned char n_bytes[] = + "\xc3\x90\x07\xbe\xf1\x85\xfc\x1a\x43\xb1\xa5\x15\xce\x71\x34\xfc\xc1" + "\x87\x27\x28\x38\xa4\xcf\x7c\x1a\x82\xa8\xdc\x04\x14\xd0\x3f\xb4\xfe" + "\x20\x4a\xdd\xd9\x0d\xd7\xcd\x61\x8c\xbd\x61\xa8\x10\xb5\x63\x1c\x29" + "\x15\xcb\x41\xee\x43\x91\x7f\xeb\xa5\x2c\xab\x81\x75\x0d\xa3\x3d\xe4" + "\xc8\x49\xb9\xca\x5a\x55\xa1\xbb\x09\xd1\xfb\xcd\xa2\xd2\x12\xa4\x85" + "\xdf\xa5\x65\xc9\x27\x2d\x8b\xd7\x8b\xfe\x6d\xc4\xd1\xd9\x83\x1c\x91" + "\x7d\x3d\xd0\xa4\xcd\xe1\xe7\xb9\x7a\x11\x38\xf9\x8b\x3c\xec\x30\xb6" + "\x36\xb9\x92\x64\x81\x56\x3c\xbc\xf9\x49\xfb\xba\x82\xb7\xa0\xfa\x65" + "\x79\x83\xb9\x4c\xa7\xfd\x53\x0b\x5a\xe4\xde\xf9\xfc\x38\x7e\xb5\x2c" + "\xa0\xc3\xb2\xfc\x7c\x38\xb0\x63\x50\xaf\x00\xaa\xb2\xad\x49\x54\x1e" + "\x8b\x11\x88\x9b\x6e\xae\x3b\x23\xa3\xdd\x53\x51\x80\x7a\x0b\x91\x4e" + "\x6d\x32\x01\xbd\x17\x81\x12\x64\x9f\x84\xae\x76\x53\x1a\x63\xa0\xda" + "\xcc\x45\x04\x72\xb0\xa7\xfb\xfa\x02\x39\x53\xc1\x83\x1f\x88\x54\x47" + "\x88\x63\x20\x71\x5d\xe2\xaa\x7c\x53\x39\x5e\x35\x25\xee\xe6\x5c\x15" + "\x5e\x14\xbe\x99\xde\x25\x19\xe7\x13\xdb\xce\xa3\xd3\x6c\x5c\xbb\x0e" + "\x6b"; + +static const unsigned char sha1_sig[] = + "\x69\x99\x89\x28\xe0\x38\x34\x91\x29\xb6\xac\x4b\xe9\x51\xbd\xbe\xc8" + "\x1a\x2d\xb6\xca\x99\xa3\x9f\x6a\x8b\x94\x5a\x51\x37\xd5\x8d\xae\x87" + "\xed\xbc\x8e\xb8\xa3\x60\x6b\xf6\xe6\x72\xfc\x26\x2a\x39\x2b\xfe\x88" + "\x1a\xa9\xd1\x93\xc7\xb9\xf8\xb6\x45\xa1\xf9\xa1\x56\x78\x7b\x00\xec" + "\x33\x83\xd4\x93\x25\x48\xb3\x50\x09\xd0\xbc\x7f\xac\x67\xc7\xa2\x7f" + "\xfc\xf6\x5a\xef\xf8\x5a\xad\x52\x74\xf5\x71\x34\xd9\x3d\x33\x8b\x4d" + "\x99\x64\x7e\x14\x59\xbe\xdf\x26\x8a\x67\x96\x6c\x1f\x79\x85\x10\x0d" + "\x7f\xd6\xa4\xba\x57\x41\x03\x71\x4e\x8c\x17\xd5\xc4\xfb\x4a\xbe\x66" + "\x45\x15\x45\x0c\x02\xe0\x10\xe1\xbb\x33\x8d\x90\x34\x3c\x94\xa4\x4c" + "\x7c\xd0\x5e\x90\x76\x80\x59\xb2\xfa\x54\xbf\xa9\x86\xb8\x84\x1e\x28" + "\x48\x60\x2f\x9e\xa4\xbc\xd4\x9c\x20\x27\x16\xac\x33\xcb\xcf\xab\x93" + "\x7a\x3b\x74\xa0\x18\x92\xa1\x4f\xfc\x52\x19\xee\x7a\x13\x73\xba\x36" + "\xaf\x78\x5d\xb6\x1f\x96\x76\x15\x73\xee\x04\xa8\x70\x27\xf7\xe7\xfa" + "\xe8\xf6\xc8\x5f\x4a\x81\x56\x0a\x94\xf3\xc6\x98\xd2\x93\xc4\x0b\x49" + "\x6b\x44\xd3\x73\xa2\xe3\xef\x5d\x9e\x68\xac\xa7\x42\xb1\xbb\x65\xbe" + "\x59"; + +static const unsigned char sha256_sig[] = + "\x0f\x8c\xdb\xe6\xb6\x21\xc8\xc5\x28\x76\x7d\xf6\xf2\x3b\x78\x47\x77" + "\x03\x34\xc5\x5e\xc0\xda\x42\x41\xc0\x0f\x97\xd3\xd0\x53\xa1\xd6\x87" + "\xe4\x16\x29\x9a\xa5\x59\xf4\x01\xad\xc9\x04\xe7\x61\xe2\xcb\x79\x73" + "\xce\xe0\xa6\x85\xe5\x10\x8c\x4b\xc5\x68\x3b\x96\x42\x3f\x56\xb3\x6d" + "\x89\xc4\xff\x72\x36\xf2\x3f\xed\xe9\xb8\xe3\xae\xab\x3c\xb7\xaa\xf7" + "\x1f\x8f\x26\x6b\xee\xc1\xac\x72\x89\x23\x8b\x7a\xd7\x8c\x84\xf3\xf5" + "\x97\xa8\x8d\xd3\xef\xb2\x5e\x06\x04\x21\xdd\x28\xa2\x28\x83\x68\x9b" + "\xac\x34\xdd\x36\x33\xda\xdd\xa4\x59\xc7\x5a\x4d\xf3\x83\x06\xd5\xc0" + "\x0d\x1f\x4f\x47\x2f\x9f\xcc\xc2\x0d\x21\x1e\x82\xb9\x3d\xf3\xa4\x1a" + "\xa6\xd8\x0e\x72\x1d\x71\x17\x1c\x54\xad\x37\x3e\xa4\x0e\x70\x86\x53" + "\xfb\x40\xad\xb9\x14\xf8\x8d\x93\xbb\xd7\xe7\x31\xce\xe0\x98\xda\x27" + "\x1c\x18\x8e\xd8\x85\xcb\xa7\xb1\x18\xac\x8c\xa8\x9d\xa9\xe2\xf6\x30" + "\x95\xa4\x81\xf4\x1c\xa0\x31\xd5\xc7\x9d\x28\x33\xee\x7f\x08\x4f\xcb" + "\xd1\x14\x17\xdf\xd0\x88\x78\x47\x29\xaf\x6c\xb2\x62\xa6\x30\x87\x29" + "\xaa\x80\x19\x7d\x2f\x05\xe3\x7e\x23\x73\x88\x08\xcc\xbd\x50\x46\x09" + "\x2a"; + +static const unsigned char sha512_sig[] = + "\x15\xda\x87\x87\x1f\x76\x08\xd3\x9d\x3a\xb9\xd2\x6a\x0e\x3b\x7d\xdd" + "\xec\x7d\xc4\x6d\x26\xf5\x04\xd3\x76\xc7\x83\xc4\x81\x69\x35\xe9\x47" + "\xbf\x49\xd1\xc0\xf9\x01\x4e\x0a\x34\x5b\xd0\xec\x6e\xe2\x2e\xe9\x2d" + "\x00\xfd\xe0\xa0\x28\x54\x53\x19\x49\x6d\xd2\x58\xb9\x47\xfa\x45\xad" + "\xd2\x1d\x52\xac\x80\xcb\xfc\x91\x97\x84\x58\x5f\xab\x21\x62\x60\x79" + "\xb8\x8a\x83\xe1\xf1\xcb\x05\x4c\x92\x56\x62\xd9\xbf\xa7\x81\x34\x23" + "\xdf\xd7\xa7\xc4\xdf\xde\x96\x00\x57\x4b\x78\x85\xb9\x3b\xdd\x3f\x98" + "\x88\x59\x1d\x48\xcf\x5a\xa8\xb7\x2a\x8b\x77\x93\x8e\x38\x3a\x0c\xa7" + "\x8a\x5f\xe6\x9f\xcb\xf0\x9a\x6b\xb6\x91\x04\x8b\x69\x6a\x37\xee\xa2" + "\xad\x5f\x31\x20\x96\xd6\x51\x80\xbf\x62\x48\xb8\xe4\x94\x10\x86\x4e" + "\xf2\x22\x1e\xa4\xd5\x54\xfe\xe1\x35\x49\xaf\xf8\x62\xfc\x11\xeb\xf7" + "\x3d\xd5\x5e\xaf\x11\xbd\x3d\xa9\x3a\x9f\x7f\xe8\xb4\x0d\xa2\xbb\x1c" + "\xbd\x4c\xed\x9e\x81\xb1\xec\xd3\xea\xaa\x03\xe3\x14\xdf\x8c\xb3\x78" + "\x85\x5e\x87\xad\xec\x41\x1a\xa9\x4f\xd2\xe6\xc6\xbe\xfa\xb8\x10\xea" + "\x74\x25\x36\x0c\x23\xe2\x24\xb7\x21\xb7\x0d\xaf\xf6\xb4\x31\xf5\x75" + "\xf1"; + +static isc_result_t +check_algorithm(unsigned char algorithm) { + BIGNUM *n = NULL, *e = NULL; + EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create(); + EVP_PKEY *pkey = NULL; + const EVP_MD *type = NULL; + const unsigned char *sig = NULL; + int status; + isc_result_t ret = ISC_R_SUCCESS; + size_t len; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa = NULL; +#else + OSSL_PARAM *params = NULL; + OSSL_PARAM_BLD *bld = NULL; + EVP_PKEY_CTX *ctx = NULL; +#endif + + if (evp_md_ctx == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + + switch (algorithm) { + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + type = EVP_sha1(); /* SHA1 + RSA */ + sig = sha1_sig; + len = sizeof(sha1_sig) - 1; + break; + case DST_ALG_RSASHA256: + type = EVP_sha256(); /* SHA256 + RSA */ + sig = sha256_sig; + len = sizeof(sha256_sig) - 1; + break; + case DST_ALG_RSASHA512: + type = EVP_sha512(); + sig = sha512_sig; + len = sizeof(sha512_sig) - 1; + break; + default: + DST_RET(ISC_R_NOTIMPLEMENTED); + } + + if (type == NULL) { + DST_RET(ISC_R_NOTIMPLEMENTED); + } + + /* + * Construct pkey. + */ + e = BN_bin2bn(e_bytes, sizeof(e_bytes) - 1, NULL); + n = BN_bin2bn(n_bytes, sizeof(n_bytes) - 1, NULL); + if (e == NULL || n == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + rsa = RSA_new(); + if (rsa == NULL) { + DST_RET(dst__openssl_toresult2("RSA_new", + DST_R_OPENSSLFAILURE)); + } + status = RSA_set0_key(rsa, n, e, NULL); + if (status != 1) { + DST_RET(dst__openssl_toresult2("RSA_set0_key", + DST_R_OPENSSLFAILURE)); + } + + /* These are now managed by OpenSSL. */ + n = NULL; + e = NULL; + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_new", + DST_R_OPENSSLFAILURE)); + } + status = EVP_PKEY_set1_RSA(pkey, rsa); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_set1_RSA", + DST_R_OPENSSLFAILURE)); + } +#else + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_new", + DST_R_OPENSSLFAILURE)); + } + if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e) != 1) + { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN", + DST_R_OPENSSLFAILURE)); + } + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_to_param", + DST_R_OPENSSLFAILURE)); + } + ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (ctx == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", + DST_R_OPENSSLFAILURE)); + } + status = EVP_PKEY_fromdata_init(ctx); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata_init", + DST_R_OPENSSLFAILURE)); + } + status = EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params); + if (status != 1 || pkey == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata", + DST_R_OPENSSLFAILURE)); + } +#endif + + /* + * Check that we can verify the signature. + */ + if (EVP_DigestInit_ex(evp_md_ctx, type, NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_VerifyFinal(evp_md_ctx, sig, len, pkey) != 1) + { + DST_RET(ISC_R_NOTIMPLEMENTED); + } + +err: + BN_free(e); + BN_free(n); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (rsa != NULL) { + RSA_free(rsa); + } +#else + if (bld != NULL) { + OSSL_PARAM_BLD_free(bld); + } + if (ctx != NULL) { + EVP_PKEY_CTX_free(ctx); + } + if (params != NULL) { + OSSL_PARAM_free(params); + } +#endif + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + if (evp_md_ctx != NULL) { + EVP_MD_CTX_destroy(evp_md_ctx); + } + ERR_clear_error(); + return (ret); +} + isc_result_t dst__opensslrsa_init(dst_func_t **funcp, unsigned char algorithm) { + isc_result_t result; + REQUIRE(funcp != NULL); - UNUSED(algorithm); + result = check_algorithm(algorithm); - if (*funcp == NULL) { - *funcp = &opensslrsa_functions; + if (result == ISC_R_SUCCESS) { + if (*funcp == NULL) { + *funcp = &opensslrsa_functions; + } + } else if (result == ISC_R_NOTIMPLEMENTED) { + result = ISC_R_SUCCESS; } - return (ISC_R_SUCCESS); + + return (result); } diff --git a/util/gen-rsa-sha-vectors.c b/util/gen-rsa-sha-vectors.c new file mode 100644 index 0000000000..7f76036b84 --- /dev/null +++ b/util/gen-rsa-sha-vectors.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Generate test vectors for lib/dns/opensslrsa_link.c as: + * + * Fedora 33 doesn't support RSASHA1 in future mode. There is no easy + * check for this other than by attempting to perform a verification + * using known good signatures. We don't attempt to sign with RSASHA1 + * as that would not work in FIPS mode. RSASHA1 is verify only. + * + * The test vectors were generated using OpenSSL 3.0 and + * util/gen-rsa-sha-vectors.c. Rerunning will generate a new set of + * test vectors as the private key is not preserved. + * + * e.g. + * cc util/gen-rsa-sha-vectors.c -I /opt/local/include \ + * -L /opt/local/lib -lcrypto + */ + +int +main() { + BIGNUM *e = BN_new(); + BIGNUM *n = BN_new(); + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + EVP_PKEY *pkey = NULL; + unsigned char buf[512]; + size_t bytes; + EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create(); + unsigned int siglen = sizeof(buf); + + if (e == NULL || n == NULL || ctx == NULL || evp_md_ctx == NULL) { + return (1); + } + + BN_set_bit(e, 0); + BN_set_bit(e, 16); + + if (EVP_PKEY_keygen_init(ctx) != 1 || + EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) != 1 || + EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, e) != 1 || + EVP_PKEY_keygen(ctx, &pkey) != 1 || pkey == NULL) + { + return (1); + } + + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n); + if (n == NULL) { + return (1); + } + + bytes = BN_num_bytes(e); + BN_bn2bin(e, buf); + printf("unsigned char e_bytes[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n"); + + bytes = BN_num_bytes(n); + BN_bn2bin(n, buf); + printf("unsigned char n_bytes[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + if (EVP_DigestInit_ex(evp_md_ctx, EVP_sha1(), NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_SignFinal(evp_md_ctx, buf, &siglen, pkey) != 1) + { + return (1); + } + bytes = siglen; + printf("unsigned char sha1_sig[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + if (EVP_DigestInit_ex(evp_md_ctx, EVP_sha256(), NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_SignFinal(evp_md_ctx, buf, &siglen, pkey) != 1) + { + return (1); + } + bytes = siglen; + printf("unsigned char sha256_sig[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + if (EVP_DigestInit_ex(evp_md_ctx, EVP_sha512(), NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_SignFinal(evp_md_ctx, buf, &siglen, pkey) != 1) + { + return (1); + } + bytes = siglen; + printf("unsigned char sha512_sig[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + return (0); +}