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