nut13.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. //! NUT-13: Deterministic Secrets
  2. //!
  3. //! <https://github.com/cashubtc/nuts/blob/main/13.md>
  4. use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
  5. use thiserror::Error;
  6. use tracing::instrument;
  7. use super::nut00::{BlindedMessage, PreMint, PreMintSecrets};
  8. use super::nut01::SecretKey;
  9. use super::nut02::Id;
  10. use crate::amount::SplitTarget;
  11. use crate::dhke::blind_message;
  12. use crate::secret::Secret;
  13. use crate::util::hex;
  14. use crate::{Amount, SECP256K1};
  15. /// NUT13 Error
  16. #[derive(Debug, Error)]
  17. pub enum Error {
  18. /// DHKE error
  19. #[error(transparent)]
  20. DHKE(#[from] crate::dhke::Error),
  21. /// Amount Error
  22. #[error(transparent)]
  23. Amount(#[from] crate::amount::Error),
  24. /// NUT00 Error
  25. #[error(transparent)]
  26. NUT00(#[from] crate::nuts::nut00::Error),
  27. /// NUT02 Error
  28. #[error(transparent)]
  29. NUT02(#[from] crate::nuts::nut02::Error),
  30. /// Bip32 Error
  31. #[error(transparent)]
  32. Bip32(#[from] bitcoin::bip32::Error),
  33. }
  34. impl Secret {
  35. /// Create new [`Secret`] from xpriv
  36. pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
  37. let path = derive_path_from_keyset_id(keyset_id)?
  38. .child(ChildNumber::from_hardened_idx(counter)?)
  39. .child(ChildNumber::from_normal_idx(0)?);
  40. let derived_xpriv = xpriv.derive_priv(&SECP256K1, &path)?;
  41. Ok(Self::new(hex::encode(
  42. derived_xpriv.private_key.secret_bytes(),
  43. )))
  44. }
  45. }
  46. impl SecretKey {
  47. /// Create new [`SecretKey`] from xpriv
  48. pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
  49. let path = derive_path_from_keyset_id(keyset_id)?
  50. .child(ChildNumber::from_hardened_idx(counter)?)
  51. .child(ChildNumber::from_normal_idx(1)?);
  52. let derived_xpriv = xpriv.derive_priv(&SECP256K1, &path)?;
  53. Ok(Self::from(derived_xpriv.private_key))
  54. }
  55. }
  56. impl PreMintSecrets {
  57. /// Generate blinded messages from predetermined secrets and blindings
  58. /// factor
  59. #[instrument(skip(xpriv))]
  60. pub fn from_xpriv(
  61. keyset_id: Id,
  62. counter: u32,
  63. xpriv: ExtendedPrivKey,
  64. amount: Amount,
  65. amount_split_target: &SplitTarget,
  66. ) -> Result<Self, Error> {
  67. let mut pre_mint_secrets = PreMintSecrets::new(keyset_id);
  68. let mut counter = counter;
  69. for amount in amount.split_targeted(amount_split_target)? {
  70. let secret = Secret::from_xpriv(xpriv, keyset_id, counter)?;
  71. let blinding_factor = SecretKey::from_xpriv(xpriv, keyset_id, counter)?;
  72. let (blinded, r) = blind_message(&secret.to_bytes(), Some(blinding_factor))?;
  73. let blinded_message = BlindedMessage::new(amount, keyset_id, blinded);
  74. let pre_mint = PreMint {
  75. blinded_message,
  76. secret: secret.clone(),
  77. r,
  78. amount,
  79. };
  80. pre_mint_secrets.secrets.push(pre_mint);
  81. counter += 1;
  82. }
  83. Ok(pre_mint_secrets)
  84. }
  85. /// New [`PreMintSecrets`] from xpriv with a zero amount used for change
  86. pub fn from_xpriv_blank(
  87. keyset_id: Id,
  88. counter: u32,
  89. xpriv: ExtendedPrivKey,
  90. amount: Amount,
  91. ) -> Result<Self, Error> {
  92. if amount <= Amount::ZERO {
  93. return Ok(PreMintSecrets::new(keyset_id));
  94. }
  95. let count = ((u64::from(amount) as f64).log2().ceil() as u64).max(1);
  96. let mut pre_mint_secrets = PreMintSecrets::new(keyset_id);
  97. let mut counter = counter;
  98. for _ in 0..count {
  99. let secret = Secret::from_xpriv(xpriv, keyset_id, counter)?;
  100. let blinding_factor = SecretKey::from_xpriv(xpriv, keyset_id, counter)?;
  101. let (blinded, r) = blind_message(&secret.to_bytes(), Some(blinding_factor))?;
  102. let amount = Amount::ZERO;
  103. let blinded_message = BlindedMessage::new(amount, keyset_id, blinded);
  104. let pre_mint = PreMint {
  105. blinded_message,
  106. secret: secret.clone(),
  107. r,
  108. amount,
  109. };
  110. pre_mint_secrets.secrets.push(pre_mint);
  111. counter += 1;
  112. }
  113. Ok(pre_mint_secrets)
  114. }
  115. /// Generate blinded messages from predetermined secrets and blindings
  116. /// factor
  117. pub fn restore_batch(
  118. keyset_id: Id,
  119. xpriv: ExtendedPrivKey,
  120. start_count: u32,
  121. end_count: u32,
  122. ) -> Result<Self, Error> {
  123. let mut pre_mint_secrets = PreMintSecrets::new(keyset_id);
  124. for i in start_count..=end_count {
  125. let secret = Secret::from_xpriv(xpriv, keyset_id, i)?;
  126. let blinding_factor = SecretKey::from_xpriv(xpriv, keyset_id, i)?;
  127. let (blinded, r) = blind_message(&secret.to_bytes(), Some(blinding_factor))?;
  128. let blinded_message = BlindedMessage::new(Amount::ZERO, keyset_id, blinded);
  129. let pre_mint = PreMint {
  130. blinded_message,
  131. secret: secret.clone(),
  132. r,
  133. amount: Amount::ZERO,
  134. };
  135. pre_mint_secrets.secrets.push(pre_mint);
  136. }
  137. Ok(pre_mint_secrets)
  138. }
  139. }
  140. fn derive_path_from_keyset_id(id: Id) -> Result<DerivationPath, Error> {
  141. let index = (u64::try_from(id)? % (2u64.pow(31) - 1)) as u32;
  142. let keyset_child_number = ChildNumber::from_hardened_idx(index)?;
  143. Ok(DerivationPath::from(vec![
  144. ChildNumber::from_hardened_idx(129372)?,
  145. ChildNumber::from_hardened_idx(0)?,
  146. keyset_child_number,
  147. ]))
  148. }
  149. #[cfg(test)]
  150. mod tests {
  151. use std::str::FromStr;
  152. use bip39::Mnemonic;
  153. use bitcoin::Network;
  154. use super::*;
  155. #[test]
  156. fn test_secret_from_seed() {
  157. let seed =
  158. "half depart obvious quality work element tank gorilla view sugar picture humble";
  159. let mnemonic = Mnemonic::from_str(seed).unwrap();
  160. let seed: [u8; 64] = mnemonic.to_seed("");
  161. let xpriv = ExtendedPrivKey::new_master(Network::Bitcoin, &seed).unwrap();
  162. let keyset_id = Id::from_str("009a1f293253e41e").unwrap();
  163. let test_secrets = [
  164. "485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae",
  165. "8f2b39e8e594a4056eb1e6dbb4b0c38ef13b1b2c751f64f810ec04ee35b77270",
  166. "bc628c79accd2364fd31511216a0fab62afd4a18ff77a20deded7b858c9860c8",
  167. "59284fd1650ea9fa17db2b3acf59ecd0f2d52ec3261dd4152785813ff27a33bf",
  168. "576c23393a8b31cc8da6688d9c9a96394ec74b40fdaf1f693a6bb84284334ea0",
  169. ];
  170. for (i, test_secret) in test_secrets.iter().enumerate() {
  171. let secret = Secret::from_xpriv(xpriv, keyset_id, i.try_into().unwrap()).unwrap();
  172. assert_eq!(secret, Secret::from_str(test_secret).unwrap())
  173. }
  174. }
  175. #[test]
  176. fn test_r_from_seed() {
  177. let seed =
  178. "half depart obvious quality work element tank gorilla view sugar picture humble";
  179. let mnemonic = Mnemonic::from_str(seed).unwrap();
  180. let seed: [u8; 64] = mnemonic.to_seed("");
  181. let xpriv = ExtendedPrivKey::new_master(Network::Bitcoin, &seed).unwrap();
  182. let keyset_id = Id::from_str("009a1f293253e41e").unwrap();
  183. let test_rs = [
  184. "ad00d431add9c673e843d4c2bf9a778a5f402b985b8da2d5550bf39cda41d679",
  185. "967d5232515e10b81ff226ecf5a9e2e2aff92d66ebc3edf0987eb56357fd6248",
  186. "b20f47bb6ae083659f3aa986bfa0435c55c6d93f687d51a01f26862d9b9a4899",
  187. "fb5fca398eb0b1deb955a2988b5ac77d32956155f1c002a373535211a2dfdc29",
  188. "5f09bfbfe27c439a597719321e061e2e40aad4a36768bb2bcc3de547c9644bf9",
  189. ];
  190. for (i, test_r) in test_rs.iter().enumerate() {
  191. let r = SecretKey::from_xpriv(xpriv, keyset_id, i.try_into().unwrap()).unwrap();
  192. assert_eq!(r, SecretKey::from_hex(test_r).unwrap())
  193. }
  194. }
  195. }