Struct secret_tree::SecretTree

source ·
pub struct SecretTree { /* private fields */ }
Expand description

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

§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

source

pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self

Generates a tree by sampling its seed from the supplied RNG.

source

pub fn from_seed(seed: Seed) -> Self

Creates a tree from the seed.

source

pub fn from_slice(bytes: &[u8]) -> Result<Self, TryFromSliceError>

Restores a tree from the seed specified as a byte slice.

§Errors

Returns an error if bytes has an invalid length (not SEED_LEN).

source

pub fn seed(&self) -> &Seed

Returns the tree seed.

source

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.)

source

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.

source

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.

source

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.

source

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.

source

pub fn child(&self, name: Name) -> Self

Produces a child with the specified string identifier.

source

pub fn index(&self, index: u64) -> Self

Produces a child with the specified integer index.

source

pub fn digest(&self, digest: &[u8; 32]) -> Self

Produces a child with the specified 32-byte digest (e.g., an output of SHA-256, SHA3-256 or Keccak256 hash functions).

This method can be used for arbitrarily-sized keys by first digesting them with a collision-resistant hash function.

Trait Implementations§

source§

impl Debug for SecretTree

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

source§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V