forked from denoland/deno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ext/node): add
crypto.checkPrime
API (denoland#18465)
Towards denoland#18455 This commit implements `checkPrimeSync` and `checkPrime` in node:crypto using the Miller-Rabin primality test (fun fact: it actually is a test for composite numbers) It first compares the candidate against many known small primes and if not, proceeds to run the Miller-Rabin primality test. http:https://nickle.org/examples/miller-rabin.5c used as reference implementation.
- Loading branch information
1 parent
67e21e7
commit 10012c2
Showing
9 changed files
with
751 additions
and
8 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
302 changes: 302 additions & 0 deletions
302
cli/tests/node_compat/test/parallel/test-crypto-prime.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,302 @@ | ||
// deno-fmt-ignore-file | ||
// deno-lint-ignore-file | ||
|
||
// Copyright Joyent and Node contributors. All rights reserved. MIT license. | ||
|
||
'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); | ||
|
||
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)); | ||
// })); | ||
// } | ||
|
||
|
||
// ['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_ARG_TYPE' | ||
// }); | ||
// }); | ||
|
||
// [-1, 0, 2 ** 31, 2 ** 31 + 1, 2 ** 32 - 1, 2 ** 32].forEach((size) => { | ||
// assert.throws(() => generatePrime(size, common.mustNotCall()), { | ||
// code: 'ERR_OUT_OF_RANGE', | ||
// message: />= 1 && <= 2147483647/ | ||
// }); | ||
// assert.throws(() => generatePrimeSync(size), { | ||
// code: 'ERR_OUT_OF_RANGE', | ||
// message: />= 1 && <= 2147483647/ | ||
// }); | ||
// }); | ||
|
||
// ['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' | ||
// }); | ||
|
||
// if (process.versions.openssl >= '1.1.1f') { | ||
// // 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' | ||
}); | ||
}); | ||
|
||
for (const checks of ['hello', {}, []]) { | ||
assert.throws(() => checkPrime(2n, { checks }, common.mustNotCall()), { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /checks/ | ||
}); | ||
assert.throws(() => checkPrimeSync(2n, { checks }), { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /checks/ | ||
}); | ||
} | ||
|
||
for (const checks of [-(2 ** 31), -1, 2 ** 31, 2 ** 32 - 1, 2 ** 32, 2 ** 50]) { | ||
assert.throws(() => checkPrime(2n, { checks }, common.mustNotCall()), { | ||
code: 'ERR_OUT_OF_RANGE', | ||
message: /<= 2147483647/ | ||
}); | ||
assert.throws(() => checkPrimeSync(2n, { checks }), { | ||
code: 'ERR_OUT_OF_RANGE', | ||
message: /<= 2147483647/ | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.