diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fca0c6a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 10 + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..785badf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: -Dwarnings + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo build --all-features + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo test --all-features + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + - run: cargo clippy --all-features --all-targets -- -D warnings + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all -- --check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1e41e1e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,24 @@ +name: Release +on: + push: + tags: ["v*"] + +permissions: + contents: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }} + + github-release: + runs-on: ubuntu-latest + needs: publish + steps: + - uses: actions/checkout@v6 + - uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1c081e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/target +Cargo.lock +*.swp +*.swo +.DS_Store +donors/ +.refs/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..42b7c6d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "xml-sec" +version = "0.1.0" +edition = "2021" +rust-version = "1.75" +license = "Apache-2.0" +description = "Pure Rust XML Security: XMLDSig, XMLEnc, C14N. Drop-in replacement for libxmlsec1." +repository = "https://github.com/structured-world/xml-sec" +homepage = "https://github.com/structured-world/xml-sec" +documentation = "https://docs.rs/xml-sec" +keywords = ["xml", "xmldsig", "xmlenc", "c14n", "saml"] +categories = ["cryptography", "web-programming", "authentication"] +readme = "README.md" + +[dependencies] +# XML parsing +roxmltree = "0.20" + +# Crypto +ring = "0.17" + +# X.509 certificates +x509-parser = "0.16" +der = "0.7" + +# Error handling +thiserror = "2" + +[dev-dependencies] +# W3C test vectors +base64 = "0.22" + +[features] +default = ["xmldsig", "c14n"] +xmldsig = [] # XML Digital Signatures (sign + verify) +xmlenc = [] # XML Encryption (encrypt + decrypt) +c14n = [] # XML Canonicalization (inclusive + exclusive) diff --git a/README.md b/README.md index 9760148..c7b474b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,39 @@ # xml-sec -Pure Rust XML Security: XMLDSig, XMLEnc, C14N. Drop-in replacement for libxmlsec1. + +Pure Rust XML Security library. Drop-in replacement for libxmlsec1. + +**No C dependencies. No cmake. No system libraries. Just `cargo add xml-sec`.** + +## Features + +- **C14N** — XML Canonicalization (inclusive + exclusive, W3C compliant) +- **XMLDSig** — XML Digital Signatures (sign + verify, enveloped/enveloping/detached) +- **XMLEnc** — XML Encryption (symmetric + asymmetric) +- **X.509** — Certificate-based key extraction and validation + +## Why? + +Every SAML, SOAP, and WS-Security implementation depends on libxmlsec1 — a C library that: +- Requires cmake + libxml2 + OpenSSL/NSS/GnuTLS to build +- Breaks on Alpine/musl static linking +- Has decades of CVEs in XML parsing and signature validation +- Cannot cross-compile easily + +`xml-sec` is a ground-up Rust rewrite using `roxmltree` + `ring` + `x509-parser`. Single `cargo build`, works everywhere Rust works. + +## Status + +**Pre-release.** API is unstable. Not ready for production use. + +## Specifications + +| Spec | Status | +|------|--------| +| [Canonical XML 1.0](https://www.w3.org/TR/xml-c14n/) | Planned | +| [Exclusive C14N](https://www.w3.org/TR/xml-exc-c14n/) | Planned | +| [XMLDSig](https://www.w3.org/TR/xmldsig-core1/) | Planned | +| [XMLEnc](https://www.w3.org/TR/xmlenc-core1/) | Planned | + +## License + +Apache-2.0 diff --git a/pr b/pr new file mode 100644 index 0000000..b800827 --- /dev/null +++ b/pr @@ -0,0 +1,75 @@ + +--- +Промпт 1 — Source Tree Inventory + +Мне нужно проанализировать кодовые базы-доноры для проекта xml-sec (Pure Rust XML Security). + +### Агент 1: xmlsec1 (~/projects/sw/xml-sec/donors/xmlsec/) + +Ты анализируешь исходный код xmlsec1 (C, XML Security Library) для справочного каталога. + +ЗАДАЧА: Создать структурированный каталог исходников. + +ПРАВИЛА ФИЛЬТРАЦИИ: +- ВКЛЮЧАТЬ: .c/.h файлы из: src/ (core), src/openssl/ (crypto backend), include/xmlsec/ +- ВКЛЮЧАТЬ: tests/aleksey/ (reference test vectors — W3C compliance) +- ИСКЛЮЧАТЬ: src/nss/, src/gnutls/, src/mscrypto/, src/mscng/ (alternative crypto backends) +- ИСКЛЮЧАТЬ: win32/, docs/, scripts/, m4/ +- ИСКЛЮЧАТЬ: файлы < 20 строк + +ФОРМАТ ВЫВОДА — файл ~/projects/sw/xml-sec/.refs/xmlsec-tree.md + +ПРОЦЕДУРА: +1. cd ~/projects/sw/xml-sec/donors/xmlsec/ && git rev-parse HEAD +2. Focus: src/c14n.c (canonicalization), src/xmldsig.c (signatures), src/xmlenc.c (encryption), + src/transforms.c (transform chain), src/keys.c (key management), src/x509.c (certificates) +3. Для каждого файла: read first 100 lines → describe struct/function signatures + +### Агент 2: samael (~/projects/sw/xml-sec/donors/samael/) + +Ты анализируешь исходный код samael (Rust, SAML2 library) — reference для XML signature handling. + +ПРАВИЛА ФИЛЬТРАЦИИ: +- ВКЛЮЧАТЬ: src/ (all .rs files) +- ИСКЛЮЧАТЬ: tests/, examples/ + +Focus: как samael делает XML signature verification, где вызывает C xmlsec1, какие Rust structures определены. + +ФОРМАТ ВЫВОДА — файл ~/projects/sw/xml-sec/.refs/samael-tree.md + +--- + +Промпт 2 — Feature-Targeted Deep Analysis + +FEATURE-БЛОКИ (8 штук): + +1. C14N Inclusive — xmlsec1 src/c14n.c, алгоритм каноникализации, namespace processing, attribute sorting +2. C14N Exclusive — xmlsec1 src/c14n.c (exclusive mode), namespace rendering rules +3. XMLDSig Verify — xmlsec1 src/xmldsig.c, verification pipeline: Reference → DigestValue → SignatureValue +4. XMLDSig Sign — xmlsec1 src/xmldsig.c, signing pipeline: transforms → digest → sign +5. Transform Chain — xmlsec1 src/transforms.c, enveloped-signature transform, XPath filter +6. XMLEnc — xmlsec1 src/xmlenc.c, symmetric/asymmetric encryption, key wrapping +7. Key Management — xmlsec1 src/keys.c, src/x509.c, KeyInfo element processing +8. samael Integration — samael src/xmlsec.rs, how Rust SAML calls xmlsec1 FFI, what we replace + +--- + +Промпт 3 — Architecture Documents + +Создать ~/projects/sw/xml-sec/arch/ с документами: +- arch/c14n.md — Canonicalization design +- arch/xmldsig.md — Digital Signatures design +- arch/xmlenc.md — Encryption design +- arch/transforms.md — Transform pipeline design +- arch/keys.md — Key management design +- arch/ROADMAP.md — Phased implementation plan +- arch/DECISIONS.md — ADR log + +--- + +Промпт 4 — Roadmap + +Phase 1 (3 мес): C14N + XMLDSig verify-only → samael can use xml-sec for SAML assertion validation +Phase 2 (3 мес): XMLDSig sign + KeyInfo → full SAML IdP support +Phase 3 (3 мес): XMLEnc + full transform pipeline +Phase 4 (3 мес): W3C conformance test suite, production hardening, 1.0 release diff --git a/src/c14n/mod.rs b/src/c14n/mod.rs new file mode 100644 index 0000000..4a65e69 --- /dev/null +++ b/src/c14n/mod.rs @@ -0,0 +1,23 @@ +//! XML Canonicalization (C14N). +//! +//! Implements: +//! - [Canonical XML 1.0](https://www.w3.org/TR/xml-c14n/) +//! - [Canonical XML 1.1](https://www.w3.org/TR/xml-c14n11/) +//! - [Exclusive XML Canonicalization](https://www.w3.org/TR/xml-exc-c14n/) + +/// Canonicalization algorithm selection. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum C14nAlgorithm { + /// Canonical XML 1.0 (with comments). + Inclusive10WithComments, + /// Canonical XML 1.0 (without comments). + Inclusive10, + /// Canonical XML 1.1 (with comments). + Inclusive11WithComments, + /// Canonical XML 1.1 (without comments). + Inclusive11, + /// Exclusive Canonical XML (with comments). + Exclusive10WithComments, + /// Exclusive Canonical XML (without comments). + Exclusive10, +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..219e55d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,33 @@ +//! Error types for xml-sec. + +/// Errors that can occur during XML security operations. +#[derive(Debug, thiserror::Error)] +pub enum XmlSecError { + /// XML parsing error. + #[error("XML parse error: {0}")] + XmlParse(String), + + /// Canonicalization error. + #[error("C14N error: {0}")] + Canonicalization(String), + + /// Signature verification failed. + #[error("Signature verification failed: {0}")] + SignatureInvalid(String), + + /// Unsupported algorithm. + #[error("Unsupported algorithm: {0}")] + UnsupportedAlgorithm(String), + + /// Certificate error. + #[error("Certificate error: {0}")] + Certificate(String), + + /// Cryptographic operation failed. + #[error("Crypto error: {0}")] + Crypto(String), + + /// Key not found or invalid. + #[error("Key error: {0}")] + Key(String), +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..992babe --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,36 @@ +//! # xml-sec — Pure Rust XML Security +//! +//! Drop-in replacement for libxmlsec1. XMLDSig, XMLEnc, C14N — no C dependencies. +//! +//! ## Features +//! +//! - **C14N** — XML Canonicalization (inclusive + exclusive) +//! - **XMLDSig** — XML Digital Signatures (sign + verify) +//! - **XMLEnc** — XML Encryption (encrypt + decrypt) +//! - **X.509** — Certificate-based key extraction +//! +//! ## Quick Start +//! +//! ```rust,no_run +//! use xml_sec::{XmlSigner, XmlVerifier}; +//! +//! // Verify a signed XML document +//! let doc = std::fs::read_to_string("signed.xml").unwrap(); +//! let cert = std::fs::read("cert.pem").unwrap(); +//! let valid = XmlVerifier::new(&cert).verify(&doc).unwrap(); +//! ``` + +#![deny(unsafe_code)] +#![deny(clippy::unwrap_used)] +#![warn(missing_docs)] + +pub mod c14n; +pub mod error; + +#[cfg(feature = "xmldsig")] +pub mod xmldsig; + +#[cfg(feature = "xmlenc")] +pub mod xmlenc; + +pub use error::XmlSecError; diff --git a/src/xmldsig/mod.rs b/src/xmldsig/mod.rs new file mode 100644 index 0000000..04a4156 --- /dev/null +++ b/src/xmldsig/mod.rs @@ -0,0 +1,3 @@ +//! XML Digital Signatures (XMLDSig). +//! +//! Implements [XML Signature Syntax and Processing](https://www.w3.org/TR/xmldsig-core1/). diff --git a/src/xmlenc/mod.rs b/src/xmlenc/mod.rs new file mode 100644 index 0000000..0132432 --- /dev/null +++ b/src/xmlenc/mod.rs @@ -0,0 +1,3 @@ +//! XML Encryption (XMLEnc). +//! +//! Implements [XML Encryption Syntax and Processing](https://www.w3.org/TR/xmlenc-core1/).