Fix off-by-one in BN_rand
If BN_rand is called with |bits| set to 1 and |top| set to 1 then a 1 byte
buffer overflow can occur.
See also upstream's efee575ad464bfb60bf72dcb73f9b51768f4b1a1. But rather than
making |BN_rand| fail, be consistent with the |bits| = 0 case and just don't
set the bits that don't exist. Add tests to ensure the degenerate cases behave.
Change-Id: I5e9fbe6fd8f7f7b2e011a680f2fbe6d7ed4dab65
Reviewed-on: https://boringssl-review.googlesource.com/4893
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc
index 9aa2bf5..01460ab 100644
--- a/crypto/bn/bn_test.cc
+++ b/crypto/bn/bn_test.cc
@@ -114,6 +114,7 @@
static bool test_dec2bn(FILE *fp, BN_CTX *ctx);
static bool test_hex2bn(FILE *fp, BN_CTX *ctx);
static bool test_asc2bn(FILE *fp, BN_CTX *ctx);
+static bool test_rand();
// g_results can be set to true to cause the result of each computation to be
// printed.
@@ -308,6 +309,12 @@
}
fflush(stdout);
+ message(stdout, "BN_rand");
+ if (!test_rand()) {
+ return 1;
+ }
+ fflush(stdout);
+
printf("PASS\n");
return 0;
}
@@ -1617,3 +1624,47 @@
return true;
}
+
+static bool test_rand() {
+ ScopedBIGNUM bn(BN_new());
+ if (!bn) {
+ return false;
+ }
+
+ // Test BN_rand accounts for degenerate cases with |top| and |bottom|
+ // parameters.
+ if (!BN_rand(bn.get(), 0, 0 /* top */, 0 /* bottom */) ||
+ !BN_is_zero(bn.get())) {
+ fprintf(stderr, "BN_rand gave a bad result.\n");
+ return false;
+ }
+ if (!BN_rand(bn.get(), 0, 1 /* top */, 1 /* bottom */) ||
+ !BN_is_zero(bn.get())) {
+ fprintf(stderr, "BN_rand gave a bad result.\n");
+ return false;
+ }
+
+ if (!BN_rand(bn.get(), 1, 0 /* top */, 0 /* bottom */) ||
+ !BN_is_word(bn.get(), 1)) {
+ fprintf(stderr, "BN_rand gave a bad result.\n");
+ return false;
+ }
+ if (!BN_rand(bn.get(), 1, 1 /* top */, 0 /* bottom */) ||
+ !BN_is_word(bn.get(), 1)) {
+ fprintf(stderr, "BN_rand gave a bad result.\n");
+ return false;
+ }
+ if (!BN_rand(bn.get(), 1, -1 /* top */, 1 /* bottom */) ||
+ !BN_is_word(bn.get(), 1)) {
+ fprintf(stderr, "BN_rand gave a bad result.\n");
+ return false;
+ }
+
+ if (!BN_rand(bn.get(), 2, 1 /* top */, 0 /* bottom */) ||
+ !BN_is_word(bn.get(), 3)) {
+ fprintf(stderr, "BN_rand gave a bad result.\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/crypto/bn/random.c b/crypto/bn/random.c
index 3be7510..549ac48 100644
--- a/crypto/bn/random.c
+++ b/crypto/bn/random.c
@@ -144,7 +144,7 @@
}
if (top != -1) {
- if (top) {
+ if (top && bits > 1) {
if (bit == 0) {
buf[0] = 1;
buf[1] |= 0x80;
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index d4c85df..ec1c8ff 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -548,15 +548,15 @@
/* Random and prime number generation. */
-/* BN_rand sets |rnd| to a random number of length |bits|. If |top| is zero,
- * the most-significant bit will be set. If |top| is one, the two most
- * significant bits will be set.
+/* BN_rand sets |rnd| to a random number of length |bits|. If |top| is zero, the
+ * most-significant bit, if any, will be set. If |top| is one, the two most
+ * significant bits, if any, will be set.
*
* If |top| is -1 then no extra action will be taken and |BN_num_bits(rnd)| may
* not equal |bits| if the most significant bits randomly ended up as zeros.
*
- * If |bottom| is non-zero, the least-significant bit will be set. The function
- * returns one on success or zero otherwise. */
+ * If |bottom| is non-zero, the least-significant bit, if any, will be set. The
+ * function returns one on success or zero otherwise. */
OPENSSL_EXPORT int BN_rand(BIGNUM *rnd, int bits, int top, int bottom);
/* BN_pseudo_rand is an alias for |BN_rand|. */