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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
tests/fixtures/xmlenc/aleksey-xmlenc-01/*.tmpl -text whitespace=-trailing-space,-space-before-tab
tests/fixtures/xmlenc/01-phaos-xmlenc-3/** -text whitespace=-trailing-space,-space-before-tab
tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/** -text whitespace=-blank-at-eof
45 changes: 32 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -Dwarnings
XMLSEC1_VERSION: 1.3.12
XMLSEC1_SHA256: 24045199af12d93fe5fdbbbf7e386e823e4842071e9432e2b90ac108b889a923
XMLSEC1_PREFIX: ${{ github.workspace }}/.tools/xmlsec1-1.3.13-5fdd47dc3575
XMLSEC1_BIN: ${{ github.workspace }}/.tools/xmlsec1-1.3.13-5fdd47dc3575/bin/xmlsec1
LD_LIBRARY_PATH: ${{ github.workspace }}/.tools/xmlsec1-1.3.13-5fdd47dc3575/lib

jobs:
build-matrix:
Expand All @@ -21,6 +25,8 @@ jobs:
rust: [stable, "1.92.0"]
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
Expand All @@ -43,6 +49,8 @@ jobs:
rust: [stable, "1.92.0"]
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
Expand All @@ -51,17 +59,9 @@ jobs:
run: sudo apt-get update
- name: Build pinned xmlsec1 for XMLDSig interop tests
run: |
sudo apt-get install --yes build-essential libltdl-dev libssl-dev libxml2-dev pkg-config
curl --fail --location --retry 3 --output xmlsec1.tar.gz "https://github.com/lsh123/xmlsec/releases/download/${XMLSEC1_VERSION}/xmlsec1-${XMLSEC1_VERSION}.tar.gz"
echo "${XMLSEC1_SHA256} xmlsec1.tar.gz" | sha256sum --check --strict
tar --extract --file xmlsec1.tar.gz
pushd "xmlsec1-${XMLSEC1_VERSION}"
./configure --disable-static --with-openssl
make --jobs "$(nproc)"
sudo make install
popd
sudo ldconfig
xmlsec1 --version
sudo apt-get install --yes autoconf automake build-essential libltdl-dev libssl-dev libtool libxml2-dev pkg-config
scripts/install-xmlsec1.sh
"$XMLSEC1_BIN" --version
- uses: Swatinem/rust-cache@v2
- run: cargo nextest run --all-features
- run: cargo test --doc --all-features
Expand All @@ -77,6 +77,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
Expand All @@ -87,7 +89,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
- run: cargo fmt --manifest-path fuzz/Cargo.toml -- --check

fuzz-smoke:
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@nightly
# cargo-fuzz 0.13.1's published lockfile pins rustix 0.36.5, which no
# longer compiles on current nightly. Keep the tool version pinned while
# allowing compatible patch-level transitive dependencies.
- run: cargo +nightly install cargo-fuzz --version 0.13.1
- run: cargo +nightly fuzz run xmldsig_verify -- -runs=256 -max_len=65536
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/target
/.tools
/fuzz/artifacts
/fuzz/corpus/*/*
!/fuzz/corpus/xmldsig_verify/signature.xml
/fuzz/target
Cargo.lock
*.swp
*.swo
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- *(xmldsig)* rename the curve-specific ECDSA `SignatureAlgorithm` variants to
`EcdsaSha256` and `EcdsaSha384`; add legacy `DsaSha1` and `HmacSha1` variants
- *(xmldsig)* add `SignedInfo::hmac_output_length_bits` and make
`SignatureAlgorithm` and `SignedInfo` non-exhaustive
- *(xmldsig)* enforce operation key-strength policy consistently for direct and
resolver-produced built-in verification keys
- *(crypto)* use the hardened pure-Rust `sad-rsa` backend for RSA private operations

## [0.1.10](https://github.com/structured-world/xml-sec/compare/v0.1.9...v0.1.10) - 2026-08-04

### Added
Expand Down
18 changes: 14 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,52 @@ roxmltree = { version = "0.21", features = ["positions"] }
quick-xml = "0.41"

# Crypto
rsa = { version = "0.10.0-rc.18", optional = true }
rsa = { package = "sad-rsa", version = "0.2.3", features = ["sha1", "sha2"], optional = true }
sha1 = { version = "0.11", features = ["oid"], optional = true }
sha2 = { version = "0.11", features = ["oid"], optional = true }
p256 = { version = "0.14", features = ["ecdsa"], optional = true }
p384 = { version = "0.14", features = ["ecdsa"], optional = true }
p521 = { version = "0.14", features = ["ecdsa"], optional = true }
dsa = { version = "0.7", optional = true }
ed25519-dalek = { version = "3", features = ["pkcs8"], optional = true }
hmac = { version = "0.13", optional = true }
signature = { version = "3", optional = true }
subtle = { version = "2", optional = true }
getrandom = { version = "0.4", features = ["sys_rng"], optional = true }
sxd-document-no-unsafe = { version = "0.4.1", default-features = false, features = ["no-unsafe"], optional = true }
sxd-xpath-no-unsafe = { version = "0.5.1", default-features = false, features = ["no-unsafe"], optional = true }
aes = { version = "0.9.1", optional = true }
aes = { version = "0.9.2", optional = true }
aes-gcm = { version = "0.11.0", optional = true }
aes-kw = { version = "0.3.1", optional = true }
cbc = { version = "0.2.1", optional = true }

# X.509 certificates
x509-parser = { version = "0.18", features = ["verify"], optional = true }
x509-cert = { version = "0.3", default-features = false, optional = true }
x520-stringprep = { version = "1", features = ["alloc"], optional = true }
der = { version = "0.8", optional = true }
crypto-bigint = { version = "0.7", optional = true }

# Base64 encoding/decoding
base64 = "0.22"
base64 = "0.23"
Comment thread
polaz marked this conversation as resolved.

# Error handling
thiserror = "2"

[dev-dependencies]
rcgen = "0.14.6"
rand_chacha = "0.10"
time = "0.3.53"
time = "0.3.55"

[features]
default = ["xmldsig", "c14n"]
xmldsig = [ # XML Digital Signatures (sign + verify)
"dep:der",
"dep:crypto-bigint",
"dep:dsa",
"dep:ed25519-dalek",
"dep:getrandom",
"dep:hmac",
"dep:p256",
"dep:p384",
"dep:p521",
Expand All @@ -71,6 +79,8 @@ xmldsig = [ # XML Digital Signatures (sign + verify)
"dep:sxd-document-no-unsafe",
"dep:sxd-xpath-no-unsafe",
"dep:x509-parser",
"dep:x509-cert",
"dep:x520-stringprep",
]
xmlenc = [ # XML Encryption (encrypt + decrypt)
"dep:aes",
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,26 @@ Currently implemented (core paths):
- XMLDSig parsing, same-document URI dereference, enveloped/C14N/Base64/XPath 1.0/XPath Filter 2.0 transform chains, and digest verification
- XMLDSig full verify pipeline (`SignedInfo` canonicalization + `SignatureValue` verification)
- XMLDSig template signing pipeline (`DigestValue` fill + `SignedInfo` canonicalization + `SignatureValue` fill), including enveloped SAML Response templates
- Typed signing and verification policy covers XML parsing, explicit transforms, implicit reference canonicalization, `SignedInfo` canonicalization, and outbound RSA key strength under shared work limits
- XMLDSig signing KeyInfo writer for embedded X.509 certificates
- Built-in verification-key resolution from embedded X.509/DER/`KeyValue` sources and configured `KeyName`, X.509 subject, issuer/serial, SKI, or digest selectors
- RSA PKCS#1 v1.5 verification helpers for SHA-1 / SHA-256 / SHA-384 / SHA-512
- ECDSA verification helpers for P-256/SHA-256 and P-384/SHA-384
- RSA PKCS#1 v1.5 and ECDSA P-256/P-384 signing from PKCS#8 private keys
- Opt-in X.509 certificate-chain validation with explicit trust anchors, validity checks, CA constraints, and CRLs
- ECDSA SHA-256/SHA-384 verification for P-256, P-384, and P-521 keys
- Legacy DSA-SHA1 and HMAC-SHA1 verification, including truncated HMAC output
- RSA PKCS#1 v1.5 and ECDSA SHA-256/SHA-384 signing with P-256/P-384 PKCS#8 keys
- Opt-in X.509 certificate-chain validation with explicit trust anchors, validity and path-length checks, NameConstraints, authenticated CRLs, typed path-wide ExtendedKeyUsage policy, and RSA-PSS/Ed25519 certificate-signature support. Duplicate certificate, CRL, and CRL-entry extension OIDs, malformed SAN identities, unsupported delta CRLs, `removeFromCRL` entries in complete CRLs, and invalid name constraints are rejected; implemented critical extensions are processed and every other critical extension fails closed.
- Caller-supplied external references and X.509 `RetrievalMethod` resolution with bounded RFC 3986 `xml:base` processing and no implicit I/O
- XMLEnc AES-128/256-CBC and AES-128/256-GCM encryption/decryption with direct
keys, RSA-OAEP key transport, AES-128/256-KW, multiple recipients, and
Element/Content document replacement
Element/Content document replacement; document, node, and aggregate recipient
limits plus outbound RSA key-strength policy cover caller-constructed ciphertext and generated replacement output
before expensive work. CBC failures expose no decrypted
padding details, but CBC remains unauthenticated and can be excluded by policy

Still in progress:
- XMLDSig DSA, HMAC, and RSA-PSS signature algorithms
- XMLDSig DSA-SHA256, broader HMAC verification/signing, and RSA-PSS `SignatureMethod` algorithms
- Complete XMLDSig and XMLEnc conformance-suite classification
- Production hardening, fuzzing, benchmarks, and API stabilization
- Expanded fuzz coverage, benchmarks, production hardening, and API stabilization

## XMLDSig Usage

Expand Down Expand Up @@ -100,7 +106,7 @@ Current MSRV: Rust 1.92.
| [Canonical XML 1.0](https://www.w3.org/TR/xml-c14n/) | Implemented; full-document and document-subset vectors |
| [Canonical XML 1.1](https://www.w3.org/TR/xml-c14n11/) | Implemented; `xml:id` and `xml:base` subset rules |
| [Exclusive C14N](https://www.w3.org/TR/xml-exc-c14n/) | Implemented; `InclusiveNamespaces PrefixList` support |
| [XMLDSig](https://www.w3.org/TR/xmldsig-core1/) | Core sign/verify pipelines implemented; additional algorithms and conformance coverage in progress |
| [XMLDSig](https://www.w3.org/TR/xmldsig-core1/) | Core sign/verify pipelines and the complete Merlin corpus implemented; additional algorithms and conformance suites in progress |
| [XMLEnc](https://www.w3.org/TR/xmlenc-core1/) | Core AES-CBC/GCM encrypt/decrypt with RSA-OAEP and AES-KW implemented; broader conformance coverage in progress |

## License
Expand Down
Loading