1'use strict'; 2const common = require('../common'); 3if (!common.hasCrypto) 4 common.skip('missing crypto'); 5 6const assert = require('assert'); 7const fs = require('fs'); 8const path = require('path'); 9const exec = require('child_process').exec; 10const crypto = require('crypto'); 11const fixtures = require('../common/fixtures'); 12 13// Test certificates 14const certPem = fixtures.readKey('rsa_cert.crt'); 15const keyPem = fixtures.readKey('rsa_private.pem'); 16const keySize = 2048; 17 18{ 19 const Sign = crypto.Sign; 20 const instance = Sign('SHA256'); 21 assert(instance instanceof Sign, 'Sign is expected to return a new ' + 22 'instance when called without `new`'); 23} 24 25{ 26 const Verify = crypto.Verify; 27 const instance = Verify('SHA256'); 28 assert(instance instanceof Verify, 'Verify is expected to return a new ' + 29 'instance when called without `new`'); 30} 31 32// Test handling of exceptional conditions 33{ 34 const library = { 35 configurable: true, 36 set() { 37 throw new Error('bye, bye, library'); 38 } 39 }; 40 Object.defineProperty(Object.prototype, 'library', library); 41 42 assert.throws(() => { 43 crypto.createSign('sha1').sign( 44 `-----BEGIN RSA PRIVATE KEY----- 45 AAAAAAAAAAAA 46 -----END RSA PRIVATE KEY-----`); 47 }, { message: 'bye, bye, library' }); 48 49 delete Object.prototype.library; 50 51 const errorStack = { 52 configurable: true, 53 set() { 54 throw new Error('bye, bye, error stack'); 55 } 56 }; 57 Object.defineProperty(Object.prototype, 'opensslErrorStack', errorStack); 58 59 assert.throws(() => { 60 crypto.createSign('SHA1') 61 .update('Test123') 62 .sign({ 63 key: keyPem, 64 padding: crypto.constants.RSA_PKCS1_OAEP_PADDING 65 }); 66 }, { message: 'bye, bye, error stack' }); 67 68 delete Object.prototype.opensslErrorStack; 69} 70 71assert.throws( 72 () => crypto.createVerify('SHA256').verify({ 73 key: certPem, 74 padding: null, 75 }, ''), 76 { 77 code: 'ERR_INVALID_OPT_VALUE', 78 name: 'TypeError', 79 message: 'The value "null" is invalid for option "padding"' 80 }); 81 82assert.throws( 83 () => crypto.createVerify('SHA256').verify({ 84 key: certPem, 85 saltLength: null, 86 }, ''), 87 { 88 code: 'ERR_INVALID_OPT_VALUE', 89 name: 'TypeError', 90 message: 'The value "null" is invalid for option "saltLength"' 91 }); 92 93// Test signing and verifying 94{ 95 const s1 = crypto.createSign('SHA1') 96 .update('Test123') 97 .sign(keyPem, 'base64'); 98 let s1stream = crypto.createSign('SHA1'); 99 s1stream.end('Test123'); 100 s1stream = s1stream.sign(keyPem, 'base64'); 101 assert.strictEqual(s1, s1stream, `${s1} should equal ${s1stream}`); 102 103 const verified = crypto.createVerify('SHA1') 104 .update('Test') 105 .update('123') 106 .verify(certPem, s1, 'base64'); 107 assert.strictEqual(verified, true); 108} 109 110{ 111 const s2 = crypto.createSign('SHA256') 112 .update('Test123') 113 .sign(keyPem, 'latin1'); 114 let s2stream = crypto.createSign('SHA256'); 115 s2stream.end('Test123'); 116 s2stream = s2stream.sign(keyPem, 'latin1'); 117 assert.strictEqual(s2, s2stream, `${s2} should equal ${s2stream}`); 118 119 let verified = crypto.createVerify('SHA256') 120 .update('Test') 121 .update('123') 122 .verify(certPem, s2, 'latin1'); 123 assert.strictEqual(verified, true); 124 125 const verStream = crypto.createVerify('SHA256'); 126 verStream.write('Tes'); 127 verStream.write('t12'); 128 verStream.end('3'); 129 verified = verStream.verify(certPem, s2, 'latin1'); 130 assert.strictEqual(verified, true); 131} 132 133{ 134 const s3 = crypto.createSign('SHA1') 135 .update('Test123') 136 .sign(keyPem, 'buffer'); 137 let verified = crypto.createVerify('SHA1') 138 .update('Test') 139 .update('123') 140 .verify(certPem, s3); 141 assert.strictEqual(verified, true); 142 143 const verStream = crypto.createVerify('SHA1'); 144 verStream.write('Tes'); 145 verStream.write('t12'); 146 verStream.end('3'); 147 verified = verStream.verify(certPem, s3); 148 assert.strictEqual(verified, true); 149} 150 151// Special tests for RSA_PKCS1_PSS_PADDING 152{ 153 function testPSS(algo, hLen) { 154 // Maximum permissible salt length 155 const max = keySize / 8 - hLen - 2; 156 157 function getEffectiveSaltLength(saltLength) { 158 switch (saltLength) { 159 case crypto.constants.RSA_PSS_SALTLEN_DIGEST: 160 return hLen; 161 case crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN: 162 return max; 163 default: 164 return saltLength; 165 } 166 } 167 168 const signSaltLengths = [ 169 crypto.constants.RSA_PSS_SALTLEN_DIGEST, 170 getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST), 171 crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, 172 getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN), 173 0, 16, 32, 64, 128, 174 ]; 175 176 const verifySaltLengths = [ 177 crypto.constants.RSA_PSS_SALTLEN_DIGEST, 178 getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST), 179 getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN), 180 0, 16, 32, 64, 128, 181 ]; 182 const errMessage = /^Error:.*data too large for key size$/; 183 184 const data = Buffer.from('Test123'); 185 186 signSaltLengths.forEach((signSaltLength) => { 187 if (signSaltLength > max) { 188 // If the salt length is too big, an Error should be thrown 189 assert.throws(() => { 190 crypto.createSign(algo) 191 .update(data) 192 .sign({ 193 key: keyPem, 194 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 195 saltLength: signSaltLength 196 }); 197 }, errMessage); 198 assert.throws(() => { 199 crypto.sign(algo, data, { 200 key: keyPem, 201 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 202 saltLength: signSaltLength 203 }); 204 }, errMessage); 205 } else { 206 // Otherwise, a valid signature should be generated 207 const s4 = crypto.createSign(algo) 208 .update(data) 209 .sign({ 210 key: keyPem, 211 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 212 saltLength: signSaltLength 213 }); 214 const s4_2 = crypto.sign(algo, data, { 215 key: keyPem, 216 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 217 saltLength: signSaltLength 218 }); 219 220 [s4, s4_2].forEach((sig) => { 221 let verified; 222 verifySaltLengths.forEach((verifySaltLength) => { 223 // Verification should succeed if and only if the salt length is 224 // correct 225 verified = crypto.createVerify(algo) 226 .update(data) 227 .verify({ 228 key: certPem, 229 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 230 saltLength: verifySaltLength 231 }, sig); 232 assert.strictEqual(verified, crypto.verify(algo, data, { 233 key: certPem, 234 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 235 saltLength: verifySaltLength 236 }, sig)); 237 const saltLengthCorrect = getEffectiveSaltLength(signSaltLength) === 238 getEffectiveSaltLength(verifySaltLength); 239 assert.strictEqual(verified, saltLengthCorrect); 240 }); 241 242 // Verification using RSA_PSS_SALTLEN_AUTO should always work 243 verified = crypto.createVerify(algo) 244 .update(data) 245 .verify({ 246 key: certPem, 247 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 248 saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO 249 }, sig); 250 assert.strictEqual(verified, true); 251 assert.strictEqual(verified, crypto.verify(algo, data, { 252 key: certPem, 253 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 254 saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO 255 }, sig)); 256 257 // Verifying an incorrect message should never work 258 const wrongData = Buffer.from('Test1234'); 259 verified = crypto.createVerify(algo) 260 .update(wrongData) 261 .verify({ 262 key: certPem, 263 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 264 saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO 265 }, sig); 266 assert.strictEqual(verified, false); 267 assert.strictEqual(verified, crypto.verify(algo, wrongData, { 268 key: certPem, 269 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 270 saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO 271 }, sig)); 272 }); 273 } 274 }); 275 } 276 277 testPSS('SHA1', 20); 278 testPSS('SHA256', 32); 279} 280 281// Test vectors for RSA_PKCS1_PSS_PADDING provided by the RSA Laboratories: 282// https://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm 283{ 284 // We only test verification as we cannot specify explicit salts when signing 285 function testVerify(cert, vector) { 286 const verified = crypto.createVerify('SHA1') 287 .update(Buffer.from(vector.message, 'hex')) 288 .verify({ 289 key: cert, 290 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 291 saltLength: vector.salt.length / 2 292 }, vector.signature, 'hex'); 293 assert.strictEqual(verified, true); 294 } 295 296 const examples = JSON.parse(fixtures.readSync('pss-vectors.json', 'utf8')); 297 298 for (const key in examples) { 299 const example = examples[key]; 300 const publicKey = example.publicKey.join('\n'); 301 example.tests.forEach((test) => testVerify(publicKey, test)); 302 } 303} 304 305// Test exceptions for invalid `padding` and `saltLength` values 306{ 307 [null, NaN, 'boom', {}, [], true, false] 308 .forEach((invalidValue) => { 309 assert.throws(() => { 310 crypto.createSign('SHA256') 311 .update('Test123') 312 .sign({ 313 key: keyPem, 314 padding: invalidValue 315 }); 316 }, { 317 code: 'ERR_INVALID_OPT_VALUE', 318 name: 'TypeError' 319 }); 320 321 assert.throws(() => { 322 crypto.createSign('SHA256') 323 .update('Test123') 324 .sign({ 325 key: keyPem, 326 padding: crypto.constants.RSA_PKCS1_PSS_PADDING, 327 saltLength: invalidValue 328 }); 329 }, { 330 code: 'ERR_INVALID_OPT_VALUE', 331 name: 'TypeError' 332 }); 333 }); 334 335 assert.throws(() => { 336 crypto.createSign('SHA1') 337 .update('Test123') 338 .sign({ 339 key: keyPem, 340 padding: crypto.constants.RSA_PKCS1_OAEP_PADDING 341 }); 342 }, { 343 code: 'ERR_OSSL_RSA_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE', 344 message: /illegal or unsupported padding mode/, 345 opensslErrorStack: [ 346 'error:06089093:digital envelope routines:EVP_PKEY_CTX_ctrl:' + 347 'command not supported', 348 ], 349 }); 350} 351 352// Test throws exception when key options is null 353{ 354 assert.throws(() => { 355 crypto.createSign('SHA1').update('Test123').sign(null, 'base64'); 356 }, { 357 code: 'ERR_CRYPTO_SIGN_KEY_REQUIRED', 358 name: 'Error' 359 }); 360} 361 362{ 363 const sign = crypto.createSign('SHA1'); 364 const verify = crypto.createVerify('SHA1'); 365 366 [1, [], {}, undefined, null, true, Infinity].forEach((input) => { 367 const errObj = { 368 code: 'ERR_INVALID_ARG_TYPE', 369 name: 'TypeError', 370 message: 'The "algorithm" argument must be of type string.' + 371 `${common.invalidArgTypeHelper(input)}` 372 }; 373 assert.throws(() => crypto.createSign(input), errObj); 374 assert.throws(() => crypto.createVerify(input), errObj); 375 376 errObj.message = 'The "data" argument must be of type string or an ' + 377 'instance of Buffer, TypedArray, or DataView.' + 378 common.invalidArgTypeHelper(input); 379 assert.throws(() => sign.update(input), errObj); 380 assert.throws(() => verify.update(input), errObj); 381 assert.throws(() => sign._write(input, 'utf8', () => {}), errObj); 382 assert.throws(() => verify._write(input, 'utf8', () => {}), errObj); 383 }); 384 385 [ 386 Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array, 387 ].forEach((clazz) => { 388 // These should all just work 389 sign.update(new clazz()); 390 verify.update(new clazz()); 391 }); 392 393 [1, {}, [], Infinity].forEach((input) => { 394 let prop = '"key" argument'; 395 let value = input; 396 if (typeof input === 'object') { 397 prop = '"key.key" property'; 398 value = undefined; 399 } 400 const errObj = { 401 code: 'ERR_INVALID_ARG_TYPE', 402 name: 'TypeError', 403 message: `The ${prop} must be of type string or ` + 404 'an instance of Buffer, TypedArray, DataView, or KeyObject.' + 405 common.invalidArgTypeHelper(value) 406 }; 407 408 assert.throws(() => sign.sign(input), errObj); 409 assert.throws(() => verify.verify(input), errObj); 410 411 errObj.message = 'The "signature" argument must be of type string or an ' + 412 'instance of Buffer, TypedArray, or DataView.' + 413 common.invalidArgTypeHelper(input); 414 assert.throws(() => verify.verify('test', input), errObj); 415 }); 416} 417 418{ 419 assert.throws( 420 () => crypto.createSign('sha8'), 421 /Unknown message digest/); 422 assert.throws( 423 () => crypto.sign('sha8', Buffer.alloc(1), keyPem), 424 /Unknown message digest/); 425} 426 427[ 428 { private: fixtures.readKey('ed25519_private.pem', 'ascii'), 429 public: fixtures.readKey('ed25519_public.pem', 'ascii'), 430 algo: null, 431 sigLen: 64 }, 432 { private: fixtures.readKey('ed448_private.pem', 'ascii'), 433 public: fixtures.readKey('ed448_public.pem', 'ascii'), 434 algo: null, 435 sigLen: 114 }, 436 { private: fixtures.readKey('rsa_private_2048.pem', 'ascii'), 437 public: fixtures.readKey('rsa_public_2048.pem', 'ascii'), 438 algo: 'sha1', 439 sigLen: 256 }, 440].forEach((pair) => { 441 const algo = pair.algo; 442 443 { 444 const data = Buffer.from('Hello world'); 445 const sig = crypto.sign(algo, data, pair.private); 446 assert.strictEqual(sig.length, pair.sigLen); 447 448 assert.strictEqual(crypto.verify(algo, data, pair.private, sig), 449 true); 450 assert.strictEqual(crypto.verify(algo, data, pair.public, sig), 451 true); 452 } 453 454 { 455 const data = Buffer.from('Hello world'); 456 const privKeyObj = crypto.createPrivateKey(pair.private); 457 const pubKeyObj = crypto.createPublicKey(pair.public); 458 459 const sig = crypto.sign(algo, data, privKeyObj); 460 assert.strictEqual(sig.length, pair.sigLen); 461 462 assert.strictEqual(crypto.verify(algo, data, privKeyObj, sig), true); 463 assert.strictEqual(crypto.verify(algo, data, pubKeyObj, sig), true); 464 } 465 466 { 467 const data = Buffer.from('Hello world'); 468 const otherData = Buffer.from('Goodbye world'); 469 const otherSig = crypto.sign(algo, otherData, pair.private); 470 assert.strictEqual(crypto.verify(algo, data, pair.private, otherSig), 471 false); 472 } 473 474 [ 475 Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array, 476 ].forEach((clazz) => { 477 const data = new clazz(); 478 const sig = crypto.sign(algo, data, pair.private); 479 assert.strictEqual(crypto.verify(algo, data, pair.private, sig), 480 true); 481 }); 482}); 483 484[1, {}, [], true, Infinity].forEach((input) => { 485 const data = Buffer.alloc(1); 486 const sig = Buffer.alloc(1); 487 const errObj = { 488 code: 'ERR_INVALID_ARG_TYPE', 489 name: 'TypeError', 490 message: 'The "data" argument must be an instance of Buffer, ' + 491 'TypedArray, or DataView.' + 492 common.invalidArgTypeHelper(input) 493 }; 494 495 assert.throws(() => crypto.sign(null, input, 'asdf'), errObj); 496 assert.throws(() => crypto.verify(null, input, 'asdf', sig), errObj); 497 498 let prop = '"key" argument'; 499 let value = input; 500 if (typeof input === 'object') { 501 prop = '"key.key" property'; 502 value = undefined; 503 } 504 errObj.message = `The ${prop} must be of type string or ` + 505 'an instance of Buffer, TypedArray, DataView, or KeyObject.' + 506 common.invalidArgTypeHelper(value); 507 508 assert.throws(() => crypto.sign(null, data, input), errObj); 509 assert.throws(() => crypto.verify(null, data, input, sig), errObj); 510 511 errObj.message = 'The "signature" argument must be an instance of ' + 512 'Buffer, TypedArray, or DataView.' + 513 common.invalidArgTypeHelper(input); 514 assert.throws(() => crypto.verify(null, data, 'test', input), errObj); 515}); 516 517{ 518 const data = Buffer.from('Hello world'); 519 const keys = [['ec-key.pem', 64], ['dsa_private_1025.pem', 40]]; 520 521 for (const [file, length] of keys) { 522 const privKey = fixtures.readKey(file); 523 [ 524 crypto.createSign('sha1').update(data).sign(privKey), 525 crypto.sign('sha1', data, privKey), 526 crypto.sign('sha1', data, { key: privKey, dsaEncoding: 'der' }), 527 ].forEach((sig) => { 528 // Signature length variability due to DER encoding 529 assert(sig.length >= length + 4 && sig.length <= length + 8); 530 531 assert.strictEqual( 532 crypto.createVerify('sha1').update(data).verify(privKey, sig), 533 true 534 ); 535 assert.strictEqual(crypto.verify('sha1', data, privKey, sig), true); 536 }); 537 538 // Test (EC)DSA signature conversion. 539 const opts = { key: privKey, dsaEncoding: 'ieee-p1363' }; 540 let sig = crypto.sign('sha1', data, opts); 541 // Unlike DER signatures, IEEE P1363 signatures have a predictable length. 542 assert.strictEqual(sig.length, length); 543 assert.strictEqual(crypto.verify('sha1', data, opts, sig), true); 544 assert.strictEqual(crypto.createVerify('sha1') 545 .update(data) 546 .verify(opts, sig), true); 547 548 // Test invalid signature lengths. 549 for (const i of [-2, -1, 1, 2, 4, 8]) { 550 sig = crypto.randomBytes(length + i); 551 assert.throws(() => { 552 crypto.verify('sha1', data, opts, sig); 553 }, { 554 message: 'Malformed signature' 555 }); 556 } 557 } 558 559 // Test verifying externally signed messages. 560 const extSig = Buffer.from('494c18ab5c8a62a72aea5041966902bcfa229821af2bf65' + 561 '0b5b4870d1fe6aebeaed9460c62210693b5b0a300033823' + 562 '33d9529c8abd8c5948940af944828be16c', 'hex'); 563 for (const ok of [true, false]) { 564 assert.strictEqual( 565 crypto.verify('sha256', data, { 566 key: fixtures.readKey('ec-key.pem'), 567 dsaEncoding: 'ieee-p1363' 568 }, extSig), 569 ok 570 ); 571 572 assert.strictEqual( 573 crypto.createVerify('sha256').update(data).verify({ 574 key: fixtures.readKey('ec-key.pem'), 575 dsaEncoding: 'ieee-p1363' 576 }, extSig), 577 ok 578 ); 579 580 extSig[Math.floor(Math.random() * extSig.length)] ^= 1; 581 } 582 583 // Non-(EC)DSA keys should ignore the option. 584 const sig = crypto.sign('sha1', data, { 585 key: keyPem, 586 dsaEncoding: 'ieee-p1363' 587 }); 588 assert.strictEqual(crypto.verify('sha1', data, certPem, sig), true); 589 assert.strictEqual( 590 crypto.verify('sha1', data, { 591 key: certPem, 592 dsaEncoding: 'ieee-p1363' 593 }, sig), 594 true 595 ); 596 assert.strictEqual( 597 crypto.verify('sha1', data, { 598 key: certPem, 599 dsaEncoding: 'der' 600 }, sig), 601 true 602 ); 603 604 for (const dsaEncoding of ['foo', null, {}, 5, true, NaN]) { 605 assert.throws(() => { 606 crypto.sign('sha1', data, { 607 key: certPem, 608 dsaEncoding 609 }); 610 }, { 611 code: 'ERR_INVALID_OPT_VALUE' 612 }); 613 } 614} 615 616 617// RSA-PSS Sign test by verifying with 'openssl dgst -verify' 618// Note: this particular test *must* be the last in this file as it will exit 619// early if no openssl binary is found 620{ 621 if (!common.opensslCli) 622 common.skip('node compiled without OpenSSL CLI.'); 623 624 const pubfile = fixtures.path('keys', 'rsa_public_2048.pem'); 625 const privkey = fixtures.readKey('rsa_private_2048.pem'); 626 627 const msg = 'Test123'; 628 const s5 = crypto.createSign('SHA256') 629 .update(msg) 630 .sign({ 631 key: privkey, 632 padding: crypto.constants.RSA_PKCS1_PSS_PADDING 633 }); 634 635 const tmpdir = require('../common/tmpdir'); 636 tmpdir.refresh(); 637 638 const sigfile = path.join(tmpdir.path, 's5.sig'); 639 fs.writeFileSync(sigfile, s5); 640 const msgfile = path.join(tmpdir.path, 's5.msg'); 641 fs.writeFileSync(msgfile, msg); 642 643 const cmd = 644 `"${common.opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${ 645 sigfile}" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "${ 646 msgfile}"`; 647 648 exec(cmd, common.mustCall((err, stdout, stderr) => { 649 assert(stdout.includes('Verified OK')); 650 })); 651} 652