Crate secret_tree
source ·Expand description
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
.
§Crate features
The crate is no_std
-compatible. There is optional std
support enabled via the std
feature,
which is on by default.
§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" |
Seed for a digest child (1st iter) | digest[..16] | b"digest0\0\0...\0" |
Seed for a digest child (2nd iter) | digest[16..] | b"digest1\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 and digest children, we utilize the entire salt section, while libsodium only uses the first 8 bytes.
For digest children, the derivation procedure is applied 2 times, taking the first 16 bytes and the remaining 16 bytes of the digest respectively. The 32-byte key derived on the first iteration is used as the master key input for the second iteration. Such a procedure is necessary because Blake2b only supports 16-byte salts.
§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).
Structs§
- Name of a child
SecretTree
. - Seeded structure that can be used to produce secrets and child
SecretTree
s. - Seed for a
SecretTree
.
Enums§
- Errors that can occur when calling
SecretTree::try_fill()
. - Errors that can occur when converting a
&str
intoName
.
Constants§
Traits§
- Converts a type to a mutable byte slice. This is used within the crate to fill secret values with the RNG output.