Skip to main content

xml_sec/xmlenc/
encapsulation.rs

1//! Explicit recipient key for the experimental libxmlsec1 KEM protocol.
2
3use super::{
4    DataEncryptionAlgorithm, DecryptionKeyResolver, KeyCandidateBudget, KeyEncryptionKeySource,
5    KeyWrapAlgorithm, XmlEncError,
6};
7use crate::provider::{CryptoProvider, KeyDecapsulationKey, RecoveredContentKey};
8
9/// Recipient-owned private handle; no key is derived from untrusted hints.
10pub struct EncapsulationDecryptor<'a> {
11    key: RecipientKey<'a>,
12}
13
14enum RecipientKey<'a> {
15    Borrowed(&'a dyn KeyDecapsulationKey),
16    Owned(std::sync::Arc<dyn KeyDecapsulationKey>),
17}
18
19impl RecipientKey<'_> {
20    fn as_ref(&self) -> &dyn KeyDecapsulationKey {
21        match self {
22            Self::Borrowed(key) => *key,
23            Self::Owned(key) => key.as_ref(),
24        }
25    }
26}
27
28impl<'a> EncapsulationDecryptor<'a> {
29    /// Bind one explicit trusted recipient key for content or nested KEK recovery.
30    pub fn new(key: &'a dyn KeyDecapsulationKey) -> Self {
31        Self {
32            key: RecipientKey::Borrowed(key),
33        }
34    }
35
36    /// Retain a provider-owned handle selected from an inventory.
37    pub fn provider_key(key: std::sync::Arc<dyn KeyDecapsulationKey>) -> Self {
38        Self {
39            key: RecipientKey::Owned(key),
40        }
41    }
42
43    fn recover(
44        &self,
45        provider: &dyn CryptoProvider,
46        width: usize,
47        descriptor: &crate::key_establishment::EncapsulationMechanism,
48        policy: &crate::policy::DecryptionPolicy,
49        budget: &mut KeyCandidateBudget,
50    ) -> Result<Vec<RecoveredContentKey>, XmlEncError> {
51        if width == 0 || width > 32 {
52            return Err(crate::key_establishment::KeyEstablishmentError::Structure(
53                "consuming key exceeds KEM secret width",
54            )
55            .into());
56        }
57        budget.consume(1)?;
58        budget.key_establishment.commit_all(
59            &policy.key_establishment,
60            0,
61            0,
62            (width + core::mem::size_of::<RecoveredContentKey>()) as u128,
63        )?;
64        let secret = budget.key_establishment.decapsulate(
65            &policy.key_establishment,
66            provider,
67            self.key.as_ref(),
68            descriptor.algorithm,
69            &descriptor.ciphertext,
70        )?;
71        // libxmlsec1 1.3.13's experimental extension supplies the first requested
72        // octets directly (no KDF). This is interoperability, not RFC 9935 framing.
73        // https://github.com/lsh123/xmlsec/blob/xmlsec-1_3_13/src/xmlenc.c
74        // FIPS 203 section 7.3 returns the real-or-rejection secret without a
75        // validity bit. This candidate does not attest ciphertext validity;
76        // the consuming content cipher/key wrap must still verify integrity,
77        // unless check_kem_content explicitly delegates it to the caller.
78        // https://doi.org/10.6028/NIST.FIPS.203
79        Ok(vec![RecoveredContentKey::confirmed(
80            secret[..width].to_vec(),
81        )])
82    }
83}
84
85impl DecryptionKeyResolver for EncapsulationDecryptor<'_> {
86    fn resolve_key(
87        &self,
88        _: &dyn CryptoProvider,
89        _: DataEncryptionAlgorithm,
90        _: Option<&super::EncryptedKey>,
91    ) -> Result<Vec<u8>, XmlEncError> {
92        Err(XmlEncError::KeyNotFound)
93    }
94
95    fn resolve_encapsulation_content_keys_with_policy(
96        &self,
97        provider: &dyn CryptoProvider,
98        algorithm: DataEncryptionAlgorithm,
99        descriptor: &crate::key_establishment::EncapsulationMechanism,
100        policy: &crate::policy::DecryptionPolicy,
101        budget: &mut KeyCandidateBudget,
102    ) -> Result<Vec<RecoveredContentKey>, XmlEncError> {
103        policy.key_establishment.check_kem_content(algorithm)?;
104        self.recover(provider, algorithm.key_len(), descriptor, policy, budget)
105    }
106
107    fn resolve_key_encryption_keys_with_policy(
108        &self,
109        provider: &dyn CryptoProvider,
110        algorithm: KeyWrapAlgorithm,
111        source: KeyEncryptionKeySource<'_>,
112        policy: &crate::policy::DecryptionPolicy,
113        budget: &mut KeyCandidateBudget,
114    ) -> Result<Vec<RecoveredContentKey>, XmlEncError> {
115        let KeyEncryptionKeySource::Encapsulation(descriptor) = source else {
116            return Err(XmlEncError::KeyNotFound);
117        };
118        self.recover(provider, algorithm.key_len(), descriptor, policy, budget)
119    }
120}