secret_tree/lib.rs
1//! Hierarchical secret derivation with Blake2b and random number generators.
2//!
3//! # How it works
4//!
5//! This crate provides [`SecretTree`] – a structure produced from a 32-byte seed that
6//! may be converted into a secret key or a cryptographically secure
7//! pseudo-random number generator (CSPRNG).
8//! Besides that, an `SecretTree` can produce child trees, which are
9//! identified by a string [`Name`] or an integer index. This enables creating
10//! *hierarchies* of secrets (like `some_secret/0`, `some_secret/1` and `other_secret/foo/1/bar`),
11//! which are ultimately derived from a single `SecretTree`. It’s enough to securely store
12//! the seed of this root tree (e.g., in a passphrase-encrypted form) to recreate all secrets.
13//!
14//! The derived secrets cannot be linked; leakage of a derived secret does not compromise
15//! sibling secrets or the parent `SecretTree`.
16//!
17//! # Crate features
18//!
19//! The crate is `no_std`-compatible. There is optional `std` support enabled via the `std` feature,
20//! which is on by default.
21//!
22//! # Implementation details
23//!
24//! `SecretTree` uses the [Blake2b] keyed hash function to derive the following kinds of data:
25//!
26//! - secret key
27//! - CSPRNG seed (the RNG used is [`ChaChaRng`])
28//! - seeds for child `SecretTree`s
29//!
30//! The procedure is similar to the use of Blake2b for key derivation in [libsodium]\:
31//!
32//! - Blake2b is used with a custom initialization block. The block has two
33//! customizable parameters of interest: *salt* and *personalization* (each is 16 bytes).
34//! See the table below for information how these two parameters are set for each type
35//! of derived data.
36//! - The key is the seed of the `SecretTree` instance used for derivation.
37//! - The message is an empty bit string.
38//!
39//! The length of derived data is 32 bytes in all cases.
40//!
41//! ## Salt and personalization
42//!
43//! | Data type | Salt | Personalization |
44//! |:----------|:-----|:----------------|
45//! | Secret key | `[0; 16]` | `b"bytes\0\0...\0"` |
46//! | CSPRNG seed | `[0; 16]` | `b"rng\0\0...\0"` |
47//! | Seed for a [named child](SecretTree::child()) | `name.as_bytes()` (zero-padded) | `b"name\0\0...\0"` |
48//! | Seed for an [indexed child](SecretTree::index()) | `LittleEndian(index)` | `b"index\0\0...\0"` |
49//! | Seed for a [digest child](SecretTree::digest()) (1st iter) | `digest[..16]` | `b"digest0\0\0...\0"` |
50//! | Seed for a digest child (2nd iter) | `digest[16..]` | `b"digest1\0\0...\0"` |
51//!
52//! Derivation of a secret key, CSPRNG seed and seeds for indexed children are
53//! all fully compatible with libsodium.
54//! libsodium uses the salt section in the Blake2b initialization block to store
55//! the *index* of a child key, and the personalization section to store its *context*.
56//!
57//! For example, the CSPRNG seed can be computed as follows (if we translate libsodium API
58//! from C to Rust):
59//!
60//! ```
61//! use rand::SeedableRng;
62//! use rand_chacha::ChaChaRng;
63//! # fn crypto_kdf_derive_from_key(_: &mut [u8], _: u64, _: &[u8; 8], _: &[u8; 32]) {}
64//!
65//! let parent_seed: [u8; 32] = // ...
66//! # [0; 32];
67//! let mut rng_seed = [0; 32];
68//! crypto_kdf_derive_from_key(
69//! &mut rng_seed,
70//! /* index */ 0,
71//! /* context */ b"rng\0\0\0\0\0",
72//! /* master_key */ &parent_seed,
73//! );
74//! let rng = ChaChaRng::from_seed(rng_seed);
75//! ```
76//!
77//! In case of named and digest children, we utilize the entire salt section, while libsodium
78//! only uses the first 8 bytes.
79//!
80//! For digest children, the derivation procedure is applied 2 times, taking the first 16 bytes
81//! and the remaining 16 bytes of the digest respectively. The 32-byte key derived on the first
82//! iteration is used as the master key input for the second iteration. Such a procedure
83//! is necessary because Blake2b only supports 16-byte salts.
84//!
85//! # Design motivations
86//!
87//! - We allow to derive RNGs besides keys in order to allow a richer variety of applications.
88//! RNGs can be used in more complex use cases than fixed-size byte arrays,
89//! e.g., when the length of the secret depends on previous RNG output, or RNG is used to sample
90//! a complex distribution.
91//! - Derivation in general (instead of using a single `SeedableRng` to create all secrets)
92//! allows to add new secrets or remove old ones without worrying about compatibility.
93//! - Child RNGs identified by an index can be used to derive secrets of the same type,
94//! the quantity of which is unbounded. As an example, they can be used to produce
95//! blinding factors for [Pedersen commitments] (e.g., in a privacy-focused cryptocurrency).
96//! - Some steps are taken to make it difficult to use `SecretTree` incorrectly. For example,
97//! `rng()` and `fill()` methods consume the tree instance, which makes it harder to reuse
98//! the same RNG for multiple purposes (which is not intended).
99//!
100//! [libsodium]: https://download.libsodium.org/doc/key_derivation
101//! [Blake2b]: https://tools.ietf.org/html/rfc7693
102//! [Pedersen commitments]: https://en.wikipedia.org/wiki/Commitment_scheme
103
104#![cfg_attr(not(feature = "std"), no_std)]
105// Documentation settings
106#![doc(html_root_url = "https://docs.rs/secret-tree/0.5.0")]
107// Linter settings
108#![warn(missing_docs, missing_debug_implementations)]
109#![warn(clippy::all, clippy::pedantic)]
110#![allow(
111 clippy::missing_errors_doc,
112 clippy::must_use_candidate,
113 clippy::module_name_repetitions
114)]
115
116#[cfg(all(not(feature = "std"), test))]
117extern crate std;
118
119use rand::{CryptoRng, RngCore, SeedableRng};
120use rand_chacha::ChaChaRng;
121use secrecy::{zeroize::Zeroize, ExposeSecret, Secret};
122
123use core::{
124 array::TryFromSliceError,
125 convert::TryInto,
126 fmt,
127 str::{self, FromStr},
128};
129
130mod byte_slice;
131mod kdf;
132
133pub use crate::{byte_slice::AsByteSliceMut, kdf::SEED_LEN};
134
135use crate::kdf::{derive_key, try_derive_key, Index, CONTEXT_LEN, SALT_LEN};
136
137/// Maximum byte length of a [`Name`] (16).
138pub const MAX_NAME_LEN: usize = SALT_LEN;
139
140/// Alias for a [`Secret`] array that contains seed bytes.
141pub type Seed = Secret<[u8; SEED_LEN]>;
142
143/// Seeded structure that can be used to produce secrets and child `SecretTree`s.
144///
145/// # Usage
146///
147/// During the program lifecycle, a root `SecretTree` should be restored from
148/// a secure persistent form (e.g., a passphrase-encrypted file) and then used to derive
149/// child trees and secrets. On the first use, the root should be initialized from a CSPRNG, such
150/// as `rand::thread_rng()`. The tree is not needed during the program execution and can
151/// be safely dropped after deriving necessary secrets (which zeroes out the tree seed).
152///
153/// It is possible to modify the derivation hierarchy over the course of program evolution
154/// by adding new secrets or abandoning the existing ones.
155/// However, the purpose of any given tree path should be fixed; that is, if some version
156/// of a program used path `foo/bar` to derive an Ed25519 keypair, a newer version
157/// shouldn’t use `foo/bar` to derive an AES-128 key. Violating this rule may lead
158/// to leaking the secret.
159///
160/// # Examples
161///
162/// ```
163/// use secret_tree::{SecretTree, Name};
164/// use rand::{Rng, thread_rng};
165/// use secrecy::{ExposeSecret, Secret};
166///
167/// let tree = SecretTree::new(&mut thread_rng());
168/// // Don't forget to securely store secrets! Here, we wrap them
169/// // in a container that automatically zeroes the secret on drop.
170/// let first_secret: Secret<[u8; 32]> = tree
171/// .child(Name::new("first"))
172/// .create_secret();
173///
174/// // We can derive hierarchical secrets. The secrets below
175/// // follow logical paths `sequence/0`, `sequence/1`, .., `sequence/4`
176/// // relative to the `tree`.
177/// let child_store = tree.child(Name::new("sequence"));
178/// let more_secrets: Vec<Secret<[u64; 4]>> = (0..5)
179/// .map(|i| Secret::new(child_store.index(i).rng().gen()))
180/// .collect();
181///
182/// // The tree is compactly stored as a single 32-byte seed.
183/// let seed = tree.seed().to_owned();
184/// drop(tree);
185///
186/// // If we restore the tree from the seed, we can restore all derived secrets.
187/// let tree = SecretTree::from_seed(seed);
188/// let restored_secret: Secret<[u8; 32]> = tree
189/// .child(Name::new("first"))
190/// .create_secret();
191/// assert_eq!(
192/// first_secret.expose_secret(),
193/// restored_secret.expose_secret()
194/// );
195/// ```
196#[derive(Debug)]
197#[must_use = "A tree should generate a secret or child tree"]
198pub struct SecretTree {
199 seed: Seed,
200}
201
202impl SecretTree {
203 const FILL_BYTES_CONTEXT: [u8; CONTEXT_LEN] = *b"bytes\0\0\0";
204 const RNG_CONTEXT: [u8; CONTEXT_LEN] = *b"rng\0\0\0\0\0";
205 const NAME_CONTEXT: [u8; CONTEXT_LEN] = *b"name\0\0\0\0";
206 const INDEX_CONTEXT: [u8; CONTEXT_LEN] = *b"index\0\0\0";
207 const DIGEST_START_CONTEXT: [u8; CONTEXT_LEN] = *b"digest0\0";
208 const DIGEST_END_CONTEXT: [u8; CONTEXT_LEN] = *b"digest1\0";
209
210 /// Generates a tree by sampling its seed from the supplied RNG.
211 pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
212 let mut seed = [0; 32];
213 rng.fill_bytes(&mut seed);
214 Self {
215 seed: Secret::new(seed),
216 }
217 }
218
219 /// Creates a tree from the seed.
220 pub fn from_seed(seed: Seed) -> Self {
221 Self { seed }
222 }
223
224 /// Restores a tree from the seed specified as a byte slice.
225 ///
226 /// # Errors
227 ///
228 /// Returns an error if `bytes` has an invalid length (not [`SEED_LEN`]).
229 pub fn from_slice(bytes: &[u8]) -> Result<Self, TryFromSliceError> {
230 let seed: [u8; 32] = bytes.try_into()?;
231 Ok(Self {
232 seed: Secret::new(seed),
233 })
234 }
235
236 /// Returns the tree seed.
237 pub fn seed(&self) -> &Seed {
238 &self.seed
239 }
240
241 /// Converts this tree into a cryptographically secure pseudo-random number generator
242 /// (CSPRNG). This RNG can then be used to reproducibly create secrets (e.g., secret keys).
243 ///
244 /// # Security
245 ///
246 /// [`Self::fill()`] should be preferred if the secret allows it. While using a CSPRNG
247 /// to generate secrets is theoretically sound, it introduces a new entity that
248 /// may leak information.
249 /// `fill()` is especially useful if the filled buffer implements zeroing on drop;
250 /// the state of a CSPRNG generator returned by `rng()` **is not** zeroed on drop and thus
251 /// creates a potential attack vector. (However theoretical it may be; `ChaChaRng`
252 /// has a notably small state size - ~160 bytes, so it may be better localized
253 /// and have lower risk to be accessed by the adversary than other CSPRNG implementations.)
254 pub fn rng(self) -> ChaChaRng {
255 let mut seed = <ChaChaRng as SeedableRng>::Seed::default();
256 derive_key(
257 seed.as_mut(),
258 Index::None,
259 Self::RNG_CONTEXT,
260 self.seed.expose_secret(),
261 );
262 ChaChaRng::from_seed(seed)
263 }
264
265 /// Tries to fill the specified buffer with a key derived from the seed of this tree.
266 ///
267 /// # Errors
268 ///
269 /// Errors if the buffer does not have length `16..=64` bytes. Use [`Self::rng()`]
270 /// if the buffer size may be outside these bounds, or if the secret must be derived
271 /// in a more complex way.
272 pub fn try_fill<T: AsByteSliceMut + ?Sized>(self, dest: &mut T) -> Result<(), FillError> {
273 try_derive_key(
274 dest.as_byte_slice_mut(),
275 Index::None,
276 Self::FILL_BYTES_CONTEXT,
277 self.seed.expose_secret(),
278 )?;
279 dest.convert_to_le();
280 Ok(())
281 }
282
283 /// Fills the specified buffer with a key derived from the seed of this tree.
284 ///
285 /// # Panics
286 ///
287 /// Panics in the same cases when [`Self::try_fill()`] returns an error.
288 pub fn fill<T: AsByteSliceMut + ?Sized>(self, dest: &mut T) {
289 self.try_fill(dest).unwrap_or_else(|err| {
290 panic!("Failed filling a buffer from `SecretTree`: {}", err);
291 });
292 }
293
294 /// Tries to create a secret by instantiating a buffer and filling it with a key derived from
295 /// the seed of this tree. Essentially, this is a more high-level wrapper around
296 /// [`Self::try_fill()`].
297 ///
298 /// # Errors
299 ///
300 /// Returns an error if `T` does not have length `16..=64` bytes. Use [`Self::rng()`]
301 /// if the buffer size may be outside these bounds, or if the secret must be derived
302 /// in a more complex way.
303 pub fn try_create_secret<T>(self) -> Result<Secret<T>, FillError>
304 where
305 T: AsByteSliceMut + Default + Zeroize,
306 {
307 let mut secret_value = T::default();
308 self.try_fill(&mut secret_value)?;
309 Ok(Secret::new(secret_value))
310 }
311
312 /// Creates a secret by instantiating a buffer and filling it with a key derived from
313 /// the seed of this tree.
314 ///
315 /// # Panics
316 ///
317 /// Panics in the same cases when [`Self::try_create_secret()`] returns an error.
318 pub fn create_secret<T>(self) -> Secret<T>
319 where
320 T: AsByteSliceMut + Default + Zeroize,
321 {
322 self.try_create_secret().unwrap_or_else(|err| {
323 panic!("Failed creating a secret from `SecretTree`: {}", err);
324 })
325 }
326
327 /// Produces a child with the specified string identifier.
328 pub fn child(&self, name: Name) -> Self {
329 let mut child_seed = [0_u8; 32];
330 derive_key(
331 &mut child_seed,
332 Index::Bytes(name.0),
333 Self::NAME_CONTEXT,
334 self.seed.expose_secret(),
335 );
336 Self::from_seed(Secret::new(child_seed))
337 }
338
339 /// Produces a child with the specified integer index.
340 pub fn index(&self, index: u64) -> Self {
341 let mut child_seed = [0_u8; 32];
342 derive_key(
343 &mut child_seed,
344 Index::Number(index),
345 Self::INDEX_CONTEXT,
346 self.seed.expose_secret(),
347 );
348 Self::from_seed(Secret::new(child_seed))
349 }
350
351 /// Produces a child with the specified 32-byte digest (e.g., an output of SHA-256,
352 /// SHA3-256 or Keccak256 hash functions).
353 ///
354 /// This method can be used for arbitrarily-sized keys by first digesting them
355 /// with a collision-resistant hash function.
356 pub fn digest(&self, digest: &[u8; 32]) -> Self {
357 let mut first_half_of_digest = [0_u8; SALT_LEN];
358 first_half_of_digest.copy_from_slice(&digest[0..SALT_LEN]);
359 let mut second_half_of_digest = [0_u8; SALT_LEN];
360 second_half_of_digest.copy_from_slice(&digest[SALT_LEN..]);
361
362 let mut intermediate_seed = [0_u8; 32];
363 derive_key(
364 &mut intermediate_seed,
365 Index::Bytes(first_half_of_digest),
366 Self::DIGEST_START_CONTEXT,
367 self.seed.expose_secret(),
368 );
369 let intermediate_seed = Secret::new(intermediate_seed);
370
371 let mut child_seed = [0_u8; 32];
372 derive_key(
373 &mut child_seed,
374 Index::Bytes(second_half_of_digest),
375 Self::DIGEST_END_CONTEXT,
376 intermediate_seed.expose_secret(),
377 );
378 Self::from_seed(Secret::new(child_seed))
379 }
380}
381
382/// Errors that can occur when calling [`SecretTree::try_fill()`].
383#[derive(Debug)]
384#[non_exhaustive]
385pub enum FillError {
386 /// The supplied buffer is too small to be filled.
387 BufferTooSmall {
388 /// Byte size of the supplied buffer.
389 size: usize,
390 /// Minimum byte size for supported buffers.
391 min_supported_size: usize,
392 },
393 /// The supplied buffer is too large to be filled.
394 BufferTooLarge {
395 /// Byte size of the supplied buffer.
396 size: usize,
397 /// Maximum byte size for supported buffers.
398 max_supported_size: usize,
399 },
400}
401
402impl fmt::Display for FillError {
403 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
404 match self {
405 Self::BufferTooSmall {
406 size,
407 min_supported_size,
408 } => {
409 write!(
410 formatter,
411 "supplied buffer ({size} bytes) is too small to be filled; \
412 min supported size is {min_supported} bytes",
413 size = size,
414 min_supported = min_supported_size
415 )
416 }
417
418 Self::BufferTooLarge {
419 size,
420 max_supported_size,
421 } => {
422 write!(
423 formatter,
424 "supplied buffer ({size} bytes) is too large to be filled; \
425 max supported size is {max_supported} bytes",
426 size = size,
427 max_supported = max_supported_size
428 )
429 }
430 }
431 }
432}
433
434#[cfg(feature = "std")]
435impl std::error::Error for FillError {}
436
437/// Name of a child [`SecretTree`].
438///
439/// Used in [`SecretTree::child()`]; see its documentation for more context.
440///
441/// An original `str` can be extracted from `Name` using [`AsRef`] / [`Display`](fmt::Display)
442/// implementations:
443///
444/// ```
445/// # use secret_tree::Name;
446/// const NAME: Name = Name::new("test_name");
447/// assert_eq!(NAME.as_ref(), "test_name");
448/// assert_eq!(NAME.to_string(), "test_name");
449/// ```
450#[derive(Clone, Copy, PartialEq, Eq, Hash)]
451pub struct Name([u8; SALT_LEN]);
452
453impl Name {
454 /// Creates a new `Name`.
455 ///
456 /// The supplied string must be no more than [`MAX_NAME_LEN`] bytes in length
457 /// and must not contain null chars `'\0'`.
458 ///
459 /// This is a constant method, which perform all relevant checks during compilation in
460 /// a constant context:
461 ///
462 /// ```
463 /// # use secret_tree::Name;
464 /// const NAME: Name = Name::new("some_name");
465 /// ```
466 ///
467 /// For example, this won't compile since the name is too long (17 chars):
468 ///
469 /// ```compile_fail
470 /// # use secret_tree::Name;
471 /// const OVERLY_LONG_NAME: Name = Name::new("Overly long name!");
472 /// ```
473 ///
474 /// ...And this won't compile because the name contains a `\0` char:
475 ///
476 /// ```compile_fail
477 /// # use secret_tree::Name;
478 /// const NAME_WITH_ZERO_CHARS: Name = Name::new("12\03");
479 /// ```
480 ///
481 /// # Panics
482 ///
483 /// Panics if `name` is overly long or contains null chars.
484 /// Use the [`FromStr`] implementation for a fallible / non-panicking alternative.
485 pub const fn new(name: &str) -> Self {
486 let bytes = name.as_bytes();
487
488 let mut i = 0;
489 let mut buffer = [0_u8; SALT_LEN];
490 while i < name.len() {
491 assert!(bytes[i] != 0, "name contains a null char");
492 buffer[i] = bytes[i];
493 i += 1;
494 }
495 Name(buffer)
496 }
497}
498
499impl FromStr for Name {
500 type Err = NameError;
501
502 fn from_str(name: &str) -> Result<Self, Self::Err> {
503 let byte_len = name.as_bytes().len();
504 if byte_len > SALT_LEN {
505 return Err(NameError::TooLong);
506 }
507 if name.as_bytes().contains(&0) {
508 return Err(NameError::NullChar);
509 }
510
511 let mut bytes = [0; SALT_LEN];
512 bytes[..byte_len].copy_from_slice(name.as_bytes());
513 Ok(Self(bytes))
514 }
515}
516
517impl AsRef<str> for Name {
518 fn as_ref(&self) -> &str {
519 let str_len = self.0.iter().position(|&ch| ch == 0).unwrap_or(SALT_LEN);
520 unsafe {
521 // SAFETY: safe by construction; we only ever create `Name`s from valid UTF-8 sequences.
522 str::from_utf8_unchecked(&self.0[..str_len])
523 }
524 }
525}
526
527impl fmt::Debug for Name {
528 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
529 formatter.debug_tuple("Name").field(&self.as_ref()).finish()
530 }
531}
532
533impl fmt::Display for Name {
534 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
535 formatter.write_str(self.as_ref())
536 }
537}
538
539/// Errors that can occur when converting a `&str` into [`Name`].
540#[derive(Debug)]
541#[non_exhaustive]
542pub enum NameError {
543 /// The string is too long. `Name`s should be 0..=16 bytes.
544 TooLong,
545 /// Name contains a null char `\0`.
546 NullChar,
547}
548
549impl fmt::Display for NameError {
550 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
551 formatter.write_str(match self {
552 Self::TooLong => "name is too long, 0..=16 bytes expected",
553 Self::NullChar => "name contains a null char",
554 })
555 }
556}
557
558#[cfg(feature = "std")]
559impl std::error::Error for NameError {}
560
561#[cfg(doctest)]
562doc_comment::doctest!("../README.md");
563
564#[cfg(test)]
565mod tests {
566 use super::*;
567
568 use rand::{Rng, SeedableRng};
569 use std::vec;
570
571 #[test]
572 fn children_with_same_bytes_in_key() {
573 let name = Name::new("A");
574 let index = u64::from(b'A');
575 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
576 let named_child = tree.child(name);
577 let indexed_child = tree.index(index);
578 assert_ne!(
579 named_child.seed.expose_secret(),
580 indexed_child.seed.expose_secret()
581 );
582 }
583
584 #[test]
585 fn fill_and_rng_result_in_different_data() {
586 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
587 let mut buffer = [0_u64; 8];
588 tree.child(Name::new("foo")).fill(&mut buffer);
589 let other_buffer: [u64; 8] = tree.child(Name::new("foo")).rng().gen();
590 assert_ne!(buffer, other_buffer);
591 }
592
593 #[test]
594 #[should_panic(expected = "supplied buffer (12 bytes) is too small to be filled")]
595 fn filling_undersized_key() {
596 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
597 let mut buffer = [0_u8; 12];
598 tree.fill(&mut buffer);
599 }
600
601 #[test]
602 fn error_filling_undersized_key() {
603 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
604 let mut buffer = [0_u8; 12];
605 let err = tree.try_fill(&mut buffer).unwrap_err();
606
607 assert!(matches!(
608 err,
609 FillError::BufferTooSmall {
610 size: 12,
611 min_supported_size: 16,
612 }
613 ));
614 let err = err.to_string();
615 assert!(
616 err.contains("supplied buffer (12 bytes) is too small to be filled"),
617 "{}",
618 err
619 );
620 assert!(err.contains("min supported size is 16 bytes"), "{}", err);
621 }
622
623 #[test]
624 #[should_panic(expected = "supplied buffer (80 bytes) is too large to be filled")]
625 fn filling_oversized_key() {
626 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
627 let mut buffer = [0_u64; 10];
628 tree.fill(&mut buffer);
629 }
630
631 #[test]
632 fn error_filling_oversized_key() {
633 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
634 let mut buffer = [0_u64; 10];
635 let err = tree.try_fill(&mut buffer).unwrap_err();
636
637 assert!(matches!(
638 err,
639 FillError::BufferTooLarge {
640 size: 80,
641 max_supported_size: 64,
642 }
643 ));
644 let err = err.to_string();
645 assert!(
646 err.contains("supplied buffer (80 bytes) is too large to be filled"),
647 "{}",
648 err
649 );
650 assert!(err.contains("max supported size is 64 bytes"), "{}", err);
651 }
652
653 #[test]
654 fn filling_acceptable_buffers() {
655 let mut u8_buffer = [0_u8; 40];
656 let mut i32_buffer = [0_i32; 16];
657 let mut u128_buffer = [0_u128];
658 // Using `Vec` to store secrets is usually a bad idea because of its placement in heap;
659 // here it is used just to test capabilities.
660 let mut vec_buffer = vec![0_u16; 24];
661
662 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
663 tree.child(Name::new("u8")).fill(&mut u8_buffer[..]);
664 tree.child(Name::new("i32")).fill(&mut i32_buffer);
665 tree.child(Name::new("u128")).fill(&mut u128_buffer);
666 tree.child(Name::new("vec")).fill(&mut vec_buffer[..]);
667 }
668
669 #[test]
670 #[should_panic]
671 fn name_with_null_chars_cannot_be_created() {
672 let _name = Name::new("some\0name");
673 }
674
675 #[test]
676 fn name_with_null_chars_error() {
677 let err = Name::from_str("some\0name").unwrap_err();
678 assert!(matches!(err, NameError::NullChar));
679 }
680
681 #[test]
682 #[should_panic]
683 fn overly_long_name_cannot_be_created() {
684 let _name = Name::new("Overly long name?");
685 }
686
687 #[test]
688 fn overly_long_name_error() {
689 let err = Name::from_str("Overly long name?").unwrap_err();
690 assert!(matches!(err, NameError::TooLong));
691 }
692
693 #[test]
694 fn name_new_pads_input_with_zeros() {
695 const SAMPLES: &[(Name, &[u8; MAX_NAME_LEN])] = &[
696 (Name::new(""), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"),
697 (Name::new("O"), b"O\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"),
698 (Name::new("Ov"), b"Ov\0\0\0\0\0\0\0\0\0\0\0\0\0\0"),
699 (Name::new("Ove"), b"Ove\0\0\0\0\0\0\0\0\0\0\0\0\0"),
700 (Name::new("Over"), b"Over\0\0\0\0\0\0\0\0\0\0\0\0"),
701 (Name::new("Overl"), b"Overl\0\0\0\0\0\0\0\0\0\0\0"),
702 (Name::new("Overly"), b"Overly\0\0\0\0\0\0\0\0\0\0"),
703 (Name::new("Overly "), b"Overly \0\0\0\0\0\0\0\0\0"),
704 (Name::new("Overly l"), b"Overly l\0\0\0\0\0\0\0\0"),
705 (Name::new("Overly lo"), b"Overly lo\0\0\0\0\0\0\0"),
706 (Name::new("Overly lon"), b"Overly lon\0\0\0\0\0\0"),
707 (Name::new("Overly long"), b"Overly long\0\0\0\0\0"),
708 (Name::new("Overly long "), b"Overly long \0\0\0\0"),
709 (Name::new("Overly long n"), b"Overly long n\0\0\0"),
710 (Name::new("Overly long na"), b"Overly long na\0\0"),
711 (Name::new("Overly long nam"), b"Overly long nam\0"),
712 (Name::new("Overly long name"), b"Overly long name"),
713 ];
714
715 for (i, &(name, expected_bytes)) in SAMPLES.iter().enumerate() {
716 assert_eq!(name.0, *expected_bytes);
717 let expected_str = &"Overly long name"[..i];
718 assert_eq!(name.to_string(), expected_str);
719 assert_eq!(name.as_ref(), expected_str);
720 assert!(format!("{:?}", name).contains(expected_str));
721 }
722 }
723
724 #[test]
725 fn buffers_with_different_size_should_be_unrelated() {
726 let tree = SecretTree::new(&mut ChaChaRng::seed_from_u64(123));
727 let mut bytes = [0_u8; 16];
728 tree.child(Name::new("foo")).fill(&mut bytes);
729 let mut other_bytes = [0_u8; 32];
730 tree.child(Name::new("foo")).fill(&mut other_bytes);
731 assert!(bytes.iter().zip(&other_bytes).any(|(&x, &y)| x != y));
732 }
733
734 #[test]
735 fn digest_derivation_depends_on_all_bits_of_digest() {
736 const RNG_SEED: u64 = 12345;
737
738 let mut rng = ChaChaRng::seed_from_u64(RNG_SEED);
739 let tree = SecretTree::new(&mut rng);
740 let mut digest = [0_u8; 32];
741 rng.fill_bytes(&mut digest);
742
743 let child_seed = tree.digest(&digest).seed;
744 for byte_idx in 0..32 {
745 for bit_idx in 0..8 {
746 let mut mutated_digest = digest;
747 mutated_digest[byte_idx] ^= 1 << bit_idx;
748 assert_ne!(mutated_digest, digest);
749
750 let mutated_child_seed = tree.digest(&mutated_digest).seed;
751 assert_ne!(
752 child_seed.expose_secret(),
753 mutated_child_seed.expose_secret()
754 );
755 }
756 }
757 }
758}