1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6 7const assert = require('assert'); 8const crypto = require('crypto'); 9const { 10 createSign, 11 createVerify, 12 publicEncrypt, 13 privateDecrypt, 14 sign, 15 verify, 16} = crypto; 17 18// The values below (modp2/modp2buf) are for a 1024 bits long prime from 19// RFC 2412 E.2, see https://tools.ietf.org/html/rfc2412. */ 20const modp2buf = Buffer.from([ 21 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 22 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 23 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 24 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 25 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95, 26 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 27 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 28 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 29 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 30 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb, 31 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 32 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81, 33 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 34]); 35 36function testDH({ publicKey: alicePublicKey, privateKey: alicePrivateKey }, 37 { publicKey: bobPublicKey, privateKey: bobPrivateKey }, 38 expectedValue) { 39 const buf1 = crypto.diffieHellman({ 40 privateKey: alicePrivateKey, 41 publicKey: bobPublicKey, 42 }); 43 const buf2 = crypto.diffieHellman({ 44 privateKey: bobPrivateKey, 45 publicKey: alicePublicKey, 46 }); 47 assert.deepStrictEqual(buf1, buf2); 48 49 if (expectedValue !== undefined) 50 assert.deepStrictEqual(buf1, expectedValue); 51} 52 53// Asserts that the size of the given key (in chars or bytes) is within 10% of 54// the expected size. 55function 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. 66function 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. 77function 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. 100function 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 109const pkcs1PubExp = getRegExpForPEM('RSA PUBLIC KEY'); 110const pkcs1PrivExp = getRegExpForPEM('RSA PRIVATE KEY'); 111const pkcs1EncExp = (cipher) => getRegExpForPEM('RSA PRIVATE KEY', cipher); 112const spkiExp = getRegExpForPEM('PUBLIC KEY'); 113const pkcs8Exp = getRegExpForPEM('PRIVATE KEY'); 114const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY'); 115const sec1Exp = getRegExpForPEM('EC PRIVATE KEY'); 116const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); 117 118module.exports = { 119 modp2buf, 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, 132}; 133