1use crate::policy::{
4 KeyAgreementAlgorithm, KeyDerivationAlgorithm, KeyEstablishmentPolicy, PolicyViolation,
5};
6use crate::provider::{
7 CryptoProvider, KdfContext, KdfParameters, KeyAgreementKey, KeyAgreementParameters,
8 ProviderCapability, ProviderError, ProviderInputError, ProviderOperation,
9};
10use crate::xmldsig::{DigestAlgorithm, SignatureAlgorithm};
11
12use super::XmlEncError;
13
14pub struct KeyEstablishmentBudget<'a> {
20 policy: &'a KeyEstablishmentPolicy,
21 usage: KeyEstablishmentUsage,
22}
23
24#[derive(Debug, Default)]
27pub(super) struct KeyEstablishmentUsage {
28 hash_blocks: usize,
29 owned_bytes: usize,
30 modular_work: usize,
31}
32
33impl<'a> KeyEstablishmentBudget<'a> {
34 pub fn new(policy: &'a KeyEstablishmentPolicy) -> Result<Self, PolicyViolation> {
36 policy.validate()?;
37 Ok(Self {
38 policy,
39 usage: KeyEstablishmentUsage::default(),
40 })
41 }
42
43 pub fn derive_key(
46 &mut self,
47 provider: &dyn CryptoProvider,
48 parameters: &KdfParameters<'_>,
49 secret: &[u8],
50 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
51 self.usage
52 .derive_key(self.policy, provider, parameters, secret)
53 }
54
55 pub fn agree_and_derive(
58 &mut self,
59 provider: &dyn CryptoProvider,
60 key: &dyn KeyAgreementKey,
61 agreement: &KeyAgreementParameters<'_>,
62 parameters: &KdfParameters<'_>,
63 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
64 self.usage
65 .agree_and_derive(self.policy, provider, key, agreement, parameters)
66 }
67
68 pub const fn hash_blocks(&self) -> usize {
70 self.usage.hash_blocks
71 }
72
73 pub const fn owned_bytes(&self) -> usize {
75 self.usage.owned_bytes
76 }
77
78 pub const fn modular_work(&self) -> usize {
80 self.usage.modular_work
81 }
82
83 pub(crate) fn reserve_dh_import(
84 &mut self,
85 p_bits: usize,
86 q_bits: usize,
87 p_twos: usize,
88 q_twos: usize,
89 ) -> Result<(), XmlEncError> {
90 self.policy.validate()?;
91 if self
92 .policy
93 .check_agreement(KeyAgreementAlgorithm::DhEs)
94 .is_err()
95 {
96 self.policy
97 .check_agreement(KeyAgreementAlgorithm::LegacyDh)?;
98 }
99 self.policy.check_dh_domain(p_bits, q_bits)?;
100 reserve(
101 crate::policy::resource_name::DH_MODULUS_BITS,
102 0,
103 p_bits as u128,
104 self.policy.max_dh_modulus_bits,
105 )?;
106 let bytes = |bits: usize, twos: usize| {
111 let slot = bits.div_ceil(64) as u128 * 8 + 64;
112 slot * (512 + 64 * (64 + twos as u128))
113 };
114 self.usage.commit_dh_reservation(
115 self.policy,
116 modular_cost(p_bits, p_bits, 64)
117 + modular_cost(q_bits, q_bits, 64)
118 + modular_cost(p_bits, p_bits, 2),
119 bytes(p_bits, p_twos) + bytes(q_bits, q_twos),
120 )
121 }
122}
123
124fn modular_cost(bits: usize, exponent_bits: usize, count: usize) -> u128 {
125 let limbs = bits.div_ceil(usize::BITS as usize) as u128;
126 (limbs * limbs).div_ceil(64) * (exponent_bits as u128 + 64) * 8 * count as u128
130}
131
132impl KeyEstablishmentUsage {
133 pub(super) fn agree_and_derive(
134 &mut self,
135 policy: &KeyEstablishmentPolicy,
136 provider: &dyn CryptoProvider,
137 key: &dyn KeyAgreementKey,
138 agreement: &KeyAgreementParameters<'_>,
139 parameters: &KdfParameters<'_>,
140 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
141 policy.validate()?;
142 let mechanism = KeyAgreementAlgorithm::from_uri(agreement.algorithm)
143 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(agreement.algorithm.into()))?;
144 policy.check_agreement(mechanism)?;
145 let width = match (mechanism, agreement.peer_public_key) {
146 (KeyAgreementAlgorithm::X25519, bytes) if bytes.len() == 32 => 32,
147 (KeyAgreementAlgorithm::EcdhEs, [4, tail @ ..])
148 if matches!(tail.len(), 64 | 96 | 132) =>
149 {
150 tail.len() / 2
151 }
152 (KeyAgreementAlgorithm::EcdhEs, [2 | 3, tail @ ..])
153 if matches!(tail.len(), 32 | 48 | 66) =>
154 {
155 tail.len()
156 }
157 (KeyAgreementAlgorithm::DhEs | KeyAgreementAlgorithm::LegacyDh, bytes) => {
158 let (p_bits, q_bits) = key
159 .dh_domain_bits()
160 .ok_or_else(|| invalid(ProviderInputError::DhKey))?;
161 policy.check_dh_domain(p_bits, q_bits)?;
162 let width = p_bits.div_ceil(8);
163 if bytes.is_empty() || bytes.len() > width || width == 0 {
164 return Err(invalid(ProviderInputError::DhKey));
165 }
166 width
167 }
168 _ => return Err(invalid(ProviderInputError::EcdhKey)),
169 };
170 let (algorithm, digest, blocks) = preflight(parameters, width)?;
175 if mechanism == KeyAgreementAlgorithm::LegacyDh
181 && algorithm != KeyDerivationAlgorithm::LegacyDh
182 {
183 return Err(invalid(ProviderInputError::LegacyDhKdfParameters));
184 }
185 policy.check_derivation(algorithm, digest)?;
186 provider.require_capability(ProviderCapability::KeyAgreement(agreement))?;
187 provider.require_capability(ProviderCapability::Kdf(parameters))?;
188 let dh = matches!(
189 mechanism,
190 KeyAgreementAlgorithm::DhEs | KeyAgreementAlgorithm::LegacyDh
191 );
192 let modular = if dh {
193 modular_cost(width * 8, width * 8, 2)
194 } else {
195 0
196 };
197 let workspace = if dh { (width as u128 + 64) * 256 } else { 0 };
198 self.commit_all(
199 policy,
200 blocks,
201 modular,
202 width as u128 + parameters.output_len as u128 + workspace,
203 )?;
204 let secret = zeroize::Zeroizing::new(provider.agree_key(key, agreement)?);
205 check_width(&secret, width)?;
206 let output = zeroize::Zeroizing::new(provider.derive_key(parameters, &secret)?);
207 check_width(&output, parameters.output_len)?;
208 Ok(output)
209 }
210
211 pub(super) fn derive_key(
212 &mut self,
213 policy: &KeyEstablishmentPolicy,
214 provider: &dyn CryptoProvider,
215 parameters: &KdfParameters<'_>,
216 secret: &[u8],
217 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
218 policy.validate()?;
219 let (algorithm, digest, blocks) = preflight(parameters, secret.len())?;
220 policy.check_derivation(algorithm, digest)?;
221 provider.require_capability(ProviderCapability::Kdf(parameters))?;
222 self.commit_reservation(policy, blocks, parameters.output_len as u128)?;
223 let output = zeroize::Zeroizing::new(provider.derive_key(parameters, secret)?);
224 check_width(&output, parameters.output_len)?;
225 Ok(output)
226 }
227
228 fn commit_reservation(
229 &mut self,
230 policy: &KeyEstablishmentPolicy,
231 blocks: u128,
232 allocations: u128,
233 ) -> Result<(), XmlEncError> {
234 self.commit_all(policy, blocks, 0, allocations)
235 }
236
237 fn commit_dh_reservation(
238 &mut self,
239 policy: &KeyEstablishmentPolicy,
240 modular: u128,
241 allocations: u128,
242 ) -> Result<(), XmlEncError> {
243 self.commit_all(policy, 0, modular, allocations)
244 }
245
246 fn commit_all(
247 &mut self,
248 policy: &KeyEstablishmentPolicy,
249 blocks: u128,
250 modular: u128,
251 allocations: u128,
252 ) -> Result<(), XmlEncError> {
253 let work = reserve(
254 crate::policy::resource_name::KEY_ESTABLISHMENT_HASH_BLOCKS,
255 self.hash_blocks,
256 blocks,
257 policy.max_hash_blocks,
258 )?;
259 let bytes = reserve(
260 crate::policy::resource_name::KEY_ESTABLISHMENT_OWNED_BYTES,
261 self.owned_bytes,
262 allocations,
263 policy.max_owned_bytes,
264 )?;
265 let modular = reserve(
266 crate::policy::resource_name::KEY_ESTABLISHMENT_MODULAR_WORK,
267 self.modular_work,
268 modular,
269 policy.max_modular_work,
270 )?;
271 self.hash_blocks = work;
274 self.owned_bytes = bytes;
275 self.modular_work = modular;
276 Ok(())
277 }
278}
279
280fn check_width(bytes: &[u8], expected: usize) -> Result<(), XmlEncError> {
281 if bytes.len() != expected {
282 return Err(ProviderError::InvalidKeySize {
283 expected,
284 actual: bytes.len(),
285 }
286 .into());
287 }
288 Ok(())
289}
290
291fn reserve(
292 resource: &'static str,
293 used: usize,
294 count: u128,
295 maximum: usize,
296) -> Result<usize, PolicyViolation> {
297 if used > maximum || count > (maximum - used) as u128 {
298 return Err(PolicyViolation::ResourceLimit {
299 resource,
300 maximum,
301 actual: usize::try_from(used as u128 + count).unwrap_or(usize::MAX),
302 });
303 }
304 Ok(used + count as usize)
305}
306
307fn invalid(kind: ProviderInputError) -> XmlEncError {
308 ProviderError::InvalidInput(kind).into()
309}
310
311fn unsupported(uri: &str) -> XmlEncError {
312 ProviderError::Unsupported {
313 operation: ProviderOperation::Kdf,
314 algorithm: Some(uri.into()),
315 }
316 .into()
317}
318
319fn preflight(
327 parameters: &KdfParameters<'_>,
328 secret_len: usize,
329) -> Result<(KeyDerivationAlgorithm, DigestAlgorithm, u128), XmlEncError> {
330 let algorithm = KeyDerivationAlgorithm::from_uri(parameters.algorithm)
331 .ok_or_else(|| unsupported(parameters.algorithm))?;
332 let uri = parameters
333 .digest
334 .ok_or_else(|| unsupported(parameters.algorithm))?;
335 let digest = match algorithm {
336 KeyDerivationAlgorithm::Hkdf | KeyDerivationAlgorithm::Pbkdf2 => {
337 match SignatureAlgorithm::from_uri(uri) {
338 Some(SignatureAlgorithm::HmacSha1) => DigestAlgorithm::Sha1,
339 Some(SignatureAlgorithm::HmacSha224) => DigestAlgorithm::Sha224,
340 Some(SignatureAlgorithm::HmacSha256) => DigestAlgorithm::Sha256,
341 Some(SignatureAlgorithm::HmacSha384) => DigestAlgorithm::Sha384,
342 Some(SignatureAlgorithm::HmacSha512) => DigestAlgorithm::Sha512,
343 _ => return Err(unsupported(uri)),
344 }
345 }
346 _ => match DigestAlgorithm::from_uri(uri) {
347 Some(
348 value @ (DigestAlgorithm::Sha1
349 | DigestAlgorithm::Sha224
350 | DigestAlgorithm::Sha256
351 | DigestAlgorithm::Sha384
352 | DigestAlgorithm::Sha512),
353 ) => value,
354 _ => return Err(unsupported(uri)),
355 },
356 };
357 let width = digest.output_len();
358 let block = if width <= 32 { 64u128 } else { 128 };
359 let padding = if width <= 32 { 9u128 } else { 17 };
360 let hash = |bytes: u128| (bytes + padding).div_ceil(block);
361 let hmac_key = |bytes: usize| {
362 if bytes as u128 > block {
363 hash(bytes as u128) + 2
364 } else {
365 2
366 }
367 };
368 let output_blocks = parameters.output_len.div_ceil(width) as u128;
369 let work = match algorithm {
370 KeyDerivationAlgorithm::Pbkdf2 => {
371 if parameters.iterations == 0
372 || parameters.output_len == 0
373 || octets(parameters.info).is_none_or(|info| !info.is_empty())
374 || output_blocks > u32::MAX as u128
375 {
376 return Err(invalid(ProviderInputError::Pbkdf2Parameters));
377 }
378 let salt = parameters.salt.len() as u128;
381 hmac_key(secret_len)
382 + salt / block
383 + output_blocks
384 * (hash(salt % block + 4) + 1 + 2 * (parameters.iterations as u128 - 1))
385 }
386 KeyDerivationAlgorithm::Hkdf => {
387 let info = octets(parameters.info)
388 .ok_or_else(|| invalid(ProviderInputError::HkdfParameters))?;
389 if parameters.iterations != 0 || output_blocks > 255 {
390 return Err(invalid(ProviderInputError::HkdfParameters));
391 }
392 hmac_key(parameters.salt.len())
395 + hash(secret_len as u128)
396 + 1
397 + 2
398 + output_blocks * (hash(width as u128 + info.len() as u128 + 1) + 1)
399 }
400 KeyDerivationAlgorithm::ConcatKdf => {
401 if parameters.iterations != 0
402 || !parameters.salt.is_empty()
403 || parameters.output_len == 0
404 || output_blocks > u32::MAX as u128
405 {
406 return Err(invalid(ProviderInputError::ConcatKdfParameters));
407 }
408 let (bytes, bits) = match parameters.info {
409 KdfContext::Octets(bytes) => (bytes, bytes.len() as u128 * 8),
410 KdfContext::Bits { bytes, bit_len } => (bytes, bit_len as u128),
411 _ => return Err(invalid(ProviderInputError::ConcatKdfParameters)),
412 };
413 let used = (bits % 8) as usize;
414 if bits.div_ceil(8) != bytes.len() as u128
415 || (used != 0 && bytes[bytes.len() - 1] & (0xff >> used) != 0)
416 {
417 return Err(invalid(ProviderInputError::ConcatKdfParameters));
418 }
419 output_blocks * hash(4 + secret_len as u128 + bits.div_ceil(8))
420 }
421 KeyDerivationAlgorithm::LegacyDh => {
422 let KdfContext::LegacyDh {
423 encryption_algorithm,
424 nonce,
425 } = parameters.info
426 else {
427 return Err(invalid(ProviderInputError::LegacyDhKdfParameters));
428 };
429 if parameters.iterations != 0
430 || !parameters.salt.is_empty()
431 || parameters.output_len == 0
432 || output_blocks > 255
433 || parameters.output_len.checked_mul(8).is_none()
434 {
435 return Err(invalid(ProviderInputError::LegacyDhKdfParameters));
436 }
437 output_blocks
438 * hash(
439 secret_len as u128
440 + 2
441 + encryption_algorithm.len() as u128
442 + nonce.len() as u128
443 + 40,
444 )
445 }
446 };
447 Ok((algorithm, digest, work))
448}
449
450fn octets(context: KdfContext<'_>) -> Option<&[u8]> {
451 match context {
452 KdfContext::Octets(bytes) => Some(bytes),
453 KdfContext::Bits { bytes, bit_len }
454 if bit_len.is_multiple_of(8) && bit_len / 8 == bytes.len() =>
455 {
456 Some(bytes)
457 }
458 _ => None,
459 }
460}