[][src]Crate secret_tree

Hierarchical secret derivation with Blake2b and random number generators.

How it works

This crate provides SecretTree – a structure produced from a 32-byte seed that may be converted into a secret key or a cryptographically secure pseudo-random number generator (CSPRNG). Besides that, an SecretTree can produce child trees, which are identified by a string Name or an integer index. This enables creating hierarchies of secrets (like some_secret/0, some_secret/1 and other_secret/foo/1/bar), which are ultimately derived from a single SecretTree. It’s enough to securely store the seed of this root tree (e.g., in a passphrase-encrypted form) to recreate all secrets.

The derived secrets cannot be linked; leakage of a derived secret does not compromise sibling secrets or the parent SecretTree.

Implementation details

SecretTree uses the Blake2b keyed hash function to derive the following kinds of data:

  • secret key
  • CSPRNG seed (the RNG used is ChaChaRng)
  • seeds for child SecretTrees

The procedure is similar to the use of Blake2b for key derivation in libsodium:

  • Blake2b is used with a custom initialization block. The block has two customizable parameters of interest: salt and personalization (each is 16 bytes). See the table below for information how these two parameters are set for each type of derived data.
  • The key is the seed of the SecretTree instance used for derivation.
  • The message is an empty bit string.

The length of derived data is 32 bytes in all cases.

Salt and personalization

Data typeSaltPersonalization
Secret key[0; 16]b"bytes\0\0...\0"
CSPRNG seed[0; 16]b"rng\0\0...\0"
Seed for a named childname.as_bytes() (zero-padded)b"name\0\0...\0"
Seed for an indexed childLittleEndian(index)b"index\0\0...\0"

Derivation of a secret key, CSPRNG seed and seeds for indexed children are all fully compatible with libsodium. libsodium uses the salt section in the Blake2b initialization block to store the index of a child key, and the personalization section to store its context.

For example, the CSPRNG seed can be computed as follows (if we translate libsodium API from C to Rust):

use rand::{SeedableRng};
use rand_chacha::ChaChaRng;

let parent_seed: [u8; 32] = // ...
let mut rng_seed = [0; 32];
    &mut rng_seed,
    /* index */ 0,
    /* context */ b"rng\0\0\0\0\0",
    /* master_key */ &parent_seed,
let rng = ChaChaRng::from_seed(rng_seed);

In case of named children, we utilize the entire salt section, while libsodium only uses the first 8 bytes.

Design motivations

  • We allow to derive RNGs besides keys in order to allow a richer variety of applications. RNGs can be used in more complex use cases than fixed-size byte arrays, e.g., when the length of the secret depends on previous RNG output, or RNG is used to sample a complex distribution.
  • Derivation in general (instead of using a single SeedableRng to create all secrets) allows to add new secrets or remove old ones without worrying about compatibility.
  • Child RNGs identified by an index can be used to derive secrets of the same type, the quantity of which is unbounded. As an example, they can be used to produce blinding factors for Pedersen commitments (e.g., in a privacy-focused cryptocurrency).
  • Some steps are taken to make it difficult to use SecretTree incorrectly. For example, rng() and fill() methods consume the tree instance, which makes it harder to reuse the same RNG for multiple purposes (which is not intended).

Crate features

The crate supports both rand v0.6 and v0.7 (the latter is used by default). To signal the version, specify a rand-06 or rand-07 feature (naturally, they are mutually exclusive). rand-07 is on by default, so it is necessary to specify default-features = false if using rand-06.



Name of a child SecretTree.


Seeded structure that can be used to produce secrets and child SecretTrees.



Maximum byte length of a Name (16).


Byte length of a RngTree seed (32).

Type Definitions


Alias for an array that contains seed bytes.