Skip to main content

xdoc/signature/
algorithms.rs

1use crate::core::{ErrorKind, XmlError, XmlResult};
2
3pub const XMLDSIG_ENVELOPED_SIGNATURE_URI: &str =
4    "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
5pub const XMLDSIG_SIGNED_PROPERTIES_TYPE_URI: &str = "http://uri.etsi.org/01903#SignedProperties";
6
7const C14N_11_URI: &str = "http://www.w3.org/2006/12/xml-c14n11";
8const C14N_10_URI: &str = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
9const EXCLUSIVE_C14N_10_URI: &str = "http://www.w3.org/2001/10/xml-exc-c14n#";
10const SHA1_URI: &str = "http://www.w3.org/2000/09/xmldsig#sha1";
11const SHA256_URI: &str = "http://www.w3.org/2001/04/xmlenc#sha256";
12const SHA512_URI: &str = "http://www.w3.org/2001/04/xmlenc#sha512";
13const RSA_SHA1_URI: &str = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
14const RSA_SHA256_URI: &str = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
15
16/// Canonicalization algorithms recognized by the signature module.
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
18pub enum CanonicalizationAlgorithm {
19    #[default]
20    CanonicalXml11,
21    CanonicalXml10,
22    ExclusiveXml10,
23}
24
25impl CanonicalizationAlgorithm {
26    pub fn uri(self) -> &'static str {
27        match self {
28            Self::CanonicalXml11 => C14N_11_URI,
29            Self::CanonicalXml10 => C14N_10_URI,
30            Self::ExclusiveXml10 => EXCLUSIVE_C14N_10_URI,
31        }
32    }
33
34    pub fn from_uri(uri: &str) -> XmlResult<Self> {
35        match uri {
36            C14N_11_URI => Ok(Self::CanonicalXml11),
37            C14N_10_URI => Ok(Self::CanonicalXml10),
38            EXCLUSIVE_C14N_10_URI => Ok(Self::ExclusiveXml10),
39            _ => Err(unsupported_algorithm("canonicalization", uri)),
40        }
41    }
42}
43
44/// Digest algorithms recognized by XMLDSig/XAdES.
45#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
46pub enum DigestAlgorithm {
47    /// Legacy SHA-1 URI support for parsing and explicit rejection.
48    Sha1,
49    #[default]
50    Sha256,
51    Sha512,
52}
53
54impl DigestAlgorithm {
55    pub fn uri(self) -> &'static str {
56        match self {
57            Self::Sha1 => SHA1_URI,
58            Self::Sha256 => SHA256_URI,
59            Self::Sha512 => SHA512_URI,
60        }
61    }
62
63    pub fn from_uri(uri: &str) -> XmlResult<Self> {
64        match uri {
65            SHA1_URI => Ok(Self::Sha1),
66            SHA256_URI => Ok(Self::Sha256),
67            SHA512_URI => Ok(Self::Sha512),
68            _ => Err(unsupported_algorithm("digest", uri)),
69        }
70    }
71
72    pub fn ensure_allowed_for_generation(self) -> XmlResult<()> {
73        match self {
74            Self::Sha1 => Err(XmlError::new(
75                ErrorKind::Signature,
76                "SHA-1 is not allowed for signature generation",
77            )),
78            Self::Sha256 | Self::Sha512 => Ok(()),
79        }
80    }
81}
82
83/// Signature algorithms recognized by XMLDSig/XAdES.
84#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
85pub enum SignatureAlgorithm {
86    /// Legacy RSA-SHA1 URI support for parsing and explicit rejection.
87    RsaSha1,
88    #[default]
89    RsaSha256,
90}
91
92impl SignatureAlgorithm {
93    pub fn uri(self) -> &'static str {
94        match self {
95            Self::RsaSha1 => RSA_SHA1_URI,
96            Self::RsaSha256 => RSA_SHA256_URI,
97        }
98    }
99
100    pub fn from_uri(uri: &str) -> XmlResult<Self> {
101        match uri {
102            RSA_SHA1_URI => Ok(Self::RsaSha1),
103            RSA_SHA256_URI => Ok(Self::RsaSha256),
104            _ => Err(unsupported_algorithm("signature", uri)),
105        }
106    }
107
108    pub fn ensure_allowed_for_generation(self) -> XmlResult<()> {
109        match self {
110            Self::RsaSha1 => Err(XmlError::new(
111                ErrorKind::Signature,
112                "RSA-SHA1 is not allowed for signature generation",
113            )),
114            Self::RsaSha256 => Ok(()),
115        }
116    }
117}
118
119fn unsupported_algorithm(kind: &str, uri: &str) -> XmlError {
120    XmlError::new(
121        ErrorKind::Signature,
122        format!("unsupported {kind} algorithm URI `{uri}`"),
123    )
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn signature_algorithms_expose_xml_uris() {
132        assert_eq!(
133            CanonicalizationAlgorithm::CanonicalXml11.uri(),
134            "http://www.w3.org/2006/12/xml-c14n11"
135        );
136        assert_eq!(
137            CanonicalizationAlgorithm::CanonicalXml10.uri(),
138            "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
139        );
140        assert_eq!(
141            DigestAlgorithm::Sha256.uri(),
142            "http://www.w3.org/2001/04/xmlenc#sha256"
143        );
144        assert_eq!(
145            DigestAlgorithm::Sha512.uri(),
146            "http://www.w3.org/2001/04/xmlenc#sha512"
147        );
148        assert_eq!(
149            SignatureAlgorithm::RsaSha256.uri(),
150            "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
151        );
152    }
153
154    #[test]
155    fn signature_algorithms_parse_known_uris() {
156        assert_eq!(
157            CanonicalizationAlgorithm::from_uri("http://www.w3.org/2006/12/xml-c14n11")
158                .expect("c14n uri"),
159            CanonicalizationAlgorithm::CanonicalXml11
160        );
161        assert_eq!(
162            CanonicalizationAlgorithm::from_uri("http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
163                .expect("c14n 1.0 uri"),
164            CanonicalizationAlgorithm::CanonicalXml10
165        );
166        assert_eq!(
167            DigestAlgorithm::from_uri("http://www.w3.org/2001/04/xmlenc#sha256")
168                .expect("digest uri"),
169            DigestAlgorithm::Sha256
170        );
171        assert_eq!(
172            DigestAlgorithm::from_uri("http://www.w3.org/2001/04/xmlenc#sha512")
173                .expect("digest uri"),
174            DigestAlgorithm::Sha512
175        );
176        assert_eq!(
177            SignatureAlgorithm::from_uri("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
178                .expect("signature uri"),
179            SignatureAlgorithm::RsaSha256
180        );
181    }
182
183    #[test]
184    fn signature_algorithms_reject_unknown_uris() {
185        let error = DigestAlgorithm::from_uri("urn:unknown")
186            .expect_err("unknown digest algorithm must fail");
187
188        assert_eq!(error.kind(), &ErrorKind::Signature);
189        assert!(error.message().contains("unsupported digest algorithm"));
190    }
191
192    #[test]
193    fn signature_algorithms_reject_sha1_for_generation() {
194        let digest_error = DigestAlgorithm::Sha1
195            .ensure_allowed_for_generation()
196            .expect_err("sha1 digest must be rejected");
197        let signature_error = SignatureAlgorithm::RsaSha1
198            .ensure_allowed_for_generation()
199            .expect_err("rsa-sha1 signature must be rejected");
200
201        assert_eq!(digest_error.kind(), &ErrorKind::Signature);
202        assert_eq!(signature_error.kind(), &ErrorKind::Signature);
203    }
204}