1'use strict'; 2const common = require('../common'); 3if (!common.hasCrypto) 4 common.skip('missing crypto'); 5 6const assert = require('assert'); 7const crypto = require('crypto'); 8 9const constants = crypto.constants; 10 11const fixtures = require('../common/fixtures'); 12 13// Test certificates 14const certPem = fixtures.readKey('rsa_cert.crt'); 15const keyPem = fixtures.readKey('rsa_private.pem'); 16const rsaKeySize = 2048; 17const rsaPubPem = fixtures.readKey('rsa_public.pem', 'ascii'); 18const rsaKeyPem = fixtures.readKey('rsa_private.pem', 'ascii'); 19const rsaKeyPemEncrypted = fixtures.readKey('rsa_private_encrypted.pem', 20 'ascii'); 21const dsaPubPem = fixtures.readKey('dsa_public.pem', 'ascii'); 22const dsaKeyPem = fixtures.readKey('dsa_private.pem', 'ascii'); 23const dsaKeyPemEncrypted = fixtures.readKey('dsa_private_encrypted.pem', 24 'ascii'); 25const rsaPkcs8KeyPem = fixtures.readKey('rsa_private_pkcs8.pem'); 26const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem'); 27 28const decryptError = { 29 message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' + 30 'bad decrypt', 31 code: 'ERR_OSSL_EVP_BAD_DECRYPT', 32 reason: 'bad decrypt', 33 function: 'EVP_DecryptFinal_ex', 34 library: 'digital envelope routines', 35}; 36 37// Test RSA encryption/decryption 38{ 39 const input = 'I AM THE WALRUS'; 40 const bufferToEncrypt = Buffer.from(input); 41 const bufferPassword = Buffer.from('password'); 42 43 let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt); 44 45 let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer); 46 assert.strictEqual(decryptedBuffer.toString(), input); 47 48 decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer); 49 assert.strictEqual(decryptedBuffer.toString(), input); 50 51 let decryptedBufferWithPassword = crypto.privateDecrypt({ 52 key: rsaKeyPemEncrypted, 53 passphrase: 'password' 54 }, encryptedBuffer); 55 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 56 57 encryptedBuffer = crypto.publicEncrypt({ 58 key: rsaKeyPemEncrypted, 59 passphrase: 'password' 60 }, bufferToEncrypt); 61 62 decryptedBufferWithPassword = crypto.privateDecrypt({ 63 key: rsaKeyPemEncrypted, 64 passphrase: 'password' 65 }, encryptedBuffer); 66 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 67 68 encryptedBuffer = crypto.privateEncrypt({ 69 key: rsaKeyPemEncrypted, 70 passphrase: bufferPassword 71 }, bufferToEncrypt); 72 73 decryptedBufferWithPassword = crypto.publicDecrypt({ 74 key: rsaKeyPemEncrypted, 75 passphrase: bufferPassword 76 }, encryptedBuffer); 77 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 78 79 // Now with explicit RSA_PKCS1_PADDING. 80 encryptedBuffer = crypto.privateEncrypt({ 81 padding: crypto.constants.RSA_PKCS1_PADDING, 82 key: rsaKeyPemEncrypted, 83 passphrase: bufferPassword 84 }, bufferToEncrypt); 85 86 decryptedBufferWithPassword = crypto.publicDecrypt({ 87 padding: crypto.constants.RSA_PKCS1_PADDING, 88 key: rsaKeyPemEncrypted, 89 passphrase: bufferPassword 90 }, encryptedBuffer); 91 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 92 93 // Omitting padding should be okay because RSA_PKCS1_PADDING is the default. 94 decryptedBufferWithPassword = crypto.publicDecrypt({ 95 key: rsaKeyPemEncrypted, 96 passphrase: bufferPassword 97 }, encryptedBuffer); 98 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 99 100 // Now with RSA_NO_PADDING. Plaintext needs to match key size. 101 const plaintext = 'x'.repeat(rsaKeySize / 8); 102 encryptedBuffer = crypto.privateEncrypt({ 103 padding: crypto.constants.RSA_NO_PADDING, 104 key: rsaKeyPemEncrypted, 105 passphrase: bufferPassword 106 }, Buffer.from(plaintext)); 107 108 decryptedBufferWithPassword = crypto.publicDecrypt({ 109 padding: crypto.constants.RSA_NO_PADDING, 110 key: rsaKeyPemEncrypted, 111 passphrase: bufferPassword 112 }, encryptedBuffer); 113 assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext); 114 115 encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt); 116 117 decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); 118 assert.strictEqual(decryptedBuffer.toString(), input); 119 120 encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt); 121 122 decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); 123 assert.strictEqual(decryptedBuffer.toString(), input); 124 125 encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt); 126 127 decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer); 128 assert.strictEqual(decryptedBuffer.toString(), input); 129 130 assert.throws(() => { 131 crypto.privateDecrypt({ 132 key: rsaKeyPemEncrypted, 133 passphrase: 'wrong' 134 }, bufferToEncrypt); 135 }, decryptError); 136 137 assert.throws(() => { 138 crypto.publicEncrypt({ 139 key: rsaKeyPemEncrypted, 140 passphrase: 'wrong' 141 }, encryptedBuffer); 142 }, decryptError); 143 144 encryptedBuffer = crypto.privateEncrypt({ 145 key: rsaKeyPemEncrypted, 146 passphrase: Buffer.from('password') 147 }, bufferToEncrypt); 148 149 assert.throws(() => { 150 crypto.publicDecrypt({ 151 key: rsaKeyPemEncrypted, 152 passphrase: Buffer.from('wrong') 153 }, encryptedBuffer); 154 }, decryptError); 155} 156 157function test_rsa(padding, encryptOaepHash, decryptOaepHash) { 158 const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32; 159 const input = Buffer.allocUnsafe(size); 160 for (let i = 0; i < input.length; i++) 161 input[i] = (i * 7 + 11) & 0xff; 162 const bufferToEncrypt = Buffer.from(input); 163 164 padding = constants[padding]; 165 166 const encryptedBuffer = crypto.publicEncrypt({ 167 key: rsaPubPem, 168 padding: padding, 169 oaepHash: encryptOaepHash 170 }, bufferToEncrypt); 171 172 let decryptedBuffer = crypto.privateDecrypt({ 173 key: rsaKeyPem, 174 padding: padding, 175 oaepHash: decryptOaepHash 176 }, encryptedBuffer); 177 assert.deepStrictEqual(decryptedBuffer, input); 178 179 decryptedBuffer = crypto.privateDecrypt({ 180 key: rsaPkcs8KeyPem, 181 padding: padding, 182 oaepHash: decryptOaepHash 183 }, encryptedBuffer); 184 assert.deepStrictEqual(decryptedBuffer, input); 185} 186 187test_rsa('RSA_NO_PADDING'); 188test_rsa('RSA_PKCS1_PADDING'); 189test_rsa('RSA_PKCS1_OAEP_PADDING'); 190 191// Test OAEP with different hash functions. 192test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1'); 193test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined); 194test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256'); 195test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512'); 196assert.throws(() => { 197 test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512'); 198}, { 199 code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR' 200}); 201 202// The following RSA-OAEP test cases were created using the WebCrypto API to 203// ensure compatibility when using non-SHA1 hash functions. 204{ 205 const { decryptionTests } = 206 JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8')); 207 208 for (const { ct, oaepHash, oaepLabel } of decryptionTests) { 209 const decrypted = crypto.privateDecrypt({ 210 key: rsaPkcs8KeyPem, 211 oaepHash, 212 oaepLabel: oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined 213 }, Buffer.from(ct, 'hex')); 214 215 assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js'); 216 } 217} 218 219// Test invalid oaepHash and oaepLabel options. 220for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) { 221 assert.throws(() => { 222 fn({ 223 key: rsaPubPem, 224 oaepHash: 'Hello world' 225 }, Buffer.alloc(10)); 226 }, { 227 code: 'ERR_OSSL_EVP_INVALID_DIGEST' 228 }); 229 230 for (const oaepHash of [0, false, null, Symbol(), () => {}]) { 231 assert.throws(() => { 232 fn({ 233 key: rsaPubPem, 234 oaepHash 235 }, Buffer.alloc(10)); 236 }, { 237 code: 'ERR_INVALID_ARG_TYPE' 238 }); 239 } 240 241 for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}, 'foo']) { 242 assert.throws(() => { 243 fn({ 244 key: rsaPubPem, 245 oaepLabel 246 }, Buffer.alloc(10)); 247 }, { 248 code: 'ERR_INVALID_ARG_TYPE' 249 }); 250 } 251} 252 253// Test RSA key signing/verification 254let rsaSign = crypto.createSign('SHA1'); 255let rsaVerify = crypto.createVerify('SHA1'); 256assert.ok(rsaSign); 257assert.ok(rsaVerify); 258 259const expectedSignature = fixtures.readKey( 260 'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1', 261 'hex' 262); 263 264rsaSign.update(rsaPubPem); 265let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex'); 266assert.strictEqual(rsaSignature, expectedSignature); 267 268rsaVerify.update(rsaPubPem); 269assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); 270 271// Test RSA PKCS#8 key signing/verification 272rsaSign = crypto.createSign('SHA1'); 273rsaSign.update(rsaPubPem); 274rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex'); 275assert.strictEqual(rsaSignature, expectedSignature); 276 277rsaVerify = crypto.createVerify('SHA1'); 278rsaVerify.update(rsaPubPem); 279assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); 280 281// Test RSA key signing/verification with encrypted key 282rsaSign = crypto.createSign('SHA1'); 283rsaSign.update(rsaPubPem); 284const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' }; 285rsaSignature = rsaSign.sign(signOptions, 'hex'); 286assert.strictEqual(rsaSignature, expectedSignature); 287 288rsaVerify = crypto.createVerify('SHA1'); 289rsaVerify.update(rsaPubPem); 290assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); 291 292rsaSign = crypto.createSign('SHA1'); 293rsaSign.update(rsaPubPem); 294assert.throws(() => { 295 const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' }; 296 rsaSign.sign(signOptions, 'hex'); 297}, decryptError); 298 299// 300// Test RSA signing and verification 301// 302{ 303 const privateKey = fixtures.readKey('rsa_private_b.pem'); 304 const publicKey = fixtures.readKey('rsa_public_b.pem'); 305 306 const input = 'I AM THE WALRUS'; 307 308 const signature = fixtures.readKey( 309 'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256', 310 'hex' 311 ); 312 313 const sign = crypto.createSign('SHA256'); 314 sign.update(input); 315 316 const output = sign.sign(privateKey, 'hex'); 317 assert.strictEqual(output, signature); 318 319 const verify = crypto.createVerify('SHA256'); 320 verify.update(input); 321 322 assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true); 323 324 // Test the legacy signature algorithm name. 325 const sign2 = crypto.createSign('RSA-SHA256'); 326 sign2.update(input); 327 328 const output2 = sign2.sign(privateKey, 'hex'); 329 assert.strictEqual(output2, signature); 330 331 const verify2 = crypto.createVerify('SHA256'); 332 verify2.update(input); 333 334 assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true); 335} 336 337 338// 339// Test DSA signing and verification 340// 341{ 342 const input = 'I AM THE WALRUS'; 343 344 // DSA signatures vary across runs so there is no static string to verify 345 // against. 346 const sign = crypto.createSign('SHA1'); 347 sign.update(input); 348 const signature = sign.sign(dsaKeyPem, 'hex'); 349 350 const verify = crypto.createVerify('SHA1'); 351 verify.update(input); 352 353 assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); 354 355 // Test the legacy 'DSS1' name. 356 const sign2 = crypto.createSign('DSS1'); 357 sign2.update(input); 358 const signature2 = sign2.sign(dsaKeyPem, 'hex'); 359 360 const verify2 = crypto.createVerify('DSS1'); 361 verify2.update(input); 362 363 assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true); 364} 365 366 367// 368// Test DSA signing and verification with PKCS#8 private key 369// 370{ 371 const input = 'I AM THE WALRUS'; 372 373 // DSA signatures vary across runs so there is no static string to verify 374 // against. 375 const sign = crypto.createSign('SHA1'); 376 sign.update(input); 377 const signature = sign.sign(dsaPkcs8KeyPem, 'hex'); 378 379 const verify = crypto.createVerify('SHA1'); 380 verify.update(input); 381 382 assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); 383} 384 385 386// 387// Test DSA signing and verification with encrypted key 388// 389const input = 'I AM THE WALRUS'; 390 391{ 392 const sign = crypto.createSign('SHA1'); 393 sign.update(input); 394 assert.throws(() => { 395 sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex'); 396 }, decryptError); 397} 398 399{ 400 // DSA signatures vary across runs so there is no static string to verify 401 // against. 402 const sign = crypto.createSign('SHA1'); 403 sign.update(input); 404 const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' }; 405 const signature = sign.sign(signOptions, 'hex'); 406 407 const verify = crypto.createVerify('SHA1'); 408 verify.update(input); 409 410 assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); 411} 412