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(reader: &mut impl std::io::Read) -> Result<Self::Encoding, DecodeError>;
69
70    /// Random generation function for the external secret type
71    fn random<R>(rng: &mut R) -> Self::Encoding
72    where
73        R: rand::RngCore + rand::CryptoRng;
74}
75
76/// Just uses 64 random bytes and derives the secret from them
77#[derive(Debug)]
78pub struct PlainRootSecretStrategy;
79
80impl RootSecretStrategy for PlainRootSecretStrategy {
81    type Encoding = [u8; 64];
82
83    fn to_root_secret(secret: &Self::Encoding) -> DerivableSecret {
84        const FEDIMINT_CLIENT_NONCE: &[u8] = b"Fedimint Client Salt";
85        DerivableSecret::new_root(secret.as_ref(), FEDIMINT_CLIENT_NONCE)
86    }
87
88    fn consensus_encode(
89        secret: &Self::Encoding,
90        writer: &mut impl Write,
91    ) -> std::io::Result<usize> {
92        secret.consensus_encode(writer)
93    }
94
95    fn consensus_decode(reader: &mut impl Read) -> Result<Self::Encoding, DecodeError> {
96        Self::Encoding::consensus_decode(reader, &ModuleRegistry::default())
97    }
98
99    fn random<R>(rng: &mut R) -> Self::Encoding
100    where
101        R: RngCore + CryptoRng,
102    {
103        let mut secret = [0u8; 64];
104        rng.fill(&mut secret);
105        secret
106    }
107}
108
109/// Convenience function to derive fedimint-client root secret
110/// using the default (0) wallet number, given a global root secret
111/// that's managed externally by a consumer of fedimint-client.
112///
113/// See docs/secret_derivation.md
114///
115/// `global_root_secret/<key-type=per-federation=0>/<federation-id>/
116/// <wallet-number=0>/<key-type=fedimint-client=0>`
117pub fn get_default_client_secret(
118    global_root_secret: &DerivableSecret,
119    federation_id: &FederationId,
120) -> DerivableSecret {
121    let multi_federation_root_secret = global_root_secret.child_key(ChildId(0));
122    let federation_root_secret = multi_federation_root_secret.federation_key(federation_id);
123    let federation_wallet_root_secret = federation_root_secret.child_key(ChildId(0)); // wallet-number=0
124    federation_wallet_root_secret.child_key(ChildId(0)) // key-type=fedimint-client=0
125}