node/test/parallel/test-crypto-prime.js
Tobias Nießen 11d20106ea crypto: avoid infinite loops in prime generation
PR-URL: https://github.com/nodejs/node/pull/37212
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
2021-02-08 16:47:58 -08:00

280 lines
7.1 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const {
generatePrime,
generatePrimeSync,
checkPrime,
checkPrimeSync,
} = require('crypto');
const { promisify } = require('util');
const pgeneratePrime = promisify(generatePrime);
const pCheckPrime = promisify(checkPrime);
['hello', false, {}, []].forEach((i) => {
assert.throws(() => generatePrime(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrimeSync(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
['hello', false, 123].forEach((i) => {
assert.throws(() => generatePrime(80, i, common.mustNotCall()), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrimeSync(80, i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
['hello', false, 123].forEach((i) => {
assert.throws(() => generatePrime(80, {}), {
code: 'ERR_INVALID_CALLBACK'
});
});
[-1, 0].forEach((i) => {
assert.throws(() => generatePrime(i, common.mustNotCall()), {
code: 'ERR_OUT_OF_RANGE'
});
assert.throws(() => generatePrimeSync(i), {
code: 'ERR_OUT_OF_RANGE'
});
});
['test', -1, {}, []].forEach((i) => {
assert.throws(() => generatePrime(8, { safe: i }, common.mustNotCall()), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrime(8, { rem: i }, common.mustNotCall()), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrime(8, { add: i }, common.mustNotCall()), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrimeSync(8, { safe: i }), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrimeSync(8, { rem: i }), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => generatePrimeSync(8, { add: i }), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
{
// Negative BigInts should not be converted to 0 silently.
assert.throws(() => generatePrime(20, { add: -1n }, common.mustNotCall()), {
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "options.add" is out of range. It must be >= 0. ' +
'Received -1n'
});
assert.throws(() => generatePrime(20, { rem: -1n }, common.mustNotCall()), {
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "options.rem" is out of range. It must be >= 0. ' +
'Received -1n'
});
assert.throws(() => checkPrime(-1n, common.mustNotCall()), {
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "candidate" is out of range. It must be >= 0. ' +
'Received -1n'
});
}
generatePrime(80, common.mustSucceed((prime) => {
assert(checkPrimeSync(prime));
checkPrime(prime, common.mustSucceed((result) => {
assert(result);
}));
}));
assert(checkPrimeSync(generatePrimeSync(80)));
generatePrime(80, {}, common.mustSucceed((prime) => {
assert(checkPrimeSync(prime));
}));
assert(checkPrimeSync(generatePrimeSync(80, {})));
generatePrime(32, { safe: true }, common.mustSucceed((prime) => {
assert(checkPrimeSync(prime));
const buf = Buffer.from(prime);
const val = buf.readUInt32BE();
const check = (val - 1) / 2;
buf.writeUInt32BE(check);
assert(checkPrimeSync(buf));
}));
{
const prime = generatePrimeSync(32, { safe: true });
assert(checkPrimeSync(prime));
const buf = Buffer.from(prime);
const val = buf.readUInt32BE();
const check = (val - 1) / 2;
buf.writeUInt32BE(check);
assert(checkPrimeSync(buf));
}
const add = 12;
const rem = 11;
const add_buf = Buffer.from([add]);
const rem_buf = Buffer.from([rem]);
generatePrime(
32,
{ add: add_buf, rem: rem_buf },
common.mustSucceed((prime) => {
assert(checkPrimeSync(prime));
const buf = Buffer.from(prime);
const val = buf.readUInt32BE();
assert.strictEqual(val % add, rem);
}));
{
const prime = generatePrimeSync(32, { add: add_buf, rem: rem_buf });
assert(checkPrimeSync(prime));
const buf = Buffer.from(prime);
const val = buf.readUInt32BE();
assert.strictEqual(val % add, rem);
}
{
const prime = generatePrimeSync(32, { add: BigInt(add), rem: BigInt(rem) });
assert(checkPrimeSync(prime));
const buf = Buffer.from(prime);
const val = buf.readUInt32BE();
assert.strictEqual(val % add, rem);
}
{
// The behavior when specifying only add without rem should depend on the
// safe option.
if (process.versions.openssl >= '1.1.1f') {
generatePrime(128, {
bigint: true,
add: 5n
}, common.mustSucceed((prime) => {
assert(checkPrimeSync(prime));
assert.strictEqual(prime % 5n, 1n);
}));
generatePrime(128, {
bigint: true,
safe: true,
add: 5n
}, common.mustSucceed((prime) => {
assert(checkPrimeSync(prime));
assert.strictEqual(prime % 5n, 3n);
}));
}
}
{
// 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'
});
});
['hello', {}, []].forEach((i) => {
assert.throws(() => checkPrime(2, { checks: i }), {
code: 'ERR_INVALID_ARG_TYPE'
}, common.mustNotCall());
assert.throws(() => checkPrimeSync(2, { checks: i }), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
assert(!checkPrimeSync(Buffer.from([0x1])));
assert(checkPrimeSync(Buffer.from([0x2])));
assert(checkPrimeSync(Buffer.from([0x3])));
assert(!checkPrimeSync(Buffer.from([0x4])));
assert(
!checkPrimeSync(
Buffer.from([0x1]),
{
fast: true,
trialDivision: true,
checks: 10
}));
(async function() {
const prime = await pgeneratePrime(36);
assert(await pCheckPrime(prime));
})().then(common.mustCall());
assert.throws(() => {
generatePrimeSync(32, { bigint: '' });
}, { code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => {
generatePrime(32, { bigint: '' }, common.mustNotCall());
}, { code: 'ERR_INVALID_ARG_TYPE' });
{
const prime = generatePrimeSync(3, { bigint: true });
assert.strictEqual(typeof prime, 'bigint');
assert.strictEqual(prime, 7n);
assert(checkPrimeSync(prime));
checkPrime(prime, common.mustSucceed(assert));
}
{
generatePrime(3, { bigint: true }, common.mustSucceed((prime) => {
assert.strictEqual(typeof prime, 'bigint');
assert.strictEqual(prime, 7n);
assert(checkPrimeSync(prime));
checkPrime(prime, common.mustSucceed(assert));
}));
}