Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ permissions:

env:
CARGO_TERM_COLOR: always
# RSA-PSS vectors always run; CI additionally requires reciprocal live checks.
OPENSSL_BIN: openssl
RUSTFLAGS: -Dwarnings
XMLSEC1_PREFIX: ${{ github.workspace }}/.tools/xmlsec1-1.3.13
XMLSEC1_BIN: ${{ github.workspace }}/.tools/xmlsec1-1.3.13/bin/xmlsec1
Expand Down
3 changes: 2 additions & 1 deletion LICENSE-THIRD-PARTY
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
The xml-sec crate includes code derived from the following projects. Their
copyright and permission notices are preserved below.

sad-rsa 0.10.2 (src/provider/rsa_pkcs1v15.rs)
sad-rsa 0.10.2 (src/provider/rsa_pkcs1v15.rs, src/provider/rsa_pss.rs)
------------------------------------------

Source: https://github.com/sadco-io/sad-rsa
Local fixed-width recovery adaptation: docs/rsa-recovery-patch.md
Local parameterized PSS adaptation: docs/rsa-pss-patch.md

Copyright (c) 2025-2026 Sadco Security Team
Copyright (c) 2015-2025 RustCrypto Developers
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ xml-sec = { version = "0.1", default-features = false, features = ["xmldsig", "c
| Area | Available today |
|------|-----------------|
| Canonicalization | Canonical XML 1.0/1.1, Exclusive C14N, comments, and document subsets |
| XML signatures | XMLDSig signing and verification, RSA/DSA/ECDSA/HMAC, XPath transforms, `Manifest`, `KeyInfo`, and caller-provided references |
| XML signatures | XMLDSig signing and verification, RSA-PSS/PKCS#1, DSA/ECDSA/HMAC, XPath transforms, `Manifest`, `KeyInfo`, and caller-provided references |
| XML encryption | AES-CBC/GCM, RSA-OAEP, AES Key Wrap, multiple recipients, and Element/Content replacement |
| Optional compatibility algorithms | `legacy-algorithms`: MD5/RIPEMD-160 signatures and digests, AES-192, TripleDES-CBC/CMS wrap, and RSA-1.5 transport; library policy denies these by default |
| X.509 | Certificate key extraction, chain validation, CRLs, required key authorization by default, and typed trust evidence |
Expand Down
16 changes: 16 additions & 0 deletions compatibility/libxmlsec1-1.3.13-rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@
}
},
"rules": [
{
"id": "native-rsa-pss-uri",
"kinds": ["algorithm-uri"],
"name_regex": "^xmlSecHrefRsaPssSha(?:224|256|384|512|3_224|3_256|3_384|3_512)$",
"outcome": "behavior-compatible",
"rationale": "RSA-PSS fixed URI signing and verification execute through CryptoProvider with exact digest-sized salt and matching MGF1; the complete upstream PSS family is exercised by public pipeline tests.",
"evidence": "native-algorithm-tests"
},
{
"id": "legacy-rsa-pss-uri",
"kinds": ["algorithm-uri"],
"name_regex": "^xmlSecHrefRsaPssSha1$",
"outcome": "compatibility-profile-only",
"rationale": "RSA-PSS SHA-1 is implemented but requires explicit legacy signature policy permission.",
"evidence": "legacy-policy-tests"
},
{
"id": "native-xpath-uri",
"kinds": ["algorithm-uri"],
Expand Down
28 changes: 19 additions & 9 deletions compatibility/libxmlsec1-1.3.13.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@
"rationale": "The legacy-algorithms feature implements this mechanism through RustCrypto. Library defaults deny it; an explicit immutable algorithm allowlist is required. Other providers may report the capability unavailable without fallback.",
"evidence": "legacy-policy-tests"
},
"legacy-rsa-pss-uri": {
"outcome": "compatibility-profile-only",
"rationale": "RSA-PSS SHA-1 is implemented but requires explicit legacy signature policy permission.",
"evidence": "legacy-policy-tests"
},
"native-cli-behavior-options": {
"outcome": "behavior-compatible",
"rationale": "The native CLI implements this provider-independent option with donor-compatible parsing and command behavior.",
Expand All @@ -94,6 +99,11 @@
"rationale": "The native CLI implements this option only for the formats, algorithms, or fixed RustCrypto provider behavior advertised by its capability registry.",
"evidence": "native-cli-tests"
},
"native-rsa-pss-uri": {
"outcome": "behavior-compatible",
"rationale": "RSA-PSS fixed URI signing and verification execute through CryptoProvider with exact digest-sized salt and matching MGF1; the complete upstream PSS family is exercised by public pipeline tests.",
"evidence": "native-algorithm-tests"
},
"native-sha1-verification-uri": {
"outcome": "behavior-compatible",
"rationale": "The native Rust API implements SHA-1 reference digests; secure signing defaults reject SHA-1 while an explicit compatibility policy can opt in.",
Expand Down Expand Up @@ -5006,71 +5016,71 @@
"source": "src/strings.c",
"line": 766,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha1-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "legacy-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha224",
"source": "src/strings.c",
"line": 769,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha224-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha256",
"source": "src/strings.c",
"line": 772,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha384",
"source": "src/strings.c",
"line": 775,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha384-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha3_224",
"source": "src/strings.c",
"line": 781,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha3-224-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha3_256",
"source": "src/strings.c",
"line": 784,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha3-256-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha3_384",
"source": "src/strings.c",
"line": 787,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha3-384-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha3_512",
"source": "src/strings.c",
"line": 790,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha3-512-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
"name": "xmlSecHrefRsaPssSha512",
"source": "src/strings.c",
"line": 778,
"detail": "http://www.w3.org/2007/05/xmldsig-more#sha512-rsa-MGF1",
"classification": "remaining-uri-surface"
"classification": "native-rsa-pss-uri"
},
{
"kind": "algorithm-uri",
Expand Down
16 changes: 15 additions & 1 deletion docs/crypto-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ SHA-1. Operational errors expose typed redacted classes, never raw module
diagnostics or credentials. An explicit invalid-ciphertext result maps to
`AuthenticationFailed`; a module's generic failure remains an operational error,
not proof of a bad authentication tag. Both paths release no plaintext.
RSA-PSS/ECDSA signing, certificate/CRL verification,
RSA-PSS signing/verification supports SHA-1/256/384/512 with independent
MGF1 SHA-1/224/256/384/512 and explicit salt length, when the token accepts
the selected parameters. SHA-224/SHA-3 PSS message digests are not advertised
because the adapter does not expose matching combined token mechanisms.
ECDSA signing, certificate/CRL verification,
private software key import, raw symmetric encryption, and KDF execution are
not advertised by this adapter.

Expand Down Expand Up @@ -86,6 +90,15 @@ Without it, that selection is an explicit unavailable-provider error.

## Mechanisms

RustCrypto implements all nine fixed RSA-PSS SHA-1/SHA-2/SHA-3 signature URIs
and parameterized `rsa-pss` via `SignatureAlgorithm::RsaPss(RsaPssParameters)`.
Message hash and MGF1 hash may differ; salt length is exact, including zero,
and must fit the RSA modulus. XML defaults are SHA-256, digest-sized salt and
trailer 1 ([RFC 9231 section 2.3.9](https://www.rfc-editor.org/rfc/rfc9231.html#section-2.3.9)).
SHA-1 in either hash requires explicit legacy policy permission. Fixed URI
variants reject parameters rather than silently overriding their contract.
The narrow [local padding adaptation](rsa-pss-patch.md) retains sad-rsa arithmetic.

`legacy-algorithms` adds RustCrypto MD5/RIPEMD-160 signatures and digests, AES-192
content encryption and key wrap, TripleDES content encryption and CMS wrap, and RSA-1.5
transport. All operations still go through `CryptoProvider` and require explicit library policy
Expand All @@ -95,6 +108,7 @@ every primitive present in the underlying AWS-LC library or an approved-service

The AWS provider supports SHA-1/SHA-2 and SHA3-256/384/512 digests; RSA PKCS#1 v1.5
signing with SHA-256/384/512 and verification with those digests or SHA-1; native
RSA-PSS signing/verification with SHA-256/384/512, matching MGF1 and digest-sized salt;
ECDSA curve/digest combinations exposed by AWS-LC; AES-128/256 CBC and GCM;
AES-128/256 key wrap; and RSA-OAEP with SHA-1/256/384/512 when MGF uses the same
digest. RSA-OAEP labels are preserved. Certificate and CRL verification uses DER
Expand Down
27 changes: 27 additions & 0 deletions docs/rsa-pss-patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Parameterized RSA-PSS Adaptation

`src/provider/rsa_pss.rs` adapts sad-rsa 0.10.2's PSS encoding and MGF1 from
`src/pss.rs` and `src/algorithms/{pss,mgf}.rs`. RSA arithmetic, blinding and
fault checking remain in that dependency's `hazmat` primitives. No additional
RSA engine or separately published crate is introduced.

The dependency's PSS API uses one digest for both the message and MGF1.
[RFC 9231 section 2.3.9](https://www.rfc-editor.org/rfc/rfc9231.html#section-2.3.9)
permits independent hashes and an explicit salt length. The local adaptation
implements those parameters using the existing SHA implementations. MGF1 hashes
borrowed chunks into stack storage rather than allocating a digest object or
buffer for every block.

[RFC 8017 sections 9.1.1 and 9.1.2](https://www.rfc-editor.org/rfc/rfc8017.html#section-9.1)
govern encoding and checking. Verification requires the exact selected salt
length; it never guesses one from the signature. Modulus capacity is checked
before randomness or padding allocation, and nonzero leading representative
octets are rejected rather than silently truncated for non-byte-aligned keys.
RSAVP1 rejects signature integers greater than or equal to the modulus before
exponentiation, as required by [RFC 8017 section 5.2.2](https://www.rfc-editor.org/rfc/rfc8017.html#section-5.2.2);
raw modular exponentiation would otherwise accept aliases of a valid signature.
The signing padding buffer, including its random salt, is zeroized on drop.

When updating sad-rsa, compare the named donor functions and hazmat interfaces
with this module. Independent dependency and XML-security oracle tests protect
the standard encodings and the parameterized extension.
43 changes: 37 additions & 6 deletions docs/xmldsig.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use wildcard match arms and obtain `SignedInfo` through the parser.
The `xmldsig` feature provides signing and verification pipelines for same-document XML
signatures and detached references whose payloads the caller supplies. It supports inclusive and
exclusive canonicalization, enveloped signatures,
Base64, XPath 1.0, and XPath Filter 2.0 transforms, RSA PKCS#1 v1.5, DSA,
Base64, XPath 1.0, and XPath Filter 2.0 transforms, RSA PKCS#1 v1.5, RSA-PSS, DSA,
ECDSA with P-256/P-384/P-521 keys, HMAC truncation, embedded X.509 certificates,
and configured key resolution.

Expand Down Expand Up @@ -230,8 +230,8 @@ implementation only when the provider has no primitive work to observe.
`VerifyContext::provider` covers every verification-time cryptographic operation, including
reference digests, document signatures, `X509Digest` selector evaluation, X.509 candidate-path
edges, complete certificate paths, and CRL authentication performed by `DefaultKeyResolver`.
Certificate authentication uses a separate typed algorithm contract so RSA-PSS parameters and
Ed25519 are not collapsed into the narrower XMLDSig `SignatureMethod` enum. Unsupported certificate
Certificate authentication uses a separate typed algorithm contract because certificate encodings
and algorithm permissions are independent of XMLDSig `SignatureMethod`. Unsupported certificate
OIDs remain typed path errors rather than ordinary signature mismatches. Every certificate OID
represented by that contract reaches the selected provider; the built-in provider may reject a
capability such as ECDSA-SHA512 while a custom provider can implement it. For an
Expand Down Expand Up @@ -458,7 +458,8 @@ These are
historical interoperability mechanisms, not recommended algorithms for new documents.

Implemented signature methods include DSA-SHA1/SHA256; HMAC-SHA1/SHA224/SHA256/SHA384/SHA512;
RSA PKCS#1 v1.5 with SHA-1/SHA224/SHA256/SHA384/SHA512; and ECDSA
RSA PKCS#1 v1.5 with SHA-1/SHA224/SHA256/SHA384/SHA512; RSA-PSS with SHA-1,
SHA224/SHA256/SHA384/SHA512 and SHA3-224/256/384/512; and ECDSA
SHA-1/SHA224/SHA256/SHA384/SHA512 and SHA3-224/256/384/512. ECDSA selects P-256, P-384, or P-521
from the key independently of the hash identifier. Digest methods include
SHA-1/SHA224/SHA256/SHA384/SHA512 and SHA3-224/256/384/512. EdDSA supports pure Ed25519,
Expand Down Expand Up @@ -537,5 +538,35 @@ and these post-quantum methods
require absent parameters; RSA PKCS#1 accepts NULL or absent; RSA-PSS requires valid typed
parameters. An `id-RSASSA-PSS` issuer key with absent parameters imposes no parameter restrictions,
as required by RFC 4055 section 3.3; present key parameters constrain the signature hash, MGF,
minimum salt length, and trailer field. XMLDSig `SignatureMethod` RSA-PSS and implicit external
resource loading are not currently supported.
minimum salt length, and trailer field. Implicit external resource loading is not supported.

## RSA-PSS Parameters

The nine fixed RSA-PSS methods use MGF1 with the signature digest and a salt of that digest's
width. `SignatureAlgorithm::RsaPss(RsaPssParameters)` represents the parameterized method from
[RFC 9231 section 2.3.9](https://www.rfc-editor.org/rfc/rfc9231.html#section-2.3.9), with independent
`digest`, `mgf_digest`, and exact `salt_len`. The builder emits `RSAPSSParams`; template signing
and verification parse the same typed parameters.

An omitted digest defaults to SHA-256. An omitted MGF uses the signature digest; an explicit
MGF1 element without its digest uses SHA-256. An omitted salt length uses the signature digest's
width. Only trailer field 1 is supported. Parsing rejects duplicate, reordered, unknown, or
malformed parameters, and salt lengths outside the nonnegative `xs:int` range. Key preflight
rejects salts that cannot fit the RSA encoded message before signing work.

SHA-1 in either the message digest or MGF requires explicit legacy permission for the exact
signature method, or an explicit `KeyTrustPolicy::parameterized_rsa_pss` family permission.
`RsaPssPermission::AllSupported` in the signing or verification policy grants the parameterized
family without enumerating salt lengths; verification retains the independent legacy trust gate.
All family permissions default to `ExactOnly`. The compatibility CLI sets both permissions before
reading input, never deriving policy from a document's parameters.
An `id-RSASSA-PSS` public key also enforces its hash, MGF, minimum salt length,
and trailer restrictions; an unrestricted RSA key does not add those restrictions.

RustCrypto supports independent hashes and exact salts. Other providers expose only their native
capabilities, with no fallback; see [provider capabilities](crypto-providers.md). The narrow
[local RSA-PSS adaptation](rsa-pss-patch.md) retains the existing RSA arithmetic, blinding, and
fault checks. Tests cover every pinned xmlsec1 RSA-PSS fixture, reciprocal signing, restricted
keys, malformed encodings, mixed hashes, and salt/modulus boundaries.
Saved OpenSSL vectors run without external programs; set `OPENSSL_BIN` to additionally require
live reciprocal OpenSSL checks. A configured but unavailable oracle fails the test. CI enables it.
9 changes: 9 additions & 0 deletions scripts/import-donor-fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ if (( ${#fixture_paths[@]} == 0 )); then
"xmlenc/aleksey-xmlenc-01/enc-aes256-kt-rsa_oaep_sha1-params.tmpl"
"xmlenc/aleksey-xmlenc-01/enc-aes256-kt-rsa_oaep_enc11_sha512_mgf1_sha512.tmpl"
)
# Complete fixed-URI RSA-PSS family from the upstream DSig runner.
for hash in sha1 sha224 sha256 sha384 sha512 sha3_224 sha3_256 sha3_384 sha3_512; do
for extension in xml tmpl; do
fixture_paths+=("xmldsig/aleksey-xmldsig-01/enveloping-rsa-pss-$hash.$extension")
if [[ "$hash" != sha1 ]]; then
fixture_paths+=("xmldsig/aleksey-xmldsig-01/enveloped-$hash-rsa-pss-$hash.$extension")
fi
done
done
fi

for relative_path in "${fixture_paths[@]}"; do
Expand Down
Loading
Loading