[−][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
SecretTree
s
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 type | Salt | Personalization |
---|---|---|
Secret key | [0; 16] | b"bytes\0\0...\0" |
CSPRNG seed | [0; 16] | b"rng\0\0...\0" |
Seed for a named child | name.as_bytes() (zero-padded) | b"name\0\0...\0" |
Seed for an indexed child | LittleEndian(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]; crypto_kdf_derive_from_key( &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()
andfill()
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
.
Structs
Name | Name of a child |
SecretTree | Seeded structure that can be used to produce secrets and child |
Constants
MAX_NAME_LEN | Maximum byte length of a |
SEED_LEN | Byte length of a |
Type Definitions
Seed | Alias for an array that contains seed bytes. |