From 55fd6b6611604ba2d667a68c3c96e6618ecf6452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 3 Feb 2021 18:31:17 +0100 Subject: [PATCH] crypto: avoid infinite loops in prime generation PR-URL: https://github.com/nodejs/node/pull/37212 Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell Reviewed-By: Rich Trott --- src/crypto/crypto_random.cc | 20 ++++++++++++++++ test/parallel/test-crypto-prime.js | 38 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index ceac834cb94d6f..b24f8f32136ffa 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -127,6 +127,26 @@ Maybe RandomPrimeTraits::AdditionalConfig( return Nothing(); } + if (params->add) { + if (BN_num_bits(params->add.get()) > bits) { + // If we allowed this, the best case would be returning a static prime + // that wasn't generated randomly. The worst case would be an infinite + // loop within OpenSSL, blocking the main thread or one of the threads + // in the thread pool. + THROW_ERR_OUT_OF_RANGE(env, "invalid options.add"); + return Nothing(); + } + + if (params->rem) { + if (BN_cmp(params->add.get(), params->rem.get()) != 1) { + // This would definitely lead to an infinite loop if allowed since + // OpenSSL does not check this condition. + THROW_ERR_OUT_OF_RANGE(env, "invalid options.rem"); + return Nothing(); + } + } + } + params->bits = bits; params->safe = safe; params->prime.reset(BN_secure_new()); diff --git a/test/parallel/test-crypto-prime.js b/test/parallel/test-crypto-prime.js index f51b091f536b45..7afb34651f5227 100644 --- a/test/parallel/test-crypto-prime.js +++ b/test/parallel/test-crypto-prime.js @@ -181,6 +181,44 @@ generatePrime( } } +{ + // This is impossible because it implies (prime % 2**64) == 1 and + // prime < 2**64, meaning prime = 1, but 1 is not prime. + for (const add of [2n ** 64n, 2n ** 65n]) { + assert.throws(() => { + generatePrimeSync(64, { add }); + }, { + code: 'ERR_OUT_OF_RANGE', + message: 'invalid options.add' + }); + } + + // Any parameters with rem >= add lead to an impossible condition. + for (const rem of [7n, 8n, 3000n]) { + assert.throws(() => { + generatePrimeSync(64, { add: 7n, rem }); + }, { + code: 'ERR_OUT_OF_RANGE', + message: 'invalid options.rem' + }); + } + + // This is possible, but not allowed. It implies prime == 7, which means that + // we did not actually generate a random prime. + assert.throws(() => { + generatePrimeSync(3, { add: 8n, rem: 7n }); + }, { + code: 'ERR_OUT_OF_RANGE' + }); + + // This is possible and allowed (but makes little sense). + assert.strictEqual(generatePrimeSync(4, { + add: 15n, + rem: 13n, + bigint: true + }), 13n); +} + [1, 'hello', {}, []].forEach((i) => { assert.throws(() => checkPrime(i), { code: 'ERR_INVALID_ARG_TYPE'