1use crate::policy::{KeyAgreementAlgorithm, ResourcePolicy};
4use crate::provider::{KdfContext, KdfParameters, KeyAgreementParameters};
5use crate::xml::dom::Node;
6use crate::xmldsig::parse::{KeyInfo, KeyInfoParsingSession};
7
8use super::parse::{decode_bounded_base64_text, require_element, validate_metadata_len};
9use super::types::{XMLDSIG_NS, XMLENC_NS, XMLENC11_NS};
10use super::{KeyDerivationMethod, XmlEncError};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct AgreementMethod {
16 pub algorithm: KeyAgreementAlgorithm,
18 pub method: Option<KeyDerivationMethod>,
21 pub nonce: Vec<u8>,
25 pub legacy_digest: Option<String>,
27 pub originator: Option<KeyInfo>,
29 pub recipient: Option<KeyInfo>,
31}
32
33impl AgreementMethod {
34 pub fn derive_key(
37 &self,
38 consuming_algorithm: &str,
39 output_len: usize,
40 key: &dyn crate::provider::KeyAgreementKey,
41 peer: &[u8],
42 provider: &dyn crate::provider::CryptoProvider,
43 budget: &mut super::KeyEstablishmentBudget<'_>,
44 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
45 let agreement = KeyAgreementParameters {
46 algorithm: self.algorithm.uri(),
47 peer_public_key: peer,
48 };
49 let parameters = self.parameters(consuming_algorithm, output_len)?;
50 budget.agree_and_derive(provider, key, &agreement, ¶meters)
51 }
52
53 pub(super) fn parameters<'a>(
54 &'a self,
55 consuming_algorithm: &'a str,
56 output_len: usize,
57 ) -> Result<KdfParameters<'a>, XmlEncError> {
58 if self.algorithm == KeyAgreementAlgorithm::LegacyDh {
59 return Ok(KdfParameters {
63 algorithm: self.algorithm.uri(),
64 digest: self.legacy_digest.as_deref(),
65 salt: &[],
66 info: KdfContext::LegacyDh {
67 encryption_algorithm: consuming_algorithm,
68 nonce: &self.nonce,
69 },
70 iterations: 0,
71 output_len,
72 });
73 }
74 self.method
81 .as_ref()
82 .ok_or(XmlEncError::MissingRequired(
83 "agreement key derivation method",
84 ))?
85 .parameters(output_len)
86 }
87}
88
89pub(super) fn parse(
90 node: Node<'_, '_>,
91 resources: &ResourcePolicy,
92 key_info: &mut KeyInfoParsingSession<'_>,
93 provider: &dyn crate::provider::CryptoProvider,
94) -> Result<AgreementMethod, XmlEncError> {
95 require_element(node, XMLENC_NS, "AgreementMethod")?;
96 let algorithm = node
97 .attribute("Algorithm")
98 .ok_or(XmlEncError::MissingRequired("AgreementMethod Algorithm"))?;
99 validate_metadata_len(algorithm.len(), resources.max_encryption_metadata_bytes)?;
100 let algorithm = KeyAgreementAlgorithm::from_uri(algorithm)
101 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(algorithm.into()))?;
102 let mut result = AgreementMethod {
103 algorithm,
104 method: None,
105 nonce: Vec::new(),
106 legacy_digest: None,
107 originator: None,
108 recipient: None,
109 };
110 let mut phase = 0;
111 let mut nonce_seen = false;
112 for child in node.children().filter(Node::is_element) {
117 match (child.tag_name().namespace(), child.tag_name().name()) {
118 (Some(XMLENC_NS), "KA-Nonce") if phase == 0 && !nonce_seen => {
119 result.nonce = decode_bounded_base64_text(
120 child,
121 "KA-Nonce",
122 resources.max_encryption_metadata_bytes,
123 )?;
124 nonce_seen = true;
125 }
126 (Some(XMLENC11_NS), "KeyDerivationMethod") if phase <= 1 && result.method.is_none() => {
127 result.method = Some(super::key_derivation::parse_node(
128 child,
129 resources.max_encryption_metadata_bytes,
130 )?);
131 phase = 1;
132 }
133 (Some(XMLDSIG_NS), "DigestMethod") if phase <= 1 && result.legacy_digest.is_none() => {
134 if algorithm != KeyAgreementAlgorithm::LegacyDh
135 || child.children().any(|node| node.is_element())
136 {
137 return Err(invalid(
138 "DigestMethod is only defined for legacy DH agreement",
139 ));
140 }
141 let digest = child
142 .attribute("Algorithm")
143 .ok_or(XmlEncError::MissingRequired("DigestMethod Algorithm"))?;
144 validate_metadata_len(digest.len(), resources.max_encryption_metadata_bytes)?;
145 result.legacy_digest = Some(digest.into());
146 phase = 1;
147 }
148 (Some(XMLENC_NS), "OriginatorKeyInfo") if phase <= 1 => {
149 preflight_role_metadata(child, resources.max_encryption_metadata_bytes)?;
150 result.originator = Some(
151 key_info
152 .parse_agreement_role(child, provider)
153 .map_err(map_key_info_error)?,
154 );
155 phase = 2;
156 }
157 (Some(XMLENC_NS), "RecipientKeyInfo") if phase <= 2 => {
158 preflight_role_metadata(child, resources.max_encryption_metadata_bytes)?;
159 result.recipient = Some(
160 key_info
161 .parse_agreement_role(child, provider)
162 .map_err(map_key_info_error)?,
163 );
164 phase = 3;
165 }
166 (Some(namespace), _) if namespace != XMLENC_NS && phase <= 1 => {
167 if child.has_tag_name((XMLENC11_NS, "KeyDerivationMethod"))
170 || child.has_tag_name((XMLDSIG_NS, "DigestMethod"))
171 {
172 return Err(invalid("duplicate agreement parameter"));
173 }
174 phase = 1;
175 }
176 _ => return Err(invalid("unexpected or unordered AgreementMethod child")),
177 }
178 }
179 if algorithm == KeyAgreementAlgorithm::LegacyDh && result.method.is_some() {
180 return Err(invalid("legacy DH URI fixes the derivation algorithm"));
181 }
182 Ok(result)
183}
184
185fn invalid(message: &str) -> XmlEncError {
186 XmlEncError::InvalidStructure(message.into())
187}
188
189fn preflight_role_metadata(node: Node<'_, '_>, maximum: usize) -> Result<(), XmlEncError> {
190 for child in node.descendants().filter(Node::is_element) {
191 for attribute in child.attributes() {
192 validate_metadata_len(attribute.value().len(), maximum)?;
193 }
194 if child.children().any(|node| node.is_element()) {
195 continue;
196 }
197 let binary = matches!(
198 (child.tag_name().namespace(), child.tag_name().name()),
199 (
200 Some(XMLDSIG_NS),
201 "Modulus"
202 | "Exponent"
203 | "P"
204 | "Q"
205 | "G"
206 | "Y"
207 | "J"
208 | "Seed"
209 | "PgenCounter"
210 | "X509Certificate"
211 | "X509CRL"
212 | "X509SKI",
213 ) | (
214 Some(XMLENC_NS),
215 "P" | "Q" | "Generator" | "Public" | "seed" | "pgenCounter"
216 ) | (
217 Some(crate::xmldsig::parse::XMLDSIG11_NS),
218 "PublicKey" | "DEREncodedKeyValue" | "X509Digest",
219 )
220 );
221 if binary {
222 let payload = crate::xmldsig::whitespace::XmlBase64Payload::bounded(
223 child,
224 usize::MAX,
225 usize::MAX,
226 )
227 .map_err(|_| invalid("invalid agreement key binary metadata"))?;
228 validate_metadata_len(payload.decoded_len, maximum)?;
229 } else if child.tag_name().namespace() == Some(XMLDSIG_NS)
230 && matches!(
231 child.tag_name().name(),
232 "KeyName" | "X509SubjectName" | "X509IssuerName" | "X509SerialNumber" | "XPath"
233 )
234 {
235 let mut length = 0usize;
236 for text in child
237 .children()
238 .filter(Node::is_text)
239 .filter_map(|node| node.text())
240 {
241 length = length
242 .checked_add(text.len())
243 .ok_or_else(|| invalid("agreement metadata length overflow"))?;
244 validate_metadata_len(length, maximum)?;
245 }
246 }
247 }
248 Ok(())
249}
250
251pub(super) fn validate_role_metadata(info: &KeyInfo, maximum: usize) -> Result<(), XmlEncError> {
252 use crate::xmldsig::parse::{KeyInfoSource, KeyValueInfo, RetrievalMethodTransforms};
253 let check = |bytes: &[u8]| validate_metadata_len(bytes.len(), maximum);
254 let check_optional = |bytes: &Option<Vec<u8>>| check(bytes.as_deref().unwrap_or(&[]));
255 for source in &info.sources {
256 match source {
257 KeyInfoSource::KeyName(value) | KeyInfoSource::KeyInfoReference { uri: value } => {
258 check(value.as_bytes())?
259 }
260 KeyInfoSource::DerEncodedKeyValue(value) => check(value)?,
261 KeyInfoSource::KeyValue(value) => match value {
262 KeyValueInfo::Dh {
263 p,
264 q,
265 generator,
266 public,
267 seed,
268 pgen_counter,
269 } => {
270 for bytes in [p, q, generator, seed, pgen_counter] {
271 check_optional(bytes)?;
272 }
273 check(public)?;
274 }
275 KeyValueInfo::Dsa { p, q, g, y } => {
276 for bytes in [p, q, g] {
277 check_optional(bytes)?;
278 }
279 check(y)?;
280 }
281 KeyValueInfo::Rsa { modulus, exponent } => {
282 check(modulus)?;
283 check(exponent)?;
284 }
285 KeyValueInfo::Ec {
286 curve_oid,
287 public_key,
288 } => {
289 check(curve_oid.as_bytes())?;
290 check(public_key)?;
291 }
292 KeyValueInfo::Unsupported {
293 namespace,
294 local_name,
295 } => {
296 if let Some(namespace) = namespace {
297 check(namespace.as_bytes())?;
298 }
299 check(local_name.as_bytes())?;
300 }
301 KeyValueInfo::InvalidEcKeyValue => {}
302 },
303 KeyInfoSource::RetrievalMethod {
304 uri,
305 resource_type,
306 transforms,
307 } => {
308 check(uri.as_bytes())?;
309 if let Some(kind) = resource_type {
310 check(kind.as_bytes())?;
311 }
312 if let RetrievalMethodTransforms::X509DataNodeSetFilter {
313 expression,
314 namespaces,
315 } = transforms
316 {
317 check(expression.as_bytes())?;
318 for (prefix, uri) in namespaces {
319 check(prefix.as_bytes())?;
320 check(uri.as_bytes())?;
321 }
322 }
323 }
324 KeyInfoSource::X509Data(data) => {
325 for bytes in data.certificates.iter().chain(&data.crls).chain(&data.skis) {
326 check(bytes)?;
327 }
328 for value in &data.subject_names {
329 check(value.as_bytes())?;
330 }
331 for (issuer, serial) in &data.issuer_serials {
332 check(issuer.as_bytes())?;
333 check(serial.as_bytes())?;
334 }
335 for (algorithm, bytes) in &data.digests {
336 check(algorithm.as_bytes())?;
337 check(bytes)?;
338 }
339 }
340 }
341 }
342 Ok(())
343}
344
345pub(super) fn map_key_info_error(error: crate::xmldsig::parse::ParseError) -> XmlEncError {
346 match error {
347 crate::xmldsig::parse::ParseError::Policy(error) => XmlEncError::Policy(error),
348 crate::xmldsig::parse::ParseError::Provider(error) => XmlEncError::Provider(error),
349 error => invalid(&error.to_string()),
350 }
351}
352
353pub struct AgreementDecryptor<'a> {
356 expected: &'a AgreementMethod,
357 key: &'a dyn crate::provider::KeyAgreementKey,
358 peer: &'a [u8],
359 purpose: AgreementPurpose,
360}
361
362enum AgreementPurpose {
363 Content(super::DataEncryptionAlgorithm),
364 Wrapping(super::KeyWrapAlgorithm),
365}
366
367impl<'a> AgreementDecryptor<'a> {
368 pub fn content(
371 expected: &'a AgreementMethod,
372 key: &'a dyn crate::provider::KeyAgreementKey,
373 peer: &'a [u8],
374 algorithm: super::DataEncryptionAlgorithm,
375 ) -> Self {
376 Self {
377 expected,
378 key,
379 peer,
380 purpose: AgreementPurpose::Content(algorithm),
381 }
382 }
383
384 pub fn wrapping(
386 expected: &'a AgreementMethod,
387 key: &'a dyn crate::provider::KeyAgreementKey,
388 peer: &'a [u8],
389 algorithm: super::KeyWrapAlgorithm,
390 ) -> Self {
391 Self {
392 expected,
393 key,
394 peer,
395 purpose: AgreementPurpose::Wrapping(algorithm),
396 }
397 }
398
399 fn derive(
400 &self,
401 provider: &dyn crate::provider::CryptoProvider,
402 uri: &str,
403 width: usize,
404 policy: &crate::policy::DecryptionPolicy,
405 budget: &mut super::KeyCandidateBudget,
406 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
407 let agreement = KeyAgreementParameters {
408 algorithm: self.expected.algorithm.uri(),
409 peer_public_key: self.peer,
410 };
411 let parameters = self.expected.parameters(uri, width)?;
412 let mut secret =
413 budget.agree_and_derive(provider, policy, self.key, &agreement, ¶meters)?;
414 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
415 core::mem::take(&mut *secret),
416 )])
417 }
418}
419
420impl super::DecryptionKeyResolver for AgreementDecryptor<'_> {
421 fn resolve_key_encryption_keys_with_policy(
422 &self,
423 provider: &dyn crate::provider::CryptoProvider,
424 algorithm: super::KeyWrapAlgorithm,
425 source: super::KeyEncryptionKeySource<'_>,
426 policy: &crate::policy::DecryptionPolicy,
427 budget: &mut super::KeyCandidateBudget,
428 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
429 let super::KeyEncryptionKeySource::Agreement(descriptor) = source else {
430 return Err(XmlEncError::KeyNotFound);
431 };
432 if !matches!(self.purpose, AgreementPurpose::Wrapping(expected) if expected == algorithm)
433 || descriptor != self.expected
434 {
435 return Err(XmlEncError::KeyNotFound);
436 }
437 budget.require_available(3)?;
439 self.derive(
440 provider,
441 algorithm.uri(),
442 algorithm.key_len(),
443 policy,
444 budget,
445 )
446 }
447 fn resolve_agreement_content_keys_with_policy(
448 &self,
449 provider: &dyn crate::provider::CryptoProvider,
450 algorithm: super::DataEncryptionAlgorithm,
451 descriptor: &AgreementMethod,
452 policy: &crate::policy::DecryptionPolicy,
453 budget: &mut super::KeyCandidateBudget,
454 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
455 if !matches!(self.purpose, AgreementPurpose::Content(expected) if expected == algorithm)
456 || descriptor != self.expected
457 {
458 return Err(XmlEncError::KeyNotFound);
459 }
460 self.derive(
461 provider,
462 algorithm.uri(),
463 algorithm.key_len(),
464 policy,
465 budget,
466 )
467 }
468
469 fn resolve_key(
470 &self,
471 _provider: &dyn crate::provider::CryptoProvider,
472 _algorithm: super::DataEncryptionAlgorithm,
473 _encrypted_key: Option<&super::EncryptedKey>,
474 ) -> Result<Vec<u8>, XmlEncError> {
475 Err(XmlEncError::KeyNotFound)
478 }
479}