Skip to content

Commit 1dd9158

Browse files
panvatargos
authored andcommitted
crypto: fix rsa-pss one-shot sign/verify error handling
fixes #39822 PR-URL: #39830 Reviewed-By: James M Snell <[email protected]>
1 parent 4ac703c commit 1dd9158

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

src/crypto/crypto_sig.cc

+3
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ bool SignTraits::DeriveBits(
686686
nullptr,
687687
params.key.get())) {
688688
crypto::CheckThrow(env, SignBase::Error::kSignInit);
689+
return false;
689690
}
690691
break;
691692
case SignConfiguration::kVerify:
@@ -696,6 +697,7 @@ bool SignTraits::DeriveBits(
696697
nullptr,
697698
params.key.get())) {
698699
crypto::CheckThrow(env, SignBase::Error::kSignInit);
700+
return false;
699701
}
700702
break;
701703
}
@@ -713,6 +715,7 @@ bool SignTraits::DeriveBits(
713715
padding,
714716
salt_length)) {
715717
crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
718+
return false;
716719
}
717720

718721
switch (params.mode) {

test/parallel/test-crypto-sign-verify.js

+111
Original file line numberDiff line numberDiff line change
@@ -631,3 +631,114 @@ assert.throws(
631631
assert(stdout.includes('Verified OK'));
632632
}));
633633
}
634+
635+
{
636+
// Test RSA-PSS.
637+
{
638+
// This key pair does not restrict the message digest algorithm or salt
639+
// length.
640+
const publicPem = fixtures.readKey('rsa_pss_public_2048.pem');
641+
const privatePem = fixtures.readKey('rsa_pss_private_2048.pem');
642+
643+
const publicKey = crypto.createPublicKey(publicPem);
644+
const privateKey = crypto.createPrivateKey(privatePem);
645+
646+
for (const key of [privatePem, privateKey]) {
647+
// Any algorithm should work.
648+
for (const algo of ['sha1', 'sha256']) {
649+
// Any salt length should work.
650+
for (const saltLength of [undefined, 8, 10, 12, 16, 18, 20]) {
651+
const signature = crypto.sign(algo, 'foo', { key, saltLength });
652+
653+
for (const pkey of [key, publicKey, publicPem]) {
654+
const okay = crypto.verify(
655+
algo,
656+
'foo',
657+
{ key: pkey, saltLength },
658+
signature
659+
);
660+
661+
assert.ok(okay);
662+
}
663+
}
664+
}
665+
}
666+
}
667+
668+
{
669+
// This key pair enforces sha256 as the message digest and the MGF1
670+
// message digest and a salt length of at least 16 bytes.
671+
const publicPem =
672+
fixtures.readKey('rsa_pss_public_2048_sha256_sha256_16.pem');
673+
const privatePem =
674+
fixtures.readKey('rsa_pss_private_2048_sha256_sha256_16.pem');
675+
676+
const publicKey = crypto.createPublicKey(publicPem);
677+
const privateKey = crypto.createPrivateKey(privatePem);
678+
679+
for (const key of [privatePem, privateKey]) {
680+
// Signing with anything other than sha256 should fail.
681+
assert.throws(() => {
682+
crypto.sign('sha1', 'foo', key);
683+
}, /digest not allowed/);
684+
685+
// Signing with salt lengths less than 16 bytes should fail.
686+
for (const saltLength of [8, 10, 12]) {
687+
assert.throws(() => {
688+
crypto.sign('sha256', 'foo', { key, saltLength });
689+
}, /pss saltlen too small/);
690+
}
691+
692+
// Signing with sha256 and appropriate salt lengths should work.
693+
for (const saltLength of [undefined, 16, 18, 20]) {
694+
const signature = crypto.sign('sha256', 'foo', { key, saltLength });
695+
696+
for (const pkey of [key, publicKey, publicPem]) {
697+
const okay = crypto.verify(
698+
'sha256',
699+
'foo',
700+
{ key: pkey, saltLength },
701+
signature
702+
);
703+
704+
assert.ok(okay);
705+
}
706+
}
707+
}
708+
}
709+
710+
{
711+
// This key enforces sha512 as the message digest and sha256 as the MGF1
712+
// message digest.
713+
const publicPem =
714+
fixtures.readKey('rsa_pss_public_2048_sha512_sha256_20.pem');
715+
const privatePem =
716+
fixtures.readKey('rsa_pss_private_2048_sha512_sha256_20.pem');
717+
718+
const publicKey = crypto.createPublicKey(publicPem);
719+
const privateKey = crypto.createPrivateKey(privatePem);
720+
721+
// Node.js usually uses the same hash function for the message and for MGF1.
722+
// However, when a different MGF1 message digest algorithm has been
723+
// specified as part of the key, it should automatically switch to that.
724+
// This behavior is required by sections 3.1 and 3.3 of RFC4055.
725+
for (const key of [privatePem, privateKey]) {
726+
// sha256 matches the MGF1 hash function and should be used internally,
727+
// but it should not be permitted as the main message digest algorithm.
728+
for (const algo of ['sha1', 'sha256']) {
729+
assert.throws(() => {
730+
crypto.sign(algo, 'foo', key);
731+
}, /digest not allowed/);
732+
}
733+
734+
// sha512 should produce a valid signature.
735+
const signature = crypto.sign('sha512', 'foo', key);
736+
737+
for (const pkey of [key, publicKey, publicPem]) {
738+
const okay = crypto.verify('sha512', 'foo', pkey, signature);
739+
740+
assert.ok(okay);
741+
}
742+
}
743+
}
744+
}

0 commit comments

Comments
 (0)