pub fn contains_ds_object(signature_xml: &str) -> Result<bool, uppsala::XmlError> {
const XMLDSIG_NS: &str = "http://www.w3.org/2000/09/xmldsig#";
let doc = crate::xml::parse_secure_metadata(signature_xml)?;
let Some(root) = doc.document_element() else {
return Ok(false);
};
for node in doc.descendants(root) {
let Some(elem) = doc.element(node) else {
continue;
};
if elem.name.local_name == "Object"
&& elem.name.namespace_uri.as_deref() == Some(XMLDSIG_NS)
{
return Ok(true);
}
}
Ok(false)
}
pub const HMAC_SIGNATURE_ALGORITHMS: &[&str] = &[
"http://www.w3.org/2000/09/xmldsig#hmac-sha1",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha224",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha384",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha512",
"http://www.w3.org/2001/04/xmldsig-more#hmac-md5",
"http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160",
];
pub fn is_hmac_algorithm(algorithm_uri: &str) -> bool {
HMAC_SIGNATURE_ALGORITHMS.contains(&algorithm_uri)
|| bergshamra_crypto::sign::is_hmac_algorithm(algorithm_uri)
}
pub fn contains_hmac_signature_method(signed_xml: &str) -> Result<bool, uppsala::XmlError> {
const XMLDSIG_NS: &str = "http://www.w3.org/2000/09/xmldsig#";
let doc = crate::xml::parse_secure_metadata(signed_xml)?;
let Some(root) = doc.document_element() else {
return Ok(false);
};
for node in doc.descendants(root) {
let Some(elem) = doc.element(node) else {
continue;
};
if elem.name.local_name == "SignatureMethod"
&& elem.name.namespace_uri.as_deref() == Some(XMLDSIG_NS)
{
if let Some(alg) = elem.get_attribute("Algorithm") {
if is_hmac_algorithm(alg) {
return Ok(true);
}
}
}
}
Ok(false)
}
pub const KNOWN_SIGNATURE_ALGORITHMS: &[&str] = &[
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha224",
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
"http://www.w3.org/2007/05/xmldsig-more#rsa-pss",
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1",
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha224",
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384",
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512",
"http://www.w3.org/2000/09/xmldsig#dsa-sha1",
"http://www.w3.org/2009/xmldsig11#dsa-sha256",
"http://www.w3.org/2000/09/xmldsig#hmac-sha1",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha224",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha384",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha512",
];
pub fn is_known_algorithm(algorithm_uri: &str) -> bool {
KNOWN_SIGNATURE_ALGORITHMS.contains(&algorithm_uri)
}
pub const CBC_ENCRYPTION_ALGORITHMS: &[&str] = &[
"http://www.w3.org/2001/04/xmlenc#aes128-cbc",
"http://www.w3.org/2001/04/xmlenc#aes192-cbc",
"http://www.w3.org/2001/04/xmlenc#aes256-cbc",
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc",
];
pub const GCM_ENCRYPTION_ALGORITHMS: &[&str] = &[
"http://www.w3.org/2009/xmlenc11#aes128-gcm",
"http://www.w3.org/2009/xmlenc11#aes192-gcm",
"http://www.w3.org/2009/xmlenc11#aes256-gcm",
];
pub fn is_cbc_algorithm(algorithm_uri: &str) -> bool {
CBC_ENCRYPTION_ALGORITHMS.contains(&algorithm_uri)
}
pub fn is_gcm_algorithm(algorithm_uri: &str) -> bool {
GCM_ENCRYPTION_ALGORITHMS.contains(&algorithm_uri)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_contains_ds_object_with_prefix() {
let xml = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo/>
<ds:SignatureValue>abc</ds:SignatureValue>
<ds:Object>malicious content</ds:Object>
</ds:Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(true));
}
#[test]
fn test_contains_ds_object_without_prefix() {
let xml = r#"<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo/>
<SignatureValue>abc</SignatureValue>
<Object>malicious content</Object>
</Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(true));
}
#[test]
fn test_no_ds_object() {
let xml = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo/>
<ds:SignatureValue>abc</ds:SignatureValue>
<ds:KeyInfo/>
</ds:Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(false));
}
#[test]
fn test_dsig_prefix_object() {
let xml = r#"<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
<dsig:Object>content</dsig:Object>
</dsig:Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(true));
}
#[test]
fn test_self_closing_object() {
let xml = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Object />
</ds:Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(true));
}
#[test]
fn test_ignores_non_dsig_object() {
let xml = r#"<Signature xmlns="urn:example:not-dsig">
<Object>application content</Object>
</Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(false));
}
#[test]
fn test_dsig_object_with_unusual_prefix() {
let xml = r#"<sig:Signature xmlns:sig="http://www.w3.org/2000/09/xmldsig#">
<sig:Object>content</sig:Object>
</sig:Signature>"#;
assert_eq!(contains_ds_object(xml), Ok(true));
}
#[test]
fn test_unparseable_xml_fails_closed() {
let xml = "<ds:Signature><ds:Object>unterminated";
assert!(contains_ds_object(xml).is_err());
}
#[test]
fn test_scans_tolerate_comments_in_metadata() {
let with_comment = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- published by test federation -->
<ds:SignedInfo>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
</ds:SignedInfo>
</ds:Signature>"#;
assert_eq!(contains_ds_object(with_comment), Ok(false));
assert_eq!(contains_hmac_signature_method(with_comment), Ok(false));
let obj = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- c --><ds:Object>x</ds:Object>
</ds:Signature>"#;
assert_eq!(contains_ds_object(obj), Ok(true));
let hmac = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- c --><ds:SignedInfo>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
</ds:SignedInfo>
</ds:Signature>"#;
assert_eq!(contains_hmac_signature_method(hmac), Ok(true));
}
#[test]
fn test_scans_still_reject_cdata() {
let xml = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><![CDATA[x]]></ds:Signature>"#;
assert!(contains_ds_object(xml).is_err());
assert!(contains_hmac_signature_method(xml).is_err());
}
#[test]
fn test_known_algorithms() {
assert!(is_known_algorithm(
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
));
assert!(is_known_algorithm(
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
));
assert!(is_known_algorithm(
"http://www.w3.org/2000/09/xmldsig#rsa-sha1"
));
assert!(!is_known_algorithm("http://example.com/unknown-algorithm"));
}
#[test]
fn test_cbc_algorithms() {
assert!(is_cbc_algorithm(
"http://www.w3.org/2001/04/xmlenc#aes128-cbc"
));
assert!(is_cbc_algorithm(
"http://www.w3.org/2001/04/xmlenc#aes256-cbc"
));
assert!(is_cbc_algorithm(
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
));
assert!(!is_cbc_algorithm(
"http://www.w3.org/2009/xmlenc11#aes128-gcm"
));
}
#[test]
fn test_gcm_algorithms() {
assert!(is_gcm_algorithm(
"http://www.w3.org/2009/xmlenc11#aes128-gcm"
));
assert!(is_gcm_algorithm(
"http://www.w3.org/2009/xmlenc11#aes256-gcm"
));
assert!(!is_gcm_algorithm(
"http://www.w3.org/2001/04/xmlenc#aes128-cbc"
));
}
#[test]
fn test_hmac_algorithms_cover_every_backend_hmac() {
for uri in [
"http://www.w3.org/2000/09/xmldsig#hmac-sha1",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha224",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha384",
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha512",
"http://www.w3.org/2001/04/xmldsig-more#hmac-md5",
"http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160",
] {
assert!(is_hmac_algorithm(uri), "{uri} must be flagged as HMAC");
assert!(
bergshamra_crypto::sign::is_hmac_algorithm(uri),
"{uri} should also be an HMAC per the backend"
);
}
assert!(!is_hmac_algorithm(
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
));
}
#[test]
fn test_contains_hmac_ripemd160_signature_method() {
let xml = r#"<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160"/>
</ds:SignedInfo>
</ds:Signature>"#;
assert_eq!(contains_hmac_signature_method(xml), Ok(true));
}
}