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 key_info
150 .preflight_agreement_role_candidates(child)
151 .map_err(map_key_info_error)?;
152 preflight_role_metadata(child, resources.max_encryption_metadata_bytes)?;
153 result.originator = Some(
154 key_info
155 .parse_agreement_role(child, provider)
156 .map_err(map_key_info_error)?,
157 );
158 phase = 2;
159 }
160 (Some(XMLENC_NS), "RecipientKeyInfo") if phase <= 2 => {
161 key_info
162 .preflight_agreement_role_candidates(child)
163 .map_err(map_key_info_error)?;
164 preflight_role_metadata(child, resources.max_encryption_metadata_bytes)?;
165 result.recipient = Some(
166 key_info
167 .parse_agreement_role(child, provider)
168 .map_err(map_key_info_error)?,
169 );
170 phase = 3;
171 }
172 (Some(namespace), _) if namespace != XMLENC_NS && phase <= 1 => {
173 if child.has_tag_name((XMLENC11_NS, "KeyDerivationMethod"))
176 || child.has_tag_name((XMLDSIG_NS, "DigestMethod"))
177 {
178 return Err(invalid("duplicate agreement parameter"));
179 }
180 phase = 1;
181 }
182 _ => return Err(invalid("unexpected or unordered AgreementMethod child")),
183 }
184 }
185 if algorithm == KeyAgreementAlgorithm::LegacyDh && result.method.is_some() {
186 return Err(invalid("legacy DH URI fixes the derivation algorithm"));
187 }
188 Ok(result)
189}
190
191fn invalid(message: &str) -> XmlEncError {
192 XmlEncError::InvalidStructure(message.into())
193}
194
195fn preflight_role_metadata(node: Node<'_, '_>, maximum: usize) -> Result<(), XmlEncError> {
196 for child in node.descendants().filter(Node::is_element) {
197 for attribute in child.attributes() {
198 validate_metadata_len(attribute.value().len(), maximum)?;
199 }
200 if child.children().any(|node| node.is_element()) {
201 continue;
202 }
203 let binary = matches!(
204 (child.tag_name().namespace(), child.tag_name().name()),
205 (
206 Some(XMLDSIG_NS),
207 "Modulus"
208 | "Exponent"
209 | "P"
210 | "Q"
211 | "G"
212 | "Y"
213 | "J"
214 | "Seed"
215 | "PgenCounter"
216 | "X509Certificate"
217 | "X509CRL"
218 | "X509SKI",
219 ) | (
220 Some(XMLENC_NS),
221 "P" | "Q" | "Generator" | "Public" | "seed" | "pgenCounter"
222 ) | (
223 Some(crate::xmldsig::parse::XMLDSIG11_NS),
224 "PublicKey" | "DEREncodedKeyValue" | "X509Digest",
225 )
226 );
227 if binary {
228 let payload = crate::xmldsig::whitespace::XmlBase64Payload::bounded(
229 child,
230 usize::MAX,
231 usize::MAX,
232 )
233 .map_err(|_| invalid("invalid agreement key binary metadata"))?;
234 validate_metadata_len(payload.decoded_len, maximum)?;
235 } else if child.tag_name().namespace() == Some(XMLDSIG_NS)
236 && matches!(
237 child.tag_name().name(),
238 "KeyName" | "X509SubjectName" | "X509IssuerName" | "X509SerialNumber" | "XPath"
239 )
240 {
241 let mut length = 0usize;
242 for text in child
243 .children()
244 .filter(Node::is_text)
245 .filter_map(|node| node.text())
246 {
247 length = length
248 .checked_add(text.len())
249 .ok_or_else(|| invalid("agreement metadata length overflow"))?;
250 validate_metadata_len(length, maximum)?;
251 }
252 }
253 }
254 Ok(())
255}
256
257pub(super) fn validate_role_metadata(info: &KeyInfo, maximum: usize) -> Result<(), XmlEncError> {
258 use crate::xmldsig::parse::{KeyInfoSource, KeyValueInfo, RetrievalMethodTransforms};
259 let check = |bytes: &[u8]| validate_metadata_len(bytes.len(), maximum);
260 let check_optional = |bytes: &Option<Vec<u8>>| check(bytes.as_deref().unwrap_or(&[]));
261 for source in &info.sources {
262 match source {
263 KeyInfoSource::KeyName(value) | KeyInfoSource::KeyInfoReference { uri: value } => {
264 check(value.as_bytes())?
265 }
266 KeyInfoSource::DerEncodedKeyValue(value) => check(value)?,
267 KeyInfoSource::KeyValue(value) => match value {
268 KeyValueInfo::Dh {
269 p,
270 q,
271 generator,
272 public,
273 seed,
274 pgen_counter,
275 } => {
276 for bytes in [p, q, generator, seed, pgen_counter] {
277 check_optional(bytes)?;
278 }
279 check(public)?;
280 }
281 KeyValueInfo::Dsa { p, q, g, y } => {
282 for bytes in [p, q, g] {
283 check_optional(bytes)?;
284 }
285 check(y)?;
286 }
287 KeyValueInfo::Rsa { modulus, exponent } => {
288 check(modulus)?;
289 check(exponent)?;
290 }
291 KeyValueInfo::Ec {
292 curve_oid,
293 public_key,
294 } => {
295 check(curve_oid.as_bytes())?;
296 check(public_key)?;
297 }
298 KeyValueInfo::Unsupported {
299 namespace,
300 local_name,
301 } => {
302 if let Some(namespace) = namespace {
303 check(namespace.as_bytes())?;
304 }
305 check(local_name.as_bytes())?;
306 }
307 KeyValueInfo::InvalidEcKeyValue => {}
308 },
309 KeyInfoSource::RetrievalMethod {
310 uri,
311 resource_type,
312 transforms,
313 } => {
314 check(uri.as_bytes())?;
315 if let Some(kind) = resource_type {
316 check(kind.as_bytes())?;
317 }
318 if let RetrievalMethodTransforms::X509DataNodeSetFilter {
319 expression,
320 namespaces,
321 } = transforms
322 {
323 check(expression.as_bytes())?;
324 for (prefix, uri) in namespaces {
325 check(prefix.as_bytes())?;
326 check(uri.as_bytes())?;
327 }
328 }
329 }
330 KeyInfoSource::X509Data(data) => {
331 for bytes in data.certificates.iter().chain(&data.crls).chain(&data.skis) {
332 check(bytes)?;
333 }
334 for value in &data.subject_names {
335 check(value.as_bytes())?;
336 }
337 for (issuer, serial) in &data.issuer_serials {
338 check(issuer.as_bytes())?;
339 check(serial.as_bytes())?;
340 }
341 for (algorithm, bytes) in &data.digests {
342 check(algorithm.as_bytes())?;
343 check(bytes)?;
344 }
345 }
346 }
347 }
348 Ok(())
349}
350
351pub(super) fn map_key_info_error(error: crate::xmldsig::parse::ParseError) -> XmlEncError {
352 match error {
353 crate::xmldsig::parse::ParseError::Policy(error) => XmlEncError::Policy(error),
354 crate::xmldsig::parse::ParseError::Provider(error) => XmlEncError::Provider(error),
355 error => invalid(&error.to_string()),
356 }
357}
358
359pub struct AgreementDecryptor<'a> {
362 expected: &'a AgreementMethod,
363 key: &'a dyn crate::provider::KeyAgreementKey,
364 peer: &'a [u8],
365 purpose: AgreementPurpose,
366}
367
368enum AgreementPurpose {
369 Content(super::DataEncryptionAlgorithm),
370 Wrapping(super::KeyWrapAlgorithm),
371}
372
373impl<'a> AgreementDecryptor<'a> {
374 pub fn content(
377 expected: &'a AgreementMethod,
378 key: &'a dyn crate::provider::KeyAgreementKey,
379 peer: &'a [u8],
380 algorithm: super::DataEncryptionAlgorithm,
381 ) -> Self {
382 Self {
383 expected,
384 key,
385 peer,
386 purpose: AgreementPurpose::Content(algorithm),
387 }
388 }
389
390 pub fn wrapping(
392 expected: &'a AgreementMethod,
393 key: &'a dyn crate::provider::KeyAgreementKey,
394 peer: &'a [u8],
395 algorithm: super::KeyWrapAlgorithm,
396 ) -> Self {
397 Self {
398 expected,
399 key,
400 peer,
401 purpose: AgreementPurpose::Wrapping(algorithm),
402 }
403 }
404
405 fn derive(
406 &self,
407 provider: &dyn crate::provider::CryptoProvider,
408 uri: &str,
409 width: usize,
410 policy: &crate::policy::DecryptionPolicy,
411 budget: &mut super::KeyCandidateBudget,
412 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
413 let agreement = KeyAgreementParameters {
414 algorithm: self.expected.algorithm.uri(),
415 peer_public_key: self.peer,
416 };
417 let parameters = self.expected.parameters(uri, width)?;
418 let mut secret =
419 budget.agree_and_derive(provider, policy, self.key, &agreement, ¶meters)?;
420 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
421 core::mem::take(&mut *secret),
422 )])
423 }
424}
425
426impl super::DecryptionKeyResolver for AgreementDecryptor<'_> {
427 fn resolve_key_encryption_keys_with_policy(
428 &self,
429 provider: &dyn crate::provider::CryptoProvider,
430 algorithm: super::KeyWrapAlgorithm,
431 source: super::KeyEncryptionKeySource<'_>,
432 policy: &crate::policy::DecryptionPolicy,
433 budget: &mut super::KeyCandidateBudget,
434 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
435 let super::KeyEncryptionKeySource::Agreement(descriptor) = source else {
436 return Err(XmlEncError::KeyNotFound);
437 };
438 if !matches!(self.purpose, AgreementPurpose::Wrapping(expected) if expected == algorithm)
439 || descriptor != self.expected
440 {
441 return Err(XmlEncError::KeyNotFound);
442 }
443 budget.require_available(3)?;
445 self.derive(
446 provider,
447 algorithm.uri(),
448 algorithm.key_len(),
449 policy,
450 budget,
451 )
452 }
453 fn resolve_agreement_content_keys_with_policy(
454 &self,
455 provider: &dyn crate::provider::CryptoProvider,
456 algorithm: super::DataEncryptionAlgorithm,
457 descriptor: &AgreementMethod,
458 policy: &crate::policy::DecryptionPolicy,
459 budget: &mut super::KeyCandidateBudget,
460 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
461 if !matches!(self.purpose, AgreementPurpose::Content(expected) if expected == algorithm)
462 || descriptor != self.expected
463 {
464 return Err(XmlEncError::KeyNotFound);
465 }
466 self.derive(
467 provider,
468 algorithm.uri(),
469 algorithm.key_len(),
470 policy,
471 budget,
472 )
473 }
474
475 fn resolve_key(
476 &self,
477 _provider: &dyn crate::provider::CryptoProvider,
478 _algorithm: super::DataEncryptionAlgorithm,
479 _encrypted_key: Option<&super::EncryptedKey>,
480 ) -> Result<Vec<u8>, XmlEncError> {
481 Err(XmlEncError::KeyNotFound)
484 }
485}