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 ec = new TextEncoder(); 29 30const openssl1DecryptError = { 31 message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' + 32 'bad decrypt', 33 code: 'ERR_OSSL_EVP_BAD_DECRYPT', 34 reason: 'bad decrypt', 35 function: 'EVP_DecryptFinal_ex', 36 library: 'digital envelope routines', 37}; 38 39const decryptError = common.hasOpenSSL3 ? 40 { message: 'error:1C800064:Provider routines::bad decrypt' } : 41 openssl1DecryptError; 42 43const decryptPrivateKeyError = common.hasOpenSSL3 ? { 44 message: 'error:1C800064:Provider routines::bad decrypt', 45} : openssl1DecryptError; 46 47function getBufferCopy(buf) { 48 return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); 49} 50 51// Test RSA encryption/decryption 52{ 53 const input = 'I AM THE WALRUS'; 54 const bufferToEncrypt = Buffer.from(input); 55 const bufferPassword = Buffer.from('password'); 56 57 let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt); 58 59 // Test other input types 60 let otherEncrypted; 61 { 62 const ab = getBufferCopy(ec.encode(rsaPubPem)); 63 const ab2enc = getBufferCopy(bufferToEncrypt); 64 65 crypto.publicEncrypt(ab, ab2enc); 66 crypto.publicEncrypt(new Uint8Array(ab), new Uint8Array(ab2enc)); 67 crypto.publicEncrypt(new DataView(ab), new DataView(ab2enc)); 68 otherEncrypted = crypto.publicEncrypt({ 69 key: Buffer.from(ab).toString('hex'), 70 encoding: 'hex' 71 }, Buffer.from(ab2enc).toString('hex')); 72 } 73 74 let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer); 75 const otherDecrypted = crypto.privateDecrypt(rsaKeyPem, otherEncrypted); 76 assert.strictEqual(decryptedBuffer.toString(), input); 77 assert.strictEqual(otherDecrypted.toString(), input); 78 79 decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer); 80 assert.strictEqual(decryptedBuffer.toString(), input); 81 82 let decryptedBufferWithPassword = crypto.privateDecrypt({ 83 key: rsaKeyPemEncrypted, 84 passphrase: 'password' 85 }, encryptedBuffer); 86 87 const otherDecryptedBufferWithPassword = crypto.privateDecrypt({ 88 key: rsaKeyPemEncrypted, 89 passphrase: ec.encode('password') 90 }, encryptedBuffer); 91 92 assert.strictEqual( 93 otherDecryptedBufferWithPassword.toString(), 94 decryptedBufferWithPassword.toString()); 95 96 decryptedBufferWithPassword = crypto.privateDecrypt({ 97 key: rsaKeyPemEncrypted, 98 passphrase: 'password' 99 }, encryptedBuffer); 100 101 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 102 103 encryptedBuffer = crypto.publicEncrypt({ 104 key: rsaKeyPemEncrypted, 105 passphrase: 'password' 106 }, bufferToEncrypt); 107 108 decryptedBufferWithPassword = crypto.privateDecrypt({ 109 key: rsaKeyPemEncrypted, 110 passphrase: 'password' 111 }, encryptedBuffer); 112 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 113 114 encryptedBuffer = crypto.privateEncrypt({ 115 key: rsaKeyPemEncrypted, 116 passphrase: bufferPassword 117 }, bufferToEncrypt); 118 119 decryptedBufferWithPassword = crypto.publicDecrypt({ 120 key: rsaKeyPemEncrypted, 121 passphrase: bufferPassword 122 }, encryptedBuffer); 123 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 124 125 // Now with explicit RSA_PKCS1_PADDING. 126 encryptedBuffer = crypto.privateEncrypt({ 127 padding: crypto.constants.RSA_PKCS1_PADDING, 128 key: rsaKeyPemEncrypted, 129 passphrase: bufferPassword 130 }, bufferToEncrypt); 131 132 decryptedBufferWithPassword = crypto.publicDecrypt({ 133 padding: crypto.constants.RSA_PKCS1_PADDING, 134 key: rsaKeyPemEncrypted, 135 passphrase: bufferPassword 136 }, encryptedBuffer); 137 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 138 139 // Omitting padding should be okay because RSA_PKCS1_PADDING is the default. 140 decryptedBufferWithPassword = crypto.publicDecrypt({ 141 key: rsaKeyPemEncrypted, 142 passphrase: bufferPassword 143 }, encryptedBuffer); 144 assert.strictEqual(decryptedBufferWithPassword.toString(), input); 145 146 // Now with RSA_NO_PADDING. Plaintext needs to match key size. 147 // OpenSSL 3.x has a rsa_check_padding that will cause an error if 148 // RSA_NO_PADDING is used. 149 if (!common.hasOpenSSL3) { 150 { 151 const plaintext = 'x'.repeat(rsaKeySize / 8); 152 encryptedBuffer = crypto.privateEncrypt({ 153 padding: crypto.constants.RSA_NO_PADDING, 154 key: rsaKeyPemEncrypted, 155 passphrase: bufferPassword 156 }, Buffer.from(plaintext)); 157 158 decryptedBufferWithPassword = crypto.publicDecrypt({ 159 padding: crypto.constants.RSA_NO_PADDING, 160 key: rsaKeyPemEncrypted, 161 passphrase: bufferPassword 162 }, encryptedBuffer); 163 assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext); 164 } 165 } 166 167 encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt); 168 169 decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); 170 assert.strictEqual(decryptedBuffer.toString(), input); 171 172 encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt); 173 174 decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); 175 assert.strictEqual(decryptedBuffer.toString(), input); 176 177 encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt); 178 179 decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer); 180 assert.strictEqual(decryptedBuffer.toString(), input); 181 182 assert.throws(() => { 183 crypto.privateDecrypt({ 184 key: rsaKeyPemEncrypted, 185 passphrase: 'wrong' 186 }, bufferToEncrypt); 187 }, decryptError); 188 189 assert.throws(() => { 190 crypto.publicEncrypt({ 191 key: rsaKeyPemEncrypted, 192 passphrase: 'wrong' 193 }, encryptedBuffer); 194 }, decryptError); 195 196 encryptedBuffer = crypto.privateEncrypt({ 197 key: rsaKeyPemEncrypted, 198 passphrase: Buffer.from('password') 199 }, bufferToEncrypt); 200 201 assert.throws(() => { 202 crypto.publicDecrypt({ 203 key: rsaKeyPemEncrypted, 204 passphrase: Buffer.from('wrong') 205 }, encryptedBuffer); 206 }, decryptError); 207} 208 209function test_rsa(padding, encryptOaepHash, decryptOaepHash) { 210 const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32; 211 const input = Buffer.allocUnsafe(size); 212 for (let i = 0; i < input.length; i++) 213 input[i] = (i * 7 + 11) & 0xff; 214 const bufferToEncrypt = Buffer.from(input); 215 216 padding = constants[padding]; 217 218 const encryptedBuffer = crypto.publicEncrypt({ 219 key: rsaPubPem, 220 padding: padding, 221 oaepHash: encryptOaepHash 222 }, bufferToEncrypt); 223 224 225 if (padding === constants.RSA_PKCS1_PADDING) { 226 assert.throws(() => { 227 crypto.privateDecrypt({ 228 key: rsaKeyPem, 229 padding: padding, 230 oaepHash: decryptOaepHash 231 }, encryptedBuffer); 232 }, { code: 'ERR_INVALID_ARG_VALUE' }); 233 assert.throws(() => { 234 crypto.privateDecrypt({ 235 key: rsaPkcs8KeyPem, 236 padding: padding, 237 oaepHash: decryptOaepHash 238 }, encryptedBuffer); 239 }, { code: 'ERR_INVALID_ARG_VALUE' }); 240 } else { 241 let decryptedBuffer = crypto.privateDecrypt({ 242 key: rsaKeyPem, 243 padding: padding, 244 oaepHash: decryptOaepHash 245 }, encryptedBuffer); 246 assert.deepStrictEqual(decryptedBuffer, input); 247 248 decryptedBuffer = crypto.privateDecrypt({ 249 key: rsaPkcs8KeyPem, 250 padding: padding, 251 oaepHash: decryptOaepHash 252 }, encryptedBuffer); 253 assert.deepStrictEqual(decryptedBuffer, input); 254 } 255} 256 257test_rsa('RSA_NO_PADDING'); 258test_rsa('RSA_PKCS1_PADDING'); 259test_rsa('RSA_PKCS1_OAEP_PADDING'); 260 261// Test OAEP with different hash functions. 262test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1'); 263test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined); 264test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256'); 265test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512'); 266assert.throws(() => { 267 test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512'); 268}, { 269 code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR' 270}); 271 272// The following RSA-OAEP test cases were created using the WebCrypto API to 273// ensure compatibility when using non-SHA1 hash functions. 274{ 275 const { decryptionTests } = 276 JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8')); 277 278 for (const { ct, oaepHash, oaepLabel } of decryptionTests) { 279 const label = oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined; 280 const copiedLabel = oaepLabel ? getBufferCopy(label) : undefined; 281 282 const decrypted = crypto.privateDecrypt({ 283 key: rsaPkcs8KeyPem, 284 oaepHash, 285 oaepLabel: oaepLabel ? label : undefined 286 }, Buffer.from(ct, 'hex')); 287 288 assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js'); 289 290 const otherDecrypted = crypto.privateDecrypt({ 291 key: rsaPkcs8KeyPem, 292 oaepHash, 293 oaepLabel: copiedLabel 294 }, Buffer.from(ct, 'hex')); 295 296 assert.strictEqual(otherDecrypted.toString('utf8'), 'Hello Node.js'); 297 } 298} 299 300// Test invalid oaepHash and oaepLabel options. 301for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) { 302 assert.throws(() => { 303 fn({ 304 key: rsaPubPem, 305 oaepHash: 'Hello world' 306 }, Buffer.alloc(10)); 307 }, { 308 code: 'ERR_OSSL_EVP_INVALID_DIGEST' 309 }); 310 311 for (const oaepHash of [0, false, null, Symbol(), () => {}]) { 312 assert.throws(() => { 313 fn({ 314 key: rsaPubPem, 315 oaepHash 316 }, Buffer.alloc(10)); 317 }, { 318 code: 'ERR_INVALID_ARG_TYPE' 319 }); 320 } 321 322 for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}]) { 323 assert.throws(() => { 324 fn({ 325 key: rsaPubPem, 326 oaepLabel 327 }, Buffer.alloc(10)); 328 }, { 329 code: 'ERR_INVALID_ARG_TYPE' 330 }); 331 } 332} 333 334// Test RSA key signing/verification 335let rsaSign = crypto.createSign('SHA1'); 336let rsaVerify = crypto.createVerify('SHA1'); 337assert.ok(rsaSign); 338assert.ok(rsaVerify); 339 340const expectedSignature = fixtures.readKey( 341 'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1', 342 'hex' 343); 344 345rsaSign.update(rsaPubPem); 346let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex'); 347assert.strictEqual(rsaSignature, expectedSignature); 348 349rsaVerify.update(rsaPubPem); 350assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); 351 352// Test RSA PKCS#8 key signing/verification 353rsaSign = crypto.createSign('SHA1'); 354rsaSign.update(rsaPubPem); 355rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex'); 356assert.strictEqual(rsaSignature, expectedSignature); 357 358rsaVerify = crypto.createVerify('SHA1'); 359rsaVerify.update(rsaPubPem); 360assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); 361 362// Test RSA key signing/verification with encrypted key 363rsaSign = crypto.createSign('SHA1'); 364rsaSign.update(rsaPubPem); 365const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' }; 366rsaSignature = rsaSign.sign(signOptions, 'hex'); 367assert.strictEqual(rsaSignature, expectedSignature); 368 369rsaVerify = crypto.createVerify('SHA1'); 370rsaVerify.update(rsaPubPem); 371assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); 372 373rsaSign = crypto.createSign('SHA1'); 374rsaSign.update(rsaPubPem); 375assert.throws(() => { 376 const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' }; 377 rsaSign.sign(signOptions, 'hex'); 378}, decryptPrivateKeyError); 379 380// 381// Test RSA signing and verification 382// 383{ 384 const privateKey = fixtures.readKey('rsa_private_b.pem'); 385 const publicKey = fixtures.readKey('rsa_public_b.pem'); 386 387 const input = 'I AM THE WALRUS'; 388 389 const signature = fixtures.readKey( 390 'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256', 391 'hex' 392 ); 393 394 const sign = crypto.createSign('SHA256'); 395 sign.update(input); 396 397 const output = sign.sign(privateKey, 'hex'); 398 assert.strictEqual(output, signature); 399 400 const verify = crypto.createVerify('SHA256'); 401 verify.update(input); 402 403 assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true); 404 405 // Test the legacy signature algorithm name. 406 const sign2 = crypto.createSign('RSA-SHA256'); 407 sign2.update(input); 408 409 const output2 = sign2.sign(privateKey, 'hex'); 410 assert.strictEqual(output2, signature); 411 412 const verify2 = crypto.createVerify('SHA256'); 413 verify2.update(input); 414 415 assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true); 416} 417 418 419// 420// Test DSA signing and verification 421// 422{ 423 const input = 'I AM THE WALRUS'; 424 425 // DSA signatures vary across runs so there is no static string to verify 426 // against. 427 const sign = crypto.createSign('SHA1'); 428 sign.update(input); 429 const signature = sign.sign(dsaKeyPem, 'hex'); 430 431 const verify = crypto.createVerify('SHA1'); 432 verify.update(input); 433 434 assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); 435 436 // Test the legacy 'DSS1' name. 437 const sign2 = crypto.createSign('DSS1'); 438 sign2.update(input); 439 const signature2 = sign2.sign(dsaKeyPem, 'hex'); 440 441 const verify2 = crypto.createVerify('DSS1'); 442 verify2.update(input); 443 444 assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true); 445} 446 447 448// 449// Test DSA signing and verification with PKCS#8 private key 450// 451{ 452 const input = 'I AM THE WALRUS'; 453 454 // DSA signatures vary across runs so there is no static string to verify 455 // against. 456 const sign = crypto.createSign('SHA1'); 457 sign.update(input); 458 const signature = sign.sign(dsaPkcs8KeyPem, 'hex'); 459 460 const verify = crypto.createVerify('SHA1'); 461 verify.update(input); 462 463 assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); 464} 465 466 467// 468// Test DSA signing and verification with encrypted key 469// 470const input = 'I AM THE WALRUS'; 471 472{ 473 const sign = crypto.createSign('SHA1'); 474 sign.update(input); 475 assert.throws(() => { 476 sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex'); 477 }, decryptPrivateKeyError); 478} 479 480{ 481 // DSA signatures vary across runs so there is no static string to verify 482 // against. 483 const sign = crypto.createSign('SHA1'); 484 sign.update(input); 485 const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' }; 486 const signature = sign.sign(signOptions, 'hex'); 487 488 const verify = crypto.createVerify('SHA1'); 489 verify.update(input); 490 491 assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); 492} 493