From 1e553dd6db831a29b322539d1f39f7ea9d162bad Mon Sep 17 00:00:00 2001 From: Max Ammann Date: Wed, 1 Apr 2026 12:36:11 +0200 Subject: [PATCH 1/3] minor cleanup for the new reduce feature --- src/crypto/mod.rs | 5 +++- src/crypto/xmlsec/wrapper/xmldsig.rs | 6 ++-- src/idp/tests.rs | 42 ++++++++++++++-------------- src/service_provider/mod.rs | 22 +++++++++++---- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index a4ff692..4067c26 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -65,11 +65,14 @@ impl From> for CertificateDer { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ReduceMode { /// Returns xmlsec's pre-digest content for exactly one verified reference across the document. + /// + /// This is the strictest mode. It only works if there is one Signature element + /// in the XML. PreDigest, /// Legacy mode that preserves the verified content and every element ancestor up to the /// document root. /// - /// This is kept for compatibility with older callers. It is not the default because unsigned + /// This is kept for compatibility with older callers. It is not recommended because unsigned /// ancestors can survive reduction in this mode. ValidateAndMark, /// Returns a rooted XML document containing only xmlsec-verified content. diff --git a/src/crypto/xmlsec/wrapper/xmldsig.rs b/src/crypto/xmlsec/wrapper/xmldsig.rs index 350104b..e706e7c 100644 --- a/src/crypto/xmlsec/wrapper/xmldsig.rs +++ b/src/crypto/xmlsec/wrapper/xmldsig.rs @@ -68,8 +68,7 @@ impl XmlSecSignatureContext { let uri = if ref_ctx.uri.is_null() { None } else { - let uri_cstr = - std::ffi::CStr::from_ptr(ref_ctx.uri as *const std::ffi::c_char); + let uri_cstr = std::ffi::CStr::from_ptr(ref_ctx.uri as *const std::ffi::c_char); Some( uri_cstr .to_str() @@ -85,8 +84,7 @@ impl XmlSecSignatureContext { let data_ptr = bindings::xmlSecBufferGetData(predigest_buf); let data_size = bindings::xmlSecBufferGetSize(predigest_buf); - let predigest_xml = - predigest_xml_from_raw_buffer(data_ptr, data_size as usize)?; + let predigest_xml = predigest_xml_from_raw_buffer(data_ptr, data_size as usize)?; result.push(VerifiedReference { uri, predigest_xml }); } diff --git a/src/idp/tests.rs b/src/idp/tests.rs index 8e40f4f..5bac54c 100644 --- a/src/idp/tests.rs +++ b/src/idp/tests.rs @@ -371,18 +371,18 @@ fn test_malicious_ancestors_not_included() { "/test_vectors/idp_2_metadata_public.pem" ))); - for (reduce_mode, result) in [ - (ReduceMode::PreDigest, true), - (ReduceMode::ValidateAndMark, true), - (ReduceMode::ValidateAndMarkNoAncestors, true), + for (reduce_mode, should_contain_attacker_url) in [ + (ReduceMode::PreDigest, false), + (ReduceMode::ValidateAndMark, false), + (ReduceMode::ValidateAndMarkNoAncestors, false), ] { let reduced = XmlSec::reduce_xml_to_signed(signed_xml, &[cert.clone()], reduce_mode) .expect("reduce_xml_to_signed should succeed"); assert_eq!( - !reduced.contains("https://attacker.evil.com"), - result, - "ancestor should not be contained in {reduce_mode:?}" + reduced.contains("https://attacker.evil.com"), + should_contain_attacker_url, + "Attacker URL containment mismatch for {reduce_mode:?}: expected {should_contain_attacker_url}" ); } } @@ -398,18 +398,18 @@ fn test_object_reference_removed() { "/test_vectors/idp_2_metadata_public.pem" ))); - for (reduce_mode, result) in [ - (ReduceMode::PreDigest, true), - (ReduceMode::ValidateAndMark, true), - (ReduceMode::ValidateAndMarkNoAncestors, true), + for (reduce_mode, should_contain_object) in [ + (ReduceMode::PreDigest, false), + (ReduceMode::ValidateAndMark, false), + (ReduceMode::ValidateAndMarkNoAncestors, false), ] { let reduced = XmlSec::reduce_xml_to_signed(signed_xml, &[cert.clone()], reduce_mode) .expect("reduce_xml_to_signed should succeed"); assert_eq!( - !reduced.contains("ds:Object"), - result, - "object should not be present in {reduce_mode:?}" + reduced.contains("ds:Object"), + should_contain_object, + "Object containment mismatch for {reduce_mode:?}: expected {should_contain_object}" ); } } @@ -448,18 +448,18 @@ fn test_xpath_transforms_validated() { "/test_vectors/idp_2_metadata_public.pem" ))); - for (reduce_mode, result) in [ - (ReduceMode::PreDigest, true), - (ReduceMode::ValidateAndMark, true), - (ReduceMode::ValidateAndMarkNoAncestors, true), + for (reduce_mode, should_contain_malicious) in [ + (ReduceMode::PreDigest, false), + (ReduceMode::ValidateAndMark, false), + (ReduceMode::ValidateAndMarkNoAncestors, false), ] { let reduced = XmlSec::reduce_xml_to_signed(signed_xml, &[cert.clone()], reduce_mode) .expect("reduce_xml_to_signed should succeed"); assert_eq!( - !reduced.contains("malicious"), - result, - "malicious content should not be present in {reduce_mode:?}" + reduced.contains("malicious"), + should_contain_malicious, + "Malicious content containment mismatch for {reduce_mode:?}: expected {should_contain_malicious}" ); } } diff --git a/src/service_provider/mod.rs b/src/service_provider/mod.rs index b057976..cd98f8c 100644 --- a/src/service_provider/mod.rs +++ b/src/service_provider/mod.rs @@ -364,10 +364,24 @@ impl ServiceProvider { &self, encoded_resp: &str, possible_request_ids: Option<&[&str]>, + ) -> Result> { + self.parse_base64_response_with_mode( + encoded_resp, + possible_request_ids, + ReduceMode::default(), + ) + } + + pub fn parse_base64_response_with_mode( + &self, + encoded_resp: &str, + possible_request_ids: Option<&[&str]>, + reduce_mode: ReduceMode, ) -> Result> { let bytes = general_purpose::STANDARD.decode(encoded_resp)?; let decoded = std::str::from_utf8(&bytes)?; - let assertion = self.parse_xml_response(decoded, possible_request_ids)?; + let assertion = + self.parse_xml_response_with_mode(decoded, possible_request_ids, reduce_mode)?; Ok(assertion) } @@ -376,11 +390,7 @@ impl ServiceProvider { response_xml: &str, possible_request_ids: Option<&[&str]>, ) -> Result { - self.parse_xml_response_with_mode( - response_xml, - possible_request_ids, - ReduceMode::ValidateAndMarkNoAncestors, - ) + self.parse_xml_response_with_mode(response_xml, possible_request_ids, ReduceMode::default()) } pub fn parse_xml_response_with_mode( From 56fb6ed6de41dc25ab60e66d977951facd49b801 Mon Sep 17 00:00:00 2001 From: Max Ammann Date: Thu, 2 Apr 2026 15:25:26 +0200 Subject: [PATCH 2/3] add test case for multi reference predigest mode --- src/crypto/xmlsec/mod.rs | 16 ++- src/service_provider/mod.rs | 2 +- src/service_provider/tests.rs | 29 +++++ test_vectors/cert.pem | 13 +++ test_vectors/multi_saml_response.sh | 6 + test_vectors/multi_saml_response_signed.xml | 92 +++++++++++++++ test_vectors/multi_saml_response_signed_2.xml | 105 ++++++++++++++++++ test_vectors/multi_saml_response_template.xml | 79 +++++++++++++ test_vectors/private.pem | 16 +++ test_vectors/public.pem | 6 + 10 files changed, 360 insertions(+), 4 deletions(-) create mode 100644 test_vectors/cert.pem create mode 100644 test_vectors/multi_saml_response.sh create mode 100644 test_vectors/multi_saml_response_signed.xml create mode 100644 test_vectors/multi_saml_response_signed_2.xml create mode 100644 test_vectors/multi_saml_response_template.xml create mode 100644 test_vectors/private.pem create mode 100644 test_vectors/public.pem diff --git a/src/crypto/xmlsec/mod.rs b/src/crypto/xmlsec/mod.rs index 7637f15..e2f9df0 100644 --- a/src/crypto/xmlsec/mod.rs +++ b/src/crypto/xmlsec/mod.rs @@ -208,10 +208,20 @@ impl super::CryptoProvider for XmlSec { } if reduce_mode == ReduceMode::PreDigest { - if predigest_results.len() != 1 { - return Err(CryptoError::InvalidSignature); + if predigest_results.len() == 1 { + return Ok(predigest_results.remove(0)); + } else { + for predigest in predigest_results { + return match crate::service_provider::root_element_local_name(&predigest) + .as_deref() + { + // We want to trust the full response as that includes the relevant data. + // Everything is signed so even if we found a signed Assertion in here we could trust it, for now lets keep it minimal. + Some("Response") => Ok(predigest), + _ => Err(CryptoError::InvalidSignature), + }; + } } - return Ok(predigest_results.remove(0)); } if selections.is_empty() { diff --git a/src/service_provider/mod.rs b/src/service_provider/mod.rs index cd98f8c..860ae9a 100644 --- a/src/service_provider/mod.rs +++ b/src/service_provider/mod.rs @@ -711,7 +711,7 @@ impl ServiceProvider { } } -fn root_element_local_name(xml: &str) -> Option { +pub(crate) fn root_element_local_name(xml: &str) -> Option { let mut reader = Reader::from_str(xml); loop { diff --git a/src/service_provider/tests.rs b/src/service_provider/tests.rs index a15211a..f630ae3 100644 --- a/src/service_provider/tests.rs +++ b/src/service_provider/tests.rs @@ -583,4 +583,33 @@ mod encrypted_assertion_tests { Error::AssertionSubjectConfirmationExpiredBefore { .. } )); } + + #[test] + fn test_parse_xml_response_with_empty_saml_response() { + let mut sp = create_predigest_assertion_sp_with_metadata("https://api.dev.zoo.dev/auth/saml/00000000-00000000-00000000-00000000/login", r#" + + + + + + MIIB+jCCAWOgAwIBAgIUdcXUPTE+mOSWxRCJh8ldmDMPzREwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNjA0MDIxMjQzMTNaFw0yNzA0MDIxMjQzMTNaMA8xDTALBgNVBAMMBFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJEBNDJKH5nXr0hZKcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVWCgxJp21BmKll0CkgmeKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQuBoQwgSiagVadWXwJKkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAGjUzBRMB0GA1UdDgQWBBSLoT4AEwcK1+0IMwgo6JYfA4e8ZTAfBgNVHSMEGDAWgBSLoT4AEwcK1+0IMwgo6JYfA4e8ZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAAtV1hclbZBD17LMbBwyrTj7szmmeUVISPeFEPaAKqiTXrHwRZ+akajboB2JjT3YYMXX2/eDaSvq9f20vJQUvkEAaYu8eNNDKWgm4btJFAeJT8uGxizmTspdJ0cxFSwxqaosV3qIqJgpwLbzUXEcu6mKfyqDM6AeFZdZevkxmKlE + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + +"#.parse().unwrap()); + sp.allow_idp_initiated = true; + let response_xml = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/test_vectors/multi_saml_response_signed_2.xml" + )); + + let assertion = sp + .parse_xml_response_with_mode(response_xml, None, ReduceMode::PreDigest) + .expect("signed assertion should parse in pre-digest mode"); + } } diff --git a/test_vectors/cert.pem b/test_vectors/cert.pem new file mode 100644 index 0000000..7aeb295 --- /dev/null +++ b/test_vectors/cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB+jCCAWOgAwIBAgIUdcXUPTE+mOSWxRCJh8ldmDMPzREwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNjA0MDIxMjQzMTNaFw0yNzA0MDIxMjQz +MTNaMA8xDTALBgNVBAMMBFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AJEBNDJKH5nXr0hZKcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVW +CgxJp21BmKll0CkgmeKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQu +BoQwgSiagVadWXwJKkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAGjUzBRMB0GA1Ud +DgQWBBSLoT4AEwcK1+0IMwgo6JYfA4e8ZTAfBgNVHSMEGDAWgBSLoT4AEwcK1+0I +Mwgo6JYfA4e8ZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAAtV +1hclbZBD17LMbBwyrTj7szmmeUVISPeFEPaAKqiTXrHwRZ+akajboB2JjT3YYMXX +2/eDaSvq9f20vJQUvkEAaYu8eNNDKWgm4btJFAeJT8uGxizmTspdJ0cxFSwxqaos +V3qIqJgpwLbzUXEcu6mKfyqDM6AeFZdZevkxmKlE +-----END CERTIFICATE----- diff --git a/test_vectors/multi_saml_response.sh b/test_vectors/multi_saml_response.sh new file mode 100644 index 0000000..6e4e101 --- /dev/null +++ b/test_vectors/multi_saml_response.sh @@ -0,0 +1,6 @@ +#!/bin/env bash + +openssl req -new -x509 -key test_vectors/private.der -inform DER -out test_vectors/cert.pem -days 365 -subj "/CN=Test" +openssl pkey -in test_vectors/private.der -inform DER -out test_vectors/private.pem +xmlsec1 --sign --privkey-pem:signing test_vectors/private.pem,test_vectors/cert.pem --id-attr:ID urn:oasis:names:tc:SAML:2.0:assertion:Assertion --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response --node-xpath "//*[local-name()='Assertion' and namespace-uri()='urn:oasis:names:tc:SAML:2.0:assertion']/*[local-name()='Signature' and namespace-uri()='http://www.w3.org/2000/09/xmldsig#']" --output test_vectors/multi_saml_response_signed.xml --verbose test_vectors/multi_saml_response_template.xml +xmlsec1 --sign --privkey-pem:signing test_vectors/private.pem,test_vectors/cert.pem --id-attr:ID urn:oasis:names:tc:SAML:2.0:assertion:Assertion --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response --node-xpath "/*[local-name()='Response' and namespace-uri()='urn:oasis:names:tc:SAML:2.0:protocol']/*[local-name()='Signature' and namespace-uri()='http://www.w3.org/2000/09/xmldsig#']" --output test_vectors/multi_saml_response_signed_2.xml --verbose test_vectors/multi_saml_response_signed.xml diff --git a/test_vectors/multi_saml_response_signed.xml b/test_vectors/multi_saml_response_signed.xml new file mode 100644 index 0000000..c22c450 --- /dev/null +++ b/test_vectors/multi_saml_response_signed.xml @@ -0,0 +1,92 @@ + + + https://some.idp.test/blah/ + + + + + + + + + + + + + + + + signing + + + + + + + + + + https://some.idp.test/blah/ + + + + + + + + + + + ptR4sQtcSeB51h3ggeXGv6dlvp3V3NEcvF33GhgdTlE= + + + SoTLlmi1vhoGBgwjlwFY4+CLkczw81PFDXOeK1vSlrC0eOhe89wc2Ve7fFCGe5N/ +L5luYt+QcWyoi7M3E48EuuxyYnJtznG6nLjezTUe2Rd1HvQwtDptmjyes3SD1o1/ +FM9UpYbnPAQgvdmfr/oM5R2xIxwhw7ZBDyPhk5HpwL8= + + signing + + MIIB+jCCAWOgAwIBAgIUdcXUPTE+mOSWxRCJh8ldmDMPzREwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNjA0MDIxMjQzMTNaFw0yNzA0MDIxMjQz +MTNaMA8xDTALBgNVBAMMBFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AJEBNDJKH5nXr0hZKcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVW +CgxJp21BmKll0CkgmeKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQu +BoQwgSiagVadWXwJKkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAGjUzBRMB0GA1Ud +DgQWBBSLoT4AEwcK1+0IMwgo6JYfA4e8ZTAfBgNVHSMEGDAWgBSLoT4AEwcK1+0I +Mwgo6JYfA4e8ZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAAtV +1hclbZBD17LMbBwyrTj7szmmeUVISPeFEPaAKqiTXrHwRZ+akajboB2JjT3YYMXX +2/eDaSvq9f20vJQUvkEAaYu8eNNDKWgm4btJFAeJT8uGxizmTspdJ0cxFSwxqaos +V3qIqJgpwLbzUXEcu6mKfyqDM6AeFZdZevkxmKlE + + + + + + some@customer.com + + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + + + + + thing@thing.com + + + + + Test + + + + + User + + + + diff --git a/test_vectors/multi_saml_response_signed_2.xml b/test_vectors/multi_saml_response_signed_2.xml new file mode 100644 index 0000000..e3c5c03 --- /dev/null +++ b/test_vectors/multi_saml_response_signed_2.xml @@ -0,0 +1,105 @@ + + + https://some.idp.test/blah/ + + + + + + + + + + + YsayJ9oT0o5FoLviXsIgpX+JQhv/jgcmzP/v0jbi99k= + + + dpZ+U8MkFUYgWa+sOPL+tyZtpnPsLDqNFcD5GxCgH+0Q4SSRvfUNxNFJr6N0tzl7 +SP3e3bmzSdW41YjLrOdLDrFVsdVAGdnJEOi9Bqs5joAOMjSonuG45swg7occyhlf +696d50Pjt3++yNpqJSU55/F4vILHbYLKaYunMWAet8Y= + + signing + + MIIB+jCCAWOgAwIBAgIUdcXUPTE+mOSWxRCJh8ldmDMPzREwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNjA0MDIxMjQzMTNaFw0yNzA0MDIxMjQz +MTNaMA8xDTALBgNVBAMMBFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AJEBNDJKH5nXr0hZKcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVW +CgxJp21BmKll0CkgmeKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQu +BoQwgSiagVadWXwJKkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAGjUzBRMB0GA1Ud +DgQWBBSLoT4AEwcK1+0IMwgo6JYfA4e8ZTAfBgNVHSMEGDAWgBSLoT4AEwcK1+0I +Mwgo6JYfA4e8ZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAAtV +1hclbZBD17LMbBwyrTj7szmmeUVISPeFEPaAKqiTXrHwRZ+akajboB2JjT3YYMXX +2/eDaSvq9f20vJQUvkEAaYu8eNNDKWgm4btJFAeJT8uGxizmTspdJ0cxFSwxqaos +V3qIqJgpwLbzUXEcu6mKfyqDM6AeFZdZevkxmKlE + + + + + + + + + https://some.idp.test/blah/ + + + + + + + + + + + ptR4sQtcSeB51h3ggeXGv6dlvp3V3NEcvF33GhgdTlE= + + + SoTLlmi1vhoGBgwjlwFY4+CLkczw81PFDXOeK1vSlrC0eOhe89wc2Ve7fFCGe5N/ +L5luYt+QcWyoi7M3E48EuuxyYnJtznG6nLjezTUe2Rd1HvQwtDptmjyes3SD1o1/ +FM9UpYbnPAQgvdmfr/oM5R2xIxwhw7ZBDyPhk5HpwL8= + + signing + + MIIB+jCCAWOgAwIBAgIUdcXUPTE+mOSWxRCJh8ldmDMPzREwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNjA0MDIxMjQzMTNaFw0yNzA0MDIxMjQz +MTNaMA8xDTALBgNVBAMMBFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AJEBNDJKH5nXr0hZKcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVW +CgxJp21BmKll0CkgmeKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQu +BoQwgSiagVadWXwJKkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAGjUzBRMB0GA1Ud +DgQWBBSLoT4AEwcK1+0IMwgo6JYfA4e8ZTAfBgNVHSMEGDAWgBSLoT4AEwcK1+0I +Mwgo6JYfA4e8ZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAAtV +1hclbZBD17LMbBwyrTj7szmmeUVISPeFEPaAKqiTXrHwRZ+akajboB2JjT3YYMXX +2/eDaSvq9f20vJQUvkEAaYu8eNNDKWgm4btJFAeJT8uGxizmTspdJ0cxFSwxqaos +V3qIqJgpwLbzUXEcu6mKfyqDM6AeFZdZevkxmKlE + + + + + + some@customer.com + + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + + + + + thing@thing.com + + + + + Test + + + + + User + + + + diff --git a/test_vectors/multi_saml_response_template.xml b/test_vectors/multi_saml_response_template.xml new file mode 100644 index 0000000..6b1ce56 --- /dev/null +++ b/test_vectors/multi_saml_response_template.xml @@ -0,0 +1,79 @@ + + + https://some.idp.test/blah/ + + + + + + + + + + + + + + + + signing + + + + + + + + + + https://some.idp.test/blah/ + + + + + + + + + + + + + + + + signing + + + + + + + some@customer.com + + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + + + + + thing@thing.com + + + + + Test + + + + + User + + + + diff --git a/test_vectors/private.pem b/test_vectors/private.pem new file mode 100644 index 0000000..16bb536 --- /dev/null +++ b/test_vectors/private.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJEBNDJKH5nXr0hZ +KcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVWCgxJp21BmKll0Ckg +meKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQuBoQwgSiagVadWXwJ +KkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAECgYBCXjaxvdNw6pNDnevMFnyA51wx +4OjQn6GidDkOr7bmPYp+7H3xpwkEWZfK22uPje3TafLDwv5Iqlz4Nf+5B3tAsz+5 +gwY53YMhz8pmKcnuvUxZdCq6gOLMvv8oi+e9mmp3Y3LPLk+nNLKnPpYu/WFkRSx5 +XRYq6RH/G/ik1+KbQQJBAOpLbk12RR1kylVkAlw4kg96L5C1iu2aV/2aKvaVX57y +nWwnFbvsNop+nPk0d40Q7P05mm/Fn+WsxzVltTVveC0CQQCecCiOhXsrIAUzbaWI +wkGJTqS6QXG7dVBM5Fjwh9XwrWdzUuqcgGQBSs9ZLH95jtZ6zGDn4kJ5JUf4doK2 +PO9VAkAGelduZNq8WVEO01kNW7MOIn62LY7Nyvqr1FtjvfWK24Jmvx3muD05zw5g +BYdYqyMiTTlr5bbqDsDjRRChjZdxAkEAmHPJwqHUC4ILHxpRXw+0af+/Z+1TF5Lh +iqtqnT/Hb4gbdA+D6sVr9QZcEC1OF9SGsrB4SogfeFyULRMz4VEu5QJBAKWfuetn +QcqN65eSvLnWDcUPvB1cjJKN/o5xP+XYb0pR9BVIaAGXkY8DyCQ7jSdFbWEIt/IN +53KPwi6sgHrv0f0= +-----END PRIVATE KEY----- diff --git a/test_vectors/public.pem b/test_vectors/public.pem new file mode 100644 index 0000000..8d1ad26 --- /dev/null +++ b/test_vectors/public.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRATQySh+Z169IWSnEjSGNZeB3 +mCzwRCiS18gJ6BU3YBq74uNGMiMfZR4dC2w1VgoMSadtQZipZdApIJnionbxpa9x +VMLRE66OOC/koJo4iTW37bxcYTZIANujthg0LgaEMIEomoFWnVl8CSpKHR/LceKI +wT2CtaQTvHx398Dp8QIDAQAB +-----END PUBLIC KEY----- From db7039e4010fcec952ec0c175f2db56dc2b7f402 Mon Sep 17 00:00:00 2001 From: Max Ammann Date: Wed, 8 Apr 2026 16:41:01 +0200 Subject: [PATCH 3/3] add test, showing that ValidateAndMark adds a response around assertions (should not happen) --- src/service_provider/tests.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/service_provider/tests.rs b/src/service_provider/tests.rs index f630ae3..2d0b634 100644 --- a/src/service_provider/tests.rs +++ b/src/service_provider/tests.rs @@ -412,6 +412,29 @@ mod encrypted_assertion_tests { ); } + // TODO: this should work, but it does not with ValidateAndMark + #[test] + fn test_validate_and_mark_only_assertion_signed() { + let sp = create_predigest_assertion_sp("http://sp.example.com/demo1/index.php?acs"); + let response_xml = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/test_vectors/response_signed_assertion.xml" + )); + + let assertion = sp + .parse_xml_response_with_mode( + &response_xml, + Some(&["ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"]), + ReduceMode::ValidateAndMark + ) + .unwrap(); + + assert_eq!( + assertion.issuer.value.as_deref(), + Some("http://idp.example.com/metadata.php") + ); + } + #[test] fn test_response_validation_requires_assertion_recipient_binding() { let sp = create_predigest_assertion_sp("http://sp.example.com/demo1/index.php?acs");