1use std::fmt::Debug;
2use std::io::{Read, Write};
34use 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};
1011// Derived from pre-root-secret (pre-federation-derived)
12const TYPE_PRE_ROOT_SECRET_HASH: ChildId = ChildId(0);
1314// Derived from federation-root-secret
15const TYPE_MODULE: ChildId = ChildId(0);
16const TYPE_BACKUP: ChildId = ChildId(1);
1718pub trait DeriveableSecretClientExt {
19fn derive_module_secret(&self, module_instance_id: ModuleInstanceId) -> DerivableSecret;
20fn derive_backup_secret(&self) -> DerivableSecret;
21fn derive_pre_root_secret_hash(&self) -> [u8; 8];
22}
2324impl DeriveableSecretClientExt for DerivableSecret {
25fn derive_module_secret(&self, module_instance_id: ModuleInstanceId) -> DerivableSecret {
26assert_eq!(self.level(), 0);
27self.child_key(TYPE_MODULE)
28 .child_key(ChildId(u64::from(module_instance_id)))
29 }
3031fn derive_backup_secret(&self) -> DerivableSecret {
32assert_eq!(self.level(), 0);
33self.child_key(TYPE_BACKUP)
34 }
3536fn 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.
41self.child_key(TYPE_PRE_ROOT_SECRET_HASH).to_random_bytes()
42 }
43}
4445/// 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
56type Encoding: Clone;
5758/// Conversion function from the external encoding to the internal one
59fn to_root_secret(secret: &Self::Encoding) -> DerivableSecret;
6061/// Serialization function for the external encoding
62fn consensus_encode(
63 secret: &Self::Encoding,
64 writer: &mut impl std::io::Write,
65 ) -> std::io::Result<()>;
6667/// Deserialization function for the external encoding
68fn consensus_decode_partial(
69 reader: &mut impl std::io::Read,
70 ) -> Result<Self::Encoding, DecodeError>;
7172/// Random generation function for the external secret type
73fn random<R>(rng: &mut R) -> Self::Encoding
74where
75R: rand::RngCore + rand::CryptoRng;
76}
7778/// Just uses 64 random bytes and derives the secret from them
79#[derive(Debug)]
80pub struct PlainRootSecretStrategy;
8182impl RootSecretStrategy for PlainRootSecretStrategy {
83type Encoding = [u8; 64];
8485fn to_root_secret(secret: &Self::Encoding) -> DerivableSecret {
86const FEDIMINT_CLIENT_NONCE: &[u8] = b"Fedimint Client Salt";
87 DerivableSecret::new_root(secret.as_ref(), FEDIMINT_CLIENT_NONCE)
88 }
8990fn consensus_encode(secret: &Self::Encoding, writer: &mut impl Write) -> std::io::Result<()> {
91 secret.consensus_encode(writer)
92 }
9394fn consensus_decode_partial(reader: &mut impl Read) -> Result<Self::Encoding, DecodeError> {
95Self::Encoding::consensus_decode_partial(reader, &ModuleRegistry::default())
96 }
9798fn random<R>(rng: &mut R) -> Self::Encoding
99where
100R: RngCore + CryptoRng,
101 {
102let mut secret = [0u8; 64];
103 rng.fill(&mut secret);
104 secret
105 }
106}
107108/// Convenience function to derive fedimint-client root secret
109/// using the default (0) wallet number, given a global root secret
110/// that's managed externally by a consumer of fedimint-client.
111///
112/// See docs/secret_derivation.md
113///
114/// `global_root_secret/<key-type=per-federation=0>/<federation-id>/
115/// <wallet-number=0>/<key-type=fedimint-client=0>`
116pub fn get_default_client_secret(
117 global_root_secret: &DerivableSecret,
118 federation_id: &FederationId,
119) -> DerivableSecret {
120let multi_federation_root_secret = global_root_secret.child_key(ChildId(0));
121let federation_root_secret = multi_federation_root_secret.federation_key(federation_id);
122let federation_wallet_root_secret = federation_root_secret.child_key(ChildId(0)); // wallet-number=0
123federation_wallet_root_secret.child_key(ChildId(0)) // key-type=fedimint-client=0
124}