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