zerodds_security_permissions/
signature.rs1use alloc::string::String;
26use alloc::vec::Vec;
27
28use crate::xml::{Permissions, PermissionsError, parse_permissions_xml};
29
30pub trait XmlSignatureVerifier {
32 fn verify_and_extract(&self, signed_doc: &[u8]) -> Result<Vec<u8>, PermissionsError>;
47}
48
49pub struct NoOpVerifier;
53
54impl XmlSignatureVerifier for NoOpVerifier {
55 fn verify_and_extract(&self, signed_doc: &[u8]) -> Result<Vec<u8>, PermissionsError> {
56 Ok(signed_doc.to_vec())
57 }
58}
59
60pub struct EnvelopeCheckVerifier;
67
68impl XmlSignatureVerifier for EnvelopeCheckVerifier {
69 fn verify_and_extract(&self, signed_doc: &[u8]) -> Result<Vec<u8>, PermissionsError> {
70 const BEGIN: &str = "-----BEGIN SIGNED-XML-----\n";
71 const END: &str = "\n-----END SIGNED-XML-----";
72 let s = core::str::from_utf8(signed_doc)
73 .map_err(|_| PermissionsError::Malformed("signed-xml ist kein UTF-8".into()))?;
74 let body = s
75 .strip_prefix(BEGIN)
76 .and_then(|rest| rest.strip_suffix(END))
77 .ok_or_else(|| {
78 PermissionsError::Malformed(String::from(
79 "signed-xml: envelope BEGIN/END fehlt oder ist fehlerhaft",
80 ))
81 })?;
82 Ok(body.as_bytes().to_vec())
83 }
84}
85
86pub fn open_signed_permissions<V: XmlSignatureVerifier>(
91 signed_doc: &[u8],
92 verifier: &V,
93) -> Result<Permissions, PermissionsError> {
94 let inner = verifier.verify_and_extract(signed_doc)?;
95 let xml = core::str::from_utf8(&inner)
96 .map_err(|_| PermissionsError::Malformed("verified XML ist kein UTF-8".into()))?;
97 parse_permissions_xml(xml)
98}
99
100#[cfg(test)]
101#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
102mod tests {
103 use super::*;
104
105 const RAW_XML: &str = r#"
106<permissions>
107 <grant><subject_name>CN=alice</subject_name>
108 <allow_rule><publish><topic>T</topic></publish></allow_rule>
109 </grant>
110</permissions>
111"#;
112
113 #[test]
114 fn noop_verifier_passes_through() {
115 let perms = open_signed_permissions(RAW_XML.as_bytes(), &NoOpVerifier).unwrap();
116 assert_eq!(perms.grants.len(), 1);
117 }
118
119 #[test]
120 fn envelope_verifier_extracts_inner_xml() {
121 let wrapped =
122 alloc::format!("-----BEGIN SIGNED-XML-----\n{RAW_XML}\n-----END SIGNED-XML-----");
123 let perms = open_signed_permissions(wrapped.as_bytes(), &EnvelopeCheckVerifier).unwrap();
124 assert_eq!(perms.grants.len(), 1);
125 assert_eq!(perms.grants[0].subject_name, "CN=alice");
126 }
127
128 #[test]
129 fn envelope_verifier_rejects_missing_begin() {
130 let bad = b"no envelope here";
131 let err = open_signed_permissions(bad, &EnvelopeCheckVerifier).unwrap_err();
132 assert!(matches!(err, PermissionsError::Malformed(_)));
133 }
134
135 #[test]
136 fn envelope_verifier_rejects_missing_end() {
137 let bad = b"-----BEGIN SIGNED-XML-----\n<permissions/>\n";
138 let err = open_signed_permissions(bad, &EnvelopeCheckVerifier).unwrap_err();
139 assert!(matches!(err, PermissionsError::Malformed(_)));
140 }
141
142 #[test]
143 fn verifier_failure_propagates_malformed() {
144 struct AlwaysFail;
145 impl XmlSignatureVerifier for AlwaysFail {
146 fn verify_and_extract(&self, _doc: &[u8]) -> Result<Vec<u8>, PermissionsError> {
147 Err(PermissionsError::Malformed("signature mismatch".into()))
148 }
149 }
150 let err = open_signed_permissions(RAW_XML.as_bytes(), &AlwaysFail).unwrap_err();
151 assert!(matches!(err, PermissionsError::Malformed(m) if m.contains("mismatch")));
152 }
153
154 #[test]
155 fn non_utf8_inner_is_rejected() {
156 struct BinaryVerifier;
157 impl XmlSignatureVerifier for BinaryVerifier {
158 fn verify_and_extract(&self, _doc: &[u8]) -> Result<Vec<u8>, PermissionsError> {
159 Ok(vec![0xff, 0xfe, 0x00]) }
161 }
162 let err = open_signed_permissions(b"", &BinaryVerifier).unwrap_err();
163 assert!(matches!(err, PermissionsError::Malformed(_)));
164 }
165}