Skip to content

feat: support RSASSA-PSS signature algorithms (closes #624)#625

Merged
tngan merged 1 commit into
masterfrom
feat/624-rsassa-pss
May 14, 2026
Merged

feat: support RSASSA-PSS signature algorithms (closes #624)#625
tngan merged 1 commit into
masterfrom
feat/624-rsassa-pss

Conversation

@tngan

@tngan tngan commented May 8, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #624. Adds RSASSA-PSS signature support — the three
xmldsig-more (W3C Note, 2007-05) URIs sha256-rsa-MGF1,
sha384-rsa-MGF1, sha512-rsa-MGF1 — alongside the existing PKCS#1
v1.5 set. Addresses audit follow-up F-7 in
.skills/audits/2026-04-security-audit.md.

PSS is opt-in via requestSignatureAlgorithm: algorithms.signature.RSA_SHA256_MGF1.
The default stays RSA-SHA256 (PKCS#1 v1.5) per audit fix F-3 — existing
callers see zero behaviour change.

Spec reference

  • xmldsig-core §6.4.2 — RSASSA-PSS signature method.
  • xmldsig-more (W3C Note, 2007-05) — defines the
    sha256-rsa-MGF1 / sha384-rsa-MGF1 / sha512-rsa-MGF1 URIs.
  • saml-sec-consider §6.5 — recommends PSS over PKCS#1 v1.5 and calls
    for algorithm agility so deployments can move off legacy padding
    without forking the library.
  • saml-bindings §3.4.4.1 — octet-string construction for the
    HTTP-Redirect detached signature (PSS path goes through node-rsa's
    pss-sha{256,384,512} schemes).
  • saml-bindings §3.5 — HTTP-POST envelope for the embedded XML
    signature (PSS path registers plugin classes on the xml-crypto
    SignedXml instance).

What changed

src/urn.ts

  • algorithms.signature adds RSA_SHA256_MGF1, RSA_SHA384_MGF1,
    RSA_SHA512_MGF1.
  • algorithms.digest pairs each PSS URI with its digest URI.
    SHA-256 / SHA-512 reuse the existing xmlenc digest URIs; SHA-384
    uses xmldsig-more#sha384 since no xmlenc#sha384 was ever
    published.

src/libsaml.ts

  • nrsaAliasMapping adds pss-sha{256,384,512} for the
    redirect-binding detached-signature path. node-rsa v1.1.x ships PSS
    built-in.
  • New RsaSha{256,384,512}Mgf1 plugin classes implement
    xml-crypto's SignatureAlgorithm interface (verify against
    node_modules/xml-crypto/lib/types.d.ts). They use Node's
    crypto.sign / crypto.verify with RSA_PKCS1_PSS_PADDING and
    RSA_PSS_SALTLEN_DIGEST.
  • New Sha384Hash plugin — xml-crypto v6.1.2 ships
    SHA-1 / SHA-256 / SHA-512 only.
  • registerPssAlgorithms(sig) is called inside constructSAMLSignature
    and verifySignature to attach the new classes onto each
    SignedXml instance via sig.SignatureAlgorithms /
    sig.HashAlgorithms. Confirmed against the installed v6.1.2 source —
    the API has shifted between major versions, so the registration
    pattern was verified rather than assumed.

docs/signed-saml-request.md, docs/signed-saml-response.md

  • List the three new PSS URIs alongside the PKCS#1 v1.5 ones.
  • Add a note recommending PSS for new deployments per
    saml-sec-consider §6.5 while clarifying PSS is opt-in.

.skills/audits/2026-04-security-audit.md

  • Mark F-7 as partially fixed (PSS support landed; deprecating
    PKCS#1 v1.5 is still pending in v3).
  • Update the summary table and the Future work checklist.

Constraints honoured

  • Default signing algorithm unchanged — still RSA-SHA256 (PKCS#1 v1.5)
    per audit F-3. Pinned by an explicit regression test.
  • Existing PKCS#1 v1.5 callers see no behaviour change. The full
    pre-existing test suite (316 tests) continues to pass without
    modification.
  • xml-crypto registration mechanism was verified by reading
    node_modules/xml-crypto/lib/signed-xml.js (v6.1.2) directly rather
    than guessing from version-skewed docs.

Caveats

  • xml-crypto v6.1.2's built-in HashAlgorithms map contains only
    SHA-1 / SHA-256 / SHA-512. To support sha384-rsa-MGF1 we register
    a SHA-384 hash plugin alongside the signature plugins. This is
    noted in the JSDoc on Sha384Hash.
  • PSS validation requires the verifying side to also know it's PSS —
    i.e. verifySignature(..., { signatureAlgorithm: RSA_SHA256_MGF1 })
    / verifyMessageSignature(..., RSA_SHA256_MGF1). This is the same
    contract the existing PKCS#1 v1.5 path uses.

Test plan

  • yarn test — all 325 tests pass (316 pre-existing + 9 new PSS
    tests).
  • npx tsc --noEmit — clean.
  • yarn coverage — every threshold (≥90% statements / branches /
    functions / lines) holds. Total: 97.34% stmts, 90.83% branches,
    99.2% funcs, 97.34% lines.
  • Round-trip on the redirect binding (octet-string detached
    signature) for all three PSS hashes.
  • Round-trip on the post binding (XML embedded signature) for all
    three PSS hashes.
  • Algorithm-registry pairing assertions (signature URI →
    digest URI).
  • Audit F-3 regression — unknown algorithms still throw
    ERR_UNSUPPORTED_SIGNATURE_ALGORITHM.
  • Default-stability pin — signing with no explicit algorithm
    produces a PKCS#1 v1.5 RSA-SHA256 signature, verifiable as such,
    not verifiable as PSS.

🤖 Generated with Claude Code

@tngan tngan mentioned this pull request May 8, 2026
@tngan tngan force-pushed the feat/624-rsassa-pss branch from c84fb94 to c884fbc Compare May 9, 2026 02:19
## Spec reference

- xmldsig-core §6.4.2 — RSASSA-PSS signature method.
- xmldsig-more (W3C Note, 2007-05) — defines `sha256-rsa-MGF1`,
  `sha384-rsa-MGF1`, `sha512-rsa-MGF1` URIs.
- saml-sec-consider §6.5 — recommends PSS over PKCS#1 v1.5 and calls for
  algorithm agility.
- Audit follow-up F-7 (.skills/audits/2026-04-security-audit.md).

## Why

samlify previously advertised only the PKCS#1 v1.5 RSA-SHA{1,256,512}
URIs, leaving deployments that want RSASSA-PSS — the modern
recommendation per `xmldsig-core §6.4.2` and `saml-sec-consider §6.5` —
without an opt-in. This change closes that algorithm-agility gap while
preserving the pre-existing default (RSA-SHA256, PKCS#1 v1.5) so no
existing caller observes a behaviour change.

## What

- `src/urn.ts`: register the three `xmldsig-more` (2007-05) PSS URIs in
  `algorithms.signature` and pair them in `algorithms.digest`. SHA-384's
  digest URI (`xmldsig-more#sha384`) is added since no `xmlenc#sha384`
  ever existed.
- `src/libsaml.ts`:
  - extend `nrsaAliasMapping` with `pss-sha{256,384,512}` for the
    redirect-binding detached signature path (`saml-bindings §3.4.4.1`)
    via `node-rsa`'s built-in PSS schemes;
  - add `RsaSha{256,384,512}Mgf1` plugin classes implementing
    xml-crypto's `SignatureAlgorithm` interface, using Node's
    `crypto.sign`/`crypto.verify` with `RSA_PKCS1_PSS_PADDING` and
    `RSA_PSS_SALTLEN_DIGEST`;
  - add a `Sha384Hash` plugin (xml-crypto v6.x ships SHA-1/256/512
    only);
  - register both on each `SignedXml` instance inside
    `constructSAMLSignature` and `verifySignature` via
    `registerPssAlgorithms`.
- Default signing algorithm is unchanged (RSA-SHA256, PKCS#1 v1.5).
  PSS is opt-in via
  `requestSignatureAlgorithm: algorithms.signature.RSA_SHA256_MGF1`.

## Tests

- algorithm-registry coverage: PSS URIs resolve and pair with the
  correct digest URIs.
- redirect-binding round-trip (sign + verify) for each of SHA-256,
  SHA-384, SHA-512 PSS variants — pins the node-rsa `pss-*` scheme
  routing.
- post-binding XML-signature round-trip for each variant — pins the
  xml-crypto SignatureAlgorithms / HashAlgorithms registration.
- F-3 regression: unknown signature URIs still throw
  `ERR_UNSUPPORTED_SIGNATURE_ALGORITHM` after the registry expansion.
- default-stability pin: signing without an explicit algorithm still
  produces a PKCS#1 v1.5 RSA-SHA256 signature (verifiable as such, and
  *not* verifiable as PSS) — guards against accidentally moving the
  default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tngan tngan force-pushed the feat/624-rsassa-pss branch from c884fbc to 0c6ad57 Compare May 10, 2026 03:28
@tngan tngan marked this pull request as ready for review May 14, 2026 12:34
@tngan tngan merged commit a3910b1 into master May 14, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for sha256-rsa-MGF1

1 participant