Skip to main content

bouncycastle_core/
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//! The core idea of this wrapper is to keep track of the usage of the key material, including
6//! the amount of entropy that it is presumed to contain in order to prevent users from accidentally
7//! using it inappropriately in a way that could lead to security weaknesses.
8//!
9//! Various operations within the bc-rs library will consume or produce KeyMaterial objects with
10//! specific key types. In normal use of the bc-rs APIs, users should never have to manually convert
11//! the type of a KeyMaterial object because the various function calls will set the key type appropriately.
12//!
13//! Some typical workflows would be:
14//!
15//! * Hash functions take in \[u8\] byte data and return a KeyMaterial of type RawUnknownEntropy.
16//! * 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.
17//! * Keyed KDFs that are given a key of RawFullEntropy or KeyedHashKey a KeyMaterial data of type RawLowEntropy or RawUnknownEntropy will promote it into RawFullEntropy.
18//! * Symmetric ciphers or asymmetric ciphers such as X25519 or ML-KEM that accept private key seeds will expect KeyMaterial of type AsymmetricPrivateKeySeed.
19//!
20//! However, there is a [`KeyMaterialTrait::set_key_type`] for cases where the user has more context knowledge than the library.
21//! Some conversions, such as converting a key of type RawLowEntropy into a SymmetricCipherKey, will fail unless
22//! run inside of a [`do_hazardous_operations`] closure, see below.
23//!
24//! # 🚨 Security 🚨
25//!
26//! Additional security features:
27//!   * Zeroizes on destruction.
28//!   * Implementing Display and Debug to print metadata but not key material to prevent accidental logging.
29//!
30//! # Hazardous Operations
31//!
32//! This object allows several types of manual-overrides, many of which are considered
33//! "hazardous operations" since by definition they are allowing you to bypass checks meant to detect
34//! conditions that could lead to security vulnerabilities.
35//! Consider, for example, that you are reading a symmetric key from somewhere outside the library,
36//! maybe from disk or from another process, but maybe you handed in the wrong variable and instead
37//! handed in an uninitialized (all-zero) buffer.
38//! Since this is a common bug that has catestrophic security implications, the library will normally
39//! check for all-zero KeyMoterial objects and throw an error.
40//! But there will be cases in which you really do need to use an all-zero key, so you can create
41//! one if you do it in hazardous operations mode.
42//!
43//! Examples of hazardous conversions that are required to be run inside of a do_hazardous_operations() closure:
44//!
45//! * Converting a KeyMaterial of type RawLowEntropy or RawUnknownEntropy into RawFullEntropy or any other full-entropy key type.
46//! * 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.
47//!
48//! 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.
49//! 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.
50//!
51//! See [`do_hazardous_operations`] for documentation and sample code.
52
53use crate::errors::{KeyMaterialError, SuspendableError};
54use crate::traits::{RNG, SecurityStrength};
55use bouncycastle_utils::{ct, min, secret::Secret};
56
57use core::cmp::{Ordering, PartialOrd};
58use core::fmt;
59
60/// For when it is necessary to get a zero-length dummy key (an empty HMAC salt, for example).
61pub type KeyMaterial0 = KeyMaterial<0>;
62/// Named type for a 128-bit (16-byte) key, for convenience.
63pub type KeyMaterial128 = KeyMaterial<16>;
64/// Named type for a 256-bit (32-byte) key, for convenience.
65pub type KeyMaterial256 = KeyMaterial<32>;
66/// Named type for a 512-bit (64-byte) key, for convenience.
67pub type KeyMaterial512 = KeyMaterial<64>;
68
69/// A helper class used across the bc-rust.test library to hold bytes-like key material.
70/// See [`KeyMaterial`] for for details, such as constructors.
71#[allow(private_bounds)]
72pub trait KeyMaterialTrait: KeyMaterialInternalTrait {
73    /// Loads the provided data into a new KeyMaterial of the specified type.
74    /// This is discouraged unless the caller knows the provenance of the data, such as loading it
75    /// from a cryptographic private key file.
76    ///
77    /// This behaves differently on all-zero input key depending on whether it is run within a [`do_hazardous_operations`] closure:
78    /// if not set, then it will succeed, setting the key type to [`KeyType::Zeroized`] and also return a [`KeyMaterialError::ActingOnZeroizedKey`]
79    /// to indicate that you may want to perform error-handling, which could be manually setting the key type
80    /// if you intend to allow zero keys, or do some other error-handling, like figure out why your RNG is broken.
81    /// Note that even if a [`KeyMaterialError::ActingOnZeroizedKey`] is returned, the object is still populated and usable.
82    /// For example, you could catch it like this:
83    /// ```
84    /// use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait, do_hazardous_operations};
85    /// use bouncycastle_core::key_material::KeyMaterial;
86    /// use bouncycastle_core::errors::KeyMaterialError;
87    ///
88    /// let key_bytes = [0u8; 16];
89    /// let mut key = KeyMaterial256::new();
90    /// let res = key.set_bytes_as_type(&key_bytes, KeyType::Unknown);
91    /// match res {
92    ///   Err(KeyMaterialError::ActingOnZeroizedKey) => {
93    ///     // Either figure out why your passed an all-zero key,
94    ///     // or set the key type manually, if that's what you intended.
95    ///     do_hazardous_operations(&mut key, |key| {
96    ///         key.set_key_type(KeyType::Unknown)
97    ///     }).unwrap(); // probably you should do something more elegant than .unwrap in your code ;)
98    ///   },
99    ///   Err(_) => { /* figure out what else went wrong */ },
100    ///   Ok(_) => { /* good */ },
101    /// }
102    /// ```
103    /// On the other hand, if run inside a [`do_hazardous_operations`] closure then it will just do what you asked without complaining.
104    ///
105    /// Since this zeroizes and resets the key material, this is considered a dangerous conversion.
106    ///
107    /// Will set the [`SecurityStrength`] automatically according to the following rules:
108    /// * If [`KeyType`] is [`KeyType::Zeroized`] or [`KeyType::Unknown`] then it will be [`SecurityStrength::None`].
109    /// * Otherwise it will set it based on the length of the provided source bytes.
110    fn set_bytes_as_type(
111        &mut self,
112        source: &[u8],
113        key_type: KeyType,
114    ) -> Result<(), KeyMaterialError>;
115
116    /// Get a reference to the underlying key material bytes.
117    ///
118    /// By reading the key bytes out of the [`KeyMaterialTrait`] object, you lose the protections that it offers,
119    /// however, this does not require [`do_hazardous_operations`] in the name of API ergonomics:
120    /// setting [`do_hazardous_operations`] requires a mutable reference and reading the bytes
121    /// is not an operation that should require mutability.
122    fn ref_to_bytes(&self) -> &[u8];
123
124    /// Get a mutable reference to the underlying key material bytes so that you can read or write
125    /// to the underlying bytes without needing to create a temporary buffer, especially useful in
126    /// cases where the required size of that buffer may be tricky to figure out at compile-time.
127    ///
128    /// # 🚨 Hazardous Operation🚨
129    /// This function needs to be run within a [`do_hazardous_operations`] closure.
130    ///
131    /// When writing directly to the buffer, you are responsible for setting the key_len and key_type afterward.
132    fn ref_to_bytes_mut(&mut self) -> Result<&mut [u8], KeyMaterialError>;
133
134    /// The size of the internal buffer; ie the largest key that this instance can hold.
135    /// Equivalent to the <KEY_LEN> constant param this object was created with.
136    fn capacity(&self) -> usize;
137
138    /// Length of the key material in bytes.
139    fn key_len(&self) -> usize;
140
141    /// Sets the internal key length without changing the capacity of the KeyMaterial.
142    /// Primarily intended for truncation if you are provided with a key that is larger than you need,
143    /// or to extend the length of an undersized KeyMaterial.
144    ///
145    /// If truncating, it will automatically downgrade the SecurityStrength accordingly.
146    ///
147    /// # 🚨 Hazardous Operation 🚨
148    /// Using this function to extend the length of a key is always hazardous and needs to be run
149    /// within a [`do_hazardous_operations`] closure since this can result
150    /// in a key containing a large number of zeroes, or containing key material from a previous key
151    /// held in the same buffer. When extending the length, you take responsibility for the security
152    /// implications.
153    ///
154    /// Truncation (that is, reducing the length) is always safe and does not require a
155    /// [`do_hazardous_operations`] closure.
156    fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError>;
157
158    /// Returns the [`KeyType`] of this KeyMaterial object.
159    fn key_type(&self) -> KeyType;
160
161    /// Sets (or safely converts) the [`KeyType`] of this KeyMaterial object.
162    /// Does not perform any operations on the actual key material, other than changing the key_type field.
163    ///
164    /// # 🚨 Hazardous Operation 🚨
165    /// Inside a [`do_hazardous_operations`] closure this will set the key to any [`KeyType`].
166    /// Outside such a closure, only "safe" conversions are permitted: a [`KeyType::CryptographicRandom`]
167    /// key may be converted to any type, and any type may be converted to itself (a no-op).
168    /// A hazardous conversion attempted outside a [`do_hazardous_operations`] closure returns
169    /// [`KeyMaterialError::HazardousOperationNotPermitted`], and converting a [`KeyType::Zeroized`] key
170    /// returns [`KeyMaterialError::ActingOnZeroizedKey`].
171    fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError>;
172
173    /// Security Strength, as used here, aligns with NIST SP 800-90A guidance for random number generation,
174    /// specifically section 8.4.
175    ///
176    /// The idea is to be able to track for cryptographic seeds and bytes-like key objects across the entire library,
177    /// the instatiated security level of the RNG that generated it, and whether it was handled by any intermediate
178    /// objects, such as Key Derivation Functions, that have a smaller internal security level and therefore result in
179    /// downgrading the security level of the key material.
180    ///
181    /// Note that while security strength is closely related to entropy, it is a property of the algorithms
182    /// that touched the key material and not of the key material data itself, and therefore it is
183    /// tracked independantly from key length and entropy level / key type.
184    fn security_strength(&self) -> SecurityStrength;
185
186    /// Set the [`SecurityStrength`] of the KeyMaterial.
187    ///
188    /// # 🚨 Hazardous Operation🚨
189    /// This function needs to be run within a [`do_hazardous_operations`] closure to raise the security
190    /// strength, but not to lower it.
191    ///
192    /// Outside of a [`do_hazardous_operations`] closure it will throw a
193    /// [`KeyMaterialError::HazardousOperationNotPermitted`] on a request to raise the security level, and
194    /// throw a [`KeyMaterialError::InvalidLength`] on a request to set the security level higher than the current key length. Inside a [`do_hazardous_operations`] it will do what you asked without complaining.
195    fn set_security_strength(&mut self, strength: SecurityStrength)
196    -> Result<(), KeyMaterialError>;
197
198    /// Whether or not the KeyMaterial is one of the full entropy key types.
199    fn is_full_entropy(&self) -> bool;
200
201    /// Securely resets the contents to all zeroes.
202    /// Note that KeyMaterial will automatically zeroize itself when dropped, so it is not necessary
203    /// to call this method simply because the object is going out of scope, but it provided
204    /// in case you want to zeroize it early, or before re-using the same instance of KeyMaterial to
205    /// hold a different key, potentially of a different length.
206    fn zeroize(&mut self);
207
208    /// Perform a constant-time comparison between the two key material buffers,
209    /// ignoring differences in capacity, [`KeyType`], [`SecurityStrength`], etc.
210    fn equals(&self, other: &dyn KeyMaterialTrait) -> bool;
211
212    /// Truncate this key material into the provided destination.
213    /// Not an error to provide a destination which is larger than the source.
214    /// Consumes self, use `clone()` if you intend to make a copy.
215    fn truncate(self, into: &mut dyn KeyMaterialTrait);
216}
217
218/// A wrapper for holding bytes-like key material (symmetric keys or seeds) which aims to apply a
219/// strict typing system to prevent many kinds of mis-use mistakes.
220/// The capacity of the internal buffer can be set at compile-time via the <KEY_LEN> param.
221#[derive(Clone)]
222pub struct KeyMaterial<const KEY_LEN: usize> {
223    buf: Secret<[u8; KEY_LEN]>,
224    key_len: Secret<usize>,
225    key_type: KeyType,
226    security_strength: SecurityStrength,
227    allow_hazardous_operations: bool,
228}
229
230// The explicit `#[repr(u8)]` discriminants are the stable on-the-wire encoding used by
231// `SerializableState` implementations (see the `TryFrom<u8>` impl below). Pin each value to its
232// variant name: reordering variants is fine, but never reuse or renumber an existing discriminant,
233// or previously-serialized states will be misread.
234///
235#[derive(Clone, Copy, Debug, Eq, PartialEq)]
236#[repr(u8)]
237pub enum KeyType {
238    /// The KeyMaterial is zeroized and MUST NOT be used for any cryptographic operation in this state.
239    Zeroized = 0,
240
241    /// The KeyMaterial contains non-zero data of unknown key type.
242    /// A KeyMaterial of key type Unknown will always have a [`SecurityStrength`] of [`SecurityStrength::None`].
243    ///
244    /// This is the default KeyType for data loaded via [`KeyMaterial::from_bytes`].
245    /// Promotion from Unknown to any other key type is considered to be a hazardous operation
246    /// and must be done within a [`do_hazardous_operations`] closure.
247    /// If you want to import key material directly into a known key type, use [`KeyMaterial::from_bytes_as_type`],
248    /// which does not require a hazardous operations closure.
249    Unknown = 1,
250
251    /// The KeyMaterial contains data of full entropy and can be safely converted to any other key type.
252    CryptographicRandom = 2,
253
254    /// A seed for asymmetric private keys, RNGs, and other seed-based cryptographic objects.
255    Seed = 3,
256
257    /// A MAC key.
258    MACKey = 4,
259
260    /// A key for a symmetric block or stream cipher.
261    SymmetricCipherKey = 5,
262}
263
264impl TryFrom<u8> for KeyType {
265    type Error = SuspendableError;
266
267    /// Inverse of `self as u8`; rejects unrecognized discriminants with [`SuspendableError::InvalidData`].
268    fn try_from(value: u8) -> Result<Self, Self::Error> {
269        Ok(match value {
270            0 => Self::Zeroized,
271            1 => Self::Unknown,
272            2 => Self::CryptographicRandom,
273            3 => Self::Seed,
274            4 => Self::MACKey,
275            5 => Self::SymmetricCipherKey,
276            _ => return Err(SuspendableError::InvalidData),
277        })
278    }
279}
280
281impl<const KEY_LEN: usize> Default for KeyMaterial<KEY_LEN> {
282    /// Create a new empty (zeroized) instance.
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288impl<const KEY_LEN: usize> KeyMaterial<KEY_LEN> {
289    /// Creates a new empty instance (key_len = 0, key_type = Zeroized).
290    /// If you want a properly populated instance, use [`KeyMaterial::from_rng`].
291    pub fn new() -> Self {
292        Self {
293            buf: Secret::new(),
294            key_len: Secret::new(),
295            key_type: KeyType::Zeroized,
296            security_strength: SecurityStrength::None,
297            allow_hazardous_operations: false,
298        }
299    }
300
301    /// Creates a new instance of KeyMaterial containing random bytes from the provided random number generator.
302    pub fn from_rng(rng: &mut impl RNG) -> Result<Self, KeyMaterialError> {
303        let mut key = Self::new();
304
305        do_hazardous_operations(&mut key, |key| {
306            rng.next_bytes_out(&mut key.ref_to_bytes_mut().unwrap())
307                .map_err(|_| KeyMaterialError::GenericError("RNG failed."))?;
308            Ok(())
309        })?;
310
311        *key.key_len = KEY_LEN;
312        key.key_type = KeyType::CryptographicRandom;
313        key.security_strength = rng.security_strength();
314        Ok(key)
315    }
316
317    /// Constructor.
318    /// Loads the provided data into a new KeyMaterial of type [`KeyType::Unknown`].
319    /// It will detect if you give it all-zero source data and set the key type to [`KeyType::Zeroized`] instead.
320    pub fn from_bytes(source: &[u8]) -> Result<Self, KeyMaterialError> {
321        Self::from_bytes_as_type(source, KeyType::Unknown)
322    }
323
324    /// Constructor.
325    /// Loads the provided data into a new KeyMaterial of the specified type.
326    /// This is discouraged unless the caller knows the provenance of the data, such as loading it
327    /// from a cryptographic private key file.
328    /// It will detect if you give it all-zero source data and set the key type to [`KeyType::Zeroized`] instead.
329    ///
330    /// Will set the [`SecurityStrength`] automatically according to the following rules:
331    /// * If [`KeyType`] is [`KeyType::Zeroized`] or [`KeyType::Unknown`] then it will be [`SecurityStrength::None`].
332    /// * Otherwise it will set it based on the length of the provided source bytes.
333    pub fn from_bytes_as_type(source: &[u8], key_type: KeyType) -> Result<Self, KeyMaterialError> {
334        let mut key_material = Self::default();
335
336        // Special case: catch and ignore the courtesy error about zeroized input and simply return a zeroized key.
337        match key_material.set_bytes_as_type(source, key_type) {
338            Ok(_) => Ok(key_material),
339            Err(KeyMaterialError::ActingOnZeroizedKey) => {
340                debug_assert_eq!(key_material.key_type(), KeyType::Zeroized);
341                Ok(key_material)
342            }
343            Err(e) => Err(e),
344        }
345    }
346
347    /// Copy constructor
348    pub fn from_key(other: &impl KeyMaterialTrait) -> Result<Self, KeyMaterialError> {
349        if other.key_len() > KEY_LEN {
350            return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
351        }
352
353        let mut key = Self::new();
354        key.buf[..other.key_len()].copy_from_slice(other.ref_to_bytes());
355        *key.key_len = other.key_len();
356        key.key_type = other.key_type();
357        key.security_strength = other.security_strength();
358        Ok(key)
359    }
360}
361
362impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
363    fn set_bytes_as_type(
364        &mut self,
365        source: &[u8],
366        key_type: KeyType,
367    ) -> Result<(), KeyMaterialError> {
368        let allowed_hazardous_operations = self.allow_hazardous_operations;
369
370        if source.len() > KEY_LEN {
371            return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
372        }
373
374        let new_key_type = if !allowed_hazardous_operations && ct::ct_eq_zero_bytes(source) {
375            KeyType::Zeroized
376        } else {
377            key_type
378        };
379
380        self.buf[..source.len()].copy_from_slice(source);
381        *self.key_len = source.len();
382        self.key_type = new_key_type;
383
384        do_hazardous_operations(self, |s| {
385            if new_key_type <= KeyType::Unknown {
386                s.set_security_strength(SecurityStrength::None)?;
387            } else {
388                s.set_security_strength(SecurityStrength::from_bits(source.len() * 8))?;
389            }
390            Ok(())
391        })?;
392
393        // return
394        if new_key_type == KeyType::Zeroized {
395            Err(KeyMaterialError::ActingOnZeroizedKey)
396        } else {
397            Ok(())
398        }
399    }
400
401    fn ref_to_bytes(&self) -> &[u8] {
402        &self.buf[..*self.key_len]
403    }
404
405    fn ref_to_bytes_mut(&mut self) -> Result<&mut [u8], KeyMaterialError> {
406        if !self.allow_hazardous_operations {
407            return Err(KeyMaterialError::HazardousOperationNotPermitted);
408        }
409        Ok(self.buf.as_mut())
410    }
411
412    fn capacity(&self) -> usize {
413        KEY_LEN
414    }
415
416    fn key_len(&self) -> usize {
417        *self.key_len
418    }
419
420    fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError> {
421        if key_len > KEY_LEN {
422            return Err(KeyMaterialError::InvalidLength);
423        }
424
425        // are we extending the key length, or truncating?
426        if key_len <= *self.key_len {
427            // truncation is always allowed (not hazardous)
428
429            self.security_strength =
430                min(&self.security_strength, &SecurityStrength::from_bits(key_len * 8)).clone();
431
432            if key_len == 0 {
433                self.key_type = KeyType::Zeroized;
434            }
435
436            *self.key_len = key_len;
437
438            Ok(())
439        } else {
440            if !self.allow_hazardous_operations {
441                return Err(KeyMaterialError::HazardousOperationNotPermitted);
442            }
443            *self.key_len = key_len;
444            Ok(())
445        }
446    }
447
448    fn key_type(&self) -> KeyType {
449        self.key_type.clone()
450    }
451
452    fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError> {
453        if self.allow_hazardous_operations {
454            // just do it
455            self.key_type = key_type;
456            return Ok(());
457        }
458
459        match self.key_type {
460            KeyType::Zeroized => {
461                return Err(KeyMaterialError::ActingOnZeroizedKey);
462            }
463            KeyType::CryptographicRandom => {
464                // raw full entropy can be safely converted to anything.
465                self.key_type = key_type;
466            }
467            KeyType::Unknown => match key_type {
468                KeyType::Unknown => { /* No change */ }
469                _ => {
470                    return Err(KeyMaterialError::HazardousOperationNotPermitted);
471                }
472            },
473            KeyType::MACKey => match key_type {
474                KeyType::MACKey => { /* No change */ }
475                // Else: Once a KeyMaterial is typed, it should stay that way.
476                _ => {
477                    return Err(KeyMaterialError::HazardousOperationNotPermitted);
478                }
479            },
480            KeyType::SymmetricCipherKey => match key_type {
481                KeyType::SymmetricCipherKey => { /* No change */ }
482                // Else: Once a KeyMaterial is typed, it should stay that way.
483                _ => {
484                    return Err(KeyMaterialError::HazardousOperationNotPermitted);
485                }
486            },
487            KeyType::Seed => match key_type {
488                KeyType::Seed => { /* No change */ }
489                // Else: Once a KeyMaterial is typed, it should stay that way.
490                _ => {
491                    return Err(KeyMaterialError::HazardousOperationNotPermitted);
492                }
493            },
494        }
495
496        Ok(())
497    }
498
499    fn security_strength(&self) -> SecurityStrength {
500        self.security_strength.clone()
501    }
502
503    fn set_security_strength(
504        &mut self,
505        strength: SecurityStrength,
506    ) -> Result<(), KeyMaterialError> {
507        if strength > self.security_strength && !self.allow_hazardous_operations {
508            return Err(KeyMaterialError::HazardousOperationNotPermitted);
509        };
510
511        if self.key_type <= KeyType::Unknown && strength > SecurityStrength::None {
512            return Err(KeyMaterialError::SecurityStrength(
513                "BytesLowEntropy keys cannot have a security strength other than None.",
514            ));
515        }
516
517        match strength {
518            SecurityStrength::None => { /* fine, you can always downgrade */ }
519            SecurityStrength::_112bit => {
520                if self.key_len() < 14 {
521                    return Err(KeyMaterialError::SecurityStrength(
522                        "Security strength cannot be higher than key length.",
523                    ));
524                }
525            }
526            SecurityStrength::_128bit => {
527                if self.key_len() < 16 {
528                    return Err(KeyMaterialError::SecurityStrength(
529                        "Security strength cannot be larger than key length.",
530                    ));
531                }
532            }
533            SecurityStrength::_192bit => {
534                if self.key_len() < 24 {
535                    return Err(KeyMaterialError::SecurityStrength(
536                        "Security strength cannot be larger than key length.",
537                    ));
538                }
539            }
540            SecurityStrength::_256bit => {
541                if self.key_len() < 32 {
542                    return Err(KeyMaterialError::SecurityStrength(
543                        "Security strength cannot be larger than key length.",
544                    ));
545                }
546            }
547        }
548
549        self.security_strength = strength;
550        
551        Ok(())
552    }
553
554    fn is_full_entropy(&self) -> bool {
555        match self.key_type {
556            KeyType::CryptographicRandom
557            | KeyType::Seed
558            | KeyType::MACKey
559            | KeyType::SymmetricCipherKey => true,
560            KeyType::Zeroized | KeyType::Unknown => false,
561        }
562    }
563
564    fn zeroize(&mut self) {
565        self.buf.zeroize();
566        self.key_len.zeroize();
567        self.key_type = KeyType::Zeroized;
568    }
569
570    fn equals(&self, other: &dyn KeyMaterialTrait) -> bool {
571        if self.key_len() != other.key_len() {
572            return false;
573        }
574        ct::ct_eq_bytes(&self.ref_to_bytes(), &other.ref_to_bytes())
575    }
576
577    fn truncate(self, into: &mut dyn KeyMaterialTrait) {
578        into.zeroize();
579
580        let bytes_to_copy =
581            if *self.key_len > into.capacity() { into.capacity() } else { *self.key_len };
582
583        do_hazardous_operations(into, |into| {
584            // copy the bytes
585            into.ref_to_bytes_mut()?[..bytes_to_copy]
586                .copy_from_slice(&self.ref_to_bytes()[..bytes_to_copy]);
587
588            // set the metadata
589            into.set_key_len(bytes_to_copy)?;
590            into.set_key_type(self.key_type)?;
591            into.set_security_strength(
592                min(&self.security_strength(), &SecurityStrength::from_bytes(bytes_to_copy))
593                    .clone(),
594            )?;
595
596            Ok(())
597        })
598        // Swallow all errors generated inside the hazardous operations closure.
599        // The set_* calls here are all infallible inside the hazardous closure.
600        .unwrap();
601    }
602}
603
604/// Checks for equality of the key data (using a constant-time comparison), but does not check that
605/// the two keys have the same type.
606/// Therefore, for example, two keys loaded from the same bytes, one with type [`KeyType::Unknown`] and
607/// the other with [`KeyType::MACKey`] will be considered equal.
608impl<const KEY_LEN: usize> PartialEq for KeyMaterial<KEY_LEN> {
609    fn eq(&self, other: &Self) -> bool {
610        if self.key_len != other.key_len {
611            return false;
612        }
613        ct::ct_eq_bytes(&self.buf[..*self.key_len], &other.buf[..*self.key_len])
614    }
615}
616impl<const KEY_LEN: usize> Eq for KeyMaterial<KEY_LEN> {}
617
618/// Ordering is as follows:
619/// Zeroized < BytesLowEntropy < BytesFullEntropy < {Seed = MACKey = SymmetricCipherKey}
620impl PartialOrd for KeyType {
621    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
622        match self {
623            KeyType::Zeroized => match other {
624                KeyType::Zeroized => Some(Ordering::Equal),
625                _ => Some(Ordering::Less),
626            },
627            KeyType::Unknown => match other {
628                KeyType::Zeroized => Some(Ordering::Greater),
629                KeyType::Unknown => Some(Ordering::Equal),
630                _ => Some(Ordering::Less),
631            },
632            KeyType::CryptographicRandom => match other {
633                KeyType::Zeroized | KeyType::Unknown => Some(Ordering::Greater),
634                KeyType::CryptographicRandom => Some(Ordering::Equal),
635                _ => Some(Ordering::Less),
636            },
637            KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => match other {
638                KeyType::Zeroized | KeyType::Unknown | KeyType::CryptographicRandom => {
639                    Some(Ordering::Greater)
640                }
641                KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => {
642                    Some(Ordering::Equal)
643                }
644            },
645        }
646    }
647}
648
649/// Block accidental logging of the internal key material buffer.
650impl<const KEY_LEN: usize> fmt::Display for KeyMaterial<KEY_LEN> {
651    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
652        // deref the key_len explicitly so that Secret doesn't render it as "<redacted>"
653        write!(
654            f,
655            "KeyMaterial<{}>{{ len: {}, key_type: {:?}, security_strength: {:?} }}",
656            KEY_LEN, *self.key_len, self.key_type, self.security_strength
657        )
658    }
659}
660
661/// Block accidental logging of the internal key material buffer.
662impl<const KEY_LEN: usize> fmt::Debug for KeyMaterial<KEY_LEN> {
663    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
664        // deref the key_len explicitly so that Secret doesn't render it as "<redacted>"
665        write!(
666            f,
667            "KeyMaterial<{}>{{ len: {}, key_type: {:?}, security_strength: {:?} }}",
668            KEY_LEN, *self.key_len, self.key_type, self.security_strength
669        )
670    }
671}
672
673/* Hazardous Operations Runner */
674
675/// Internal-use trait holding the low-level hazardous-operations guard toggle.
676///
677/// These methods are deliberately split out of [`KeyMaterialTrait`] into a private trait so that
678/// they are not accessible from outside this module.
679///
680/// This is a supertrait of [`KeyMaterialTrait`], so anything that implements [`KeyMaterialTrait`]
681/// also implements this. [`KeyMaterialTrait`] therefore stays dyn-compatible (both methods here are
682/// object-safe), which matters because `Box<dyn KeyMaterialTrait>` is used widely as a return type.
683trait KeyMaterialInternalTrait {
684    /// Whether this instance is currently allowed to perform potentially hazardous operations.
685    fn allows_hazardous_operations(&self) -> bool;
686    /// Sets this instance to be able to perform potentially hazardous operations such as
687    /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey,
688    /// or manually setting the key bytes via [`KeyMaterialTrait::mut_ref_to_bytes`], which then requires you to be responsible
689    /// for setting the key_len and key_type afterwards.
690    ///
691    /// The purpose of the hazardous operations guard is not to prevent the user from accessing their data,
692    /// but rather to make the developer think carefully about the operation they are about to perform,
693    /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants
694    /// further inspection.
695    ///
696    /// Prefer the scoped [`KeyMaterial::do_hazardous_operations`] wrapper, which calls this and
697    /// [`KeyMaterialInternalTrait::drop_hazardous_operations`] for you so the guard can't be left set.
698    fn allow_hazardous_operations(&mut self);
699
700    /// Resets this instance to not be able to perform potentially hazardous operations.
701    fn drop_hazardous_operations(&mut self);
702}
703
704impl<const KEY_LEN: usize> KeyMaterialInternalTrait for KeyMaterial<KEY_LEN> {
705    fn allows_hazardous_operations(&self) -> bool {
706        self.allow_hazardous_operations
707    }
708    fn allow_hazardous_operations(&mut self) {
709        self.allow_hazardous_operations = true;
710    }
711    fn drop_hazardous_operations(&mut self) {
712        self.allow_hazardous_operations = false;
713    }
714}
715
716/// Runs the provided closure within which hazardous operations are allowed.
717/// All hazardous operations will return a [`KeyMaterialError::HazardousOperationNotPermitted`]
718/// if used outside of this closure.
719///
720/// Example usage:
721///
722/// ```rust
723/// use bouncycastle_core::key_material::{KeyType, KeyMaterial256, KeyMaterialTrait, do_hazardous_operations};
724/// use bouncycastle_core::traits::SecurityStrength;
725///
726/// // Let's create an all-zero key
727/// let mut key = KeyMaterial256::default();
728///
729/// // Let's set a key of all zeroes, which the library would normally force to be
730/// // [KeyType::Zeroized], but we want to force it to [KeyType::Seed], which is considered a
731/// // hazardous operation.
732/// do_hazardous_operations(&mut key, |key| {
733///     key.set_bytes_as_type(&[8u8; 32], KeyType::Seed)
734///     // note that the closure is required to return Result<(), KeyMaterialError>,
735///     // so we can chain [KeyMaterial::set_bytes_as_type], otherwise we would need
736///     // to end with Ok(()).
737/// }).unwrap();
738///
739/// assert_eq!(key.key_len(), 32);
740/// assert_eq!(key.key_type(), KeyType::Seed);
741/// ```
742///
743/// ```rust
744/// use bouncycastle_core::key_material::{KeyType, KeyMaterial256, KeyMaterialTrait, do_hazardous_operations};
745/// use bouncycastle_core::traits::SecurityStrength;
746///
747/// // Let's create an all-zero key
748/// let mut key = KeyMaterial256::default();
749/// assert_eq!(key.key_type(), KeyType::Zeroized);
750/// assert_eq!(key.security_strength(), SecurityStrength::None);
751///
752/// // Now we want to tell the library that this all-zero key
753/// // is to be used as a 32-byte [KeyType::Seed] at the 256-bit security strength,
754/// // which the library will not allow you to do outside of the hazerdous operations closure.
755/// do_hazardous_operations(&mut key, |key| {
756///     key.set_key_len(32)?;
757///     key.set_key_type(KeyType::Seed)?;
758///     key.set_security_strength(SecurityStrength::_256bit)?;
759///     Ok(())
760/// }).unwrap();
761///
762/// assert_eq!(key.key_type(), KeyType::Seed);
763/// assert_eq!(key.security_strength(), SecurityStrength::_256bit);
764/// ```
765///
766/// Another common usage of hazardous operations is to get a direct mutable reference to the
767/// underlying KeyMaterial byte buffer; for example if you want to copy in key bytes from somewhere else.
768///
769/// ```rust
770/// use bouncycastle_core::key_material::{KeyType, KeyMaterial512, KeyMaterialTrait, do_hazardous_operations};
771/// use bouncycastle_core::traits::SecurityStrength;
772///
773/// // In this example, we initialize a KeyMateriol512 (64 bytes) with only 32 bytes of input.
774/// let mut key = KeyMaterial512::from_bytes_as_type(
775///                                 &[1u8; 32],
776///                                 KeyType::CryptographicRandom
777///                         ).unwrap();
778/// assert_eq!(key.key_len(), 32);
779///
780/// // Now we want to expand the length to 64 bytes and copy in an additional 32 bytes of key data,
781/// // using [KeyMaterial::mut_ref_to_bytes].
782/// let additional_bytes = [2u8; 32];
783/// do_hazardous_operations(&mut key, |key| {
784///     key.set_key_len(64)?;
785///     key.ref_to_bytes_mut()?[32..].copy_from_slice(&additional_bytes);
786///     Ok(())
787/// }).unwrap();
788///
789/// assert_eq!(key.key_len(), 64);
790/// // Reading the key bytes via [KeyMateriol::ref_to_bytes] is not a hazardous operation.
791/// assert_eq!(key.ref_to_bytes()[..32], [1u8; 32]);
792/// assert_eq!(key.ref_to_bytes()[32..], [2u8; 32]);
793/// ```
794///
795// Dev note: This is a free function rather than a method on [KeyMaterialTrait] because it is
796// generic over the closure type, which would make the trait non-dyn-compatible; the trait is used
797// as `&dyn KeyMaterialTrait` elsewhere (e.g. [KeyMaterialTrait::concatenate], [KeyMaterialTrait::equals]).
798// The toggle itself lives on the module-private [KeyMaterialInternalTrait], so external crates cannot
799// flip the guard by hand and must go through this scoped wrapper (hence `#[allow(private_bounds)]`).
800#[allow(private_bounds)]
801pub fn do_hazardous_operations<KEY, F>(key: &mut KEY, f: F) -> Result<(), KeyMaterialError>
802where
803    KEY: KeyMaterialTrait + ?Sized,
804    F: FnOnce(&mut KEY) -> Result<(), KeyMaterialError>,
805{
806    let allows = key.allows_hazardous_operations();
807
808    key.allow_hazardous_operations();
809    let ret = f(key);
810
811    // to allow nested closures, if this key instance allowed
812    // before entering, then leave it.
813    if !allows {
814        key.drop_hazardous_operations();
815    }
816    ret
817}