1use super::PolicyViolation;
4use crate::xmldsig::DigestAlgorithm;
5use std::collections::HashSet;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum KeyAgreementAlgorithm {
10 EcdhEs,
12 X25519,
14 DhEs,
16 LegacyDh,
18}
19
20impl KeyAgreementAlgorithm {
21 pub const fn uri(self) -> &'static str {
23 match self {
24 Self::EcdhEs => "http://www.w3.org/2009/xmlenc11#ECDH-ES",
25 Self::X25519 => "http://www.w3.org/2021/04/xmldsig-more#x25519",
26 Self::DhEs => "http://www.w3.org/2009/xmlenc11#dh-es",
27 Self::LegacyDh => "http://www.w3.org/2001/04/xmlenc#dh",
28 }
29 }
30
31 pub fn from_uri(uri: &str) -> Option<Self> {
33 [Self::EcdhEs, Self::X25519, Self::DhEs, Self::LegacyDh]
34 .into_iter()
35 .find(|algorithm| algorithm.uri() == uri)
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum KeyDerivationAlgorithm {
42 ConcatKdf,
44 Hkdf,
46 Pbkdf2,
48 LegacyDh,
50}
51
52impl KeyDerivationAlgorithm {
53 pub const fn uri(self) -> &'static str {
55 match self {
56 Self::ConcatKdf => "http://www.w3.org/2009/xmlenc11#ConcatKDF",
57 Self::Hkdf => "http://www.w3.org/2021/04/xmldsig-more#hkdf",
58 Self::Pbkdf2 => "http://www.w3.org/2009/xmlenc11#pbkdf2",
59 Self::LegacyDh => "http://www.w3.org/2001/04/xmlenc#dh",
60 }
61 }
62
63 pub fn from_uri(uri: &str) -> Option<Self> {
65 [Self::ConcatKdf, Self::Hkdf, Self::Pbkdf2, Self::LegacyDh]
66 .into_iter()
67 .find(|algorithm| algorithm.uri() == uri)
68 }
69}
70
71#[derive(Debug, Clone)]
77pub struct KeyEstablishmentPolicy {
78 pub agreement_algorithms: Option<HashSet<KeyAgreementAlgorithm>>,
80 pub derivation_algorithms: Option<HashSet<KeyDerivationAlgorithm>>,
82 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
85 pub max_hash_blocks: usize,
87 pub max_owned_bytes: usize,
91 pub max_dh_modulus_bits: usize,
93 pub minimum_dh_modulus_bits: usize,
96 pub minimum_dh_subgroup_bits: usize,
98 pub max_modular_work: usize,
101}
102
103impl Default for KeyEstablishmentPolicy {
104 fn default() -> Self {
105 Self {
106 agreement_algorithms: None,
107 derivation_algorithms: None,
108 digest_algorithms: None,
109 max_hash_blocks: 1_000_000,
110 max_owned_bytes: 8 * 1024 * 1024,
111 max_dh_modulus_bits: 8_192,
112 minimum_dh_modulus_bits: 2_048,
113 minimum_dh_subgroup_bits: 224,
114 max_modular_work: 64_000_000,
115 }
116 }
117}
118
119impl KeyEstablishmentPolicy {
120 pub fn validate(&self) -> Result<(), PolicyViolation> {
122 for (value, minimum, resource) in [
123 (
124 self.minimum_dh_modulus_bits,
125 512,
126 super::resource_name::DH_MODULUS_BITS,
127 ),
128 (
129 self.minimum_dh_subgroup_bits,
130 160,
131 super::resource_name::DH_SUBGROUP_BITS,
132 ),
133 ] {
134 if value < minimum || value > self.max_dh_modulus_bits {
135 return Err(PolicyViolation::InvalidResourceLimit {
136 resource,
137 actual: value,
138 requirement: "minimum must respect the wire floor and modulus maximum",
139 });
140 }
141 }
142 if self.minimum_dh_subgroup_bits >= self.max_dh_modulus_bits {
146 return Err(PolicyViolation::InvalidResourceLimit {
147 resource: super::resource_name::DH_SUBGROUP_BITS,
148 actual: self.minimum_dh_subgroup_bits,
149 requirement: "subgroup minimum must be strictly below modulus maximum",
150 });
151 }
152 super::ResourcePolicy::within(
153 super::resource_name::DH_MODULUS_BITS,
154 self.max_dh_modulus_bits,
155 crate::hard_limits::DH_MODULUS_BIT_CEILING,
156 )?;
157 super::ResourcePolicy::within(
158 super::resource_name::KEY_ESTABLISHMENT_MODULAR_WORK,
159 self.max_modular_work,
160 crate::hard_limits::KEY_ESTABLISHMENT_MODULAR_WORK_CEILING,
161 )?;
162 super::ResourcePolicy::within(
163 super::resource_name::KEY_ESTABLISHMENT_HASH_BLOCKS,
164 self.max_hash_blocks,
165 crate::hard_limits::KEY_ESTABLISHMENT_HASH_BLOCK_CEILING,
166 )?;
167 super::ResourcePolicy::within(
168 super::resource_name::KEY_ESTABLISHMENT_OWNED_BYTES,
169 self.max_owned_bytes,
170 crate::hard_limits::KEY_ESTABLISHMENT_BYTE_CEILING,
171 )
172 }
173
174 pub(crate) fn check_dh_domain(
176 &self,
177 p_bits: usize,
178 q_bits: usize,
179 ) -> Result<(), PolicyViolation> {
180 for (actual_bits, minimum_bits, key_type) in [
181 (p_bits, self.minimum_dh_modulus_bits, "DH modulus"),
182 (q_bits, self.minimum_dh_subgroup_bits, "DH subgroup"),
183 ] {
184 if actual_bits < minimum_bits || actual_bits > self.max_dh_modulus_bits {
185 return Err(PolicyViolation::KeySize {
186 operation: "key agreement",
187 key_type,
188 minimum_bits,
189 maximum_bits: self.max_dh_modulus_bits,
190 actual_bits,
191 });
192 }
193 }
194 Ok(())
195 }
196
197 pub fn check_agreement(&self, algorithm: KeyAgreementAlgorithm) -> Result<(), PolicyViolation> {
199 let allowed = match &self.agreement_algorithms {
200 Some(allowed) => allowed.contains(&algorithm),
201 None => matches!(
202 algorithm,
203 KeyAgreementAlgorithm::EcdhEs | KeyAgreementAlgorithm::X25519
204 ),
205 };
206 permission(allowed, "key agreement", algorithm.uri())
207 }
208
209 pub fn check_derivation(
212 &self,
213 algorithm: KeyDerivationAlgorithm,
214 digest: DigestAlgorithm,
215 ) -> Result<(), PolicyViolation> {
216 let allowed = match &self.derivation_algorithms {
217 Some(allowed) => allowed.contains(&algorithm),
218 None => algorithm != KeyDerivationAlgorithm::LegacyDh,
219 };
220 permission(allowed, "key derivation", algorithm.uri())?;
221 let allowed = match &self.digest_algorithms {
222 Some(allowed) => allowed.contains(&digest),
223 None => matches!(
224 digest,
225 DigestAlgorithm::Sha224
226 | DigestAlgorithm::Sha256
227 | DigestAlgorithm::Sha384
228 | DigestAlgorithm::Sha512
229 ),
230 };
231 permission(allowed, "key derivation digest", digest.uri())
232 }
233}
234
235fn permission(allowed: bool, operation: &'static str, uri: &str) -> Result<(), PolicyViolation> {
236 if allowed {
237 Ok(())
238 } else {
239 Err(PolicyViolation::Algorithm {
240 operation,
241 algorithm: uri.to_owned(),
242 })
243 }
244}