Skip to main content

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, Secret};
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
185impl<const KEY_LEN: usize> Secret for KeyMaterialSized<KEY_LEN> {}
186
187#[derive(Clone, Copy, Debug, Eq, PartialEq)]
188pub enum KeyType {
189    /// The KeyMaterial is zeroized and MUST NOT be used for any cryptographic operation in this state.
190    Zeroized,
191
192    /// The KeyMaterial contains data of low or unknown entropy.
193    BytesLowEntropy,
194
195    /// The KeyMaterial contains data of full entropy and can be safely converted to any other full-entropy key type.
196    BytesFullEntropy,
197
198    /// A seed for asymmetric private keys, RNGs, and other seed-based cryptographic objects.
199    Seed,
200
201    /// A MAC key.
202    MACKey,
203
204    /// A key for a symmetric block or stream cipher.
205    SymmetricCipherKey,
206}
207
208impl<const KEY_LEN: usize> Default for KeyMaterialSized<KEY_LEN> {
209    /// Create a new empty (zeroized) instance.
210    fn default() -> Self {
211        Self::new()
212    }
213}
214
215impl<const KEY_LEN: usize> KeyMaterialSized<KEY_LEN> {
216    pub fn new() -> Self {
217        Self {
218            buf: [0u8; KEY_LEN],
219            key_len: 0,
220            key_type: KeyType::Zeroized,
221            security_strength: SecurityStrength::None,
222            allow_hazardous_operations: false,
223        }
224    }
225
226    /// Create a new instance of KeyMaterial containing random bytes from the provided random number generator.
227    pub fn from_rng(rng: &mut impl RNG) -> Result<Self, KeyMaterialError> {
228        let mut key = Self::new();
229        key.allow_hazardous_operations();
230
231        rng.next_bytes_out(&mut key.mut_ref_to_bytes().unwrap())
232            .map_err(|_| KeyMaterialError::GenericError("RNG failed."))?;
233
234        key.key_len = KEY_LEN;
235        key.key_type = KeyType::BytesFullEntropy;
236        key.security_strength = rng.security_strength();
237        key.drop_hazardous_operations();
238        Ok(key)
239    }
240
241    /// Constructor.
242    /// Loads the provided data into a new KeyMaterial of type [KeyType::BytesLowEntropy].
243    /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
244    pub fn from_bytes(source: &[u8]) -> Result<Self, KeyMaterialError> {
245        Self::from_bytes_as_type(source, KeyType::BytesLowEntropy)
246    }
247
248    /// Constructor.
249    /// Loads the provided data into a new KeyMaterial of the specified type.
250    /// This is discouraged unless the caller knows the provenance of the data, such as loading it
251    /// from a cryptographic private key file.
252    /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
253    ///
254    /// Will set the [SecurityStrength] automatically according to the following rules:
255    /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None].
256    /// * Otherwise it will set it based on the length of the provided source bytes.
257    pub fn from_bytes_as_type(source: &[u8], key_type: KeyType) -> Result<Self, KeyMaterialError> {
258        let mut key_material = Self::default();
259
260        // Special case: catch and ignore the courtesy error about zeroized input and simply return a zeroized key.
261        match key_material.set_bytes_as_type(source, key_type) {
262            Ok(_) => Ok(key_material),
263            Err(KeyMaterialError::ActingOnZeroizedKey) => {
264                debug_assert_eq!(key_material.key_type(), KeyType::Zeroized);
265                Ok(key_material)
266            }
267            Err(e) => Err(e),
268        }
269    }
270
271    /// Copy constructor
272    pub fn from_key(other: &impl KeyMaterial) -> Result<Self, KeyMaterialError> {
273        if other.key_len() > KEY_LEN {
274            return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
275        }
276
277        let mut key = Self {
278            buf: [0u8; KEY_LEN],
279            key_len: other.key_len(),
280            key_type: other.key_type(),
281            security_strength: SecurityStrength::None,
282            allow_hazardous_operations: false,
283        };
284        key.buf[..other.key_len()].copy_from_slice(other.ref_to_bytes());
285        Ok(key)
286    }
287}
288
289impl<const KEY_LEN: usize> KeyMaterial for KeyMaterialSized<KEY_LEN> {
290    /// Loads the provided data into a new KeyMaterial of the specified type.
291    /// This is discouraged unless the caller knows the provenance of the data, such as loading it
292    /// from a cryptographic private key file.
293    ///
294    /// This behaves differently on all-zero input key depending on whether [KeyMaterial::allow_hazardous_operations] is set:
295    /// if not set, then it will succeed, setting the key type to [KeyType::Zeroized] and also return a [KeyMaterialError::ActingOnZeroizedKey]
296    /// to indicate that you may want to perform error-handling, which could be manually setting the key type
297    /// if you intend to allow zero keys, or do some other error-handling, like figure out why your RNG is broken.
298    /// Note that even if a [KeyMaterialError::ActingOnZeroizedKey] is returned, the object is still populated and usable.
299    /// For example, you could catch it like this:
300    /// ```
301    /// use core_interface::key_material::{KeyMaterial256, KeyType};
302    /// use core_interface::key_material::KeyMaterial;
303    /// use core_interface::errors::KeyMaterialError;
304    ///
305    /// let key_bytes = [0u8; 16];
306    /// let mut key = KeyMaterial256::new();
307    /// let res = key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy);
308    /// match res {
309    ///   Err(KeyMaterialError::ActingOnZeroizedKey) => {
310    ///     // Either figure out why your passed an all-zero key,
311    ///     // or set the key type manually, if that's what you intended.
312    ///     key.allow_hazardous_operations();
313    ///     key.set_key_type(KeyType::BytesLowEntropy).unwrap(); // probably you should do something more elegant than .unwrap in your code ;)
314    ///     key.drop_hazardous_operations();
315    ///   },
316    ///   Err(_) => { /* figure out what else went wrong */ },
317    ///   Ok(_) => { /* good */ },
318    /// }
319    /// ```
320    /// On the other hand, if [KeyMaterial::allow_hazardous_operations] is set then it will just do what you asked without complaining.
321    ///
322    /// Since this zeroizes and resets the key material, this is considered a dangerous conversion.
323    ///
324    /// Will set the [SecurityStrength] automatically according to the following rules:
325    /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None].
326    /// * Otherwise it will set it based on the length of the provided source bytes.
327    fn set_bytes_as_type(
328        &mut self,
329        source: &[u8],
330        key_type: KeyType,
331    ) -> Result<(), KeyMaterialError> {
332        let allowed_hazardous_operations = self.allow_hazardous_operations;
333        self.allow_hazardous_operations();
334
335        if source.len() > KEY_LEN {
336            return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
337        }
338
339        let new_key_type = if !allowed_hazardous_operations && ct::ct_eq_zero_bytes(source) {
340            KeyType::Zeroized
341        } else {
342            key_type
343        };
344
345        self.buf[..source.len()].copy_from_slice(source);
346        self.key_len = source.len();
347        self.key_type = new_key_type;
348
349        if new_key_type <= KeyType::BytesLowEntropy {
350            self.set_security_strength(SecurityStrength::None)?;
351        } else {
352            self.set_security_strength(SecurityStrength::from_bits(source.len() * 8))?;
353        }
354        self.drop_hazardous_operations();
355
356        // return
357        if new_key_type == KeyType::Zeroized {
358            Err(KeyMaterialError::ActingOnZeroizedKey)
359        } else {
360            Ok(())
361        }
362    }
363
364    fn ref_to_bytes(&self) -> &[u8] {
365        &self.buf[..self.key_len]
366    }
367
368    fn mut_ref_to_bytes(&mut self) -> Result<&mut [u8], KeyMaterialError> {
369        if !self.allow_hazardous_operations {
370            return Err(KeyMaterialError::HazardousOperationNotPermitted);
371        }
372        Ok(&mut self.buf)
373    }
374
375    fn capacity(&self) -> usize {
376        KEY_LEN
377    }
378
379    fn key_len(&self) -> usize {
380        self.key_len
381    }
382
383    fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError> {
384        if !self.allow_hazardous_operations {
385            return Err(KeyMaterialError::HazardousOperationNotPermitted);
386        }
387        if key_len > KEY_LEN {
388            return Err(KeyMaterialError::InvalidLength);
389        }
390        self.key_len = key_len;
391        Ok(())
392    }
393    fn key_type(&self) -> KeyType {
394        self.key_type.clone()
395    }
396    fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError> {
397        if !self.allow_hazardous_operations {
398            return Err(KeyMaterialError::HazardousOperationNotPermitted);
399        }
400        self.key_type = key_type.clone();
401        Ok(())
402    }
403    fn security_strength(&self) -> SecurityStrength {
404        self.security_strength.clone()
405    }
406
407    fn set_security_strength(
408        &mut self,
409        strength: SecurityStrength,
410    ) -> Result<(), KeyMaterialError> {
411        if strength > self.security_strength && !self.allow_hazardous_operations {
412            return Err(KeyMaterialError::HazardousOperationNotPermitted);
413        };
414
415        if self.key_type <= KeyType::BytesLowEntropy && strength > SecurityStrength::None {
416            return Err(KeyMaterialError::SecurityStrength(
417                "BytesLowEntropy keys cannot have a security strength other than None.",
418            ));
419        }
420
421        match strength {
422            SecurityStrength::None => { /* fine, you can always downgrade */ }
423            SecurityStrength::_112bit => {
424                if self.key_len() < 14 {
425                    return Err(KeyMaterialError::SecurityStrength(
426                        "Security strength cannot be higher than key length.",
427                    ));
428                }
429            }
430            SecurityStrength::_128bit => {
431                if self.key_len() < 16 {
432                    return Err(KeyMaterialError::SecurityStrength(
433                        "Security strength cannot be larger than key length.",
434                    ));
435                }
436            }
437            SecurityStrength::_192bit => {
438                if self.key_len() < 24 {
439                    return Err(KeyMaterialError::SecurityStrength(
440                        "Security strength cannot be larger than key length.",
441                    ));
442                }
443            }
444            SecurityStrength::_256bit => {
445                if self.key_len() < 32 {
446                    return Err(KeyMaterialError::SecurityStrength(
447                        "Security strength cannot be larger than key length.",
448                    ));
449                }
450            }
451        }
452
453        self.security_strength = strength;
454        self.drop_hazardous_operations();
455        Ok(())
456    }
457    /// Sets this instance to be able to perform potentially hazardous operations such as
458    /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey.
459    ///
460    /// The purpose of the hazardous operations guard is not to prevent the user from accessing their data,
461    /// but rather to make the developer think carefully about the operation they are about to perform,
462    /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants
463    /// further inspection.
464    fn allow_hazardous_operations(&mut self) {
465        self.allow_hazardous_operations = true;
466    }
467    /// Resets this instance to not be able to perform potentially hazardous operations.
468    fn drop_hazardous_operations(&mut self) {
469        self.allow_hazardous_operations = false;
470    }
471    /// Sets the key_type of this KeyMaterial object.
472    /// Does not perform any operations on the actual key material, other than changing the key_type field.
473    /// If allow_hazardous_operations is true, this method will allow conversion to any KeyType, otherwise
474    /// checking is performed to ensure that the conversion is "safe".
475    /// This drops the allow_hazardous_operations flag, so if you need to do multiple hazardous operations
476    /// on the same instance, then you'll need to call .allow_hazardous_operations() each time.
477    fn convert_key_type(&mut self, new_key_type: KeyType) -> Result<(), KeyMaterialError> {
478        if self.allow_hazardous_operations {
479            // just do it
480            self.key_type = new_key_type;
481            return Ok(());
482        }
483
484        match self.key_type {
485            KeyType::Zeroized => {
486                return Err(KeyMaterialError::ActingOnZeroizedKey);
487            }
488            KeyType::BytesFullEntropy => {
489                // raw full entropy can be safely converted to anything.
490                self.key_type = new_key_type;
491            }
492            KeyType::BytesLowEntropy => {
493                match new_key_type {
494                    KeyType::BytesLowEntropy => { /* No change */ }
495                    _ => {
496                        return Err(KeyMaterialError::HazardousOperationNotPermitted);
497                    }
498                }
499            }
500            KeyType::MACKey => {
501                match new_key_type {
502                    KeyType::MACKey => { /* No change */ }
503                    // Else: Once a KeyMaterial is typed, it should stay that way.
504                    _ => {
505                        return Err(KeyMaterialError::HazardousOperationNotPermitted);
506                    }
507                }
508            }
509            KeyType::SymmetricCipherKey => {
510                match new_key_type {
511                    KeyType::SymmetricCipherKey => { /* No change */ }
512                    // Else: Once a KeyMaterial is typed, it should stay that way.
513                    _ => {
514                        return Err(KeyMaterialError::HazardousOperationNotPermitted);
515                    }
516                }
517            }
518            KeyType::Seed => {
519                match new_key_type {
520                    KeyType::Seed => { /* No change */ }
521                    // Else: Once a KeyMaterial is typed, it should stay that way.
522                    _ => {
523                        return Err(KeyMaterialError::HazardousOperationNotPermitted);
524                    }
525                }
526            }
527        }
528
529        // each call to allow_hazardous_operations() is only good for one conversion.
530        self.drop_hazardous_operations();
531        Ok(())
532    }
533    fn is_full_entropy(&self) -> bool {
534        match self.key_type {
535            KeyType::BytesFullEntropy
536            | KeyType::Seed
537            | KeyType::MACKey
538            | KeyType::SymmetricCipherKey => true,
539            KeyType::Zeroized | KeyType::BytesLowEntropy => false,
540        }
541    }
542
543    fn zeroize(&mut self) {
544        self.buf.fill(0u8);
545        self.key_len = 0;
546        self.key_type = KeyType::Zeroized;
547    }
548
549    fn truncate(&mut self, new_len: usize) -> Result<(), KeyMaterialError> {
550        if new_len > self.key_len {
551            return Err(KeyMaterialError::InvalidLength);
552        }
553
554        self.security_strength =
555            min(&self.security_strength, &SecurityStrength::from_bits(new_len * 8)).clone();
556
557        if new_len == 0 {
558            self.key_type = KeyType::Zeroized;
559        }
560
561        self.key_len = new_len;
562        Ok(())
563    }
564
565    fn concatenate(&mut self, other: &dyn KeyMaterial) -> Result<usize, KeyMaterialError> {
566        let new_key_len = self.key_len() + other.key_len();
567        if self.key_len() + other.key_len() > KEY_LEN {
568            return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
569        }
570        self.buf[self.key_len..new_key_len].copy_from_slice(other.ref_to_bytes());
571        self.key_len += other.key_len();
572        self.key_type = max(&self.key_type, &other.key_type()).clone();
573        self.security_strength = max(&self.security_strength, &other.security_strength()).clone();
574        Ok(self.key_len())
575    }
576
577    fn equals(&self, other: &dyn KeyMaterial) -> bool {
578        if self.key_len() != other.key_len() {
579            return false;
580        }
581        ct::ct_eq_bytes(&self.ref_to_bytes(), &other.ref_to_bytes())
582    }
583}
584
585/// Checks for equality of the key data (using a constant-time comparison), but does not check that
586/// the two keys have the same type.
587/// Therefore, for example, two keys loaded from the same bytes, one with type [KeyType::BytesLowEntropy] and
588/// the other with [KeyType::MACKey] will be considered equal.
589impl<const KEY_LEN: usize> PartialEq for KeyMaterialSized<KEY_LEN> {
590    fn eq(&self, other: &Self) -> bool {
591        if self.key_len != other.key_len {
592            return false;
593        }
594        ct::ct_eq_bytes(&self.buf[..self.key_len], &other.buf[..self.key_len])
595    }
596}
597impl<const KEY_LEN: usize> Eq for KeyMaterialSized<KEY_LEN> {}
598
599/// Ordering is as follows:
600/// Zeroized < BytesLowEntropy < BytesFullEntropy < {Seed = MACKey = SymmetricCipherKey}
601impl PartialOrd for KeyType {
602    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
603        match self {
604            KeyType::Zeroized => match other {
605                KeyType::Zeroized => Some(Ordering::Equal),
606                _ => Some(Ordering::Less),
607            },
608            KeyType::BytesLowEntropy => match other {
609                KeyType::Zeroized => Some(Ordering::Greater),
610                KeyType::BytesLowEntropy => Some(Ordering::Equal),
611                _ => Some(Ordering::Less),
612            },
613            KeyType::BytesFullEntropy => match other {
614                KeyType::Zeroized | KeyType::BytesLowEntropy => Some(Ordering::Greater),
615                KeyType::BytesFullEntropy => Some(Ordering::Equal),
616                _ => Some(Ordering::Less),
617            },
618            KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => match other {
619                KeyType::Zeroized | KeyType::BytesLowEntropy | KeyType::BytesFullEntropy => {
620                    Some(Ordering::Greater)
621                }
622                KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => {
623                    Some(Ordering::Equal)
624                }
625            },
626        }
627    }
628}
629
630/// Block accidental logging of the internal key material buffer.
631impl<const KEY_LEN: usize> fmt::Display for KeyMaterialSized<KEY_LEN> {
632    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633        write!(
634            f,
635            "KeyMaterial {{ len: {}, key_type: {:?}, security_strength: {:?} }}",
636            self.key_len, self.key_type, self.security_strength
637        )
638    }
639}
640
641/// Block accidental logging of the internal key material buffer.
642impl<const KEY_LEN: usize> fmt::Debug for KeyMaterialSized<KEY_LEN> {
643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644        write!(
645            f,
646            "KeyMaterial {{ len: {}, key_type: {:?}, security_strength: {:?} }}",
647            self.key_len, self.key_type, self.security_strength
648        )
649    }
650}
651
652/// Zeroize the key material on drop.
653impl<const KEY_LEN: usize> Drop for KeyMaterialSized<KEY_LEN> {
654    fn drop(&mut self) {
655        self.zeroize()
656    }
657}