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}