fedimint_client/
secret.rs

1use std::fmt::Debug;
2use std::io::{Read, Write};
3
4use fedimint_core::config::FederationId;
5use fedimint_core::core::ModuleInstanceId;
6use fedimint_core::encoding::{Decodable, DecodeError, Encodable};
7use fedimint_core::module::registry::ModuleRegistry;
8use fedimint_derive_secret::{ChildId, DerivableSecret};
9use rand::{CryptoRng, Rng, RngCore};
10
11// Derived from pre-root-secret (pre-federation-derived)
12const TYPE_PRE_ROOT_SECRET_HASH: ChildId = ChildId(0);
13
14// Derived from federation-root-secret
15const TYPE_MODULE: ChildId = ChildId(0);
16const TYPE_BACKUP: ChildId = ChildId(1);
17
18pub trait DeriveableSecretClientExt {
19    fn derive_module_secret(&self, module_instance_id: ModuleInstanceId) -> DerivableSecret;
20    fn derive_backup_secret(&self) -> DerivableSecret;
21    fn derive_pre_root_secret_hash(&self) -> [u8; 8];
22}
23
24impl DeriveableSecretClientExt for DerivableSecret {
25    fn derive_module_secret(&self, module_instance_id: ModuleInstanceId) -> DerivableSecret {
26        assert_eq!(self.level(), 0);
27        self.child_key(TYPE_MODULE)
28            .child_key(ChildId(u64::from(module_instance_id)))
29    }
30
31    fn derive_backup_secret(&self) -> DerivableSecret {
32        assert_eq!(self.level(), 0);
33        self.child_key(TYPE_BACKUP)
34    }
35
36    fn derive_pre_root_secret_hash(&self) -> [u8; 8] {
37        // Note: this hash is derived from a pre-root-secret: one passed from the
38        // outside, before the federation ID is used to derive the
39        // federation-specific-root-secret, which gets level reset to 0.
40        // Because of that we don't care about asserting the level.
41        self.child_key(TYPE_PRE_ROOT_SECRET_HASH).to_random_bytes()
42    }
43}
44
45/// Trait defining a way to generate, serialize and deserialize a root secret.
46/// It defines a `Encoding` associated type which represents a specific
47/// representation of a secret (e.g. a bip39, slip39, CODEX32, … struct) and
48/// then defines the methods necessary for the client to interact with it.
49///
50/// We use a strategy pattern (i.e. implementing the trait on a zero sized type
51/// with the actual secret struct as an associated type instead of implementing
52/// the necessary functions directly on the secret struct) to allow external
53/// implementations on third-party types without wrapping them in newtypes.
54pub trait RootSecretStrategy: Debug {
55    /// Type representing the secret
56    type Encoding: Clone;
57
58    /// Conversion function from the external encoding to the internal one
59    fn to_root_secret(secret: &Self::Encoding) -> DerivableSecret;
60
61    /// Serialization function for the external encoding
62    fn consensus_encode(
63        secret: &Self::Encoding,
64        writer: &mut impl std::io::Write,
65    ) -> std::io::Result<usize>;
66
67    /// Deserialization function for the external encoding
68    fn consensus_decode_partial(
69        reader: &mut impl std::io::Read,
70    ) -> Result<Self::Encoding, DecodeError>;
71
72    /// Random generation function for the external secret type
73    fn random<R>(rng: &mut R) -> Self::Encoding
74    where
75        R: rand::RngCore + rand::CryptoRng;
76}
77
78/// Just uses 64 random bytes and derives the secret from them
79#[derive(Debug)]
80pub struct PlainRootSecretStrategy;
81
82impl RootSecretStrategy for PlainRootSecretStrategy {
83    type Encoding = [u8; 64];
84
85    fn to_root_secret(secret: &Self::Encoding) -> DerivableSecret {
86        const FEDIMINT_CLIENT_NONCE: &[u8] = b"Fedimint Client Salt";
87        DerivableSecret::new_root(secret.as_ref(), FEDIMINT_CLIENT_NONCE)
88    }
89
90    fn consensus_encode(
91        secret: &Self::Encoding,
92        writer: &mut impl Write,
93    ) -> std::io::Result<usize> {
94        secret.consensus_encode(writer)
95    }
96
97    fn consensus_decode_partial(reader: &mut impl Read) -> Result<Self::Encoding, DecodeError> {
98        Self::Encoding::consensus_decode_partial(reader, &ModuleRegistry::default())
99    }
100
101    fn random<R>(rng: &mut R) -> Self::Encoding
102    where
103        R: RngCore + CryptoRng,
104    {
105        let mut secret = [0u8; 64];
106        rng.fill(&mut secret);
107        secret
108    }
109}
110
111/// Convenience function to derive fedimint-client root secret
112/// using the default (0) wallet number, given a global root secret
113/// that's managed externally by a consumer of fedimint-client.
114///
115/// See docs/secret_derivation.md
116///
117/// `global_root_secret/<key-type=per-federation=0>/<federation-id>/
118/// <wallet-number=0>/<key-type=fedimint-client=0>`
119pub fn get_default_client_secret(
120    global_root_secret: &DerivableSecret,
121    federation_id: &FederationId,
122) -> DerivableSecret {
123    let multi_federation_root_secret = global_root_secret.child_key(ChildId(0));
124    let federation_root_secret = multi_federation_root_secret.federation_key(federation_id);
125    let federation_wallet_root_secret = federation_root_secret.child_key(ChildId(0)); // wallet-number=0
126    federation_wallet_root_secret.child_key(ChildId(0)) // key-type=fedimint-client=0
127}