diff --git a/crypto/fips/fips_ecdsa_selftest.c b/crypto/fips/fips_ecdsa_selftest.c index 9895aa8..77a1c77 100644 --- a/crypto/fips/fips_ecdsa_selftest.c +++ b/crypto/fips/fips_ecdsa_selftest.c @@ -65,102 +65,319 @@ #include #ifdef OPENSSL_FIPS +#include +#include "internal/nelem.h" +#include "fips_locl.h" -static const char P_256_name[] = "ECDSA P-256"; +/* functions to change the RAND_METHOD */ +static int fbytes(unsigned char *buf, int num); -static const unsigned char P_256_d[] = { - 0x51, 0xbd, 0x06, 0xa1, 0x1c, 0xda, 0xe2, 0x12, 0x99, 0xc9, 0x52, 0x3f, - 0xea, 0xa4, 0xd2, 0xd1, 0xf4, 0x7f, 0xd4, 0x3e, 0xbd, 0xf8, 0xfc, 0x87, - 0xdc, 0x82, 0x53, 0x21, 0xee, 0xa0, 0xdc, 0x64 -}; +static RAND_METHOD fake_rand; +static const RAND_METHOD *old_rand; +static int use_fake = 0; +static const unsigned char *numbers[2]; +static int numbers_len[2]; -static const unsigned char P_256_qx[] = { - 0x23, 0x89, 0xe0, 0xf4, 0x69, 0xe0, 0x49, 0xe5, 0xc7, 0xe5, 0x40, 0x6e, - 0x8f, 0x25, 0xdd, 0xad, 0x11, 0x16, 0x14, 0x9b, 0xab, 0x44, 0x06, 0x31, - 0xbf, 0x5e, 0xa6, 0x44, 0xac, 0x86, 0x00, 0x07 -}; +static int change_rand(void) +{ + /* save old rand method */ + old_rand = RAND_get_rand_method(); + if (!old_rand) + return 0; + + fake_rand = *old_rand; + /* use own random function */ + fake_rand.bytes = fbytes; + /* set new RAND_METHOD */ + if (!RAND_set_rand_method(&fake_rand)) + return 0; + + return 1; +} -static const unsigned char P_256_qy[] = { - 0xb3, 0x05, 0x0d, 0xd0, 0xdc, 0xf7, 0x40, 0xe6, 0xf9, 0xd8, 0x6d, 0x7b, - 0x63, 0xca, 0x97, 0xe6, 0x12, 0xf9, 0xd4, 0x18, 0x59, 0xbe, 0xb2, 0x5e, - 0x4a, 0x6a, 0x77, 0x23, 0xf4, 0x11, 0x9d, 0xeb -}; +static int restore_rand(void) +{ + if (!RAND_set_rand_method(old_rand)) + return 0; + + return 1; +} + +static int fbytes(unsigned char *buf, int num) +{ + int ret = 0; + static int fbytes_counter = 0; + + if (use_fake == 0) + return old_rand->bytes(buf, num); + + use_fake = 0; + + if (fbytes_counter >= OSSL_NELEM(numbers)) + goto err; + + if (numbers_len[fbytes_counter] > num) + goto err; + + /* first zero out the buffer */ + memset(buf, 0, num); + + /* Now set the "random" values */ + memcpy(buf + (num - numbers_len[fbytes_counter]), numbers[fbytes_counter], numbers_len[fbytes_counter]); + + fbytes_counter = (fbytes_counter + 1) % OSSL_NELEM(numbers); + ret = 1; +err: + return ret; +} + + + +/*- + * NIST CAVP ECDSA KATs + * 2 X9.62 KATs; one for prime fields and one for binary fields. + * + * Taken from: + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/dss/186-3ecdsatestvectors.zip + */ typedef struct { - int curve; - const char *name; - const unsigned char *x; - size_t xlen; - const unsigned char *y; - size_t ylen; - const unsigned char *d; - size_t dlen; -} EC_SELFTEST_DATA; - -# define make_ecdsa_test(nid, pr) { nid, pr##_name, \ - pr##_qx, sizeof(pr##_qx), \ - pr##_qy, sizeof(pr##_qy), \ - pr##_d, sizeof(pr##_d)} - -static EC_SELFTEST_DATA test_ec_data[] = { - make_ecdsa_test(NID_X9_62_prime256v1, P_256), -}; + const int nid; /* curve NID */ + const int md_nid; /* hash function NID */ + const unsigned char *msg; /* message to sign */ + size_t msglen; + const unsigned char *d; /* ECDSA private key */ + size_t dlen; + const unsigned char *Q; /* ECDSA public key: (Qx,Qy) */ + size_t Qlen; + const unsigned char *k; /* ECDSA nonce */ + size_t klen; + const unsigned char *r; /* ECDSA signature (r,s) */ + size_t rlen; + const unsigned char *s; + size_t slen; +} ECDSA_KAT_SELFTEST_DATA; -int FIPS_selftest_ecdsa() -{ - EC_KEY *ec = NULL; - BIGNUM *x = NULL, *y = NULL, *d = NULL; - EVP_PKEY *pk = NULL; - int rv = 0; - size_t i; - for (i = 0; i < sizeof(test_ec_data) / sizeof(EC_SELFTEST_DATA); i++) { - EC_SELFTEST_DATA *ecd = test_ec_data + i; +static const unsigned char data1_msg[] = { + 0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, + 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6, 0xf2, 0xd9, + 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, + 0x0c, 0xbd, 0x25, 0x48, 0x29, 0x35, 0xfa, 0xaf, + 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, + 0x5e, 0xe2, 0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, + 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a, + 0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, + 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb, 0x1d, 0xdf, + 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, + 0x8a, 0xd2, 0x81, 0x75, 0xbf, 0x9b, 0xd3, 0x86, + 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, + 0xc9, 0x34, 0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, + 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f, + 0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, + 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7, 0x9c, 0xe8 +}; - x = BN_bin2bn(ecd->x, ecd->xlen, x); - y = BN_bin2bn(ecd->y, ecd->ylen, y); - d = BN_bin2bn(ecd->d, ecd->dlen, d); +static const unsigned char data1_d[] = { + 0x51, 0x9b, 0x42, 0x3d, 0x71, 0x5f, 0x8b, 0x58, + 0x1f, 0x4f, 0xa8, 0xee, 0x59, 0xf4, 0x77, 0x1a, + 0x5b, 0x44, 0xc8, 0x13, 0x0b, 0x4e, 0x3e, 0xac, + 0xca, 0x54, 0xa5, 0x6d, 0xda, 0x72, 0xb4, 0x64 +}; - if (!x || !y || !d) - goto err; +static const unsigned char data1_Q[] = { + 0x04, 0x0c, 0xec, 0x02, 0x8e, 0xe0, 0x8d, 0x09, + 0xe0, 0x26, 0x72, 0xa6, 0x83, 0x10, 0x81, 0x43, + 0x54, 0xf9, 0xea, 0xbf, 0xff, 0x0d, 0xe6, 0xda, + 0xcc, 0x1c, 0xd3, 0xa7, 0x74, 0x49, 0x60, 0x76, + 0xae, 0xef, 0xf4, 0x71, 0xfb, 0xa0, 0x40, 0x98, + 0x97, 0xb6, 0xa4, 0x8e, 0x88, 0x01, 0xad, 0x12, + 0xf9, 0x5d, 0x00, 0x09, 0xb7, 0x53, 0xcf, 0x8f, + 0x51, 0xc1, 0x28, 0xbf, 0x6b, 0x0b, 0xd2, 0x7f, + 0xbd +}; - ec = EC_KEY_new_by_curve_name(ecd->curve); - if (!ec) - goto err; +static const unsigned char data1_k[] = { + 0x94, 0xa1, 0xbb, 0xb1, 0x4b, 0x90, 0x6a, 0x61, + 0xa2, 0x80, 0xf2, 0x45, 0xf9, 0xe9, 0x3c, 0x7f, + 0x3b, 0x4a, 0x62, 0x47, 0x82, 0x4f, 0x5d, 0x33, + 0xb9, 0x67, 0x07, 0x87, 0x64, 0x2a, 0x68, 0xde +}; - if (!EC_KEY_set_public_key_affine_coordinates(ec, x, y)) - goto err; +static const unsigned char data1_r[] = { + 0xe3, 0x95, 0xf6, 0xdb, 0x12, 0x71, 0x90, 0xfa, + 0x70, 0xa6, 0x80, 0xeb, 0xf6, 0x8a, 0x18, 0x35, + 0x6f, 0xef, 0xf2, 0x36, 0x65, 0xb9, 0x31, 0xc3, + 0xa2, 0x14, 0x80, 0xdf, 0x86, 0xc4, 0xec, 0xbc +}; - if (!EC_KEY_set_private_key(ec, d)) - goto err; +static const unsigned char data1_s[] = { + 0xa5, 0x01, 0x04, 0x78, 0x93, 0xd9, 0x60, 0xcc, + 0x20, 0xce, 0xbd, 0xbb, 0x6f, 0x79, 0xb9, 0x7e, + 0x45, 0x23, 0x80, 0x73, 0x87, 0x83, 0x53, 0x63, + 0xe3, 0x80, 0x2b, 0x68, 0xcf, 0x32, 0xa1, 0xa2 +}; - if ((pk = EVP_PKEY_new()) == NULL) - goto err; - EVP_PKEY_assign_EC_KEY(pk, ec); +# define make_ecdsa_kat_test(nid, md_nid, pr) { \ +nid, md_nid, \ +pr##_msg, sizeof(pr##_msg), \ +pr##_d, sizeof(pr##_d), \ +pr##_Q, sizeof(pr##_Q), \ +pr##_k, sizeof(pr##_k), \ +pr##_r, sizeof(pr##_r), \ +pr##_s, sizeof(pr##_s) \ +} - if (!fips_pkey_signature_test(pk, NULL, 0, - NULL, 0, EVP_sha256(), 0, ecd->name)) - goto err; - } +static ECDSA_KAT_SELFTEST_DATA test_ecdsa_data[] = { + make_ecdsa_kat_test(NID_secp256k1, NID_sha256, data1) +}; - rv = 1; +int FIPS_selftest_ecdsa() +{ + int rv; + size_t i, siglen, p_len; + + for (i = 0; i < sizeof(test_ecdsa_data) / sizeof(ECDSA_KAT_SELFTEST_DATA); i++) { + EC_KEY *ec = NULL; + BIGNUM *r = NULL, *s = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; + EVP_PKEY *pk = NULL; + unsigned char *sig = NULL; + unsigned char *tsig = NULL; + unsigned char *p_buf = NULL; + ECDSA_SIG *dsa_sig = NULL; + rv = 0; + + ECDSA_KAT_SELFTEST_DATA *ecd = test_ecdsa_data + i; + + /* Create the Message Digest Context */ + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (!mdctx) goto err; + + r = BN_bin2bn(ecd->r, ecd->rlen, r); + s = BN_bin2bn(ecd->s, ecd->slen, s); + + if (!r || !s) + goto err; + + /* d[] will be used to generate a key. */ + /* k[] will be used for signature generation. */ + numbers[0] = ecd->d; + numbers_len[0] = ecd->dlen; + numbers[1] = ecd->k; + numbers_len[1] = ecd->klen; + /* swap the RNG source */ + if (!change_rand()) + goto err; + + ec = EC_KEY_new_by_curve_name(ecd->nid); + if (!ec) + goto err; + + /* Use d[] to generate key. */ + use_fake = 1; + if (EC_KEY_generate_key(ec) != 1) + goto err; + + if ((pk = EVP_PKEY_new()) == NULL) + goto err; + + EVP_PKEY_assign_EC_KEY(pk, ec); + + p_len = EC_KEY_key2buf(ec, POINT_CONVERSION_UNCOMPRESSED, &p_buf, NULL); + if (!p_len) + goto err; + + /* Make sure generated public key matches */ + if (p_len != ecd->Qlen) + goto err; + if (memcmp(p_buf, ecd->Q, p_len)) + goto err; + + /* Initialise the DigestSign operation */ + if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_get_digestbynid(ecd->md_nid), NULL, pk)) + goto err; + + /* Call update with the message */ + if(1 != EVP_DigestSignUpdate(mdctx, ecd->msg, ecd->msglen)) + goto err; + + /* Finalise the DigestSign operation */ + /* First call EVP_DigestSignFinal with a NULL sig parameter to */ + /* obtain the length of the signature. Length is returned in slen */ + if(1 != EVP_DigestSignFinal(mdctx, NULL, &siglen)) + goto err; + + /* Allocate memory for the signature based on size in slen */ + if(!(sig = OPENSSL_malloc(siglen))) + goto err; + + /* Use k[] for signature. */ + use_fake = 1; + + /* Obtain the signature */ + if(1 != EVP_DigestSignFinal(mdctx, sig, &siglen)) + goto err; - err: + /* extract r and s */ + tsig = sig; + dsa_sig = d2i_ECDSA_SIG(NULL, &tsig, siglen); + if (dsa_sig == NULL) + goto err; + + sig_r = ECDSA_SIG_get0_r(dsa_sig); + sig_s = ECDSA_SIG_get0_s(dsa_sig); + if ((sig_r == NULL) || (sig_s == NULL)) + goto err; - if (x) - BN_clear_free(x); - if (y) - BN_clear_free(y); - if (d) - BN_clear_free(d); + /* Compare r and s against known. */ + if ((BN_cmp(sig_r, r) != 0) || (BN_cmp(sig_s, s) != 0)) + goto err; + + /* Verify signature */ + if(1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_get_digestbynid(ecd->md_nid), NULL, pk)) + goto err; + + if (EVP_DigestVerify(mdctx, sig, siglen, ecd->msg, ecd->msglen) != 1) + goto err; + + if (1 != restore_rand()) + goto err; + + /* Success */ + rv = 1; + + + err: + + if (mdctx) + EVP_MD_CTX_free(mdctx); + if (r) + BN_clear_free(r); + if (s) + BN_clear_free(s); + if (sig) + OPENSSL_free(sig); + if (dsa_sig) + ECDSA_SIG_free(dsa_sig); + if (p_buf) + OPENSSL_free(p_buf); if (pk) - EVP_PKEY_free(pk); + EVP_PKEY_free(pk); else if (ec) - EC_KEY_free(ec); - - return rv; + EC_KEY_free(ec); + + if (rv != 1) { + FIPSerr(FIPS_F_FIPS_SELFTEST_ECDSA, FIPS_R_SELFTEST_FAILED); + break; + } + + } + return rv; + } + #endif