| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,14 @@ if (!common.hasCrypto) | |||
| 6 | 6 | ||
| 7 | 7 | const assert = require('assert'); | |
| 8 | 8 | const crypto = require('crypto'); | |
| 9 | + const { | ||
| 10 | + createSign, | ||
| 11 | + createVerify, | ||
| 12 | + publicEncrypt, | ||
| 13 | + privateDecrypt, | ||
| 14 | + sign, | ||
| 15 | + verify, | ||
| 16 | + } = crypto; | ||
| 9 | 17 | ||
| 10 | 18 | // The values below (modp2/modp2buf) are for a 1024 bits long prime from | |
| 11 | 19 | // RFC 2412 E.2, see https://tools.ietf.org/html/rfc2412. */ | |
@@ -42,7 +50,83 @@ function testDH({ publicKey: alicePublicKey, privateKey: alicePrivateKey }, | |||
| 42 | 50 | assert.deepStrictEqual(buf1, expectedValue); | |
| 43 | 51 | } | |
| 44 | 52 | ||
| 53 | + // Asserts that the size of the given key (in chars or bytes) is within 10% of | ||
| 54 | + // the expected size. | ||
| 55 | + function assertApproximateSize(key, expectedSize) { | ||
| 56 | + const u = typeof key === 'string' ? 'chars' : 'bytes'; | ||
| 57 | + const min = Math.floor(0.9 * expectedSize); | ||
| 58 | + const max = Math.ceil(1.1 * expectedSize); | ||
| 59 | + assert(key.length >= min, | ||
| 60 | + `Key (${key.length} ${u}) is shorter than expected (${min} ${u})`); | ||
| 61 | + assert(key.length <= max, | ||
| 62 | + `Key (${key.length} ${u}) is longer than expected (${max} ${u})`); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + // Tests that a key pair can be used for encryption / decryption. | ||
| 66 | + function testEncryptDecrypt(publicKey, privateKey) { | ||
| 67 | + const message = 'Hello Node.js world!'; | ||
| 68 | + const plaintext = Buffer.from(message, 'utf8'); | ||
| 69 | + for (const key of [publicKey, privateKey]) { | ||
| 70 | + const ciphertext = publicEncrypt(key, plaintext); | ||
| 71 | + const received = privateDecrypt(privateKey, ciphertext); | ||
| 72 | + assert.strictEqual(received.toString('utf8'), message); | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + // Tests that a key pair can be used for signing / verification. | ||
| 77 | + function testSignVerify(publicKey, privateKey) { | ||
| 78 | + const message = Buffer.from('Hello Node.js world!'); | ||
| 79 | + | ||
| 80 | + function oldSign(algo, data, key) { | ||
| 81 | + return createSign(algo).update(data).sign(key); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + function oldVerify(algo, data, key, signature) { | ||
| 85 | + return createVerify(algo).update(data).verify(key, signature); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + for (const signFn of [sign, oldSign]) { | ||
| 89 | + const signature = signFn('SHA256', message, privateKey); | ||
| 90 | + for (const verifyFn of [verify, oldVerify]) { | ||
| 91 | + for (const key of [publicKey, privateKey]) { | ||
| 92 | + const okay = verifyFn('SHA256', message, key, signature); | ||
| 93 | + assert(okay); | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + // Constructs a regular expression for a PEM-encoded key with the given label. | ||
| 100 | + function getRegExpForPEM(label, cipher) { | ||
| 101 | + const head = `\\-\\-\\-\\-\\-BEGIN ${label}\\-\\-\\-\\-\\-`; | ||
| 102 | + const rfc1421Header = cipher == null ? '' : | ||
| 103 | + `\nProc-Type: 4,ENCRYPTED\nDEK-Info: ${cipher},[^\n]+\n`; | ||
| 104 | + const body = '([a-zA-Z0-9\\+/=]{64}\n)*[a-zA-Z0-9\\+/=]{1,64}'; | ||
| 105 | + const end = `\\-\\-\\-\\-\\-END ${label}\\-\\-\\-\\-\\-`; | ||
| 106 | + return new RegExp(`^${head}${rfc1421Header}\n${body}\n${end}\n$`); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + const pkcs1PubExp = getRegExpForPEM('RSA PUBLIC KEY'); | ||
| 110 | + const pkcs1PrivExp = getRegExpForPEM('RSA PRIVATE KEY'); | ||
| 111 | + const pkcs1EncExp = (cipher) => getRegExpForPEM('RSA PRIVATE KEY', cipher); | ||
| 112 | + const spkiExp = getRegExpForPEM('PUBLIC KEY'); | ||
| 113 | + const pkcs8Exp = getRegExpForPEM('PRIVATE KEY'); | ||
| 114 | + const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY'); | ||
| 115 | + const sec1Exp = getRegExpForPEM('EC PRIVATE KEY'); | ||
| 116 | + const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); | ||
| 117 | + | ||
| 45 | 118 | module.exports = { | |
| 46 | 119 | modp2buf, | |
| 47 | 120 | testDH, | |
| 121 | + assertApproximateSize, | ||
| 122 | + testEncryptDecrypt, | ||
| 123 | + testSignVerify, | ||
| 124 | + pkcs1PubExp, | ||
| 125 | + pkcs1PrivExp, | ||
| 126 | + pkcs1EncExp, // used once | ||
| 127 | + spkiExp, | ||
| 128 | + pkcs8Exp, // used once | ||
| 129 | + pkcs8EncExp, // used once | ||
| 130 | + sec1Exp, | ||
| 131 | + sec1EncExp, | ||
| 48 | 132 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,32 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + | ||
| 7 | + const assert = require('assert'); | ||
| 8 | + const { | ||
| 9 | + generateKeyPair, | ||
| 10 | + } = require('crypto'); | ||
| 11 | + | ||
| 12 | + // Test async DSA key object generation. | ||
| 13 | + { | ||
| 14 | + generateKeyPair('dsa', { | ||
| 15 | + modulusLength: common.hasOpenSSL3 ? 2048 : 512, | ||
| 16 | + divisorLength: 256 | ||
| 17 | + }, common.mustSucceed((publicKey, privateKey) => { | ||
| 18 | + assert.strictEqual(publicKey.type, 'public'); | ||
| 19 | + assert.strictEqual(publicKey.asymmetricKeyType, 'dsa'); | ||
| 20 | + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { | ||
| 21 | + modulusLength: common.hasOpenSSL3 ? 2048 : 512, | ||
| 22 | + divisorLength: 256 | ||
| 23 | + }); | ||
| 24 | + | ||
| 25 | + assert.strictEqual(privateKey.type, 'private'); | ||
| 26 | + assert.strictEqual(privateKey.asymmetricKeyType, 'dsa'); | ||
| 27 | + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { | ||
| 28 | + modulusLength: common.hasOpenSSL3 ? 2048 : 512, | ||
| 29 | + divisorLength: 256 | ||
| 30 | + }); | ||
| 31 | + })); | ||
| 32 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,64 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + | ||
| 7 | + const assert = require('assert'); | ||
| 8 | + const { | ||
| 9 | + generateKeyPair, | ||
| 10 | + } = require('crypto'); | ||
| 11 | + const { | ||
| 12 | + assertApproximateSize, | ||
| 13 | + testSignVerify, | ||
| 14 | + spkiExp, | ||
| 15 | + } = require('../common/crypto'); | ||
| 16 | + | ||
| 17 | + // Test async DSA key generation. | ||
| 18 | + { | ||
| 19 | + const privateKeyEncoding = { | ||
| 20 | + type: 'pkcs8', | ||
| 21 | + format: 'der' | ||
| 22 | + }; | ||
| 23 | + | ||
| 24 | + generateKeyPair('dsa', { | ||
| 25 | + modulusLength: common.hasOpenSSL3 ? 2048 : 512, | ||
| 26 | + divisorLength: 256, | ||
| 27 | + publicKeyEncoding: { | ||
| 28 | + type: 'spki', | ||
| 29 | + format: 'pem' | ||
| 30 | + }, | ||
| 31 | + privateKeyEncoding: { | ||
| 32 | + cipher: 'aes-128-cbc', | ||
| 33 | + passphrase: 'secret', | ||
| 34 | + ...privateKeyEncoding | ||
| 35 | + } | ||
| 36 | + }, common.mustSucceed((publicKey, privateKeyDER) => { | ||
| 37 | + assert.strictEqual(typeof publicKey, 'string'); | ||
| 38 | + assert.match(publicKey, spkiExp); | ||
| 39 | + // The private key is DER-encoded. | ||
| 40 | + assert(Buffer.isBuffer(privateKeyDER)); | ||
| 41 | + | ||
| 42 | + assertApproximateSize(publicKey, common.hasOpenSSL3 ? 1194 : 440); | ||
| 43 | + assertApproximateSize(privateKeyDER, common.hasOpenSSL3 ? 721 : 336); | ||
| 44 | + | ||
| 45 | + // Since the private key is encrypted, signing shouldn't work anymore. | ||
| 46 | + assert.throws(() => { | ||
| 47 | + return testSignVerify(publicKey, { | ||
| 48 | + key: privateKeyDER, | ||
| 49 | + ...privateKeyEncoding | ||
| 50 | + }); | ||
| 51 | + }, { | ||
| 52 | + name: 'TypeError', | ||
| 53 | + code: 'ERR_MISSING_PASSPHRASE', | ||
| 54 | + message: 'Passphrase required for encrypted key' | ||
| 55 | + }); | ||
| 56 | + | ||
| 57 | + // Signing should work with the correct password. | ||
| 58 | + testSignVerify(publicKey, { | ||
| 59 | + key: privateKeyDER, | ||
| 60 | + ...privateKeyEncoding, | ||
| 61 | + passphrase: 'secret' | ||
| 62 | + }); | ||
| 63 | + })); | ||
| 64 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,100 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + | ||
| 7 | + const assert = require('assert'); | ||
| 8 | + const { | ||
| 9 | + generateKeyPair, | ||
| 10 | + } = require('crypto'); | ||
| 11 | + | ||
| 12 | + // Test async elliptic curve key generation with 'jwk' encoding | ||
| 13 | + { | ||
| 14 | + [ | ||
| 15 | + ['ec', ['P-384', 'P-256', 'P-521', 'secp256k1']], | ||
| 16 | + ['rsa'], | ||
| 17 | + ['ed25519'], | ||
| 18 | + ['ed448'], | ||
| 19 | + ['x25519'], | ||
| 20 | + ['x448'], | ||
| 21 | + ].forEach((types) => { | ||
| 22 | + const [type, options] = types; | ||
| 23 | + switch (type) { | ||
| 24 | + case 'ec': { | ||
| 25 | + return options.forEach((curve) => { | ||
| 26 | + generateKeyPair(type, { | ||
| 27 | + namedCurve: curve, | ||
| 28 | + publicKeyEncoding: { | ||
| 29 | + format: 'jwk' | ||
| 30 | + }, | ||
| 31 | + privateKeyEncoding: { | ||
| 32 | + format: 'jwk' | ||
| 33 | + } | ||
| 34 | + }, common.mustSucceed((publicKey, privateKey) => { | ||
| 35 | + assert.strictEqual(typeof publicKey, 'object'); | ||
| 36 | + assert.strictEqual(typeof privateKey, 'object'); | ||
| 37 | + assert.strictEqual(publicKey.x, privateKey.x); | ||
| 38 | + assert.strictEqual(publicKey.y, privateKey.y); | ||
| 39 | + assert(!publicKey.d); | ||
| 40 | + assert(privateKey.d); | ||
| 41 | + assert.strictEqual(publicKey.kty, 'EC'); | ||
| 42 | + assert.strictEqual(publicKey.kty, privateKey.kty); | ||
| 43 | + assert.strictEqual(publicKey.crv, curve); | ||
| 44 | + assert.strictEqual(publicKey.crv, privateKey.crv); | ||
| 45 | + })); | ||
| 46 | + }); | ||
| 47 | + } | ||
| 48 | + case 'rsa': { | ||
| 49 | + return generateKeyPair(type, { | ||
| 50 | + modulusLength: 4096, | ||
| 51 | + publicKeyEncoding: { | ||
| 52 | + format: 'jwk' | ||
| 53 | + }, | ||
| 54 | + privateKeyEncoding: { | ||
| 55 | + format: 'jwk' | ||
| 56 | + } | ||
| 57 | + }, common.mustSucceed((publicKey, privateKey) => { | ||
| 58 | + assert.strictEqual(typeof publicKey, 'object'); | ||
| 59 | + assert.strictEqual(typeof privateKey, 'object'); | ||
| 60 | + assert.strictEqual(publicKey.kty, 'RSA'); | ||
| 61 | + assert.strictEqual(publicKey.kty, privateKey.kty); | ||
| 62 | + assert.strictEqual(typeof publicKey.n, 'string'); | ||
| 63 | + assert.strictEqual(publicKey.n, privateKey.n); | ||
| 64 | + assert.strictEqual(typeof publicKey.e, 'string'); | ||
| 65 | + assert.strictEqual(publicKey.e, privateKey.e); | ||
| 66 | + assert.strictEqual(typeof privateKey.d, 'string'); | ||
| 67 | + assert.strictEqual(typeof privateKey.p, 'string'); | ||
| 68 | + assert.strictEqual(typeof privateKey.q, 'string'); | ||
| 69 | + assert.strictEqual(typeof privateKey.dp, 'string'); | ||
| 70 | + assert.strictEqual(typeof privateKey.dq, 'string'); | ||
| 71 | + assert.strictEqual(typeof privateKey.qi, 'string'); | ||
| 72 | + })); | ||
| 73 | + } | ||
| 74 | + case 'ed25519': | ||
| 75 | + case 'ed448': | ||
| 76 | + case 'x25519': | ||
| 77 | + case 'x448': { | ||
| 78 | + generateKeyPair(type, { | ||
| 79 | + publicKeyEncoding: { | ||
| 80 | + format: 'jwk' | ||
| 81 | + }, | ||
| 82 | + privateKeyEncoding: { | ||
| 83 | + format: 'jwk' | ||
| 84 | + } | ||
| 85 | + }, common.mustSucceed((publicKey, privateKey) => { | ||
| 86 | + assert.strictEqual(typeof publicKey, 'object'); | ||
| 87 | + assert.strictEqual(typeof privateKey, 'object'); | ||
| 88 | + assert.strictEqual(publicKey.x, privateKey.x); | ||
| 89 | + assert(!publicKey.d); | ||
| 90 | + assert(privateKey.d); | ||
| 91 | + assert.strictEqual(publicKey.kty, 'OKP'); | ||
| 92 | + assert.strictEqual(publicKey.kty, privateKey.kty); | ||
| 93 | + const expectedCrv = `${type.charAt(0).toUpperCase()}${type.slice(1)}`; | ||
| 94 | + assert.strictEqual(publicKey.crv, expectedCrv); | ||
| 95 | + assert.strictEqual(publicKey.crv, privateKey.crv); | ||
| 96 | + })); | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + }); | ||
| 100 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,50 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + | ||
| 7 | + const assert = require('assert'); | ||
| 8 | + const { | ||
| 9 | + generateKeyPair, | ||
| 10 | + } = require('crypto'); | ||
| 11 | + const { | ||
| 12 | + assertApproximateSize, | ||
| 13 | + testEncryptDecrypt, | ||
| 14 | + testSignVerify, | ||
| 15 | + } = require('../common/crypto'); | ||
| 16 | + | ||
| 17 | + // Test async RSA key generation with an encrypted private key, but encoded as DER. | ||
| 18 | + { | ||
| 19 | + generateKeyPair('rsa', { | ||
| 20 | + publicExponent: 0x10001, | ||
| 21 | + modulusLength: 512, | ||
| 22 | + publicKeyEncoding: { | ||
| 23 | + type: 'pkcs1', | ||
| 24 | + format: 'der' | ||
| 25 | + }, | ||
| 26 | + privateKeyEncoding: { | ||
| 27 | + type: 'pkcs8', | ||
| 28 | + format: 'der' | ||
| 29 | + } | ||
| 30 | + }, common.mustSucceed((publicKeyDER, privateKeyDER) => { | ||
| 31 | + assert(Buffer.isBuffer(publicKeyDER)); | ||
| 32 | + assertApproximateSize(publicKeyDER, 74); | ||
| 33 | + | ||
| 34 | + assert(Buffer.isBuffer(privateKeyDER)); | ||
| 35 | + | ||
| 36 | + const publicKey = { | ||
| 37 | + key: publicKeyDER, | ||
| 38 | + type: 'pkcs1', | ||
| 39 | + format: 'der', | ||
| 40 | + }; | ||
| 41 | + const privateKey = { | ||
| 42 | + key: privateKeyDER, | ||
| 43 | + format: 'der', | ||
| 44 | + type: 'pkcs8', | ||
| 45 | + passphrase: 'secret' | ||
| 46 | + }; | ||
| 47 | + testEncryptDecrypt(publicKey, privateKey); | ||
| 48 | + testSignVerify(publicKey, privateKey); | ||
| 49 | + })); | ||
| 50 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments