Add PRNG security strength checking.
authorDr. Stephen Henson <steve@openssl.org>
Sat, 23 Apr 2011 19:55:55 +0000 (19:55 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Sat, 23 Apr 2011 19:55:55 +0000 (19:55 +0000)
14 files changed:
CHANGES
crypto/dsa/dsa.h
crypto/dsa/dsa_gen.c
crypto/dsa/dsa_key.c
crypto/dsa/dsa_ossl.c
crypto/ec/ec.h
crypto/ec/ec_key.c
crypto/ec/ec_lcl.h
crypto/ecdsa/ecs_ossl.c
crypto/fips_err.h
crypto/rsa/rsa.h
crypto/rsa/rsa_gen.c
crypto/rsa/rsa_x931g.c
fips/fips.h

diff --git a/CHANGES b/CHANGES
index 29ac9dc182bef2e8ff1f78880d33ad7fd008df3f..6e54214b404f0c936b07cf327311c2bfa3e6ed94 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 1.0.1 and 1.1.0  [xx XXX xxxx]
 
+  *) Add PRNG security strength checks to RSA, DSA and ECDSA using 
+     information in FIPS186-3, SP800-57 and SP800-131A.
+     [Steve Henson]
+
   *) CCM support via EVP. Interface is very similar to GCM case except we
      must supply all data in one chunk (i.e. no update, final) and the
      message length must be supplied if AAD is used. Add algorithm test
index b68fbce169011600dcb32b0d7f8d4c59528f49eb..86766dacb49a357a9db23f849f8accade83c18b9 100644 (file)
  */
 
 #define DSA_FLAG_NON_FIPS_ALLOW                        0x0400
+#define DSA_FLAG_FIPS_CHECKED                  0x0800
 
 #ifdef  __cplusplus
 extern "C" {
index 6517b1592d0f31137e6892ba397611399a9c1b6b..4d6bbc0d05928313e16f69ed879ee163003f509e 100644 (file)
@@ -83,6 +83,7 @@
 #include <openssl/sha.h>
 #ifdef OPENSSL_FIPS
 #include <openssl/fips.h>
+#include <openssl/fips_rand.h>
 #endif
 
 #include "dsa_locl.h"
@@ -140,7 +141,8 @@ int dsa_builtin_paramgen(DSA *ret, size_t bits, size_t qbits,
            goto err;
            }
 
-       if (FIPS_mode() && (bits < OPENSSL_DSA_FIPS_MIN_MODULUS_BITS))
+       if (FIPS_mode() && !(ret->flags & DSA_FLAG_NON_FIPS_ALLOW) 
+                       && (bits < OPENSSL_DSA_FIPS_MIN_MODULUS_BITS))
                {
                DSAerr(DSA_F_DSA_BUILTIN_PARAMGEN, DSA_R_KEY_SIZE_TOO_SMALL);
                goto err;
@@ -375,7 +377,24 @@ err:
  */
 
 
-static int dsa2_security_strength(size_t L, size_t N)
+static int fips_ffc_strength(size_t L, size_t N)
+       {
+       if (L >= 15360 && N >= 512)
+               return 256;
+       if (L >= 7680 && N >= 384)
+               return 192;
+       if (L >= 3072 && N >= 256)
+               return 128;
+       if (L >= 2048 && N >= 224)
+               return 112;
+       if (L >= 1024 && N >= 160)
+               return  80;
+       return 0;
+       }
+
+/* Valid DSA2 parameters from FIPS 186-3 */
+
+static int dsa2_valid_parameters(size_t L, size_t N)
        {
        if (L == 1024 && N == 160)
                return 80;
@@ -388,6 +407,42 @@ static int dsa2_security_strength(size_t L, size_t N)
        return 0;
        }
 
+int fips_check_dsa_prng(DSA *dsa, size_t L, size_t N)
+       {
+       int strength;
+       if (!FIPS_mode())
+               return 1;
+
+       if (dsa->flags & (DSA_FLAG_NON_FIPS_ALLOW|DSA_FLAG_FIPS_CHECKED))
+               return 1;
+
+       if (!L || !N)
+               {
+               L = BN_num_bits(dsa->p);
+               N = BN_num_bits(dsa->q);
+               }
+       if (!dsa2_valid_parameters(L, N))
+               {
+               FIPSerr(FIPS_F_FIPS_CHECK_DSA_PRNG, FIPS_R_INVALID_PARAMETERS);
+               return 0;
+               }
+
+       strength = fips_ffc_strength(L, N);
+
+       if (!strength)
+               {
+               FIPSerr(FIPS_F_FIPS_CHECK_DSA_PRNG,FIPS_R_KEY_TOO_SHORT);
+               return 0;
+               }
+
+       if (FIPS_rand_strength() >= strength)
+               return 1;
+
+       FIPSerr(FIPS_F_FIPS_CHECK_DSA_PRNG,FIPS_R_PRNG_STRENGTH_TOO_LOW);
+       return 0;
+
+       }
+
 /* This is a parameter generation algorithm for the DSA2 algorithm as
  * described in FIPS 186-3.
  */
@@ -417,13 +472,10 @@ int dsa_builtin_paramgen2(DSA *ret, size_t L, size_t N,
                    FIPS_R_FIPS_SELFTEST_FAILED);
            goto err;
            }
-#endif
-       if (!dsa2_security_strength(L, N))
-               {
-               DSAerr(DSA_F_DSA_BUILTIN_PARAMGEN2, DSA_R_INVALID_PARAMETERS);
-               ok = 0;
+
+       if (!fips_check_dsa_prng(ret, L, N))
                goto err;
-               }
+#endif
 
        if (evpmd == NULL)
                {
index 942000e3a52e2cf88d21e2edd1becd564887db87..3df9a6c6a99a608497e0b8d982efbd98121a6de1 100644 (file)
@@ -106,11 +106,14 @@ static int dsa_builtin_keygen(DSA *dsa)
        BIGNUM *pub_key=NULL,*priv_key=NULL;
 
 #ifdef OPENSSL_FIPS
-       if (FIPS_mode() && (BN_num_bits(dsa->p) < OPENSSL_DSA_FIPS_MIN_MODULUS_BITS))
+       if (FIPS_mode() && !(dsa->flags & DSA_FLAG_NON_FIPS_ALLOW)
+               && (BN_num_bits(dsa->p) < OPENSSL_DSA_FIPS_MIN_MODULUS_BITS))
                {
                DSAerr(DSA_F_DSA_BUILTIN_KEYGEN, DSA_R_KEY_SIZE_TOO_SMALL);
                goto err;
                }
+       if (!fips_check_dsa_prng(dsa, 0, 0))
+               goto err;
 #endif
 
        if ((ctx=BN_CTX_new()) == NULL) goto err;
index f1512a40dd0fcc335c40086522d9c333fc9c3352..acf7af95c40d676af1cc52bd59508b02f1cc693f 100644 (file)
@@ -150,11 +150,14 @@ static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
            return NULL;
            }
 
-       if (FIPS_mode() && (BN_num_bits(dsa->p) < OPENSSL_DSA_FIPS_MIN_MODULUS_BITS))
+       if (FIPS_mode() && !(dsa->flags & DSA_FLAG_NON_FIPS_ALLOW) 
+               && (BN_num_bits(dsa->p) < OPENSSL_DSA_FIPS_MIN_MODULUS_BITS))
                {
                DSAerr(DSA_F_DSA_DO_SIGN, DSA_R_KEY_SIZE_TOO_SMALL);
                return NULL;
                }
+       if (!fips_check_dsa_prng(dsa, 0, 0))
+               goto err;
 #endif
 
        BN_init(&m);
index 69d78642ccbc8b657a8a82d5f65efdc93bb25a04..acf9df1aabb9d0732cb24fd1fffe06e53b96847c 100644 (file)
@@ -703,11 +703,21 @@ typedef struct ec_key_st EC_KEY;
 #define EC_PKEY_NO_PARAMETERS  0x001
 #define EC_PKEY_NO_PUBKEY      0x002
 
+/* some values for the flags field */
+#define EC_FLAG_NON_FIPS_ALLOW 0x1
+#define EC_FLAG_FIPS_CHECKED   0x2
+
 /** Creates a new EC_KEY object.
  *  \return EC_KEY object or NULL if an error occurred.
  */
 EC_KEY *EC_KEY_new(void);
 
+int EC_KEY_get_flags(const EC_KEY *key);
+
+void EC_KEY_set_flags(EC_KEY *key, int flags);
+
+void EC_KEY_clear_flags(EC_KEY *key, int flags);
+
 /** Creates a new EC_KEY object using a named curve as underlying
  *  EC_GROUP object.
  *  \param  nid  NID of the named curve.
index ad3022f868d9404af3347fd1a02b180d99e82d52..4583d02f27a0f429494bfa05bc3f8632113017af 100644 (file)
@@ -80,6 +80,7 @@ EC_KEY *EC_KEY_new(void)
                }
 
        ret->version = 1;       
+       ret->flags = 0;
        ret->group   = NULL;
        ret->pub_key = NULL;
        ret->priv_key= NULL;
@@ -199,6 +200,7 @@ EC_KEY *EC_KEY_copy(EC_KEY *dest, const EC_KEY *src)
        dest->enc_flag  = src->enc_flag;
        dest->conv_form = src->conv_form;
        dest->version   = src->version;
+       dest->flags = src->flags;
 
        return dest;
        }
@@ -235,6 +237,8 @@ int EC_KEY_up_ref(EC_KEY *r)
 #ifdef OPENSSL_FIPS
 
 #include <openssl/evp.h>
+#include <openssl/fips.h>
+#include <openssl/fips_rand.h>
 
 static int fips_check_ec(EC_KEY *key)
        {
@@ -253,6 +257,46 @@ static int fips_check_ec(EC_KEY *key)
        return 1;
        }
 
+int fips_check_ec_prng(EC_KEY *ec)
+       {
+       int bits, strength;
+       if (!FIPS_mode())
+               return 1;
+
+       if (ec->flags & (EC_FLAG_NON_FIPS_ALLOW|EC_FLAG_FIPS_CHECKED))
+               return 1;
+
+       if (!ec->group)
+               return 1;
+
+       bits = BN_num_bits(&ec->group->order);
+
+       if (bits < 160)
+               {
+               FIPSerr(FIPS_F_FIPS_CHECK_DSA_PRNG,FIPS_R_KEY_TOO_SHORT);
+               return 0;
+               }
+       /* Comparable algorithm strengths: from SP800-57 table 2 */
+       if (bits >= 512)
+               strength = 256;
+       else if (bits >= 384)
+               strength = 192;
+       else if (bits >= 256)
+               strength = 128;
+       else if (bits >= 224)
+               strength = 112;
+       else
+               strength = 80;
+
+
+       if (FIPS_rand_strength() >= strength)
+               return 1;
+
+       FIPSerr(FIPS_F_FIPS_CHECK_EC_PRNG,FIPS_R_PRNG_STRENGTH_TOO_LOW);
+       return 0;
+
+       }
+
 #endif
 
 int EC_KEY_generate_key(EC_KEY *eckey)
@@ -283,6 +327,11 @@ int EC_KEY_generate_key(EC_KEY *eckey)
        if (!EC_GROUP_get_order(eckey->group, order, ctx))
                goto err;
 
+#ifdef OPENSSL_FIPS
+       if (!fips_check_ec_prng(eckey))
+               goto err;
+#endif
+
        do
                if (!BN_rand_range(priv_key, order))
                        goto err;
@@ -571,3 +620,18 @@ int EC_KEY_precompute_mult(EC_KEY *key, BN_CTX *ctx)
                return 0;
        return EC_GROUP_precompute_mult(key->group, ctx);
        }
+
+int EC_KEY_get_flags(const EC_KEY *key)
+       {
+       return key->flags;
+       }
+
+void EC_KEY_set_flags(EC_KEY *key, int flags)
+       {
+       key->flags |= flags;
+       }
+
+void EC_KEY_clear_flags(EC_KEY *key, int flags)
+       {
+       key->flags &= ~flags;
+       }
index 2e978181b9dbffb96e70b5ad38c8c488f89066fb..afa1efa4bcde4fabffaf31cf868b0bde65b9691a 100644 (file)
@@ -249,6 +249,7 @@ struct ec_key_st {
        point_conversion_form_t conv_form;
 
        int     references;
+       int     flags;
 
        EC_EXTRA_DATA *method_data;
 } /* EC_KEY */;
index 4ed29d188902ab076fbd455bc4359c626db98ca9..3518bb02e12350cace57f6c6e7ad05f2f56ebaf3 100644 (file)
@@ -133,6 +133,11 @@ static int ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
                ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
                goto err;
        }
+
+#ifdef OPENSSL_FIPS
+       if (!fips_check_ec_prng(eckey))
+               goto err;
+#endif
        
        do
        {
@@ -235,6 +240,11 @@ static ECDSA_SIG *ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
                return NULL;
        }
 
+#ifdef OPENSSL_FIPS
+       if (!fips_check_ec_prng(eckey))
+               return NULL;
+#endif
+
        ret = ECDSA_SIG_new();
        if (!ret)
        {
index 041bc1567a9519d11ab07945d7c1d94f8f9c75c1..5555f2d50b1fd0f5cf0337bae3706fd9469a1cbf 100644 (file)
@@ -77,9 +77,12 @@ static ERR_STRING_DATA FIPS_str_functs[]=
 {ERR_FUNC(FIPS_F_DSA_DO_SIGN), "DSA_do_sign"},
 {ERR_FUNC(FIPS_F_DSA_DO_VERIFY),       "DSA_do_verify"},
 {ERR_FUNC(FIPS_F_FIPS_CHECK_DSA),      "FIPS_CHECK_DSA"},
+{ERR_FUNC(FIPS_F_FIPS_CHECK_DSA_PRNG), "fips_check_dsa_prng"},
 {ERR_FUNC(FIPS_F_FIPS_CHECK_EC),       "FIPS_CHECK_EC"},
+{ERR_FUNC(FIPS_F_FIPS_CHECK_EC_PRNG),  "fips_check_ec_prng"},
 {ERR_FUNC(FIPS_F_FIPS_CHECK_INCORE_FINGERPRINT),       "FIPS_check_incore_fingerprint"},
 {ERR_FUNC(FIPS_F_FIPS_CHECK_RSA),      "fips_check_rsa"},
+{ERR_FUNC(FIPS_F_FIPS_CHECK_RSA_PRNG), "fips_check_rsa_prng"},
 {ERR_FUNC(FIPS_F_FIPS_CIPHERINIT),     "FIPS_CIPHERINIT"},
 {ERR_FUNC(FIPS_F_FIPS_DIGESTINIT),     "FIPS_DIGESTINIT"},
 {ERR_FUNC(FIPS_F_FIPS_DRBG_BYTES),     "FIPS_DRBG_BYTES"},
@@ -149,6 +152,7 @@ static ERR_STRING_DATA FIPS_str_reasons[]=
 {ERR_REASON(FIPS_R_INSUFFICIENT_SECURITY_STRENGTH),"insufficient security strength"},
 {ERR_REASON(FIPS_R_INTERNAL_ERROR)       ,"internal error"},
 {ERR_REASON(FIPS_R_INVALID_KEY_LENGTH)   ,"invalid key length"},
+{ERR_REASON(FIPS_R_INVALID_PARAMETERS)   ,"invalid parameters"},
 {ERR_REASON(FIPS_R_IN_ERROR_STATE)       ,"in error state"},
 {ERR_REASON(FIPS_R_KEY_TOO_SHORT)        ,"key too short"},
 {ERR_REASON(FIPS_R_NON_FIPS_METHOD)      ,"non fips method"},
@@ -156,6 +160,7 @@ static ERR_STRING_DATA FIPS_str_reasons[]=
 {ERR_REASON(FIPS_R_PAIRWISE_TEST_FAILED) ,"pairwise test failed"},
 {ERR_REASON(FIPS_R_PERSONALISATION_ERROR_UNDETECTED),"personalisation error undetected"},
 {ERR_REASON(FIPS_R_PERSONALISATION_STRING_TOO_LONG),"personalisation string too long"},
+{ERR_REASON(FIPS_R_PRNG_STRENGTH_TOO_LOW),"prng strength too low"},
 {ERR_REASON(FIPS_R_REQUEST_LENGTH_ERROR_UNDETECTED),"request length error undetected"},
 {ERR_REASON(FIPS_R_REQUEST_TOO_LARGE_FOR_DRBG),"request too large for drbg"},
 {ERR_REASON(FIPS_R_RESEED_COUNTER_ERROR) ,"reseed counter error"},
index b91501a8d01d2e587f23230480d1c4049926f9e5..7174f9cee8a040c3b4e25b1bd688a12e858fd752 100644 (file)
@@ -470,6 +470,10 @@ RSA *RSAPrivateKey_dup(RSA *rsa);
  */
 
 #define RSA_FLAG_NON_FIPS_ALLOW                        0x0400
+/* Application has decided PRNG is good enough to generate a key: don't
+ * check.
+ */
+#define RSA_FLAG_CHECKED                       0x0800
 
 /* BEGIN ERROR CODES */
 /* The following lines are auto generated by the script mkerr.pl. Any changes
index 484468da05e333d5f0403a3aa42a5d70ac5d9214..24f9eaf4d6dd24f330e0ff4d2120d0fc544769e5 100644 (file)
 
 
 #include <openssl/fips.h>
+#include <openssl/fips_rand.h>
 #include <openssl/evp.h>
 
+/* Check PRNG has sufficient security level to handle an RSA operation */
+
+int fips_check_rsa_prng(RSA *rsa, int bits)
+       {
+       int strength;
+       if (!FIPS_mode())
+               return 1;
+
+       if (rsa->flags & (RSA_FLAG_NON_FIPS_ALLOW|RSA_FLAG_CHECKED))
+               return 1;
+
+       if (bits == 0)
+               bits = BN_num_bits(rsa->n);
+
+       /* Should never happen */
+       if (bits < 1024)
+               {
+               FIPSerr(FIPS_F_FIPS_CHECK_RSA_PRNG,FIPS_R_KEY_TOO_SHORT);
+               return 0;
+               }
+       /* From SP800-57 */
+       if (bits < 2048)
+               strength = 80;
+       else if (bits < 3072)
+               strength = 112;
+       else if (bits < 7680)
+               strength = 128;
+       else if (bits < 15360)
+               strength = 192;
+       else 
+               strength = 256;
+
+       if (FIPS_rand_strength() >= strength)
+               return 1;
+
+       FIPSerr(FIPS_F_FIPS_CHECK_RSA_PRNG,FIPS_R_PRNG_STRENGTH_TOO_LOW);
+       return 0;
+       }
+       
+
 int fips_check_rsa(RSA *rsa)
        {
        const unsigned char tbs[] = "RSA Pairwise Check Data";
@@ -164,11 +205,14 @@ static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb)
            return 0;
            }
 
-       if (FIPS_mode() && (bits < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS))
+       if (FIPS_mode() && !(rsa->flags & RSA_FLAG_NON_FIPS_ALLOW) 
+               && (bits < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS))
            {
            FIPSerr(FIPS_F_RSA_BUILTIN_KEYGEN,FIPS_R_KEY_TOO_SHORT);
            return 0;
            }
+       if (!fips_check_rsa_prng(rsa, bits))
+           return 0;
 #endif
 
        ctx=BN_CTX_new();
index 1ccd0a1969f54162228f397c4f70569310b90f18..819a7289540884b42f1b97a190a897971cf9bf00 100644 (file)
@@ -210,7 +210,8 @@ int RSA_X931_generate_key_ex(RSA *rsa, int bits, const BIGNUM *e, BN_GENCB *cb)
        BN_CTX *ctx = NULL;
 
 #ifdef OPENSSL_FIPS
-       if (bits < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS)
+       if (FIPS_mode() && !(rsa->flags & RSA_FLAG_NON_FIPS_ALLOW) && 
+               (bits < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS))
            {
            FIPSerr(FIPS_F_RSA_X931_GENERATE_KEY_EX,FIPS_R_KEY_TOO_SHORT);
            return 0;
@@ -227,6 +228,8 @@ int RSA_X931_generate_key_ex(RSA *rsa, int bits, const BIGNUM *e, BN_GENCB *cb)
            FIPSerr(FIPS_F_RSA_X931_GENERATE_KEY_EX,FIPS_R_FIPS_SELFTEST_FAILED);
            return 0;
            }
+       if (!fips_check_rsa_prng(rsa, bits))
+           return 0;
 #endif
 
        ctx = BN_CTX_new();
index 60cc097bd667471c709fd057e818c6d4695e1ab7..e07a7955258294b8ae0622580f9ef934f534222b 100644 (file)
@@ -60,6 +60,7 @@ extern "C" {
 #endif
 
 struct dsa_st;
+struct ec_key_st;
 struct rsa_st;
 struct evp_pkey_st;
 struct env_md_st;
@@ -94,6 +95,9 @@ int FIPS_check_incore_fingerprint(void);
 
 void fips_set_selftest_fail(void);
 int fips_check_rsa(struct rsa_st *rsa);
+int fips_check_rsa_prng(struct rsa_st *rsa, int bits);
+int fips_check_dsa_prng(struct dsa_st *dsa, size_t L, size_t N);
+int fips_check_ec_prng(struct ec_key_st *ec);
 
 void FIPS_set_locking_callbacks(void (*func)(int mode, int type,
                                const char *file,int line),
@@ -239,9 +243,12 @@ void ERR_load_FIPS_strings(void);
 #define FIPS_F_DSA_DO_SIGN                              103
 #define FIPS_F_DSA_DO_VERIFY                            104
 #define FIPS_F_FIPS_CHECK_DSA                           105
+#define FIPS_F_FIPS_CHECK_DSA_PRNG                      151
 #define FIPS_F_FIPS_CHECK_EC                            106
+#define FIPS_F_FIPS_CHECK_EC_PRNG                       152
 #define FIPS_F_FIPS_CHECK_INCORE_FINGERPRINT            107
 #define FIPS_F_FIPS_CHECK_RSA                           108
+#define FIPS_F_FIPS_CHECK_RSA_PRNG                      150
 #define FIPS_F_FIPS_CIPHERINIT                          109
 #define FIPS_F_FIPS_DIGESTINIT                          110
 #define FIPS_F_FIPS_DRBG_BYTES                          111
@@ -308,6 +315,7 @@ void ERR_load_FIPS_strings(void);
 #define FIPS_R_INSUFFICIENT_SECURITY_STRENGTH           120
 #define FIPS_R_INTERNAL_ERROR                           121
 #define FIPS_R_INVALID_KEY_LENGTH                       122
+#define FIPS_R_INVALID_PARAMETERS                       144
 #define FIPS_R_IN_ERROR_STATE                           123
 #define FIPS_R_KEY_TOO_SHORT                            124
 #define FIPS_R_NON_FIPS_METHOD                          125
@@ -315,6 +323,7 @@ void ERR_load_FIPS_strings(void);
 #define FIPS_R_PAIRWISE_TEST_FAILED                     127
 #define FIPS_R_PERSONALISATION_ERROR_UNDETECTED                 128
 #define FIPS_R_PERSONALISATION_STRING_TOO_LONG          129
+#define FIPS_R_PRNG_STRENGTH_TOO_LOW                    143
 #define FIPS_R_REQUEST_LENGTH_ERROR_UNDETECTED          130
 #define FIPS_R_REQUEST_TOO_LARGE_FOR_DRBG               131
 #define FIPS_R_RESEED_COUNTER_ERROR                     132