diff --git a/test/parallel/test-crypto-dh-errors.js b/test/parallel/test-crypto-dh-errors.js new file mode 100644 index 00000000000000..fcf1922bcdba73 --- /dev/null +++ b/test/parallel/test-crypto-dh-errors.js @@ -0,0 +1,111 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +// https://github.com/nodejs/node/issues/32738 +// XXX(bnoordhuis) validateInt32() throwing ERR_OUT_OF_RANGE and RangeError +// instead of ERR_INVALID_ARG_TYPE and TypeError is questionable, IMO. +assert.throws(() => crypto.createDiffieHellman(13.37), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "sizeOrKey" is out of range. ' + + 'It must be an integer. Received 13.37', +}); + +assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "generator" is out of range. ' + + 'It must be an integer. Received 13.37', +}); + +for (const bits of [-1, 0, 1]) { + if (common.hasOpenSSL3) { + assert.throws(() => crypto.createDiffieHellman(bits), { + code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL', + name: 'Error', + message: /modulus too small/, + }); + } else { + assert.throws(() => crypto.createDiffieHellman(bits), { + code: 'ERR_OSSL_BN_BITS_TOO_SMALL', + name: 'Error', + message: /bits too small/, + }); + } +} + +for (const g of [-1, 1]) { + const ex = { + code: 'ERR_OSSL_DH_BAD_GENERATOR', + name: 'Error', + message: /bad generator/, + }; + assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex); + assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex); +} + +for (const g of [Buffer.from([]), + Buffer.from([0]), + Buffer.from([1])]) { + const ex = { + code: 'ERR_OSSL_DH_BAD_GENERATOR', + name: 'Error', + message: /bad generator/, + }; + assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex); + assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex); +} + +[ + [0x1, 0x2], + () => { }, + /abc/, + {}, +].forEach((input) => { + assert.throws( + () => crypto.createDiffieHellman(input), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); +}); + +// Invalid test: curve argument is undefined +assert.throws( + () => crypto.createECDH(), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "curve" argument must be of type string. ' + + 'Received undefined' + }); + +assert.throws( + function() { + crypto.getDiffieHellman('unknown-group'); + }, + { + name: 'Error', + code: 'ERR_CRYPTO_UNKNOWN_DH_GROUP', + message: 'Unknown DH group' + }, + 'crypto.getDiffieHellman(\'unknown-group\') ' + + 'failed to throw the expected error.' +); + +assert.throws( + () => crypto.createDiffieHellman('', true), + { + code: 'ERR_INVALID_ARG_TYPE' + } +); +[true, Symbol(), {}, () => {}, []].forEach((generator) => assert.throws( + () => crypto.createDiffieHellman('', 'base64', generator), + { code: 'ERR_INVALID_ARG_TYPE' } +)); diff --git a/test/parallel/test-crypto-dh-generate-keys.js b/test/parallel/test-crypto-dh-generate-keys.js new file mode 100644 index 00000000000000..fc277bb0d9b8e4 --- /dev/null +++ b/test/parallel/test-crypto-dh-generate-keys.js @@ -0,0 +1,63 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +{ + const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + + function unlessInvalidState(f) { + try { + return f(); + } catch (err) { + if (err.code !== 'ERR_CRYPTO_INVALID_STATE') { + throw err; + } + } + } + + function testGenerateKeysChangesKeys(setup, expected) { + const dh = crypto.createDiffieHellman(size); + setup(dh); + const firstPublicKey = unlessInvalidState(() => dh.getPublicKey()); + const firstPrivateKey = unlessInvalidState(() => dh.getPrivateKey()); + dh.generateKeys(); + const secondPublicKey = dh.getPublicKey(); + const secondPrivateKey = dh.getPrivateKey(); + function changed(shouldChange, first, second) { + if (shouldChange) { + assert.notDeepStrictEqual(first, second); + } else { + assert.deepStrictEqual(first, second); + } + } + changed(expected.includes('public'), firstPublicKey, secondPublicKey); + changed(expected.includes('private'), firstPrivateKey, secondPrivateKey); + } + + // Both the private and the public key are missing: generateKeys() generates both. + testGenerateKeysChangesKeys(() => { + // No setup. + }, ['public', 'private']); + + // Neither key is missing: generateKeys() does nothing. + testGenerateKeysChangesKeys((dh) => { + dh.generateKeys(); + }, []); + + // Only the public key is missing: generateKeys() generates only the public key. + testGenerateKeysChangesKeys((dh) => { + dh.setPrivateKey(Buffer.from('01020304', 'hex')); + }, ['public']); + + // The public key is outdated: generateKeys() generates only the public key. + testGenerateKeysChangesKeys((dh) => { + const oldPublicKey = dh.generateKeys(); + dh.setPrivateKey(Buffer.from('01020304', 'hex')); + assert.deepStrictEqual(dh.getPublicKey(), oldPublicKey); + }, ['public']); +} diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index c730eac4ce76dd..abbe1abe7e53d7 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -6,252 +6,99 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; -const dh1 = crypto.createDiffieHellman(size); -const p1 = dh1.getPrime('buffer'); -const dh2 = crypto.createDiffieHellman(p1, 'buffer'); -const key1 = dh1.generateKeys(); -const key2 = dh2.generateKeys('hex'); -const secret1 = dh1.computeSecret(key2, 'hex', 'base64'); -const secret2 = dh2.computeSecret(key1, 'latin1', 'buffer'); - -// Test Diffie-Hellman with two parties sharing a secret, -// using various encodings as we go along -assert.strictEqual(secret2.toString('base64'), secret1); -assert.strictEqual(dh1.verifyError, 0); -assert.strictEqual(dh2.verifyError, 0); - -// https://github.com/nodejs/node/issues/32738 -// XXX(bnoordhuis) validateInt32() throwing ERR_OUT_OF_RANGE and RangeError -// instead of ERR_INVALID_ARG_TYPE and TypeError is questionable, IMO. -assert.throws(() => crypto.createDiffieHellman(13.37), { - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: 'The value of "sizeOrKey" is out of range. ' + - 'It must be an integer. Received 13.37', -}); - -assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), { - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: 'The value of "generator" is out of range. ' + - 'It must be an integer. Received 13.37', -}); - -for (const bits of [-1, 0, 1]) { +{ + const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const dh1 = crypto.createDiffieHellman(size); + const p1 = dh1.getPrime('buffer'); + const dh2 = crypto.createDiffieHellman(p1, 'buffer'); + const key1 = dh1.generateKeys(); + const key2 = dh2.generateKeys('hex'); + const secret1 = dh1.computeSecret(key2, 'hex', 'base64'); + const secret2 = dh2.computeSecret(key1, 'latin1', 'buffer'); + + // Test Diffie-Hellman with two parties sharing a secret, + // using various encodings as we go along + assert.strictEqual(secret2.toString('base64'), secret1); + assert.strictEqual(dh1.verifyError, 0); + assert.strictEqual(dh2.verifyError, 0); + + // Create "another dh1" using generated keys from dh1, + // and compute secret again + const dh3 = crypto.createDiffieHellman(p1, 'buffer'); + const privkey1 = dh1.getPrivateKey(); + dh3.setPublicKey(key1); + dh3.setPrivateKey(privkey1); + + assert.deepStrictEqual(dh1.getPrime(), dh3.getPrime()); + assert.deepStrictEqual(dh1.getGenerator(), dh3.getGenerator()); + assert.deepStrictEqual(dh1.getPublicKey(), dh3.getPublicKey()); + assert.deepStrictEqual(dh1.getPrivateKey(), dh3.getPrivateKey()); + assert.strictEqual(dh3.verifyError, 0); + + const secret3 = dh3.computeSecret(key2, 'hex', 'base64'); + + assert.strictEqual(secret1, secret3); + + // computeSecret works without a public key set at all. + const dh4 = crypto.createDiffieHellman(p1, 'buffer'); + dh4.setPrivateKey(privkey1); + + assert.deepStrictEqual(dh1.getPrime(), dh4.getPrime()); + assert.deepStrictEqual(dh1.getGenerator(), dh4.getGenerator()); + assert.deepStrictEqual(dh1.getPrivateKey(), dh4.getPrivateKey()); + assert.strictEqual(dh4.verifyError, 0); + + const secret4 = dh4.computeSecret(key2, 'hex', 'base64'); + + assert.strictEqual(secret1, secret4); + + let wrongBlockLength; if (common.hasOpenSSL3) { - assert.throws(() => crypto.createDiffieHellman(bits), { - code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL', - name: 'Error', - message: /modulus too small/, - }); + wrongBlockLength = { + message: 'error:1C80006B:Provider routines::wrong final block length', + code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', + library: 'Provider routines', + reason: 'wrong final block length' + }; } else { - assert.throws(() => crypto.createDiffieHellman(bits), { - code: 'ERR_OSSL_BN_BITS_TOO_SMALL', - name: 'Error', - message: /bits too small/, - }); + wrongBlockLength = { + message: 'error:0606506D:digital envelope' + + ' routines:EVP_DecryptFinal_ex:wrong final block length', + code: 'ERR_OSSL_EVP_WRONG_FINAL_BLOCK_LENGTH', + library: 'digital envelope routines', + reason: 'wrong final block length' + }; } -} - -// Through a fluke of history, g=0 defaults to DH_GENERATOR (2). -{ - const g = 0; - crypto.createDiffieHellman('abcdef', g); - crypto.createDiffieHellman('abcdef', 'hex', g); -} - -for (const g of [-1, 1]) { - const ex = { - code: 'ERR_OSSL_DH_BAD_GENERATOR', - name: 'Error', - message: /bad generator/, - }; - assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex); - assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex); -} - -crypto.createDiffieHellman('abcdef', Buffer.from([2])); // OK - -for (const g of [Buffer.from([]), - Buffer.from([0]), - Buffer.from([1])]) { - const ex = { - code: 'ERR_OSSL_DH_BAD_GENERATOR', - name: 'Error', - message: /bad generator/, - }; - assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex); - assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex); -} - -[ - [0x1, 0x2], - () => { }, - /abc/, - {}, -].forEach((input) => { - assert.throws( - () => crypto.createDiffieHellman(input), - { - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - } - ); -}); - -// Create "another dh1" using generated keys from dh1, -// and compute secret again -const dh3 = crypto.createDiffieHellman(p1, 'buffer'); -const privkey1 = dh1.getPrivateKey(); -dh3.setPublicKey(key1); -dh3.setPrivateKey(privkey1); - -assert.deepStrictEqual(dh1.getPrime(), dh3.getPrime()); -assert.deepStrictEqual(dh1.getGenerator(), dh3.getGenerator()); -assert.deepStrictEqual(dh1.getPublicKey(), dh3.getPublicKey()); -assert.deepStrictEqual(dh1.getPrivateKey(), dh3.getPrivateKey()); -assert.strictEqual(dh3.verifyError, 0); - -const secret3 = dh3.computeSecret(key2, 'hex', 'base64'); - -assert.strictEqual(secret1, secret3); - -// computeSecret works without a public key set at all. -const dh4 = crypto.createDiffieHellman(p1, 'buffer'); -dh4.setPrivateKey(privkey1); - -assert.deepStrictEqual(dh1.getPrime(), dh4.getPrime()); -assert.deepStrictEqual(dh1.getGenerator(), dh4.getGenerator()); -assert.deepStrictEqual(dh1.getPrivateKey(), dh4.getPrivateKey()); -assert.strictEqual(dh4.verifyError, 0); - -const secret4 = dh4.computeSecret(key2, 'hex', 'base64'); -assert.strictEqual(secret1, secret4); + // Run this one twice to make sure that the dh3 clears its error properly + { + const c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); + assert.throws(() => { + c.final('utf8'); + }, wrongBlockLength); + } -let wrongBlockLength; -if (common.hasOpenSSL3) { - wrongBlockLength = { - message: 'error:1C80006B:Provider routines::wrong final block length', - code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', - library: 'Provider routines', - reason: 'wrong final block length' - }; -} else { - wrongBlockLength = { - message: 'error:0606506D:digital envelope' + - ' routines:EVP_DecryptFinal_ex:wrong final block length', - code: 'ERR_OSSL_EVP_WRONG_FINAL_BLOCK_LENGTH', - library: 'digital envelope routines', - reason: 'wrong final block length' - }; -} + { + const c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); + assert.throws(() => { + c.final('utf8'); + }, wrongBlockLength); + } -// Run this one twice to make sure that the dh3 clears its error properly -{ - const c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); assert.throws(() => { - c.final('utf8'); - }, wrongBlockLength); + dh3.computeSecret(''); + }, { message: common.hasOpenSSL3 ? + 'error:02800080:Diffie-Hellman routines::invalid secret' : + 'Supplied key is too small' }); } +// Through a fluke of history, g=0 defaults to DH_GENERATOR (2). { - const c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); - assert.throws(() => { - c.final('utf8'); - }, wrongBlockLength); + const g = 0; + crypto.createDiffieHellman('abcdef', g); + crypto.createDiffieHellman('abcdef', 'hex', g); } -assert.throws(() => { - dh3.computeSecret(''); -}, { message: common.hasOpenSSL3 ? - 'error:02800080:Diffie-Hellman routines::invalid secret' : - 'Supplied key is too small' }); - -// Invalid test: curve argument is undefined -assert.throws( - () => crypto.createECDH(), - { - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: 'The "curve" argument must be of type string. ' + - 'Received undefined' - }); - -assert.throws( - function() { - crypto.getDiffieHellman('unknown-group'); - }, - { - name: 'Error', - code: 'ERR_CRYPTO_UNKNOWN_DH_GROUP', - message: 'Unknown DH group' - }, - 'crypto.getDiffieHellman(\'unknown-group\') ' + - 'failed to throw the expected error.' -); - -assert.throws( - () => crypto.createDiffieHellman('', true), - { - code: 'ERR_INVALID_ARG_TYPE' - } -); -[true, Symbol(), {}, () => {}, []].forEach((generator) => assert.throws( - () => crypto.createDiffieHellman('', 'base64', generator), - { code: 'ERR_INVALID_ARG_TYPE' } -)); - { - function unlessInvalidState(f) { - try { - return f(); - } catch (err) { - if (err.code !== 'ERR_CRYPTO_INVALID_STATE') { - throw err; - } - } - } - - function testGenerateKeysChangesKeys(setup, expected) { - const dh = crypto.createDiffieHellman(size); - setup(dh); - const firstPublicKey = unlessInvalidState(() => dh.getPublicKey()); - const firstPrivateKey = unlessInvalidState(() => dh.getPrivateKey()); - dh.generateKeys(); - const secondPublicKey = dh.getPublicKey(); - const secondPrivateKey = dh.getPrivateKey(); - function changed(shouldChange, first, second) { - if (shouldChange) { - assert.notDeepStrictEqual(first, second); - } else { - assert.deepStrictEqual(first, second); - } - } - changed(expected.includes('public'), firstPublicKey, secondPublicKey); - changed(expected.includes('private'), firstPrivateKey, secondPrivateKey); - } - - // Both the private and the public key are missing: generateKeys() generates both. - testGenerateKeysChangesKeys(() => { - // No setup. - }, ['public', 'private']); - - // Neither key is missing: generateKeys() does nothing. - testGenerateKeysChangesKeys((dh) => { - dh.generateKeys(); - }, []); - - // Only the public key is missing: generateKeys() generates only the public key. - testGenerateKeysChangesKeys((dh) => { - dh.setPrivateKey(Buffer.from('01020304', 'hex')); - }, ['public']); - - // The public key is outdated: generateKeys() generates only the public key. - testGenerateKeysChangesKeys((dh) => { - const oldPublicKey = dh.generateKeys(); - dh.setPrivateKey(Buffer.from('01020304', 'hex')); - assert.deepStrictEqual(dh.getPublicKey(), oldPublicKey); - }, ['public']); + crypto.createDiffieHellman('abcdef', Buffer.from([2])); // OK }