Struct secret_tree::SecretTree
source · pub struct SecretTree { /* private fields */ }
Expand description
Seeded structure that can be used to produce secrets and child SecretTree
s.
§Usage
During the program lifecycle, a root SecretTree
should be restored from
a secure persistent form (e.g., a passphrase-encrypted file) and then used to derive
child trees and secrets. On the first use, the root should be initialized from a CSPRNG, such
as rand::thread_rng()
. The tree is not needed during the program execution and can
be safely dropped after deriving necessary secrets (which zeroes out the tree seed).
It is possible to modify the derivation hierarchy over the course of program evolution
by adding new secrets or abandoning the existing ones.
However, the purpose of any given tree path should be fixed; that is, if some version
of a program used path foo/bar
to derive an Ed25519 keypair, a newer version
shouldn’t use foo/bar
to derive an AES-128 key. Violating this rule may lead
to leaking the secret.
§Examples
use secret_tree::{SecretTree, Name};
use rand::{Rng, thread_rng};
use secrecy::{ExposeSecret, SecretBox};
let tree = SecretTree::new(&mut thread_rng());
// Don't forget to securely store secrets! Here, we wrap them
// in a container that automatically zeroes the secret on drop.
let first_secret: SecretBox<[u8; 32]> = tree
.child(Name::new("first"))
.create_secret();
// We can derive hierarchical secrets. The secrets below
// follow logical paths `sequence/0`, `sequence/1`, .., `sequence/4`
// relative to the `tree`.
let child_store = tree.child(Name::new("sequence"));
let more_secrets: Vec<SecretBox<[u64; 4]>> = (0..5)
.map(|i| SecretBox::new(Box::new(child_store.index(i).rng().gen())))
.collect();
// The tree is compactly stored as a single 32-byte seed.
let seed = tree.seed().clone();
drop(tree);
// If we restore the tree from the seed, we can restore all derived secrets.
let tree = SecretTree::from_seed(seed);
let restored_secret: SecretBox<[u8; 32]> = tree
.child(Name::new("first"))
.create_secret();
assert_eq!(
first_secret.expose_secret(),
restored_secret.expose_secret()
);
Implementations§
source§impl SecretTree
impl SecretTree
sourcepub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self
Generates a tree by sampling its seed from the supplied RNG.
sourcepub fn from_slice(bytes: &[u8]) -> Result<Self, TryFromSliceError>
pub fn from_slice(bytes: &[u8]) -> Result<Self, TryFromSliceError>
sourcepub fn rng(self) -> ChaChaRng
pub fn rng(self) -> ChaChaRng
Converts this tree into a cryptographically secure pseudo-random number generator (CSPRNG). This RNG can then be used to reproducibly create secrets (e.g., secret keys).
§Security
Self::fill()
should be preferred if the secret allows it. While using a CSPRNG
to generate secrets is theoretically sound, it introduces a new entity that
may leak information.
fill()
is especially useful if the filled buffer implements zeroing on drop;
the state of a CSPRNG generator returned by rng()
is not zeroed on drop and thus
creates a potential attack vector. (However theoretical it may be; ChaChaRng
has a notably small state size - ~160 bytes, so it may be better localized
and have lower risk to be accessed by the adversary than other CSPRNG implementations.)
sourcepub fn try_fill<T: AsByteSliceMut + ?Sized>(
self,
dest: &mut T,
) -> Result<(), FillError>
pub fn try_fill<T: AsByteSliceMut + ?Sized>( self, dest: &mut T, ) -> Result<(), FillError>
Tries to fill the specified buffer with a key derived from the seed of this tree.
§Errors
Errors if the buffer does not have length 16..=64
bytes. Use Self::rng()
if the buffer size may be outside these bounds, or if the secret must be derived
in a more complex way.
sourcepub fn fill<T: AsByteSliceMut + ?Sized>(self, dest: &mut T)
pub fn fill<T: AsByteSliceMut + ?Sized>(self, dest: &mut T)
Fills the specified buffer with a key derived from the seed of this tree.
§Panics
Panics in the same cases when Self::try_fill()
returns an error.
sourcepub fn try_create_secret<T>(self) -> Result<SecretBox<T>, FillError>where
T: AsByteSliceMut + Default + Zeroize,
pub fn try_create_secret<T>(self) -> Result<SecretBox<T>, FillError>where
T: AsByteSliceMut + Default + Zeroize,
Tries to create a secret by instantiating a buffer and filling it with a key derived from
the seed of this tree. Essentially, this is a more high-level wrapper around
Self::try_fill()
.
§Errors
Returns an error if T
does not have length 16..=64
bytes. Use Self::rng()
if the buffer size may be outside these bounds, or if the secret must be derived
in a more complex way.
sourcepub fn create_secret<T>(self) -> SecretBox<T>where
T: AsByteSliceMut + Default + Zeroize,
pub fn create_secret<T>(self) -> SecretBox<T>where
T: AsByteSliceMut + Default + Zeroize,
Creates a secret by instantiating a buffer and filling it with a key derived from the seed of this tree.
§Panics
Panics in the same cases when Self::try_create_secret()
returns an error.