confium_pki/xmldsig/
mod.rs1#![forbid(unsafe_code)]
10#![allow(missing_docs)] mod c14n;
13
14pub use c14n::*;
15
16use serde::{Deserialize, Serialize};
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "snake_case")]
21pub enum Canonicalization {
22 ExclusiveC14N,
24 ExclusiveC14NWithComments,
26 InclusiveC14N,
28 InclusiveC14NWithComments,
30}
31
32impl Canonicalization {
33 pub fn algorithm_id(&self) -> &'static str {
35 match self {
36 Canonicalization::ExclusiveC14N => "http://www.w3.org/2001/10/xml-exc-c14n#",
37 Canonicalization::ExclusiveC14NWithComments => {
38 "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
39 }
40 Canonicalization::InclusiveC14N => "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
41 Canonicalization::InclusiveC14NWithComments => {
42 "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
43 }
44 }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "snake_case")]
51pub enum SignatureAlgorithm {
52 EcdsaSha256,
54 Ed25519,
56 RsaSha256,
58}
59
60impl SignatureAlgorithm {
61 pub fn algorithm_id(&self) -> &'static str {
63 match self {
64 SignatureAlgorithm::EcdsaSha256 => {
65 "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
66 }
67 SignatureAlgorithm::Ed25519 => "http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519",
68 SignatureAlgorithm::RsaSha256 => "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
69 }
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Reference {
76 pub uri: String,
78 pub digest_method: String,
80 pub digest_value: Vec<u8>,
82 pub transforms: Vec<Transform>,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "snake_case")]
89pub enum Transform {
90 ExclusiveC14N,
92 InclusiveC14N,
94 EnvelopedSignature,
96 Base64Decode,
98}
99
100impl Transform {
101 pub fn algorithm_id(&self) -> &'static str {
103 match self {
104 Transform::ExclusiveC14N => "http://www.w3.org/2001/10/xml-exc-c14n#",
105 Transform::InclusiveC14N => "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
106 Transform::EnvelopedSignature => {
107 "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
108 }
109 Transform::Base64Decode => "http://www.w3.org/2000/09/xmldsig#base64",
110 }
111 }
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct SignedInfo {
117 pub canonicalization: Canonicalization,
119 pub signature_algorithm: SignatureAlgorithm,
121 pub references: Vec<Reference>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct XmlDSigSignature {
128 pub signed_info: SignedInfo,
130 pub signature_value: Vec<u8>,
132 #[serde(default)]
134 pub key_info: Option<KeyInfo>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct KeyInfo {
140 pub x509_certificates: Vec<String>,
142 #[serde(default)]
144 pub key_name: Option<String>,
145}
146
147#[derive(Debug, thiserror::Error)]
149pub enum XmlDSigError {
150 #[error("XML parse error: {0}")]
152 XmlParse(String),
153 #[error("canonicalization error: {0}")]
155 Canonicalize(String),
156 #[error("signature verification failed")]
158 VerifyFailed,
159 #[error("unsupported algorithm: {0}")]
161 UnsupportedAlgorithm(String),
162}
163
164pub fn sha256_digest(data: &[u8]) -> Vec<u8> {
166 use sha2::{Digest, Sha256};
167 let mut h = Sha256::new();
168 h.update(data);
169 h.finalize().to_vec()
170}
171
172pub fn canonicalize_exclusive(xml: &str) -> Result<String, XmlDSigError> {
175 Ok(xml.to_string())
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn algorithm_ids_correct() {
184 assert_eq!(
185 Canonicalization::ExclusiveC14N.algorithm_id(),
186 "http://www.w3.org/2001/10/xml-exc-c14n#"
187 );
188 assert_eq!(
189 SignatureAlgorithm::Ed25519.algorithm_id(),
190 "http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519"
191 );
192 }
193
194 #[test]
195 fn sha256_digest_deterministic() {
196 let d1 = sha256_digest(b"hello");
197 let d2 = sha256_digest(b"hello");
198 assert_eq!(d1, d2);
199 assert_eq!(d1.len(), 32);
200 }
201
202 #[test]
203 fn mock_canonicalize_round_trips() {
204 let xml = "<root>test</root>";
205 let canon = canonicalize_exclusive(xml).unwrap();
206 assert_eq!(canon, xml);
207 }
208}