Skip to main content

mako_as4/
lib.rs

1//! BDEW MaKo AS4 profile for German energy market communication.
2//!
3//! This crate encodes the **BDEW AS4-Profil v1.2** requirements on top
4//! of [`asx_rs`](https://docs.rs/asx-rs) v0.13, providing:
5//!
6//! - [`constants`] — BDEW-specific URIs and algorithm identifiers
7//! - [`pmode`] — [`BdewAction`] enum, [`bdew_pmode`] / [`bdew_pmode_sign_only`]
8//! - [`partner_directory`] — [`PartnerDirectory`], GLN-to-endpoint resolution
9//! - [`profile`] — [`BdewAs4Profile`], [`bdew_mako_profile_stack`],
10//!   [`bdew_push_policy`] (inbound policy with `require_encrypted_inbound`)
11//! - [`testing`] *(feature)* — [`BdewTestPki`], [`MockAs4Endpoint`],
12//!   [`generate_self_signed_bdew_keypair`] (BrainpoolP256r1)
13//!
14//! ## BDEW AS4-Profil v1.2 crypto requirements
15//!
16//! | Requirement | Algorithm | Source |
17//! |---|---|---|
18//! | Signing | **ECDSA-SHA256 + BrainpoolP256r1** | §2.2.6.2.1 / BSI TR-03116-3 §9.1 |
19//! | Signing token | **X509PKIPathv1** (`BinarySecurityToken`) | §2.2.6.2.1 |
20//! | Encryption | **ECDH-ES + ConcatKDF + AES-128-GCM** | §2.2.6.2.2 / BSI TR-03116-3 §9.2 |
21//! | Key reference | **X509SKI** | §2.2.6.2.2 |
22//! | EC curve | **BrainpoolP256r1** (both signing and encryption) | BSI TR-03116-3 |
23//!
24//! Both algorithms are **auto-detected** from the key/cert type —
25//! supply EC (BrainpoolP256r1) material and the correct paths are selected automatically.
26//!
27//! ## Signature scope
28//!
29//! The BDEW profile requires `PMode[1].Security.X509.Sign` to be set *"nach
30//! Maßgabe der Abschnitte 5.1.4 und 5.1.5 von \[AS4\]"* (§2.2.6.2.1), and those
31//! sections put the `eb:Messaging` SOAP header block inside the signature.
32//!
33//! The **whole `eb:Messaging` block** is signed, referenced by
34//! `wsu:Id="as4-messaging"`. That scope is what makes the signature meaningful:
35//! `PartyInfo`, `CollaborationInfo` and `Action` are the routing and
36//! authorization metadata, so a signature covering only `eb:MessageId` would
37//! leave all of it tamperable.
38//!
39//! On receive, the block the parser consumes is bound to the block the
40//! signature verified, so a relocated-but-still-resolvable signed element cannot
41//! be paired with an injected unsigned replacement (XML Signature Wrapping).
42//!
43//! ### Interop
44//!
45//! Verification is strict in one direction: a receiver requiring the full block
46//! rejects a sender that signs less, while a receiver checking only that *some*
47//! signature verified accepts a conformant sender either way. Conformant partner
48//! stacks sign the block, so strict verification rejects only non-conformant
49//! senders.
50//!
51//! ## ECDH-ES key derivation
52//!
53//! ConcatKDF uses the SP 800-56A raw-concatenation form — no `keydatalen` field,
54//! no JOSE-style length prefixes — which is what BSI TR-03116-3 §9.2 requires.
55//! The derivation determines the KEK, so a peer deriving it differently cannot
56//! decrypt the payload.
57//!
58//! ## Quick start
59//!
60//! ```rust
61//! use mako_as4::{BdewAs4Profile, BdewAction, bdew_pmode, constants};
62//!
63//! // Build a profile and register bilateral P-Modes for each trading partner
64//! let mut profile = BdewAs4Profile::new();
65//! profile
66//!     .register_pmode(bdew_pmode("pm-utilmd-a", "9900000000001", BdewAction::Utilmd))
67//!     .register_pmode(bdew_pmode("pm-aperak-a", "9900000000001", BdewAction::Aperak));
68//!
69//! // Fail-fast at startup
70//! profile.validate().expect("BDEW MaKo profile must satisfy all security invariants");
71//!
72//! // Resolve a P-Mode at send time
73//! let pm = profile.resolve_pmode(
74//!     "9900000000001",
75//!     constants::SERVICE,
76//!     &BdewAction::Utilmd.as_uri(),
77//! );
78//! assert!(pm.is_some());
79//! ```
80
81#![deny(unsafe_code)]
82
83pub mod constants;
84pub mod partner_directory;
85pub mod pmode;
86pub mod profile;
87#[cfg(feature = "server")]
88pub mod server;
89#[cfg(feature = "testing")]
90pub mod testing;
91
92// ── Top-level re-exports for ergonomics ──────────────────────────────────────
93
94/// Re-export `InsecureBypassAs4Verifier` for test-only AS4 receive without PKI.
95#[cfg(feature = "testing")]
96pub use asx_rs::as4::InsecureBypassAs4Verifier;
97pub use partner_directory::{PartnerDirectory, PartnerDirectoryParseError};
98pub use pmode::{
99    BdewAction, PModeRegistry, ParseBdewActionError, bdew_action_from_str, bdew_pmode,
100    bdew_pmode_sign_only, bdew_pmode_with_endpoint,
101};
102pub use profile::{As4PushPolicy, BdewAs4Profile, bdew_mako_profile_stack, bdew_push_policy};
103#[cfg(feature = "server")]
104pub use server::bdew_router_config;
105#[cfg(feature = "testing")]
106pub use testing::{
107    BdewCertPurpose, BdewKeypair, BdewTestPki, MockAs4Endpoint, MockReceivedMessage,
108    generate_self_signed_bdew_keypair,
109};