bouncycastle_core_interface/key_material.rs
1//! A helper class used across the bc-rust library to hold bytes-like key material.
2//! The main purpose is to hold metadata about the contained key material such as the key type and
3//! entropy content to prevent accidental misuse security bugs, such as deriving cryptographic keys
4//! from uninitialized data.
5//!
6//! This object allows several types of manual-overrides, which typically require setting the [KeyMaterialSized::allow_hazardous_operations] flag.
7//! For example, the raw bytes data can be extracted, or the key forced to a certain type,
8//! but well-designed use of the bc-rust.test library should not need to ever set the [KeyMaterialSized::allow_hazardous_operations] flag.
9//! The core idea of this wrapper is to keep track of the usage of the key material, including
10//! the amount of entropy that it is presumed to contain in order to prevent users from accidentally
11//! using it inappropriately in a way that could lead to security weaknesses.
12//!
13//! Various operations within the bc-rs library will consume or produce KeyMaterial objects with
14//! specific key types. In normal use of the bc-rs APIs, users should never have to manually convert
15//! the type of a KeyMaterial object because the various function calls will set the key type appropriately.
16//!
17//! Some typical workflows would be:
18//!
19//! * Hash functions take in \[u8\] byte data and return a KeyMaterial of type RawUnknownEntropy.
20//! * Password-based key derivation functions act on KeyMaterial of any type, and in the case of RawFullEntropy, RawLowEntropy, or RawUnknownEntropy, will preserve the entropy rating.
21//! * Keyed KDFs that are given a key of RawFullEntropy or KeyedHashKey a KeyMaterial data of type RawLowEntropy or RawUnknownEntropy will promote it into RawFullEntropy.
22//! * Symmetric ciphers or asymmetric ciphers such as X25519 or ML-KEM that accept private key seeds will expect KeyMaterial of type AsymmetricPrivateKeySeed.
23//!
24//! However, there is a [KeyMaterialSized::convert_key_type] for cases where the user has more context knowledge than the library.
25//! Some conversions, such as converting a key of type RawLowEntropy into a SymmetricCipherKey, will fail unless
26//! the user has explicitly allowed them via calling allow_hazardous_operations() prior to the conversion.
27//!
28//! Examples of hazardous conversions that require allow_hazardous_operations() to be called first:
29//!
30//! * Converting a KeyMaterial of type RawLowEntropy or RawUnknownEntropy into RawFullEntropy or any other full-entropy key type.
31//! * Converting any algorithm-specific key type into a different algorithm-specific key type, which is considered hazardous since key reuse between different cryptographic algorithms is generally discouraged and can sometimes lead to key leakage.
32//!
33//! Additional security features:
34//! * Zeroizes on destruction.
35//! * Implementing Display and Debug to print metadata but not key material to prevent accidental logging.
36//!
37//! As with all wrappers of this nature, the intent is to protect the user from making silly mistakes, not to prevent expert users from doing what they need to do.
38//! It as always possible, for example, to extract the bytes from a KeyMaterial object, manipulate them, and then re-wrap them in a new KeyMaterial object.
39
40use crate::errors::KeyMaterialError;
41use crate::traits::{RNG, SecurityStrength};
42use bouncycastle_utils::{ct, max, min};
43
44use std::cmp::{Ordering, PartialOrd};
45use std::fmt;
46
47/// Sometimes you just need a zero-length dummy key.
48pub type KeyMaterial0 = KeyMaterialSized<0>;
49
50pub type KeyMaterial128 = KeyMaterialSized<16>;
51pub type KeyMaterial256 = KeyMaterialSized<32>;
52pub type KeyMaterial512 = KeyMaterialSized<64>;
53
54
55/// A helper class used across the bc-rust.test library to hold bytes-like key material.
56/// See [KeyMaterialSized] for for details, such as constructors.
57pub trait KeyMaterial {
58 /// Loads the provided data into a new KeyMaterial of the specified type.
59 /// This is discouraged unless the caller knows the provenance of the data, such as loading it
60 /// from a cryptographic private key file.
61 /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
62 /// Since this zeroizes and resets the key material, this is considered a dangerous conversion.
63 ///
64 /// The only hazardous operation here that requires setting [KeyMaterial::allow_hazardous_operations] is giving it
65 /// an all-zero key, which is checked as a courtesy to catch mistakes of feeding an initialized buffer
66 /// instead of an actual key. See the note on [KeyMaterialSized::set_bytes_as_type] for suggestions
67 /// for handling this.
68 fn set_bytes_as_type(
69 &mut self,
70 source: &[u8],
71 key_type: KeyType,
72 ) -> Result<(), KeyMaterialError>;
73
74 /// Get a reference to the underlying key material bytes.
75 ///
76 /// By reading the key bytes out of the [KeyMaterial] object, you lose the protections that it offers,
77 /// however, this does not require [KeyMaterial::allow_hazardous_operations] in the name of API ergonomics:
78 /// setting [KeyMaterial::allow_hazardous_operations] requires a mutable reference and reading the bytes
79 /// is not an operation that should require mutability.
80 /// TODO -- consider whether this should consume the object
81 fn ref_to_bytes(&self) -> &[u8];
82
83 /// Get a mutable reference to the underlying key material bytes so that you can read or write
84 /// to the underlying bytes without needing to create a temporary buffer, especially useful in
85 /// cases where the required size of that buffer may be tricky to figure out at compile-time.
86 /// This requires [KeyMaterial::allow_hazardous_operations] to be set.
87 /// When writing directly to the buffer, you are responsible for setting the key_len and key_type afterwards,
88 /// and you should [KeyMaterial::drop_hazardous_operations].
89 fn mut_ref_to_bytes(&mut self) -> Result<&mut [u8], KeyMaterialError>;
90
91 /// The size of the internal buffer; ie the largest key that this instance can hold.
92 /// Equivalent to the <KEY_LEN> constant param this object was created with.
93 fn capacity(&self) -> usize;
94
95 /// Length of the key material in bytes.
96 fn key_len(&self) -> usize;
97
98 /// Requires [KeyMaterial::allow_hazardous_operations].
99 fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError>;
100
101 fn key_type(&self) -> KeyType;
102
103 /// Requires [KeyMaterial::allow_hazardous_operations].
104 fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError>;
105
106 /// Security Strength, as used here, aligns with NIST SP 800-90A guidance for random number generation,
107 /// specifically section 8.4.
108 ///
109 /// The idea is to be able to track for cryptographic seeds and bytes-like key objects across the entire library,
110 /// the instatiated security level of the RNG that generated it, and whether it was handled by any intermediate
111 /// objects, such as Key Derivation Functions, that have a smaller internal security level and therefore result in
112 /// downgrading the security level of the key material.
113 ///
114 /// Note that while security strength is closely related to entropy, it is a property of the algorithms
115 /// that touched the key material and not of the key material data itself, and therefore it is
116 /// tracked independantly from key length and entropy level / key type.
117 fn security_strength(&self) -> SecurityStrength;
118
119 /// Requires [KeyMaterial::allow_hazardous_operations] to raise the security strength, but not to lower it.
120 /// Throws [KeyMaterialError::HazardousOperationNotPermitted] on a request to raise the security level without
121 /// [KeyMaterial::allow_hazardous_operations] set.
122 /// Throws [KeyMaterialError::InvalidLength] on a request to set the security level higher than the current key length.
123 fn set_security_strength(&mut self, strength: SecurityStrength)
124 -> Result<(), KeyMaterialError>;
125
126 /// Sets this instance to be able to perform potentially hazardous conversions such as
127 /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey,
128 /// or manually setting the key bytes via [KeyMaterial::mut_ref_to_bytes], which then requires you to be responsible
129 /// for setting the key_len and key_type afterwards.
130 ///
131 /// The purpose of the hazardous_conversions guard is not to prevent the user from accessing their data,
132 /// but rather to make the developer think carefully about the operation they are about to perform,
133 /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants
134 /// further inspection.
135 fn allow_hazardous_operations(&mut self);
136
137 /// Resets this instance to not be able to perform potentially hazardous conversions.
138 fn drop_hazardous_operations(&mut self);
139
140 /// Sets the key_type of this KeyMaterial object.
141 /// Does not perform any operations on the actual key material, other than changing the key_type field.
142 /// If allow_hazardous_operations is true, this method will allow conversion to any KeyType, otherwise
143 /// checking is performed to ensure that the conversion is "safe".
144 /// This drops the allow_hazardous_operations flag, so if you need to do multiple hazardous conversions
145 /// on the same instance, then you'll need to call .allow_hazardous_operations() each time.
146 fn convert_key_type(&mut self, new_key_type: KeyType) -> Result<(), KeyMaterialError>;
147
148 fn is_full_entropy(&self) -> bool;
149
150 fn zeroize(&mut self);
151
152 /// Is simply an alias to [KeyMaterial::set_key_len], however, this does not require [KeyMaterial::allow_hazardous_operations]
153 /// since truncation is a safe operation.
154 /// If truncating below the current security strength, the security strength will be lowered accordingly.
155 fn truncate(&mut self, new_len: usize) -> Result<(), KeyMaterialError>;
156
157 /// Adds the other KeyMaterial into this one, assuming there is space.
158 /// Does not require [KeyMaterial::allow_hazardous_operations].
159 /// Throws [KeyMaterialError::InvalidLength] if this object does not have enough space to add the other one.
160 /// The resulting [KeyType] and security strength will be the lesser of the two keys.
161 /// In other words, concatenating two 128-bit full entropy keys generated at a 128-bit DRBG security level
162 /// will result in a 256-bit full entropy key still at the 128-bit DRBG security level.
163 /// Concatenating a full entropy key with a low entropy key will result in a low entropy key.
164 ///
165 /// Returns the new key_len.
166 fn concatenate(&mut self, other: &dyn KeyMaterial) -> Result<usize, KeyMaterialError>;
167
168 /// Perform a constant-time comparison between the two key material buffers,
169 /// ignoring differences in capacity, [KeyType], [SecurityStrength], etc.
170 fn equals(&self, other: &dyn KeyMaterial) -> bool;
171}
172
173/// A wrapper for holding bytes-like key material (symmetric keys or seeds) which aims to apply a
174/// strict typing system to prevent many kinds of mis-use mistakes.
175/// The capacity of the internal buffer can be set at compile-time via the <KEY_LEN> param.
176#[derive(Clone)]
177pub struct KeyMaterialSized<const KEY_LEN: usize> {
178 buf: [u8; KEY_LEN],
179 key_len: usize,
180 key_type: KeyType,
181 security_strength: SecurityStrength,
182 allow_hazardous_operations: bool,
183}
184
185#[derive(Clone, Copy, Debug, Eq, PartialEq)]
186pub enum KeyType {
187 /// The KeyMaterial is zeroized and MUST NOT be used for any cryptographic operation in this state.
188 Zeroized,
189
190 /// The KeyMaterial contains data of low or unknown entropy.
191 BytesLowEntropy,
192
193 /// The KeyMaterial contains data of full entropy and can be safely converted to any other full-entropy key type.
194 BytesFullEntropy,
195
196 /// A seed for asymmetric private keys, RNGs, and other seed-based cryptographic objects.
197 Seed,
198
199 /// A MAC key.
200 MACKey,
201
202 /// A key for a symmetric block or stream cipher.
203 SymmetricCipherKey,
204}
205
206impl<const KEY_LEN: usize> Default for KeyMaterialSized<KEY_LEN> {
207 /// Create a new empty (zeroized) instance.
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213impl<const KEY_LEN: usize> KeyMaterialSized<KEY_LEN> {
214 pub fn new() -> Self {
215 Self {
216 buf: [0u8; KEY_LEN],
217 key_len: 0,
218 key_type: KeyType::Zeroized,
219 security_strength: SecurityStrength::None,
220 allow_hazardous_operations: false,
221 }
222 }
223
224 /// Create a new instance of KeyMaterial containing random bytes from the provided random number generator.
225 pub fn from_rng(rng: &mut impl RNG) -> Result<Self, KeyMaterialError> {
226 let mut key = Self::new();
227 key.allow_hazardous_operations();
228
229 rng.next_bytes_out(&mut key.mut_ref_to_bytes().unwrap())
230 .map_err(|_| KeyMaterialError::GenericError("RNG failed."))?;
231
232 key.key_len = KEY_LEN;
233 key.key_type = KeyType::BytesFullEntropy;
234 key.security_strength = rng.security_strength();
235 key.drop_hazardous_operations();
236 Ok(key)
237 }
238
239 /// Constructor.
240 /// Loads the provided data into a new KeyMaterial of type [KeyType::BytesLowEntropy].
241 /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
242 pub fn from_bytes(source: &[u8]) -> Result<Self, KeyMaterialError> {
243 Self::from_bytes_as_type(source, KeyType::BytesLowEntropy)
244 }
245
246 /// Constructor.
247 /// Loads the provided data into a new KeyMaterial of the specified type.
248 /// This is discouraged unless the caller knows the provenance of the data, such as loading it
249 /// from a cryptographic private key file.
250 /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
251 ///
252 /// Will set the [SecurityStrength] automatically according to the following rules:
253 /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None].
254 /// * Otherwise it will set it based on the length of the provided source bytes.
255 pub fn from_bytes_as_type(source: &[u8], key_type: KeyType) -> Result<Self, KeyMaterialError> {
256 let mut key_material = Self::default();
257
258 // Special case: catch and ignore the courtesy error about zeroized input and simply return a zeroized key.
259 match key_material.set_bytes_as_type(source, key_type) {
260 Ok(_) => Ok(key_material),
261 Err(KeyMaterialError::ActingOnZeroizedKey) => {
262 debug_assert_eq!(key_material.key_type(), KeyType::Zeroized);
263 Ok(key_material)
264 }
265 Err(e) => Err(e),
266 }
267 }
268
269 /// Copy constructor
270 pub fn from_key(other: &impl KeyMaterial) -> Result<Self, KeyMaterialError> {
271 if other.key_len() > KEY_LEN {
272 return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
273 }
274
275 let mut key = Self {
276 buf: [0u8; KEY_LEN],
277 key_len: other.key_len(),
278 key_type: other.key_type(),
279 security_strength: SecurityStrength::None,
280 allow_hazardous_operations: false,
281 };
282 key.buf[..other.key_len()].copy_from_slice(other.ref_to_bytes());
283 Ok(key)
284 }
285}
286
287impl<const KEY_LEN: usize> KeyMaterial for KeyMaterialSized<KEY_LEN> {
288 /// Loads the provided data into a new KeyMaterial of the specified type.
289 /// This is discouraged unless the caller knows the provenance of the data, such as loading it
290 /// from a cryptographic private key file.
291 ///
292 /// This behaves differently on all-zero input key depending on whether [KeyMaterial::allow_hazardous_operations] is set:
293 /// if not set, then it will succeed, setting the key type to [KeyType::Zeroized] and also return a [KeyMaterialError::ActingOnZeroizedKey]
294 /// to indicate that you may want to perform error-handling, which could be manually setting the key type
295 /// if you intend to allow zero keys, or do some other error-handling, like figure out why your RNG is broken.
296 /// Note that even if a [KeyMaterialError::ActingOnZeroizedKey] is returned, the object is still populated and usable.
297 /// For example, you could catch it like this:
298 /// ```
299 /// use core_interface::key_material::{KeyMaterial256, KeyType};
300 /// use core_interface::key_material::KeyMaterial;
301 /// use core_interface::errors::KeyMaterialError;
302 ///
303 /// let key_bytes = [0u8; 16];
304 /// let mut key = KeyMaterial256::new();
305 /// let res = key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy);
306 /// match res {
307 /// Err(KeyMaterialError::ActingOnZeroizedKey) => {
308 /// // Either figure out why your passed an all-zero key,
309 /// // or set the key type manually, if that's what you intended.
310 /// key.allow_hazardous_operations();
311 /// key.set_key_type(KeyType::BytesLowEntropy).unwrap(); // probably you should do something more elegant than .unwrap in your code ;)
312 /// key.drop_hazardous_operations();
313 /// },
314 /// Err(_) => { /* figure out what else went wrong */ },
315 /// Ok(_) => { /* good */ },
316 /// }
317 /// ```
318 /// On the other hand, if [KeyMaterial::allow_hazardous_operations] is set then it will just do what you asked without complaining.
319 ///
320 /// Since this zeroizes and resets the key material, this is considered a dangerous conversion.
321 ///
322 /// Will set the [SecurityStrength] automatically according to the following rules:
323 /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None].
324 /// * Otherwise it will set it based on the length of the provided source bytes.
325 fn set_bytes_as_type(
326 &mut self,
327 source: &[u8],
328 key_type: KeyType,
329 ) -> Result<(), KeyMaterialError> {
330 let allowed_hazardous_operations = self.allow_hazardous_operations;
331 self.allow_hazardous_operations();
332
333 if source.len() > KEY_LEN {
334 return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
335 }
336
337 let new_key_type = if !allowed_hazardous_operations && ct::ct_eq_zero_bytes(source) {
338 KeyType::Zeroized
339 } else {
340 key_type
341 };
342
343 self.buf[..source.len()].copy_from_slice(source);
344 self.key_len = source.len();
345 self.key_type = new_key_type;
346
347 if new_key_type <= KeyType::BytesLowEntropy {
348 self.set_security_strength(SecurityStrength::None)?;
349 } else {
350 self.set_security_strength(SecurityStrength::from_bits(source.len() * 8))?;
351 }
352 self.drop_hazardous_operations();
353
354 // return
355 if new_key_type == KeyType::Zeroized {
356 Err(KeyMaterialError::ActingOnZeroizedKey)
357 } else {
358 Ok(())
359 }
360 }
361
362 fn ref_to_bytes(&self) -> &[u8] {
363 &self.buf[..self.key_len]
364 }
365
366 fn mut_ref_to_bytes(&mut self) -> Result<&mut [u8], KeyMaterialError> {
367 if !self.allow_hazardous_operations {
368 return Err(KeyMaterialError::HazardousOperationNotPermitted);
369 }
370 Ok(&mut self.buf)
371 }
372
373 fn capacity(&self) -> usize {
374 KEY_LEN
375 }
376
377 fn key_len(&self) -> usize {
378 self.key_len
379 }
380
381 fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError> {
382 if !self.allow_hazardous_operations {
383 return Err(KeyMaterialError::HazardousOperationNotPermitted);
384 }
385 if key_len > KEY_LEN {
386 return Err(KeyMaterialError::InvalidLength);
387 }
388 self.key_len = key_len;
389 Ok(())
390 }
391 fn key_type(&self) -> KeyType {
392 self.key_type.clone()
393 }
394 fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError> {
395 if !self.allow_hazardous_operations {
396 return Err(KeyMaterialError::HazardousOperationNotPermitted);
397 }
398 self.key_type = key_type.clone();
399 Ok(())
400 }
401 fn security_strength(&self) -> SecurityStrength {
402 self.security_strength.clone()
403 }
404
405 fn set_security_strength(
406 &mut self,
407 strength: SecurityStrength,
408 ) -> Result<(), KeyMaterialError> {
409 if strength > self.security_strength && !self.allow_hazardous_operations {
410 return Err(KeyMaterialError::HazardousOperationNotPermitted);
411 };
412
413 if self.key_type <= KeyType::BytesLowEntropy && strength > SecurityStrength::None {
414 return Err(KeyMaterialError::SecurityStrength(
415 "BytesLowEntropy keys cannot have a security strength other than None.",
416 ));
417 }
418
419 match strength {
420 SecurityStrength::None => { /* fine, you can always downgrade */ }
421 SecurityStrength::_112bit => {
422 if self.key_len() < 14 {
423 return Err(KeyMaterialError::SecurityStrength(
424 "Security strength cannot be higher than key length.",
425 ));
426 }
427 }
428 SecurityStrength::_128bit => {
429 if self.key_len() < 16 {
430 return Err(KeyMaterialError::SecurityStrength(
431 "Security strength cannot be larger than key length.",
432 ));
433 }
434 }
435 SecurityStrength::_192bit => {
436 if self.key_len() < 24 {
437 return Err(KeyMaterialError::SecurityStrength(
438 "Security strength cannot be larger than key length.",
439 ));
440 }
441 }
442 SecurityStrength::_256bit => {
443 if self.key_len() < 32 {
444 return Err(KeyMaterialError::SecurityStrength(
445 "Security strength cannot be larger than key length.",
446 ));
447 }
448 }
449 }
450
451 self.security_strength = strength;
452 self.drop_hazardous_operations();
453 Ok(())
454 }
455 /// Sets this instance to be able to perform potentially hazardous operations such as
456 /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey.
457 ///
458 /// The purpose of the hazardous operations guard is not to prevent the user from accessing their data,
459 /// but rather to make the developer think carefully about the operation they are about to perform,
460 /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants
461 /// further inspection.
462 fn allow_hazardous_operations(&mut self) {
463 self.allow_hazardous_operations = true;
464 }
465 /// Resets this instance to not be able to perform potentially hazardous operations.
466 fn drop_hazardous_operations(&mut self) {
467 self.allow_hazardous_operations = false;
468 }
469 /// Sets the key_type of this KeyMaterial object.
470 /// Does not perform any operations on the actual key material, other than changing the key_type field.
471 /// If allow_hazardous_operations is true, this method will allow conversion to any KeyType, otherwise
472 /// checking is performed to ensure that the conversion is "safe".
473 /// This drops the allow_hazardous_operations flag, so if you need to do multiple hazardous operations
474 /// on the same instance, then you'll need to call .allow_hazardous_operations() each time.
475 fn convert_key_type(&mut self, new_key_type: KeyType) -> Result<(), KeyMaterialError> {
476 if self.allow_hazardous_operations {
477 // just do it
478 self.key_type = new_key_type;
479 return Ok(());
480 }
481
482 match self.key_type {
483 KeyType::Zeroized => {
484 return Err(KeyMaterialError::ActingOnZeroizedKey);
485 }
486 KeyType::BytesFullEntropy => {
487 // raw full entropy can be safely converted to anything.
488 self.key_type = new_key_type;
489 }
490 KeyType::BytesLowEntropy => {
491 match new_key_type {
492 KeyType::BytesLowEntropy => { /* No change */ }
493 _ => {
494 return Err(KeyMaterialError::HazardousOperationNotPermitted);
495 }
496 }
497 }
498 KeyType::MACKey => {
499 match new_key_type {
500 KeyType::MACKey => { /* No change */ }
501 // Else: Once a KeyMaterial is typed, it should stay that way.
502 _ => {
503 return Err(KeyMaterialError::HazardousOperationNotPermitted);
504 }
505 }
506 }
507 KeyType::SymmetricCipherKey => {
508 match new_key_type {
509 KeyType::SymmetricCipherKey => { /* No change */ }
510 // Else: Once a KeyMaterial is typed, it should stay that way.
511 _ => {
512 return Err(KeyMaterialError::HazardousOperationNotPermitted);
513 }
514 }
515 }
516 KeyType::Seed => {
517 match new_key_type {
518 KeyType::Seed => { /* No change */ }
519 // Else: Once a KeyMaterial is typed, it should stay that way.
520 _ => {
521 return Err(KeyMaterialError::HazardousOperationNotPermitted);
522 }
523 }
524 }
525 }
526
527 // each call to allow_hazardous_operations() is only good for one conversion.
528 self.drop_hazardous_operations();
529 Ok(())
530 }
531 fn is_full_entropy(&self) -> bool {
532 match self.key_type {
533 KeyType::BytesFullEntropy
534 | KeyType::Seed
535 | KeyType::MACKey
536 | KeyType::SymmetricCipherKey => true,
537 KeyType::Zeroized | KeyType::BytesLowEntropy => false,
538 }
539 }
540
541 fn zeroize(&mut self) {
542 self.buf.fill(0u8);
543 self.key_len = 0;
544 self.key_type = KeyType::Zeroized;
545 }
546
547 fn truncate(&mut self, new_len: usize) -> Result<(), KeyMaterialError> {
548 if new_len > self.key_len {
549 return Err(KeyMaterialError::InvalidLength);
550 }
551
552 self.security_strength =
553 min(&self.security_strength, &SecurityStrength::from_bits(new_len * 8)).clone();
554
555 if new_len == 0 {
556 self.key_type = KeyType::Zeroized;
557 }
558
559 self.key_len = new_len;
560 Ok(())
561 }
562
563 fn concatenate(&mut self, other: &dyn KeyMaterial) -> Result<usize, KeyMaterialError> {
564 let new_key_len = self.key_len() + other.key_len();
565 if self.key_len() + other.key_len() > KEY_LEN {
566 return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
567 }
568 self.buf[self.key_len..new_key_len].copy_from_slice(other.ref_to_bytes());
569 self.key_len += other.key_len();
570 self.key_type = max(&self.key_type, &other.key_type()).clone();
571 self.security_strength = max(&self.security_strength, &other.security_strength()).clone();
572 Ok(self.key_len())
573 }
574
575 fn equals(&self, other: &dyn KeyMaterial) -> bool {
576 if self.key_len() != other.key_len() {
577 return false;
578 }
579 ct::ct_eq_bytes(&self.ref_to_bytes(), &other.ref_to_bytes())
580 }
581}
582
583/// Checks for equality of the key data (using a constant-time comparison), but does not check that
584/// the two keys have the same type.
585/// Therefore, for example, two keys loaded from the same bytes, one with type [KeyType::BytesLowEntropy] and
586/// the other with [KeyType::MACKey] will be considered equal.
587impl<const KEY_LEN: usize> PartialEq for KeyMaterialSized<KEY_LEN> {
588 fn eq(&self, other: &Self) -> bool {
589 if self.key_len != other.key_len {
590 return false;
591 }
592 ct::ct_eq_bytes(&self.buf[..self.key_len], &other.buf[..self.key_len])
593 }
594}
595impl<const KEY_LEN: usize> Eq for KeyMaterialSized<KEY_LEN> {}
596
597/// Ordering is as follows:
598/// Zeroized < BytesLowEntropy < BytesFullEntropy < {Seed = MACKey = SymmetricCipherKey}
599impl PartialOrd for KeyType {
600 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
601 match self {
602 KeyType::Zeroized => match other {
603 KeyType::Zeroized => Some(Ordering::Equal),
604 _ => Some(Ordering::Less),
605 },
606 KeyType::BytesLowEntropy => match other {
607 KeyType::Zeroized => Some(Ordering::Greater),
608 KeyType::BytesLowEntropy => Some(Ordering::Equal),
609 _ => Some(Ordering::Less),
610 },
611 KeyType::BytesFullEntropy => match other {
612 KeyType::Zeroized | KeyType::BytesLowEntropy => Some(Ordering::Greater),
613 KeyType::BytesFullEntropy => Some(Ordering::Equal),
614 _ => Some(Ordering::Less),
615 },
616 KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => match other {
617 KeyType::Zeroized | KeyType::BytesLowEntropy | KeyType::BytesFullEntropy => {
618 Some(Ordering::Greater)
619 }
620 KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => {
621 Some(Ordering::Equal)
622 }
623 },
624 }
625 }
626}
627
628/// Block accidental logging of the internal key material buffer.
629impl<const KEY_LEN: usize> fmt::Display for KeyMaterialSized<KEY_LEN> {
630 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631 write!(
632 f,
633 "KeyMaterial {{ len: {}, key_type: {:?}, security_strength: {:?} }}",
634 self.key_len, self.key_type, self.security_strength
635 )
636 }
637}
638
639/// Block accidental logging of the internal key material buffer.
640impl<const KEY_LEN: usize> fmt::Debug for KeyMaterialSized<KEY_LEN> {
641 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642 write!(
643 f,
644 "KeyMaterial {{ len: {}, key_type: {:?}, security_strength: {:?} }}",
645 self.key_len, self.key_type, self.security_strength
646 )
647 }
648}
649
650/// Zeroize the key material on drop.
651impl<const KEY_LEN: usize> Drop for KeyMaterialSized<KEY_LEN> {
652 fn drop(&mut self) {
653 self.zeroize()
654 }
655}