Add FFDH FIPS self-test.
This invovles a |2048|^|225| modexp, which is far from ideal, but is now
required in FIPS mode.
Change-Id: Id7384b4ba92aa74e971231bc44fa0f10434d18e2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45085
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/bn/bn.c b/crypto/fipsmodule/bn/bn.c
index 4bed2d3..424d462 100644
--- a/crypto/fipsmodule/bn/bn.c
+++ b/crypto/fipsmodule/bn/bn.c
@@ -283,6 +283,18 @@
return 1;
}
+void bn_set_static_words(BIGNUM *bn, const BN_ULONG *words, size_t num) {
+ if ((bn->flags & BN_FLG_STATIC_DATA) == 0) {
+ OPENSSL_free(bn->d);
+ }
+ bn->d = (BN_ULONG *)words;
+
+ bn->width = num;
+ bn->dmax = num;
+ bn->neg = 0;
+ bn->flags |= BN_FLG_STATIC_DATA;
+}
+
int bn_fits_in_words(const BIGNUM *bn, size_t num) {
// All words beyond |num| must be zero.
BN_ULONG mask = 0;
diff --git a/crypto/fipsmodule/bn/internal.h b/crypto/fipsmodule/bn/internal.h
index e11ee45..623e0c6 100644
--- a/crypto/fipsmodule/bn/internal.h
+++ b/crypto/fipsmodule/bn/internal.h
@@ -241,6 +241,14 @@
// least significant word first.
int bn_set_words(BIGNUM *bn, const BN_ULONG *words, size_t num);
+// bn_set_static_words acts like |bn_set_words|, but doesn't copy the data. A
+// flag is set on |bn| so that |BN_free| won't attempt to free the data.
+//
+// The |STATIC_BIGNUM| macro is probably a better solution for this outside of
+// the FIPS module. Inside of the FIPS module that macro generates rel.ro data,
+// which doesn't work with FIPS requirements.
+void bn_set_static_words(BIGNUM *bn, const BN_ULONG *words, size_t num);
+
// bn_fits_in_words returns one if |bn| may be represented in |num| words, plus
// a sign bit, and zero otherwise.
int bn_fits_in_words(const BIGNUM *bn, size_t num);
diff --git a/crypto/fipsmodule/self_check/self_check.c b/crypto/fipsmodule/self_check/self_check.c
index 6d95193..de6608b 100644
--- a/crypto/fipsmodule/self_check/self_check.c
+++ b/crypto/fipsmodule/self_check/self_check.c
@@ -246,6 +246,77 @@
return ec_key;
}
+static DH *self_test_dh(void) {
+ // This is the prime from https://tools.ietf.org/html/rfc7919#appendix-A.1,
+ // which is specifically approved for FIPS in appendix D of SP 800-56Ar3.
+ static const BN_ULONG kFFDHE2048Data[] = {
+ TOBN(0xffffffff, 0xffffffff), TOBN(0x886b4238, 0x61285c97),
+ TOBN(0xc6f34a26, 0xc1b2effa), TOBN(0xc58ef183, 0x7d1683b2),
+ TOBN(0x3bb5fcbc, 0x2ec22005), TOBN(0xc3fe3b1b, 0x4c6fad73),
+ TOBN(0x8e4f1232, 0xeef28183), TOBN(0x9172fe9c, 0xe98583ff),
+ TOBN(0xc03404cd, 0x28342f61), TOBN(0x9e02fce1, 0xcdf7e2ec),
+ TOBN(0x0b07a7c8, 0xee0a6d70), TOBN(0xae56ede7, 0x6372bb19),
+ TOBN(0x1d4f42a3, 0xde394df4), TOBN(0xb96adab7, 0x60d7f468),
+ TOBN(0xd108a94b, 0xb2c8e3fb), TOBN(0xbc0ab182, 0xb324fb61),
+ TOBN(0x30acca4f, 0x483a797a), TOBN(0x1df158a1, 0x36ade735),
+ TOBN(0xe2a689da, 0xf3efe872), TOBN(0x984f0c70, 0xe0e68b77),
+ TOBN(0xb557135e, 0x7f57c935), TOBN(0x85636555, 0x3ded1af3),
+ TOBN(0x2433f51f, 0x5f066ed0), TOBN(0xd3df1ed5, 0xd5fd6561),
+ TOBN(0xf681b202, 0xaec4617a), TOBN(0x7d2fe363, 0x630c75d8),
+ TOBN(0xcc939dce, 0x249b3ef9), TOBN(0xa9e13641, 0x146433fb),
+ TOBN(0xd8b9c583, 0xce2d3695), TOBN(0xafdc5620, 0x273d3cf1),
+ TOBN(0xadf85458, 0xa2bb4a9a), TOBN(0xffffffff, 0xffffffff),
+ };
+
+ // kFFDHE2048PrivateKeyData is a 225-bit value. (225 because that's the
+ // minimum private key size in
+ // https://tools.ietf.org/html/rfc7919#appendix-A.1.)
+ static const BN_ULONG kFFDHE2048PrivateKeyData[] = {
+ TOBN(0x187be36b, 0xd38a4fa1),
+ TOBN(0x0a152f39, 0x6458f3b8),
+ TOBN(0x0570187e, 0xc422eeb7),
+ TOBN(0x00000001, 0x91173f2a),
+ };
+
+ BIGNUM *const ffdhe2048_p = BN_new();
+ BIGNUM *const ffdhe2048_q = BN_new();
+ BIGNUM *const ffdhe2048_g = BN_new();
+ BIGNUM *ffdhe2048_priv = BN_new();
+ DH *const dh = DH_new();
+
+ if (!ffdhe2048_p || !ffdhe2048_q || !ffdhe2048_g || !ffdhe2048_priv || !dh) {
+ goto err;
+ }
+
+ bn_set_static_words(ffdhe2048_p, kFFDHE2048Data,
+ OPENSSL_ARRAY_SIZE(kFFDHE2048Data));
+ bn_set_static_words(ffdhe2048_priv, kFFDHE2048PrivateKeyData,
+ OPENSSL_ARRAY_SIZE(kFFDHE2048PrivateKeyData));
+
+ if (!BN_copy(ffdhe2048_q, ffdhe2048_p) ||
+ !BN_sub_word(ffdhe2048_q, 1) ||
+ BN_div_word(ffdhe2048_q, 2) != 0 ||
+ !BN_set_word(ffdhe2048_g, 2) ||
+ !DH_set0_key(dh, NULL, ffdhe2048_priv)) {
+ goto err;
+ }
+
+ ffdhe2048_priv = NULL;
+ if (!DH_set0_pqg(dh, ffdhe2048_p, ffdhe2048_q, ffdhe2048_g)) {
+ goto err;
+ }
+
+ return dh;
+
+err:
+ BN_free(ffdhe2048_p);
+ BN_free(ffdhe2048_q);
+ BN_free(ffdhe2048_g);
+ BN_free(ffdhe2048_priv);
+ DH_free(dh);
+ return NULL;
+}
+
#if defined(OPENSSL_ANDROID)
static const size_t kModuleDigestSize = SHA256_DIGEST_LENGTH;
#else
@@ -487,6 +558,57 @@
0x31, 0x1e, 0x2b, 0x21, 0x41, 0x8d, 0x32, 0x81,
};
+ // kFFDHE2048PublicValueData is an arbitrary public value, mod
+ // kFFDHE2048Data. (The private key happens to be 4096.)
+ static const BN_ULONG kFFDHE2048PublicValueData[] = {
+ TOBN(0x187be36b, 0xd38a4fa1), TOBN(0x0a152f39, 0x6458f3b8),
+ TOBN(0x0570187e, 0xc422eeb7), TOBN(0x18af7482, 0x91173f2a),
+ TOBN(0xe9fdac6a, 0xcff4eaaa), TOBN(0xf6afebb7, 0x6e589d6c),
+ TOBN(0xf92f8e9a, 0xb7e33fb0), TOBN(0x70acf2aa, 0x4cf36ddd),
+ TOBN(0x561ab426, 0xd07137fd), TOBN(0x5f57d037, 0x430ee91e),
+ TOBN(0xe3e768c8, 0x60d10b8a), TOBN(0xb14884d8, 0xa18af8ce),
+ TOBN(0xf8a98014, 0xa12b74e4), TOBN(0x748d407c, 0x3437b7a8),
+ TOBN(0x627588c4, 0x9875d5a7), TOBN(0xdd24a127, 0x53c8f09d),
+ TOBN(0x85a997d5, 0x0cd51aec), TOBN(0x44f0c619, 0xce348458),
+ TOBN(0x9b894b24, 0x5f6b69a1), TOBN(0xae1302f2, 0xf6d4777e),
+ TOBN(0xe6678eeb, 0x375db18e), TOBN(0x2674e1d6, 0x4fbcbdc8),
+ TOBN(0xb297a823, 0x6fa93d28), TOBN(0x6a12fb70, 0x7c8c0510),
+ TOBN(0x5c6d1aeb, 0xdb06f65b), TOBN(0xe8c2954e, 0x4c1804ca),
+ TOBN(0x06bdeac1, 0xf5500fa7), TOBN(0x6a315604, 0x189cd76b),
+ TOBN(0xbae7b0b3, 0x6e362dc0), TOBN(0xa57c73bd, 0xdc70fb82),
+ TOBN(0xfaff50d2, 0x9d573457), TOBN(0x352bd399, 0xbe84058e),
+ };
+
+ const uint8_t kDHOutput[2048 / 8] = {
+ 0x2a, 0xe6, 0xd3, 0xa6, 0x13, 0x58, 0x8e, 0xce, 0x53, 0xaa, 0xf6, 0x5d,
+ 0x9a, 0xae, 0x02, 0x12, 0xf5, 0x80, 0x3d, 0x06, 0x09, 0x76, 0xac, 0x57,
+ 0x37, 0x9e, 0xab, 0x38, 0x62, 0x25, 0x05, 0x1d, 0xf3, 0xa9, 0x39, 0x60,
+ 0xf6, 0xae, 0x90, 0xed, 0x1e, 0xad, 0x6e, 0xe9, 0xe3, 0xba, 0x27, 0xf6,
+ 0xdb, 0x54, 0xdf, 0xe2, 0xbd, 0xbb, 0x7f, 0xf1, 0x81, 0xac, 0x1a, 0xfa,
+ 0xdb, 0x87, 0x07, 0x98, 0x76, 0x90, 0x21, 0xf2, 0xae, 0xda, 0x0d, 0x84,
+ 0x97, 0x64, 0x0b, 0xbf, 0xb8, 0x8d, 0x10, 0x46, 0xe2, 0xd5, 0xca, 0x1b,
+ 0xbb, 0xe5, 0x37, 0xb2, 0x3b, 0x35, 0xd3, 0x1b, 0x65, 0xea, 0xae, 0xf2,
+ 0x03, 0xe2, 0xb6, 0xde, 0x22, 0xb7, 0x86, 0x49, 0x79, 0xfe, 0xd7, 0x16,
+ 0xf7, 0xdc, 0x9c, 0x59, 0xf5, 0xb7, 0x70, 0xc0, 0x53, 0x42, 0x6f, 0xb1,
+ 0xd2, 0x4e, 0x00, 0x25, 0x4b, 0x2d, 0x5a, 0x9b, 0xd0, 0xe9, 0x27, 0x43,
+ 0xcc, 0x00, 0x66, 0xea, 0x94, 0x7a, 0x0b, 0xb9, 0x89, 0x0c, 0x5e, 0x94,
+ 0xb8, 0x3a, 0x78, 0x9c, 0x4d, 0x84, 0xe6, 0x32, 0x2c, 0x38, 0x7c, 0xf7,
+ 0x43, 0x9c, 0xd8, 0xb8, 0x1c, 0xce, 0x24, 0x91, 0x20, 0x67, 0x7a, 0x54,
+ 0x1f, 0x7e, 0x86, 0x7f, 0xa1, 0xc1, 0x03, 0x4e, 0x2c, 0x26, 0x71, 0xb2,
+ 0x06, 0x30, 0xb3, 0x6c, 0x15, 0xcc, 0xac, 0x25, 0xe5, 0x37, 0x3f, 0x24,
+ 0x8f, 0x2a, 0x89, 0x5e, 0x3d, 0x43, 0x94, 0xc9, 0x36, 0xae, 0x40, 0x00,
+ 0x6a, 0x0d, 0xb0, 0x6e, 0x8b, 0x2e, 0x70, 0x57, 0xe1, 0x88, 0x53, 0xd6,
+ 0x06, 0x80, 0x2a, 0x4e, 0x5a, 0xf0, 0x1e, 0xaa, 0xcb, 0xab, 0x06, 0x0e,
+ 0x27, 0x0f, 0xd9, 0x88, 0xd9, 0x01, 0xe3, 0x07, 0xeb, 0xdf, 0xc3, 0x12,
+ 0xe3, 0x40, 0x88, 0x7b, 0x5f, 0x59, 0x78, 0x6e, 0x26, 0x20, 0xc3, 0xdf,
+ 0xc8, 0xe4, 0x5e,
+#if !defined(BORINGSSL_FIPS_BREAK_FFC_DH)
+ 0xb8,
+#else
+ 0x00,
+#endif
+ };
+
EVP_AEAD_CTX aead_ctx;
EVP_AEAD_CTX_zero(&aead_ctx);
RSA *rsa_key = NULL;
@@ -692,6 +814,29 @@
goto err;
}
+ // FFC Diffie-Hellman KAT
+
+ BIGNUM *const ffdhe2048_value = BN_new();
+ DH *const dh = self_test_dh();
+ int dh_ok = 0;
+ if (ffdhe2048_value && dh) {
+ bn_set_static_words(ffdhe2048_value, kFFDHE2048PublicValueData,
+ OPENSSL_ARRAY_SIZE(kFFDHE2048PublicValueData));
+
+ uint8_t dh_out[sizeof(kDHOutput)];
+ dh_ok =
+ sizeof(dh_out) == DH_size(dh) &&
+ DH_compute_key_padded(dh_out, ffdhe2048_value, dh) == sizeof(dh_out) &&
+ check_test(kDHOutput, dh_out, sizeof(dh_out), "FFC DH");
+ }
+
+ BN_free(ffdhe2048_value);
+ DH_free(dh);
+ if (!dh_ok) {
+ fprintf(stderr, "FFDH failed.\n");
+ goto err;
+ }
+
// DBRG KAT
CTR_DRBG_STATE drbg;
if (!CTR_DRBG_init(&drbg, kDRBGEntropy, kDRBGPersonalization,
diff --git a/util/fipstools/break-tests-android.sh b/util/fipstools/break-tests-android.sh
index 61b2b4f..efb166e 100644
--- a/util/fipstools/break-tests-android.sh
+++ b/util/fipstools/break-tests-android.sh
@@ -42,7 +42,7 @@
. build/envsetup.sh
-TESTS="NONE ECDSA_PWCT CRNG RSA_PWCT AES_CBC AES_GCM DES SHA_1 SHA_256 SHA_512 RSA_SIG DRBG ECDSA_SIG Z_COMPUTATION TLS_KDF"
+TESTS="NONE ECDSA_PWCT CRNG RSA_PWCT AES_CBC AES_GCM DES SHA_1 SHA_256 SHA_512 RSA_SIG DRBG ECDSA_SIG Z_COMPUTATION TLS_KDF FFC_DH"
if [ "x$1" = "x32" ]; then
lib="lib"
diff --git a/util/fipstools/break-tests.sh b/util/fipstools/break-tests.sh
index 2f698be..84c24ee 100644
--- a/util/fipstools/break-tests.sh
+++ b/util/fipstools/break-tests.sh
@@ -22,7 +22,7 @@
set -x
-TESTS="NONE ECDSA_PWCT CRNG RSA_PWCT AES_CBC AES_GCM DES SHA_1 SHA_256 SHA_512 RSA_SIG DRBG ECDSA_SIG Z_COMPUTATION TLS_KDF"
+TESTS="NONE ECDSA_PWCT CRNG RSA_PWCT AES_CBC AES_GCM DES SHA_1 SHA_256 SHA_512 RSA_SIG DRBG ECDSA_SIG Z_COMPUTATION TLS_KDF FFC_DH"
if [ "x$1" = "xbuild" ]; then
for test in $TESTS; do