From cac4fb58e02d8cf799d75212179f56c69e652ec7 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Sat, 23 Apr 2011 19:55:55 +0000 Subject: [PATCH] Add PRNG security strength checking. --- CHANGES | 4 +++ crypto/dsa/dsa.h | 1 + crypto/dsa/dsa_gen.c | 68 ++++++++++++++++++++++++++++++++++++----- crypto/dsa/dsa_key.c | 5 ++- crypto/dsa/dsa_ossl.c | 5 ++- crypto/ec/ec.h | 10 ++++++ crypto/ec/ec_key.c | 64 ++++++++++++++++++++++++++++++++++++++ crypto/ec/ec_lcl.h | 1 + crypto/ecdsa/ecs_ossl.c | 10 ++++++ crypto/fips_err.h | 5 +++ crypto/rsa/rsa.h | 4 +++ crypto/rsa/rsa_gen.c | 46 +++++++++++++++++++++++++++- crypto/rsa/rsa_x931g.c | 5 ++- fips/fips.h | 9 ++++++ 14 files changed, 225 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 29ac9dc182..6e54214b40 100644 --- 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 diff --git a/crypto/dsa/dsa.h b/crypto/dsa/dsa.h index b68fbce169..86766dacb4 100644 --- a/crypto/dsa/dsa.h +++ b/crypto/dsa/dsa.h @@ -113,6 +113,7 @@ */ #define DSA_FLAG_NON_FIPS_ALLOW 0x0400 +#define DSA_FLAG_FIPS_CHECKED 0x0800 #ifdef __cplusplus extern "C" { diff --git a/crypto/dsa/dsa_gen.c b/crypto/dsa/dsa_gen.c index 6517b1592d..4d6bbc0d05 100644 --- a/crypto/dsa/dsa_gen.c +++ b/crypto/dsa/dsa_gen.c @@ -83,6 +83,7 @@ #include #ifdef OPENSSL_FIPS #include +#include #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) { diff --git a/crypto/dsa/dsa_key.c b/crypto/dsa/dsa_key.c index 942000e3a5..3df9a6c6a9 100644 --- a/crypto/dsa/dsa_key.c +++ b/crypto/dsa/dsa_key.c @@ -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; diff --git a/crypto/dsa/dsa_ossl.c b/crypto/dsa/dsa_ossl.c index f1512a40dd..acf7af95c4 100644 --- a/crypto/dsa/dsa_ossl.c +++ b/crypto/dsa/dsa_ossl.c @@ -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); diff --git a/crypto/ec/ec.h b/crypto/ec/ec.h index 69d78642cc..acf9df1aab 100644 --- a/crypto/ec/ec.h +++ b/crypto/ec/ec.h @@ -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. diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c index ad3022f868..4583d02f27 100644 --- a/crypto/ec/ec_key.c +++ b/crypto/ec/ec_key.c @@ -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 +#include +#include 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; + } diff --git a/crypto/ec/ec_lcl.h b/crypto/ec/ec_lcl.h index 2e978181b9..afa1efa4bc 100644 --- a/crypto/ec/ec_lcl.h +++ b/crypto/ec/ec_lcl.h @@ -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 */; diff --git a/crypto/ecdsa/ecs_ossl.c b/crypto/ecdsa/ecs_ossl.c index 4ed29d1889..3518bb02e1 100644 --- a/crypto/ecdsa/ecs_ossl.c +++ b/crypto/ecdsa/ecs_ossl.c @@ -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) { diff --git a/crypto/fips_err.h b/crypto/fips_err.h index 041bc1567a..5555f2d50b 100644 --- a/crypto/fips_err.h +++ b/crypto/fips_err.h @@ -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"}, diff --git a/crypto/rsa/rsa.h b/crypto/rsa/rsa.h index b91501a8d0..7174f9cee8 100644 --- a/crypto/rsa/rsa.h +++ b/crypto/rsa/rsa.h @@ -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 diff --git a/crypto/rsa/rsa_gen.c b/crypto/rsa/rsa_gen.c index 484468da05..24f9eaf4d6 100644 --- a/crypto/rsa/rsa_gen.c +++ b/crypto/rsa/rsa_gen.c @@ -74,8 +74,49 @@ #include +#include #include +/* 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(); diff --git a/crypto/rsa/rsa_x931g.c b/crypto/rsa/rsa_x931g.c index 1ccd0a1969..819a728954 100644 --- a/crypto/rsa/rsa_x931g.c +++ b/crypto/rsa/rsa_x931g.c @@ -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(); diff --git a/fips/fips.h b/fips/fips.h index 60cc097bd6..e07a795525 100644 --- a/fips/fips.h +++ b/fips/fips.h @@ -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 -- 2.40.0