From b7d8e61ef161337783212801a01de5752a6f1422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 12 Jan 2021 15:57:59 +0100 Subject: [PATCH] crypto: fix randomInt bias Co-authored-by: Andrey Pechkurov PR-URL: https://github.com/nodejs/node/pull/36894 Refs: https://github.com/nodejs/node/pull/34600 Reviewed-By: Andrey Pechkurov Reviewed-By: James M Snell --- lib/internal/crypto/random.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index cbc549377b1a3d..3a11a74648ff05 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -218,20 +218,20 @@ function randomInt(min, max, callback) { `<= ${RAND_MAX}`, range); } - const excess = RAND_MAX % range; - const randLimit = RAND_MAX - excess; + // For (x % range) to produce an unbiased value greater than or equal to 0 and + // less than range, x must be drawn randomly from the set of integers greater + // than or equal to 0 and less than randLimit. + const randLimit = RAND_MAX - (RAND_MAX % range); if (isSync) { // Sync API while (true) { const x = randomBytes(6).readUIntBE(0, 6); - // If x > (maxVal - (maxVal % range)), we will get "modulo bias" - if (x > randLimit) { - // Try again + if (x >= randLimit) { + // Try again. continue; } - const n = (x % range) + min; - return n; + return (x % range) + min; } } else { // Async API @@ -239,9 +239,8 @@ function randomInt(min, max, callback) { randomBytes(6, (err, bytes) => { if (err) return callback(err); const x = bytes.readUIntBE(0, 6); - // If x > (maxVal - (maxVal % range)), we will get "modulo bias" - if (x > randLimit) { - // Try again + if (x >= randLimit) { + // Try again. return pickAttempt(); } const n = (x % range) + min;