1mod bit_hash;
5mod dh;
6pub use dh::RustCryptoDhKey;
7
8use crate::xmldsig::{DigestAlgorithm, SignatureAlgorithm};
9use p256::elliptic_curve::sec1::ToSec1Point;
10
11use super::{
12 KdfContext, KdfParameters, KeyAgreementKey, KeyAgreementParameters, ProviderError,
13 ProviderInputError, ProviderOperation,
14};
15
16pub(super) const X25519_URI: &str = "http://www.w3.org/2021/04/xmldsig-more#x25519";
17pub(super) const X448_URI: &str = "http://www.w3.org/2021/04/xmldsig-more#x448";
18pub(super) const ECDH_URI: &str = "http://www.w3.org/2009/xmlenc11#ECDH-ES";
19pub(super) const DH_ES_URI: &str = "http://www.w3.org/2009/xmlenc11#dh-es";
20pub(super) const DH_URI: &str = "http://www.w3.org/2001/04/xmlenc#dh";
21const HKDF_URI: &str = "http://www.w3.org/2021/04/xmldsig-more#hkdf";
22const PBKDF2_URI: &str = "http://www.w3.org/2009/xmlenc11#pbkdf2";
23const CONCAT_KDF_URI: &str = "http://www.w3.org/2009/xmlenc11#ConcatKDF";
24const LEGACY_DH_URI: &str = "http://www.w3.org/2001/04/xmlenc#dh";
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum EcdhCurve {
29 P256,
31 P384,
33 P521,
35}
36
37enum EcdhSecret {
38 P256(p256::SecretKey),
39 P384(p384::SecretKey),
40 P521(p521::SecretKey),
41}
42
43pub struct RustCryptoEcdhKey(EcdhSecret);
45
46impl core::fmt::Debug for RustCryptoEcdhKey {
47 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48 formatter
49 .debug_struct("RustCryptoEcdhKey")
50 .finish_non_exhaustive()
51 }
52}
53
54impl RustCryptoEcdhKey {
55 pub fn from_scalar(curve: EcdhCurve, bytes: &[u8]) -> Result<Self, ProviderError> {
58 let width = match curve {
59 EcdhCurve::P256 => 32,
60 EcdhCurve::P384 => 48,
61 EcdhCurve::P521 => 66,
62 };
63 if bytes.len() != width {
64 return Err(ProviderError::InvalidKeySize {
65 expected: width,
66 actual: bytes.len(),
67 });
68 }
69 let invalid = |_| ProviderError::InvalidInput(ProviderInputError::EcdhKey);
70 Ok(Self(match curve {
71 EcdhCurve::P256 => {
72 EcdhSecret::P256(p256::SecretKey::from_slice(bytes).map_err(invalid)?)
73 }
74 EcdhCurve::P384 => {
75 EcdhSecret::P384(p384::SecretKey::from_slice(bytes).map_err(invalid)?)
76 }
77 EcdhCurve::P521 => {
78 EcdhSecret::P521(p521::SecretKey::from_slice(bytes).map_err(invalid)?)
79 }
80 }))
81 }
82
83 pub fn public_key(&self) -> Vec<u8> {
85 match &self.0 {
86 EcdhSecret::P256(key) => key.public_key().to_sec1_point(false).as_bytes().to_vec(),
87 EcdhSecret::P384(key) => key.public_key().to_sec1_point(false).as_bytes().to_vec(),
88 EcdhSecret::P521(key) => key.public_key().to_sec1_point(false).as_bytes().to_vec(),
89 }
90 }
91}
92
93impl KeyAgreementKey for RustCryptoEcdhKey {
94 fn agree(&self, parameters: &KeyAgreementParameters<'_>) -> Result<Vec<u8>, ProviderError> {
95 if parameters.algorithm != ECDH_URI {
96 return Err(ProviderError::Unsupported {
97 operation: ProviderOperation::KeyAgreement,
98 algorithm: Some(parameters.algorithm.to_owned()),
99 });
100 }
101 macro_rules! agree {
102 ($key:expr, $public:ty) => {{
103 let peer = <$public>::from_sec1_bytes(parameters.peer_public_key)
108 .map_err(|_| ProviderError::InvalidInput(ProviderInputError::EcdhKey))?;
109 let shared = $key.diffie_hellman(&peer);
110 Ok(shared.raw_secret_bytes().to_vec())
111 }};
112 }
113 match &self.0 {
114 EcdhSecret::P256(key) => agree!(key, p256::PublicKey),
115 EcdhSecret::P384(key) => agree!(key, p384::PublicKey),
116 EcdhSecret::P521(key) => agree!(key, p521::PublicKey),
117 }
118 }
119}
120
121pub struct RustCryptoX25519Key(x25519_dalek::StaticSecret);
124
125impl core::fmt::Debug for RustCryptoX25519Key {
126 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127 formatter
128 .debug_struct("RustCryptoX25519Key")
129 .finish_non_exhaustive()
130 }
131}
132
133impl RustCryptoX25519Key {
134 pub fn from_bytes(bytes: [u8; 32]) -> Self {
136 Self(x25519_dalek::StaticSecret::from(bytes))
137 }
138
139 pub fn public_key(&self) -> [u8; 32] {
141 x25519_dalek::PublicKey::from(&self.0).to_bytes()
142 }
143}
144
145impl KeyAgreementKey for RustCryptoX25519Key {
146 fn agree(&self, parameters: &KeyAgreementParameters<'_>) -> Result<Vec<u8>, ProviderError> {
147 if parameters.algorithm != X25519_URI {
148 return Err(ProviderError::Unsupported {
149 operation: ProviderOperation::KeyAgreement,
150 algorithm: Some(parameters.algorithm.to_owned()),
151 });
152 }
153 let bytes: [u8; 32] =
154 parameters
155 .peer_public_key
156 .try_into()
157 .map_err(|_| ProviderError::InvalidKeySize {
158 expected: 32,
159 actual: parameters.peer_public_key.len(),
160 })?;
161 let peer = x25519_dalek::PublicKey::from(bytes);
162 let secret = self.0.diffie_hellman(&peer);
163 if !secret.was_contributory() {
168 return Err(ProviderError::AuthenticationFailed);
169 }
170 Ok(secret.as_bytes().to_vec())
171 }
172}
173
174pub struct RustCryptoX448Key(x448::StaticSecret);
176
177impl core::fmt::Debug for RustCryptoX448Key {
178 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179 formatter
180 .debug_struct("RustCryptoX448Key")
181 .finish_non_exhaustive()
182 }
183}
184
185impl RustCryptoX448Key {
186 pub fn from_bytes(bytes: [u8; 56]) -> Self {
188 Self(x448::StaticSecret::from(bytes))
189 }
190
191 pub fn from_pkcs8_der(bytes: &[u8]) -> Result<Self, ProviderError> {
194 use der::{Decode, asn1::OctetStringRef};
195 use pkcs8::{ObjectIdentifier, PrivateKeyInfoRef};
196 use subtle::ConstantTimeEq;
197 let invalid = || ProviderError::InvalidInput(ProviderInputError::EcdhKey);
198 let info = PrivateKeyInfoRef::try_from(bytes).map_err(|_| invalid())?;
199 if info.algorithm.oid != ObjectIdentifier::new_unwrap("1.3.101.111")
203 || info.algorithm.parameters.is_some()
204 {
205 return Err(invalid());
206 }
207 let scalar =
208 <&OctetStringRef>::from_der(info.private_key.as_bytes()).map_err(|_| invalid())?;
209 let scalar = zeroize::Zeroizing::new(
210 <[u8; 56]>::try_from(scalar.as_bytes()).map_err(|_| invalid())?,
211 );
212 let key = Self::from_bytes(*scalar);
213 if let Some(public) = info.public_key {
216 let public = public.as_bytes().ok_or_else(invalid)?;
217 if !bool::from(key.public_key().as_slice().ct_eq(public)) {
218 return Err(invalid());
219 }
220 }
221 Ok(key)
222 }
223
224 pub fn public_key(&self) -> [u8; 56] {
226 *x448::PublicKey::from(&self.0).as_bytes()
227 }
228}
229
230impl KeyAgreementKey for RustCryptoX448Key {
231 fn agree(&self, parameters: &KeyAgreementParameters<'_>) -> Result<Vec<u8>, ProviderError> {
232 if parameters.algorithm != X448_URI {
233 return Err(ProviderError::Unsupported {
234 operation: ProviderOperation::KeyAgreement,
235 algorithm: Some(parameters.algorithm.to_owned()),
236 });
237 }
238 let peer = x448::PublicKey::from_bytes_unchecked(parameters.peer_public_key).ok_or(
243 ProviderError::InvalidKeySize {
244 expected: 56,
245 actual: parameters.peer_public_key.len(),
246 },
247 )?;
248 let secret = self.0.diffie_hellman(&peer);
249 use subtle::ConstantTimeEq;
253 if bool::from(secret.as_bytes().ct_eq(&[0; 56])) {
254 return Err(ProviderError::AuthenticationFailed);
255 }
256 Ok(secret.as_bytes().to_vec())
257 }
258}
259
260pub(super) fn supports_kdf(parameters: &KdfParameters<'_>) -> bool {
261 match parameters.algorithm {
262 HKDF_URI | PBKDF2_URI => hmac_prf(parameters).is_some(),
263 CONCAT_KDF_URI | LEGACY_DH_URI => concat_digest(parameters).is_some(),
264 _ => false,
265 }
266}
267
268fn hmac_prf(parameters: &KdfParameters<'_>) -> Option<SignatureAlgorithm> {
269 let algorithm = SignatureAlgorithm::from_uri(parameters.digest?)?;
270 match algorithm {
271 SignatureAlgorithm::HmacSha1
272 | SignatureAlgorithm::HmacSha224
273 | SignatureAlgorithm::HmacSha256
274 | SignatureAlgorithm::HmacSha384
275 | SignatureAlgorithm::HmacSha512 => Some(algorithm),
276 _ => None,
277 }
278}
279
280pub(super) fn derive_key(
281 parameters: &KdfParameters<'_>,
282 secret: &[u8],
283) -> Result<Vec<u8>, ProviderError> {
284 if parameters.algorithm == LEGACY_DH_URI {
285 return derive_legacy_dh(parameters, secret);
286 }
287 if parameters.algorithm == CONCAT_KDF_URI {
288 return derive_concat(parameters, secret);
289 }
290 if parameters.algorithm == PBKDF2_URI {
291 return derive_pbkdf2(parameters, secret);
292 }
293 if parameters.algorithm != HKDF_URI {
294 return Err(ProviderError::Unsupported {
295 operation: ProviderOperation::Kdf,
296 algorithm: Some(parameters.algorithm.to_owned()),
297 });
298 }
299 if parameters.iterations != 0 {
300 return Err(ProviderError::InvalidInput(
301 ProviderInputError::HkdfParameters,
302 ));
303 }
304 let info = context_octets(parameters.info).ok_or(ProviderError::InvalidInput(
305 ProviderInputError::HkdfParameters,
306 ))?;
307 macro_rules! derive {
308 ($digest:ty, $width:expr) => {{
309 if parameters.output_len > 255 * $width {
313 return Err(ProviderError::InvalidInput(
314 ProviderInputError::HkdfParameters,
315 ));
316 }
317 let kdf = hkdf::Hkdf::<$digest>::new(Some(parameters.salt), secret);
318 let mut output = zeroize::Zeroizing::new(vec![0; parameters.output_len]);
319 kdf.expand(info, &mut output)
320 .map_err(|_| ProviderError::InvalidInput(ProviderInputError::HkdfParameters))?;
321 Ok(core::mem::take(&mut *output))
322 }};
323 }
324 match hmac_prf(parameters) {
325 Some(SignatureAlgorithm::HmacSha1) => derive!(sha1::Sha1, 20),
326 Some(SignatureAlgorithm::HmacSha224) => derive!(sha2::Sha224, 28),
327 Some(SignatureAlgorithm::HmacSha256) => derive!(sha2::Sha256, 32),
328 Some(SignatureAlgorithm::HmacSha384) => derive!(sha2::Sha384, 48),
329 Some(SignatureAlgorithm::HmacSha512) => derive!(sha2::Sha512, 64),
330 _ => Err(ProviderError::Unsupported {
331 operation: ProviderOperation::Kdf,
332 algorithm: Some(parameters.algorithm.to_owned()),
333 }),
334 }
335}
336
337fn derive_pbkdf2(parameters: &KdfParameters<'_>, secret: &[u8]) -> Result<Vec<u8>, ProviderError> {
338 use hmac::Mac;
339 use zeroize::Zeroize;
340
341 if parameters.iterations == 0
342 || parameters.output_len == 0
343 || context_octets(parameters.info) != Some(&[][..])
344 {
345 return Err(ProviderError::InvalidInput(
346 ProviderInputError::Pbkdf2Parameters,
347 ));
348 }
349 macro_rules! derive {
350 ($digest:ty, $width:expr) => {{
351 let blocks = parameters.output_len.div_ceil($width);
355 if u32::try_from(blocks).is_err() {
356 return Err(ProviderError::InvalidInput(
357 ProviderInputError::Pbkdf2Parameters,
358 ));
359 }
360 let prf = <hmac::Hmac<$digest> as hmac::KeyInit>::new_from_slice(secret)
361 .map_err(|_| ProviderError::InvalidInput(ProviderInputError::Pbkdf2Parameters))?;
362 let mut salted = prf.clone();
363 salted.update(parameters.salt);
364 let mut output = zeroize::Zeroizing::new(vec![0; parameters.output_len]);
365 for (index, chunk) in output.chunks_mut($width).enumerate() {
369 let counter = u32::try_from(index + 1).map_err(|_| {
370 ProviderError::InvalidInput(ProviderInputError::Pbkdf2Parameters)
371 })?;
372 let mut first = salted.clone();
373 first.update(&counter.to_be_bytes());
374 let mut previous = first.finalize().into_bytes();
375 chunk.copy_from_slice(&previous[..chunk.len()]);
376 for _ in 1..parameters.iterations {
377 let mut next = prf.clone();
378 next.update(&previous);
379 previous.zeroize();
380 previous = next.finalize().into_bytes();
381 for (byte, value) in chunk.iter_mut().zip(previous.iter()) {
382 *byte ^= value;
383 }
384 }
385 previous.zeroize();
386 }
387 Ok(core::mem::take(&mut *output))
388 }};
389 }
390 match hmac_prf(parameters) {
391 Some(SignatureAlgorithm::HmacSha1) => derive!(sha1::Sha1, 20),
392 Some(SignatureAlgorithm::HmacSha224) => derive!(sha2::Sha224, 28),
393 Some(SignatureAlgorithm::HmacSha256) => derive!(sha2::Sha256, 32),
394 Some(SignatureAlgorithm::HmacSha384) => derive!(sha2::Sha384, 48),
395 Some(SignatureAlgorithm::HmacSha512) => derive!(sha2::Sha512, 64),
396 _ => Err(ProviderError::Unsupported {
397 operation: ProviderOperation::Kdf,
398 algorithm: Some(parameters.algorithm.to_owned()),
399 }),
400 }
401}
402
403fn context_octets(context: KdfContext<'_>) -> Option<&[u8]> {
404 match context {
405 KdfContext::Octets(bytes) => Some(bytes),
406 KdfContext::Bits { bytes, bit_len }
407 if bit_len.is_multiple_of(8) && bit_len / 8 == bytes.len() =>
408 {
409 Some(bytes)
410 }
411 KdfContext::Bits { .. } | KdfContext::LegacyDh { .. } => None,
412 }
413}
414
415fn concat_digest(parameters: &KdfParameters<'_>) -> Option<DigestAlgorithm> {
416 DigestAlgorithm::from_uri(parameters.digest?)
417}
418
419fn derive_concat(parameters: &KdfParameters<'_>, secret: &[u8]) -> Result<Vec<u8>, ProviderError> {
420 let invalid = || ProviderError::InvalidInput(ProviderInputError::ConcatKdfParameters);
421 if parameters.iterations != 0 || !parameters.salt.is_empty() || parameters.output_len == 0 {
422 return Err(invalid());
423 }
424 let (info, bit_len) = match parameters.info {
425 KdfContext::Octets(bytes) => (bytes, bytes.len().checked_mul(8).ok_or_else(invalid)?),
426 KdfContext::Bits { bytes, bit_len } => (bytes, bit_len),
427 KdfContext::LegacyDh { .. } => return Err(invalid()),
428 };
429 if bit_len.div_ceil(8) != info.len() {
430 return Err(invalid());
431 }
432 let used_bits = bit_len % 8;
433 if used_bits != 0 && info[info.len() - 1] & (0xff >> used_bits) != 0 {
434 return Err(invalid());
435 }
436 let algorithm = concat_digest(parameters).ok_or_else(|| ProviderError::Unsupported {
437 operation: ProviderOperation::Kdf,
438 algorithm: Some(parameters.algorithm.to_owned()),
439 })?;
440 let width = algorithm.output_len();
441 if u32::try_from(parameters.output_len.div_ceil(width)).is_err() {
442 return Err(invalid());
443 }
444 let total_bits = 32 + secret.len() as u128 * 8 + bit_len as u128;
445 if width <= 32 && total_bits > u64::MAX as u128 {
446 return Err(invalid());
447 }
448 let mut output = zeroize::Zeroizing::new(vec![0; parameters.output_len]);
453 for (index, chunk) in output.chunks_mut(width).enumerate() {
454 let counter = u32::try_from(index + 1)
455 .map_err(|_| invalid())?
456 .to_be_bytes();
457 let block = bit_hash::hash(algorithm, &[&counter, secret], info, bit_len)?;
458 chunk.copy_from_slice(&block[..chunk.len()]);
459 }
460 Ok(core::mem::take(&mut *output))
461}
462
463fn derive_legacy_dh(
464 parameters: &KdfParameters<'_>,
465 secret: &[u8],
466) -> Result<Vec<u8>, ProviderError> {
467 let invalid = || ProviderError::InvalidInput(ProviderInputError::LegacyDhKdfParameters);
468 let KdfContext::LegacyDh {
469 encryption_algorithm,
470 nonce,
471 } = parameters.info
472 else {
473 return Err(invalid());
474 };
475 if parameters.iterations != 0 || !parameters.salt.is_empty() || parameters.output_len == 0 {
476 return Err(invalid());
477 }
478 let algorithm = concat_digest(parameters).ok_or_else(|| ProviderError::Unsupported {
479 operation: ProviderOperation::Kdf,
480 algorithm: Some(parameters.algorithm.to_owned()),
481 })?;
482 let width = algorithm.output_len();
483 if parameters.output_len.div_ceil(width) > u8::MAX as usize {
484 return Err(invalid());
485 }
486 let mut key_bits = parameters.output_len.checked_mul(8).ok_or_else(invalid)?;
487 let mut decimal = [0u8; 40];
490 let mut start = decimal.len();
491 while key_bits != 0 {
492 start -= 1;
493 decimal[start] = b'0' + (key_bits % 10) as u8;
494 key_bits /= 10;
495 }
496 let total_bytes = secret.len() as u128
497 + 2
498 + encryption_algorithm.len() as u128
499 + nonce.len() as u128
500 + (decimal.len() - start) as u128;
501 if width <= 32 && total_bytes * 8 > u64::MAX as u128 {
502 return Err(invalid());
503 }
504 const HEX: &[u8; 16] = b"0123456789ABCDEF";
509 let mut output = zeroize::Zeroizing::new(vec![0; parameters.output_len]);
510 for (index, chunk) in output.chunks_mut(width).enumerate() {
511 let counter = index + 1;
512 let ascii = [HEX[counter >> 4], HEX[counter & 15]];
513 let hash = bit_hash::hash(
514 algorithm,
515 &[
516 secret,
517 &ascii,
518 encryption_algorithm.as_bytes(),
519 nonce,
520 &decimal[start..],
521 ],
522 &[],
523 0,
524 )?;
525 chunk.copy_from_slice(&hash[..chunk.len()]);
526 }
527 Ok(core::mem::take(&mut *output))
528}