nut13.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. //! NUT-13: Deterministic Secrets
  2. //!
  3. //! <https://github.com/cashubtc/nuts/blob/main/13.md>
  4. use core::str::FromStr;
  5. use bip39::Mnemonic;
  6. use bitcoin::bip32::{DerivationPath, ExtendedPrivKey};
  7. use bitcoin::Network;
  8. use super::{Id, SecretKey};
  9. use crate::error::Error;
  10. use crate::secret::Secret;
  11. use crate::util::hex;
  12. use crate::SECP256K1;
  13. impl Secret {
  14. pub fn from_seed(mnemonic: &Mnemonic, keyset_id: Id, counter: u64) -> Result<Self, Error> {
  15. tracing::debug!(
  16. "Deriving secret for {} with count {}",
  17. keyset_id.to_string(),
  18. counter.to_string()
  19. );
  20. let path: DerivationPath = DerivationPath::from_str(&format!(
  21. "m/129372'/0'/{}'/{}'/0",
  22. u64::try_from(keyset_id)?,
  23. counter
  24. ))?;
  25. let seed: [u8; 64] = mnemonic.to_seed("");
  26. let bip32_root_key = ExtendedPrivKey::new_master(Network::Bitcoin, &seed)?;
  27. let derived_xpriv = bip32_root_key.derive_priv(&SECP256K1, &path)?;
  28. Ok(Self::new(hex::encode(
  29. derived_xpriv.private_key.secret_bytes(),
  30. )))
  31. }
  32. }
  33. impl SecretKey {
  34. pub fn from_seed(mnemonic: &Mnemonic, keyset_id: Id, counter: u64) -> Result<Self, Error> {
  35. tracing::debug!(
  36. "Deriving key for {} with count {}",
  37. keyset_id.to_string(),
  38. counter.to_string()
  39. );
  40. let path = DerivationPath::from_str(&format!(
  41. "m/129372'/0'/{}'/{}'/1",
  42. u64::try_from(keyset_id)?,
  43. counter
  44. ))?;
  45. let seed: [u8; 64] = mnemonic.to_seed("");
  46. let bip32_root_key = ExtendedPrivKey::new_master(Network::Bitcoin, &seed)?;
  47. let derived_xpriv = bip32_root_key.derive_priv(&SECP256K1, &path)?;
  48. Ok(Self::from(derived_xpriv.private_key))
  49. }
  50. }
  51. #[cfg(feature = "wallet")]
  52. mod wallet {
  53. use bip39::Mnemonic;
  54. use crate::dhke::blind_message;
  55. use crate::error::Error;
  56. use crate::nuts::{BlindedMessage, Id, PreMint, PreMintSecrets, SecretKey};
  57. use crate::secret::Secret;
  58. use crate::Amount;
  59. impl PreMintSecrets {
  60. /// Generate blinded messages from predetermined secrets and blindings
  61. /// factor
  62. pub fn from_seed(
  63. keyset_id: Id,
  64. counter: u64,
  65. mnemonic: &Mnemonic,
  66. amount: Amount,
  67. zero_amount: bool,
  68. ) -> Result<Self, Error> {
  69. let mut pre_mint_secrets = PreMintSecrets::default();
  70. let mut counter = counter;
  71. for amount in amount.split() {
  72. let secret = Secret::from_seed(mnemonic, keyset_id, counter)?;
  73. let blinding_factor = SecretKey::from_seed(mnemonic, keyset_id, counter)?;
  74. let (blinded, r) = blind_message(&secret.to_bytes(), Some(blinding_factor))?;
  75. let amount = if zero_amount { Amount::ZERO } else { amount };
  76. let blinded_message = BlindedMessage::new(amount, keyset_id, blinded);
  77. let pre_mint = PreMint {
  78. blinded_message,
  79. secret: secret.clone(),
  80. r,
  81. amount,
  82. };
  83. pre_mint_secrets.secrets.push(pre_mint);
  84. counter += 1;
  85. }
  86. Ok(pre_mint_secrets)
  87. }
  88. /// Generate blinded messages from predetermined secrets and blindings
  89. /// factor
  90. pub fn restore_batch(
  91. keyset_id: Id,
  92. mnemonic: &Mnemonic,
  93. start_count: u64,
  94. end_count: u64,
  95. ) -> Result<Self, Error> {
  96. let mut pre_mint_secrets = PreMintSecrets::default();
  97. for i in start_count..=end_count {
  98. let secret = Secret::from_seed(mnemonic, keyset_id, i)?;
  99. let blinding_factor = SecretKey::from_seed(mnemonic, keyset_id, i)?;
  100. let (blinded, r) = blind_message(&secret.to_bytes(), Some(blinding_factor))?;
  101. let blinded_message = BlindedMessage::new(Amount::ZERO, keyset_id, blinded);
  102. let pre_mint = PreMint {
  103. blinded_message,
  104. secret: secret.clone(),
  105. r,
  106. amount: Amount::ZERO,
  107. };
  108. pre_mint_secrets.secrets.push(pre_mint);
  109. }
  110. Ok(pre_mint_secrets)
  111. }
  112. }
  113. }
  114. #[cfg(test)]
  115. mod tests {
  116. use super::*;
  117. #[test]
  118. fn test_secret_from_seed() {
  119. let seed =
  120. "half depart obvious quality work element tank gorilla view sugar picture humble";
  121. let mnemonic = Mnemonic::from_str(seed).unwrap();
  122. let keyset_id = Id::from_str("009a1f293253e41e").unwrap();
  123. let test_secrets = [
  124. "485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae",
  125. "8f2b39e8e594a4056eb1e6dbb4b0c38ef13b1b2c751f64f810ec04ee35b77270",
  126. "bc628c79accd2364fd31511216a0fab62afd4a18ff77a20deded7b858c9860c8",
  127. "59284fd1650ea9fa17db2b3acf59ecd0f2d52ec3261dd4152785813ff27a33bf",
  128. "576c23393a8b31cc8da6688d9c9a96394ec74b40fdaf1f693a6bb84284334ea0",
  129. ];
  130. for (i, test_secret) in test_secrets.iter().enumerate() {
  131. let secret = Secret::from_seed(&mnemonic, keyset_id, i.try_into().unwrap()).unwrap();
  132. assert_eq!(secret, Secret::from_str(test_secret).unwrap())
  133. }
  134. }
  135. #[test]
  136. fn test_r_from_seed() {
  137. let seed =
  138. "half depart obvious quality work element tank gorilla view sugar picture humble";
  139. let mnemonic = Mnemonic::from_str(seed).unwrap();
  140. let keyset_id = Id::from_str("009a1f293253e41e").unwrap();
  141. let test_rs = [
  142. "ad00d431add9c673e843d4c2bf9a778a5f402b985b8da2d5550bf39cda41d679",
  143. "967d5232515e10b81ff226ecf5a9e2e2aff92d66ebc3edf0987eb56357fd6248",
  144. "b20f47bb6ae083659f3aa986bfa0435c55c6d93f687d51a01f26862d9b9a4899",
  145. "fb5fca398eb0b1deb955a2988b5ac77d32956155f1c002a373535211a2dfdc29",
  146. "5f09bfbfe27c439a597719321e061e2e40aad4a36768bb2bcc3de547c9644bf9",
  147. ];
  148. for (i, test_r) in test_rs.iter().enumerate() {
  149. let r = SecretKey::from_seed(&mnemonic, keyset_id, i.try_into().unwrap()).unwrap();
  150. assert_eq!(r, SecretKey::from_hex(test_r).unwrap())
  151. }
  152. }
  153. }