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}