use crate::core::{ErrorKind, XmlError, XmlResult};
pub const XMLDSIG_ENVELOPED_SIGNATURE_URI: &str =
"http://www.w3.org/2000/09/xmldsig#enveloped-signature";
pub const XMLDSIG_SIGNED_PROPERTIES_TYPE_URI: &str = "http://uri.etsi.org/01903#SignedProperties";
const C14N_11_URI: &str = "http://www.w3.org/2006/12/xml-c14n11";
const C14N_10_URI: &str = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
const EXCLUSIVE_C14N_10_URI: &str = "http://www.w3.org/2001/10/xml-exc-c14n#";
const SHA1_URI: &str = "http://www.w3.org/2000/09/xmldsig#sha1";
const SHA256_URI: &str = "http://www.w3.org/2001/04/xmlenc#sha256";
const SHA512_URI: &str = "http://www.w3.org/2001/04/xmlenc#sha512";
const RSA_SHA1_URI: &str = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
const RSA_SHA256_URI: &str = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum CanonicalizationAlgorithm {
#[default]
CanonicalXml11,
CanonicalXml10,
ExclusiveXml10,
}
impl CanonicalizationAlgorithm {
pub fn uri(self) -> &'static str {
match self {
Self::CanonicalXml11 => C14N_11_URI,
Self::CanonicalXml10 => C14N_10_URI,
Self::ExclusiveXml10 => EXCLUSIVE_C14N_10_URI,
}
}
pub fn from_uri(uri: &str) -> XmlResult<Self> {
match uri {
C14N_11_URI => Ok(Self::CanonicalXml11),
C14N_10_URI => Ok(Self::CanonicalXml10),
EXCLUSIVE_C14N_10_URI => Ok(Self::ExclusiveXml10),
_ => Err(unsupported_algorithm("canonicalization", uri)),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DigestAlgorithm {
Sha1,
#[default]
Sha256,
Sha512,
}
impl DigestAlgorithm {
pub fn uri(self) -> &'static str {
match self {
Self::Sha1 => SHA1_URI,
Self::Sha256 => SHA256_URI,
Self::Sha512 => SHA512_URI,
}
}
pub fn from_uri(uri: &str) -> XmlResult<Self> {
match uri {
SHA1_URI => Ok(Self::Sha1),
SHA256_URI => Ok(Self::Sha256),
SHA512_URI => Ok(Self::Sha512),
_ => Err(unsupported_algorithm("digest", uri)),
}
}
pub fn ensure_allowed_for_generation(self) -> XmlResult<()> {
match self {
Self::Sha1 => Err(XmlError::new(
ErrorKind::Signature,
"SHA-1 is not allowed for signature generation",
)),
Self::Sha256 | Self::Sha512 => Ok(()),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum SignatureAlgorithm {
RsaSha1,
#[default]
RsaSha256,
}
impl SignatureAlgorithm {
pub fn uri(self) -> &'static str {
match self {
Self::RsaSha1 => RSA_SHA1_URI,
Self::RsaSha256 => RSA_SHA256_URI,
}
}
pub fn from_uri(uri: &str) -> XmlResult<Self> {
match uri {
RSA_SHA1_URI => Ok(Self::RsaSha1),
RSA_SHA256_URI => Ok(Self::RsaSha256),
_ => Err(unsupported_algorithm("signature", uri)),
}
}
pub fn ensure_allowed_for_generation(self) -> XmlResult<()> {
match self {
Self::RsaSha1 => Err(XmlError::new(
ErrorKind::Signature,
"RSA-SHA1 is not allowed for signature generation",
)),
Self::RsaSha256 => Ok(()),
}
}
}
fn unsupported_algorithm(kind: &str, uri: &str) -> XmlError {
XmlError::new(
ErrorKind::Signature,
format!("unsupported {kind} algorithm URI `{uri}`"),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn signature_algorithms_expose_xml_uris() {
assert_eq!(
CanonicalizationAlgorithm::CanonicalXml11.uri(),
"http://www.w3.org/2006/12/xml-c14n11"
);
assert_eq!(
CanonicalizationAlgorithm::CanonicalXml10.uri(),
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
);
assert_eq!(
DigestAlgorithm::Sha256.uri(),
"http://www.w3.org/2001/04/xmlenc#sha256"
);
assert_eq!(
DigestAlgorithm::Sha512.uri(),
"http://www.w3.org/2001/04/xmlenc#sha512"
);
assert_eq!(
SignatureAlgorithm::RsaSha256.uri(),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
);
}
#[test]
fn signature_algorithms_parse_known_uris() {
assert_eq!(
CanonicalizationAlgorithm::from_uri("http://www.w3.org/2006/12/xml-c14n11")
.expect("c14n uri"),
CanonicalizationAlgorithm::CanonicalXml11
);
assert_eq!(
CanonicalizationAlgorithm::from_uri("http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
.expect("c14n 1.0 uri"),
CanonicalizationAlgorithm::CanonicalXml10
);
assert_eq!(
DigestAlgorithm::from_uri("http://www.w3.org/2001/04/xmlenc#sha256")
.expect("digest uri"),
DigestAlgorithm::Sha256
);
assert_eq!(
DigestAlgorithm::from_uri("http://www.w3.org/2001/04/xmlenc#sha512")
.expect("digest uri"),
DigestAlgorithm::Sha512
);
assert_eq!(
SignatureAlgorithm::from_uri("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
.expect("signature uri"),
SignatureAlgorithm::RsaSha256
);
}
#[test]
fn signature_algorithms_reject_unknown_uris() {
let error = DigestAlgorithm::from_uri("urn:unknown")
.expect_err("unknown digest algorithm must fail");
assert_eq!(error.kind(), &ErrorKind::Signature);
assert!(error.message().contains("unsupported digest algorithm"));
}
#[test]
fn signature_algorithms_reject_sha1_for_generation() {
let digest_error = DigestAlgorithm::Sha1
.ensure_allowed_for_generation()
.expect_err("sha1 digest must be rejected");
let signature_error = SignatureAlgorithm::RsaSha1
.ensure_allowed_for_generation()
.expect_err("rsa-sha1 signature must be rejected");
assert_eq!(digest_error.kind(), &ErrorKind::Signature);
assert_eq!(signature_error.kind(), &ErrorKind::Signature);
}
}