Add secure DSA nonce flag.
[openssl.git] / crypto / bn / bn_rand.c
index 8d74895..d550f49 100644 (file)
 #include "cryptlib.h"
 #include "bn_lcl.h"
 #include <openssl/rand.h>
+#include <openssl/sha.h>
 
 static int bnrand(int pseudorand, BIGNUM *rnd, int bits, int top, int bottom)
        {
@@ -313,3 +314,72 @@ int        BN_pseudo_rand_range(BIGNUM *r, const BIGNUM *range)
        {
        return bn_rand_range(1, r, range);
        }
+
+#ifndef OPENSSL_NO_SHA512
+/* BN_generate_dsa_nonce generates a random number 0 <= out < range. Unlike
+ * BN_rand_range, it also includes the contents of |priv| and |message| in the
+ * generation so that an RNG failure isn't fatal as long as |priv| remains
+ * secret. This is intended for use in DSA and ECDSA where an RNG weakness
+ * leads directly to private key exposure unless this function is used. */
+int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range, const BIGNUM* priv,
+                         const unsigned char *message, size_t message_len,
+                         BN_CTX *ctx)
+       {
+       SHA512_CTX sha;
+       /* We use 512 bits of random data per iteration to
+        * ensure that we have at least |range| bits of randomness. */
+       unsigned char random_bytes[64];
+       unsigned char digest[SHA512_DIGEST_LENGTH];
+       unsigned done, todo;
+       /* We generate |range|+8 bytes of random output. */
+       const unsigned num_k_bytes = BN_num_bytes(range) + 8;
+       unsigned char private_bytes[96];
+       unsigned char *k_bytes;
+       int ret = 0;
+
+       k_bytes = OPENSSL_malloc(num_k_bytes);
+       if (!k_bytes)
+               goto err;
+
+       /* We copy |priv| into a local buffer to avoid exposing its length. */
+       todo = sizeof(priv->d[0])*priv->top;
+       if (todo > sizeof(private_bytes))
+               {
+               /* No reasonable DSA or ECDSA key should have a private key
+                * this large and we don't handle this case in order to avoid
+                * leaking the length of the private key. */
+               BNerr(BN_F_BN_GENERATE_DSA_NONCE, BN_R_PRIVATE_KEY_TOO_LARGE);
+               goto err;
+               }
+       memcpy(private_bytes, priv->d, todo);
+       memset(private_bytes + todo, 0, sizeof(private_bytes) - todo);
+
+       for (done = 0; done < num_k_bytes;) {
+               if (RAND_bytes(random_bytes, sizeof(random_bytes)) != 1)
+                       goto err;
+               SHA512_Init(&sha);
+               SHA512_Update(&sha, &done, sizeof(done));
+               SHA512_Update(&sha, private_bytes, sizeof(private_bytes));
+               SHA512_Update(&sha, message, message_len);
+               SHA512_Update(&sha, random_bytes, sizeof(random_bytes));
+               SHA512_Final(digest, &sha);
+
+               todo = num_k_bytes - done;
+               if (todo > SHA512_DIGEST_LENGTH)
+                       todo = SHA512_DIGEST_LENGTH;
+               memcpy(k_bytes + done, digest, todo);
+               done += todo;
+       }
+
+       if (!BN_bin2bn(k_bytes, num_k_bytes, out))
+               goto err;
+       if (BN_mod(out, out, range, ctx) != 1)
+               goto err;
+       ret = 1;
+
+err:
+       if (k_bytes)
+               OPENSSL_free(k_bytes);
+       return ret;
+       }
+#endif  /* OPENSSL_NO_SHA512 */