PSS verify with saltLength: 0 rejects valid signatures

Open Beginner friendly
#1,170 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript, node.js

Research direction

Start in lib/pss.js at verify, step 11, and check how the salt substring behaves when sLen is zero. Run the reproduction in the issue, then add or run a regression test for an empty salt. Done means forge verifies valid zero-salt RSASSA-PSS signatures while existing PSS verification still passes.

Written by the indexing model from the issue text.

Description

publicKey.verify(..., forge.pss.create({ ..., saltLength: 0 })) returns false for valid RSASSA-PSS signatures with an empty salt, including signatures forge itself produced.

const forge = require('node-forge');
const crypto = require('crypto');

const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
const pub = forge.pki.publicKeyFromPem(publicKey.export({ type: 'spki', format: 'pem' }));
const prv = forge.pki.privateKeyFromPem(privateKey.export({ type: 'pkcs8', format: 'pem' }));
const pss = () => forge.pss.create({
  md: forge.md.sha256.create(),
  mgf: forge.mgf.mgf1.create(forge.md.sha256.create()),
  saltLength: 0
});
const md = () => { const m = forge.md.sha256.create(); m.update('msg'); return m; };

// signed by OpenSSL, verified by forge
const sigOpenSSL = crypto.sign('sha256', Buffer.from('msg'),
  { key: privateKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: 0 });
console.log(pub.verify(md().digest().bytes(), sigOpenSSL.toString('binary'), pss()));

// signed by forge, verified by OpenSSL, then by forge
const sigForge = prv.sign(md(), pss());
console.log(crypto.verify('sha256', Buffer.from('msg'),
  { key: publicKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: 0 },
  Buffer.from(sigForge, 'binary')));
console.log(pub.verify(md().digest().bytes(), sigForge, pss()));

node repro.js

Actual:

false
true
false

Expected:

true
true
true

Tested with node-forge 1.4.3 (same lib/pss.js as main 01119d6), Node.js v26.13.0.

In lib/pss.js verify, step 11 is var salt = db.substr(-sLen);. For sLen === 0 that is db.substr(-0), which returns all of db instead of '' (RFC 8017 9.1.2 step 11). db.substr(db.length - sLen) works for every sLen.

Credit: https://ucsbplse.github.io/

Dominant language
JavaScript
Stars
5.4k
Forks
874
Avg merge
3m
Merged PRs (30d)
6

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from digitalbazaar/forge

All issues in digitalbazaar/forge

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.