#![forbid(unsafe_code)]
#![allow(missing_docs)]
mod c14n;
pub use c14n::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Canonicalization {
ExclusiveC14N,
ExclusiveC14NWithComments,
InclusiveC14N,
InclusiveC14NWithComments,
}
impl Canonicalization {
pub fn algorithm_id(&self) -> &'static str {
match self {
Canonicalization::ExclusiveC14N => "http://www.w3.org/2001/10/xml-exc-c14n#",
Canonicalization::ExclusiveC14NWithComments => {
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
}
Canonicalization::InclusiveC14N => "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
Canonicalization::InclusiveC14NWithComments => {
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SignatureAlgorithm {
EcdsaSha256,
Ed25519,
RsaSha256,
}
impl SignatureAlgorithm {
pub fn algorithm_id(&self) -> &'static str {
match self {
SignatureAlgorithm::EcdsaSha256 => {
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
}
SignatureAlgorithm::Ed25519 => "http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519",
SignatureAlgorithm::RsaSha256 => "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Reference {
pub uri: String,
pub digest_method: String,
pub digest_value: Vec<u8>,
pub transforms: Vec<Transform>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Transform {
ExclusiveC14N,
InclusiveC14N,
EnvelopedSignature,
Base64Decode,
}
impl Transform {
pub fn algorithm_id(&self) -> &'static str {
match self {
Transform::ExclusiveC14N => "http://www.w3.org/2001/10/xml-exc-c14n#",
Transform::InclusiveC14N => "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
Transform::EnvelopedSignature => {
"http://www.w3.org/2000/09/xmldsig#enveloped-signature"
}
Transform::Base64Decode => "http://www.w3.org/2000/09/xmldsig#base64",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedInfo {
pub canonicalization: Canonicalization,
pub signature_algorithm: SignatureAlgorithm,
pub references: Vec<Reference>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct XmlDSigSignature {
pub signed_info: SignedInfo,
pub signature_value: Vec<u8>,
#[serde(default)]
pub key_info: Option<KeyInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyInfo {
pub x509_certificates: Vec<String>,
#[serde(default)]
pub key_name: Option<String>,
}
#[derive(Debug, thiserror::Error)]
pub enum XmlDSigError {
#[error("XML parse error: {0}")]
XmlParse(String),
#[error("canonicalization error: {0}")]
Canonicalize(String),
#[error("signature verification failed")]
VerifyFailed,
#[error("unsupported algorithm: {0}")]
UnsupportedAlgorithm(String),
}
pub fn sha256_digest(data: &[u8]) -> Vec<u8> {
use sha2::{Digest, Sha256};
let mut h = Sha256::new();
h.update(data);
h.finalize().to_vec()
}
pub fn canonicalize_exclusive(xml: &str) -> Result<String, XmlDSigError> {
Ok(xml.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn algorithm_ids_correct() {
assert_eq!(
Canonicalization::ExclusiveC14N.algorithm_id(),
"http://www.w3.org/2001/10/xml-exc-c14n#"
);
assert_eq!(
SignatureAlgorithm::Ed25519.algorithm_id(),
"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519"
);
}
#[test]
fn sha256_digest_deterministic() {
let d1 = sha256_digest(b"hello");
let d2 = sha256_digest(b"hello");
assert_eq!(d1, d2);
assert_eq!(d1.len(), 32);
}
#[test]
fn mock_canonicalize_round_trips() {
let xml = "<root>test</root>";
let canon = canonicalize_exclusive(xml).unwrap();
assert_eq!(canon, xml);
}
}