Ensure that, when generating small primes, the result is actually of the
authorAdam Langley <agl@chromium.org>
Tue, 23 Apr 2013 18:36:06 +0000 (14:36 -0400)
committerBen Laurie <ben@links.org>
Tue, 4 Jun 2013 17:52:30 +0000 (18:52 +0100)
requested size. Fixes OpenSSL #2701.

This change does not address the cases of generating safe primes, or
where the |add| parameter is non-NULL.

Conflicts:
crypto/bn/bn.h
crypto/bn/bn_err.c

crypto/bn/bn.h
crypto/bn/bn_err.c
crypto/bn/bn_prime.c
crypto/bn/bntest.c

index 0e5e5549379ccf278dfbc59ff56549e911d999a0..d7397cae8baf8a21053a5d993cadbbfbe5bdd498 100644 (file)
@@ -826,6 +826,8 @@ void ERR_load_BN_strings(void);
 #define BN_F_BN_EXP                                     123
 #define BN_F_BN_EXPAND2                                         108
 #define BN_F_BN_EXPAND_INTERNAL                                 120
+#define BN_F_BN_GENERATE_DSA_NONCE                      140
+#define BN_F_BN_GENERATE_PRIME_EX                       141
 #define BN_F_BN_GF2M_MOD                                131
 #define BN_F_BN_GF2M_MOD_EXP                            132
 #define BN_F_BN_GF2M_MOD_MUL                            133
@@ -854,6 +856,7 @@ void ERR_load_BN_strings(void);
 #define BN_R_ARG2_LT_ARG3                               100
 #define BN_R_BAD_RECIPROCAL                             101
 #define BN_R_BIGNUM_TOO_LONG                            114
+#define BN_R_BITS_TOO_SMALL                             118
 #define BN_R_CALLED_WITH_EVEN_MODULUS                   102
 #define BN_R_DIV_BY_ZERO                                103
 #define BN_R_ENCODING_ERROR                             104
index cfe2eb94a0ce79b4a8d12fc152f843a85a114a6e..cdc15e62f07b06b68992419afe630adddd0b572c 100644 (file)
@@ -87,6 +87,8 @@ static ERR_STRING_DATA BN_str_functs[]=
 {ERR_FUNC(BN_F_BN_EXP),        "BN_exp"},
 {ERR_FUNC(BN_F_BN_EXPAND2),    "bn_expand2"},
 {ERR_FUNC(BN_F_BN_EXPAND_INTERNAL),    "BN_EXPAND_INTERNAL"},
+{ERR_FUNC(BN_F_BN_GENERATE_DSA_NONCE), "BN_generate_dsa_nonce"},
+{ERR_FUNC(BN_F_BN_GENERATE_PRIME_EX),  "BN_generate_prime_ex"},
 {ERR_FUNC(BN_F_BN_GF2M_MOD),   "BN_GF2m_mod"},
 {ERR_FUNC(BN_F_BN_GF2M_MOD_EXP),       "BN_GF2m_mod_exp"},
 {ERR_FUNC(BN_F_BN_GF2M_MOD_MUL),       "BN_GF2m_mod_mul"},
@@ -118,6 +120,7 @@ static ERR_STRING_DATA BN_str_reasons[]=
 {ERR_REASON(BN_R_ARG2_LT_ARG3)           ,"arg2 lt arg3"},
 {ERR_REASON(BN_R_BAD_RECIPROCAL)         ,"bad reciprocal"},
 {ERR_REASON(BN_R_BIGNUM_TOO_LONG)        ,"bignum too long"},
+{ERR_REASON(BN_R_BITS_TOO_SMALL)         ,"bits too small"},
 {ERR_REASON(BN_R_CALLED_WITH_EVEN_MODULUS),"called with even modulus"},
 {ERR_REASON(BN_R_DIV_BY_ZERO)            ,"div by zero"},
 {ERR_REASON(BN_R_ENCODING_ERROR)         ,"encoding error"},
index 7b25979dd1c450324eaf08a5574fb3ba0a9b92b7..339dbec57051b2f751b1f9cde8caffe6d244662b 100644 (file)
@@ -165,6 +165,19 @@ int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe,
        BN_CTX *ctx;
        int checks = BN_prime_checks_for_size(bits);
 
+       if (bits < 2)
+               {
+               /* There are no prime numbers this small. */
+               BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
+               return 0;
+               }
+       else if (bits == 2 && safe)
+               {
+               /* The smallest safe prime (7) is three bits. */
+               BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
+               return 0;
+               }
+
        ctx=BN_CTX_new();
        if (ctx == NULL) goto err;
        BN_CTX_start(ctx);
@@ -378,27 +391,65 @@ static int probable_prime(BIGNUM *rnd, int bits)
        {
        int i;
        prime_t mods[NUMPRIMES];
-       BN_ULONG delta,maxdelta;
+       BN_ULONG delta;
+       BN_ULONG maxdelta = BN_MASK2 - primes[NUMPRIMES-1];
+       char is_single_word = bits <= BN_BITS2;
 
 again:
        if (!BN_rand(rnd,bits,1,1)) return(0);
-       /* we now have a random number 'rand' to test. */
+       /* we now have a random number 'rnd' to test. */
        for (i=1; i<NUMPRIMES; i++)
                mods[i]=(prime_t)BN_mod_word(rnd,(BN_ULONG)primes[i]);
-       maxdelta=BN_MASK2 - primes[NUMPRIMES-1];
+       /* If bits is so small that it fits into a single word then we
+        * additionally don't want to exceed that many bits. */
+       if (is_single_word)
+               {
+               BN_ULONG size_limit = (((BN_ULONG) 1) << bits) - BN_get_word(rnd) - 1;
+               if (size_limit < maxdelta)
+                       maxdelta = size_limit;
+               }
        delta=0;
-       loop: for (i=1; i<NUMPRIMES; i++)
+       loop:
+       if (is_single_word)
                {
-               /* check that rnd is not a prime and also
-                * that gcd(rnd-1,primes) == 1 (except for 2) */
-               if (((mods[i]+delta)%primes[i]) <= 1)
+               BN_ULONG rnd_word = BN_get_word(rnd);
+
+               /* In the case that the candidate prime is a single word then
+                * we check that:
+                *   1) It's greater than primes[i] because we shouldn't reject
+                *      3 as being a prime number because it's a multiple of
+                *      three.
+                *   2) That it's not a multiple of a known prime. We don't
+                *      check that rnd-1 is also coprime to all the known
+                *      primes because there aren't many small primes where
+                *      that's true. */
+               for (i=1; i<NUMPRIMES && primes[i]<rnd_word; i++)
                        {
-                       delta+=2;
-                       if (delta > maxdelta) goto again;
-                       goto loop;
+                       if ((mods[i]+delta)%primes[i] == 0)
+                               {
+                               delta+=2;
+                               if (delta > maxdelta) goto again;
+                               goto loop;
+                               }
+                       }
+               }
+       else
+               {
+               for (i=1; i<NUMPRIMES; i++)
+                       {
+                       /* check that rnd is not a prime and also
+                        * that gcd(rnd-1,primes) == 1 (except for 2) */
+                       if (((mods[i]+delta)%primes[i]) <= 1)
+                               {
+                               delta+=2;
+                               if (delta > maxdelta) goto again;
+                               goto loop;
+                               }
                        }
                }
        if (!BN_add_word(rnd,delta)) return(0);
+       if (BN_num_bits(rnd) != bits)
+               goto again;
        bn_check_top(rnd);
        return(1);
        }
index 7771e92023dfd353ff79238b0896c567fa5657f1..d22c2d43d645c8397f5658e248a3b42f40469ae5 100644 (file)
@@ -120,6 +120,7 @@ int test_gf2m_mod_sqrt(BIO *bp,BN_CTX *ctx);
 int test_gf2m_mod_solve_quad(BIO *bp,BN_CTX *ctx);
 int test_kron(BIO *bp,BN_CTX *ctx);
 int test_sqrt(BIO *bp,BN_CTX *ctx);
+int test_small_prime(BIO *bp,BN_CTX *ctx);
 int rand_neg(void);
 static int results=0;
 
@@ -264,6 +265,11 @@ int main(int argc, char *argv[])
        message(out,"BN_mod_sqrt");
        if (!test_sqrt(out,ctx)) goto err;
        (void)BIO_flush(out);
+
+       message(out,"Small prime generation");
+       if (!test_small_prime(out,ctx)) goto err;
+       (void)BIO_flush(out);
+
 #ifndef OPENSSL_NO_EC2M
        message(out,"BN_GF2m_add");
        if (!test_gf2m_add(out)) goto err;
@@ -1895,6 +1901,28 @@ int test_sqrt(BIO *bp, BN_CTX *ctx)
        return ret;
        }
 
+int test_small_prime(BIO *bp,BN_CTX *ctx)
+       {
+       static const int bits = 10;
+       int ret = 0;
+       BIGNUM r;
+
+       BN_init(&r);
+       if (!BN_generate_prime_ex(&r, bits, 0, NULL, NULL, NULL))
+               goto err;
+       if (BN_num_bits(&r) != bits)
+               {
+               BIO_printf(bp, "Expected %d bit prime, got %d bit number\n", bits, BN_num_bits(&r));
+               goto err;
+               }
+
+       ret = 1;
+
+err:
+       BN_clear(&r);
+       return ret;
+       }
+
 int test_lshift(BIO *bp,BN_CTX *ctx,BIGNUM *a_)
        {
        BIGNUM *a,*b,*c,*d;