Add fips checks for dh key agreement
authorShane Lontis <shane.lontis@oracle.com>
Sat, 29 Aug 2020 02:54:00 +0000 (12:54 +1000)
committerMatt Caswell <matt@openssl.org>
Fri, 18 Sep 2020 13:20:38 +0000 (14:20 +0100)
Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/12745)

crypto/ffc/ffc_params_generate.c
providers/implementations/exchange/build.info
providers/implementations/exchange/dh_exch.c

index 8a0b77e7f8febc4068f2c1ff2d267f6e9a116924..03b209ebad589f3979a5f8b03f2518f0348208ae 100644 (file)
  * Verify that the passed in L, N pair for DH or DSA is valid.
  * Returns 0 if invalid, otherwise it returns the security strength.
  */
-static int ffc_validate_LN(size_t L, size_t N, int type)
+static int ffc_validate_LN(size_t L, size_t N, int type, int verify)
 {
+    if (type == FFC_PARAM_TYPE_DH) {
 #ifndef FIPS_MODULE
-    if (L == 1024 && N == 160)
-        return 80;
+        /* Allow legacy 1024/160 in non fips mode */
+        if (L == 1024 && N == 160)
+            return 80;
 #endif
-
-    if (type == FFC_PARAM_TYPE_DH) {
         /* Valid DH L,N parameters from SP800-56Ar3 5.5.1 Table 1 */
         if (L == 2048 && (N == 224 || N == 256))
             return 112;
@@ -53,8 +53,12 @@ static int ffc_validate_LN(size_t L, size_t N, int type)
 #endif
     } else if (type == FFC_PARAM_TYPE_DSA) {
         /* Valid DSA L,N parameters from FIPS 186-4 Section 4.2 */
-        if (L == 1024 && N == 160)
-            return 80;
+#ifdef FIPS_MODULE
+        /* In fips mode 1024/160 can only be used for verification */
+        if (verify)
+#endif
+            if (L == 1024 && N == 160)
+                return 80;
         if (L == 2048 && (N == 224 || N == 256))
             return 112;
         if (L == 3072 && N == 256)
@@ -513,8 +517,10 @@ int ffc_params_FIPS186_4_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
         if (N == 0)
             N = (L >= 2048 ? SHA256_DIGEST_LENGTH : SHA_DIGEST_LENGTH) * 8;
         def_name = default_mdname(N);
-        if (def_name == NULL)
+        if (def_name == NULL) {
+            *res = FFC_CHECK_INVALID_Q_VALUE;
             goto err;
+        }
         md = EVP_MD_fetch(libctx, def_name, NULL);
     }
     if (md == NULL)
@@ -532,7 +538,7 @@ int ffc_params_FIPS186_4_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
      * A.1.1.3 Step (3)
      * Check that the L,N pair is an acceptable pair.
      */
-    if (L <= N || !ffc_validate_LN(L, N, type)) {
+    if (L <= N || !ffc_validate_LN(L, N, type, verify)) {
         *res = FFC_CHECK_BAD_LN_PAIR;
         goto err;
     }
@@ -773,6 +779,7 @@ err:
     return ok;
 }
 
+/* Note this function is only used for verification in fips mode */
 int ffc_params_FIPS186_2_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
                                     int mode, int type, size_t L, size_t N,
                                     int *res, BN_GENCB *cb)
@@ -793,6 +800,7 @@ int ffc_params_FIPS186_2_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
     size_t seed_len = params->seedlen;
     int verify = (mode == FFC_PARAM_MODE_VERIFY);
     unsigned int flags = verify ? params->flags : 0;
+    const char *def_name;
 
     *res = 0;
 
@@ -801,7 +809,12 @@ int ffc_params_FIPS186_2_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
     } else {
         if (N == 0)
             N = (L >= 2048 ? SHA256_DIGEST_LENGTH : SHA_DIGEST_LENGTH) * 8;
-        md = EVP_MD_fetch(libctx, default_mdname(N), NULL);
+        def_name = default_mdname(N);
+        if (def_name == NULL) {
+            *res = FFC_CHECK_INVALID_Q_VALUE;
+            goto err;
+        }
+        md = EVP_MD_fetch(libctx, def_name, NULL);
     }
     if (md == NULL)
         goto err;
@@ -809,16 +822,15 @@ int ffc_params_FIPS186_2_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
         N = EVP_MD_size(md) * 8;
     qsize = N >> 3;
 
-#ifdef FIPS_MODULE
     /*
-     * FIPS 186-4 states that validation can only be done for this pair.
-     * (Even though the original spec allowed L = 512 + 64*j (j = 0.. 8))
+     * The original spec allowed L = 512 + 64*j (j = 0.. 8)
+     * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
+     * says that 512 can be used for legacy verification.
      */
-    if (L != 1024 || N != 160) {
+    if (L < 512) {
         *res = FFC_CHECK_BAD_LN_PAIR;
         goto err;
     }
-#endif
     if (qsize != SHA_DIGEST_LENGTH
         && qsize != SHA224_DIGEST_LENGTH
         && qsize != SHA256_DIGEST_LENGTH) {
@@ -827,9 +839,6 @@ int ffc_params_FIPS186_2_gen_verify(OPENSSL_CTX *libctx, FFC_PARAMS *params,
         goto err;
     }
 
-    if (L < 512)
-        L = 512;
-
     L = (L + 63) / 64 * 64;
 
     if (seed_in != NULL) {
index 92932b9d2882a774cb4294e34928253ea1920e4c..3ae86309c7fc7dca5560c1c10c84c9ceae8c7ef5 100644 (file)
@@ -1,13 +1,12 @@
 # We make separate GOAL variables for each algorithm, to make it easy to
 # switch each to the Legacy provider when needed.
 
-$DH_GOAL=../../libimplementations.a
 $ECX_GOAL=../../libimplementations.a
-$ECDH_GOAL=../../libimplementations.a
 $KDF_GOAL=../../libimplementations.a
 
 IF[{- !$disabled{dh} -}]
-  SOURCE[$DH_GOAL]=dh_exch.c
+  SOURCE[../../libfips.a]=dh_exch.c
+  SOURCE[../../libnonfips.a]=dh_exch.c
 ENDIF
 
 IF[{- !$disabled{asm} -}]
index fad38ec93d3f3231dca42ebeb07d2637abaff43d..a8a0d4331989d25deabe9761130ed6accabbafe1 100644 (file)
@@ -23,6 +23,7 @@
 #include "prov/providercommon.h"
 #include "prov/implementations.h"
 #include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
 #include "crypto/dh.h"
 
 static OSSL_FUNC_keyexch_newctx_fn dh_newctx;
@@ -91,6 +92,43 @@ static void *dh_newctx(void *provctx)
     return pdhctx;
 }
 
+/*
+ * For DH key agreement refer to SP800-56A
+ * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf
+ * "Section 5.5.1.1FFC Domain Parameter Selection/Generation" and
+ * "Appendix D" FFC Safe-prime Groups
+ */
+static int dh_check_key(const DH *dh)
+{
+#ifdef FIPS_MODULE
+    size_t L, N;
+    const BIGNUM *p, *q;
+
+    if (dh == NULL)
+        return 0;
+
+    p = DH_get0_p(dh);
+    q = DH_get0_q(dh);
+    if (p == NULL || q == NULL)
+        return 0;
+
+    L = BN_num_bits(p);
+    if (L < 2048)
+        return 0;
+
+    /* If it is a safe prime group then it is ok */
+    if (DH_get_nid(dh))
+        return 1;
+
+    /* If not then it must be FFC, which only allows certain sizes. */
+    N = BN_num_bits(q);
+
+    return (L == 2048 && (N == 224 || N == 256));
+#else
+    return 1;
+#endif
+}
+
 static int dh_init(void *vpdhctx, void *vdh)
 {
     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
@@ -103,7 +141,7 @@ static int dh_init(void *vpdhctx, void *vdh)
     DH_free(pdhctx->dh);
     pdhctx->dh = vdh;
     pdhctx->kdf_type = PROV_DH_KDF_NONE;
-    return 1;
+    return dh_check_key(vdh);
 }
 
 static int dh_set_peer(void *vpdhctx, void *vdh)
@@ -320,7 +358,12 @@ static int dh_set_ctx_params(void *vpdhctx, const OSSL_PARAM params[])
 
         EVP_MD_free(pdhctx->kdf_md);
         pdhctx->kdf_md = EVP_MD_fetch(pdhctx->libctx, name, mdprops);
-
+#ifdef FIPS_MODULE
+        if (!ossl_prov_digest_get_approved_nid(pdhctx->kdf_md, 1)) {
+            EVP_MD_free(pdhctx->kdf_md);
+            pdhctx->kdf_md = NULL;
+        }
+#endif
         if (pdhctx->kdf_md == NULL)
             return 0;
     }