mod.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. //! NUT-14: Hashed Time Lock Contacts (HTLC)
  2. //!
  3. //! <https://github.com/cashubtc/nuts/blob/main/14.md>
  4. use std::str::FromStr;
  5. use bitcoin::secp256k1::schnorr::Signature;
  6. use serde::{Deserialize, Serialize};
  7. use thiserror::Error;
  8. use super::nut00::Witness;
  9. use super::nut10::Secret;
  10. use super::nut11::valid_signatures;
  11. use super::{Conditions, Proof};
  12. use crate::util::{hex, unix_time};
  13. pub mod serde_htlc_witness;
  14. /// NUT14 Errors
  15. #[derive(Debug, Error)]
  16. pub enum Error {
  17. /// Incorrect secret kind
  18. #[error("Secret is not a HTLC secret")]
  19. IncorrectSecretKind,
  20. /// HTLC locktime has already passed
  21. #[error("Locktime in past")]
  22. LocktimeInPast,
  23. /// Hash Required
  24. #[error("Hash required")]
  25. HashRequired,
  26. /// Hash is not valid
  27. #[error("Hash is not valid")]
  28. InvalidHash,
  29. /// Preimage does not match
  30. #[error("Preimage does not match")]
  31. Preimage,
  32. /// HTLC preimage must be valid hex encoding
  33. #[error("Preimage must be valid hex encoding")]
  34. InvalidHexPreimage,
  35. /// HTLC preimage must be exactly 32 bytes
  36. #[error("Preimage must be exactly 32 bytes (64 hex characters)")]
  37. PreimageInvalidSize,
  38. /// Witness Signatures not provided
  39. #[error("Witness did not provide signatures")]
  40. SignaturesNotProvided,
  41. /// SIG_ALL not supported in this context
  42. #[error("SIG_ALL proofs must be verified using a different method")]
  43. SigAllNotSupportedHere,
  44. /// Secp256k1 error
  45. #[error(transparent)]
  46. Secp256k1(#[from] bitcoin::secp256k1::Error),
  47. /// NUT11 Error
  48. #[error(transparent)]
  49. NUT11(#[from] super::nut11::Error),
  50. #[error(transparent)]
  51. /// Serde Error
  52. Serde(#[from] serde_json::Error),
  53. }
  54. /// HTLC Witness
  55. #[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
  56. #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
  57. pub struct HTLCWitness {
  58. /// Preimage
  59. pub preimage: String,
  60. /// Signatures
  61. #[serde(skip_serializing_if = "Option::is_none")]
  62. pub signatures: Option<Vec<String>>,
  63. }
  64. impl HTLCWitness {
  65. /// Decode the preimage from hex and verify it's exactly 32 bytes
  66. ///
  67. /// Returns the 32-byte preimage data if valid, or an error if:
  68. /// - The hex decoding fails
  69. /// - The decoded data is not exactly 32 bytes
  70. pub fn preimage_data(&self) -> Result<[u8; 32], Error> {
  71. const REQUIRED_PREIMAGE_BYTES: usize = 32;
  72. // Decode the 64-character hex string to bytes
  73. let preimage_bytes = hex::decode(&self.preimage).map_err(|_| Error::InvalidHexPreimage)?;
  74. // Verify the preimage is exactly 32 bytes
  75. if preimage_bytes.len() != REQUIRED_PREIMAGE_BYTES {
  76. return Err(Error::PreimageInvalidSize);
  77. }
  78. // Convert to fixed-size array
  79. let mut array = [0u8; 32];
  80. array.copy_from_slice(&preimage_bytes);
  81. Ok(array)
  82. }
  83. }
  84. impl Proof {
  85. /// Verify HTLC
  86. ///
  87. /// Per NUT-14, there are two spending pathways:
  88. /// 1. Receiver path (preimage + pubkeys): ALWAYS available
  89. /// 2. Sender/Refund path (refund keys, no preimage): available AFTER locktime
  90. ///
  91. /// The verification tries to determine which path is being used based on
  92. /// the witness provided, then validates accordingly.
  93. pub fn verify_htlc(&self) -> Result<(), Error> {
  94. let secret: Secret = self.secret.clone().try_into()?;
  95. let spending_conditions: Conditions = secret
  96. .secret_data()
  97. .tags()
  98. .cloned()
  99. .unwrap_or_default()
  100. .try_into()?;
  101. if spending_conditions.sig_flag == super::SigFlag::SigAll {
  102. return Err(Error::SigAllNotSupportedHere);
  103. }
  104. if secret.kind() != super::Kind::HTLC {
  105. return Err(Error::IncorrectSecretKind);
  106. }
  107. // Get the spending requirements (includes both receiver and refund paths)
  108. let now = unix_time();
  109. let requirements =
  110. super::nut10::get_pubkeys_and_required_sigs(&secret, now).map_err(Error::NUT11)?;
  111. // Try to extract HTLC witness - must be correct type
  112. let htlc_witness = match &self.witness {
  113. Some(Witness::HTLCWitness(witness)) => witness,
  114. _ => {
  115. // Wrong witness type or no witness
  116. // If refund path is available with 0 required sigs, anyone can spend
  117. if let Some(refund_path) = &requirements.refund_path {
  118. if refund_path.required_sigs == 0 {
  119. return Ok(());
  120. }
  121. }
  122. return Err(Error::IncorrectSecretKind);
  123. }
  124. };
  125. // Try to verify the preimage and capture the specific error if it fails
  126. let preimage_result = super::nut10::verify_htlc_preimage(htlc_witness, &secret);
  127. // Determine which path to use:
  128. // - If preimage is valid → use receiver path (always available)
  129. // - If preimage is invalid/missing → try refund path (if available)
  130. if preimage_result.is_ok() {
  131. // Receiver path: preimage valid, now check signatures against pubkeys
  132. if requirements.required_sigs == 0 {
  133. return Ok(());
  134. }
  135. let witness_signatures = htlc_witness
  136. .signatures
  137. .as_ref()
  138. .ok_or(Error::SignaturesNotProvided)?;
  139. let signatures: Vec<Signature> = witness_signatures
  140. .iter()
  141. .map(|s| Signature::from_str(s))
  142. .collect::<Result<Vec<_>, _>>()?;
  143. let msg: &[u8] = self.secret.as_bytes();
  144. let valid_sig_count = valid_signatures(msg, &requirements.pubkeys, &signatures)?;
  145. if valid_sig_count >= requirements.required_sigs {
  146. Ok(())
  147. } else {
  148. Err(Error::NUT11(super::nut11::Error::SpendConditionsNotMet))
  149. }
  150. } else if let Some(refund_path) = &requirements.refund_path {
  151. // Refund path: preimage not valid/provided, but locktime has passed
  152. // Check signatures against refund keys
  153. if refund_path.required_sigs == 0 {
  154. // Anyone can spend (locktime passed, no refund keys)
  155. return Ok(());
  156. }
  157. let witness_signatures = htlc_witness
  158. .signatures
  159. .as_ref()
  160. .ok_or(Error::SignaturesNotProvided)?;
  161. let signatures: Vec<Signature> = witness_signatures
  162. .iter()
  163. .map(|s| Signature::from_str(s))
  164. .collect::<Result<Vec<_>, _>>()?;
  165. let msg: &[u8] = self.secret.as_bytes();
  166. let valid_sig_count = valid_signatures(msg, &refund_path.pubkeys, &signatures)?;
  167. if valid_sig_count >= refund_path.required_sigs {
  168. Ok(())
  169. } else {
  170. Err(Error::NUT11(super::nut11::Error::SpendConditionsNotMet))
  171. }
  172. } else {
  173. // No valid preimage and refund path not available (locktime not passed)
  174. // Return the specific error from preimage verification
  175. preimage_result
  176. }
  177. }
  178. /// Add Preimage
  179. #[inline]
  180. pub fn add_preimage(&mut self, preimage: String) {
  181. let signatures = self
  182. .witness
  183. .as_ref()
  184. .map(super::nut00::Witness::signatures)
  185. .unwrap_or_default();
  186. self.witness = Some(Witness::HTLCWitness(HTLCWitness {
  187. preimage,
  188. signatures,
  189. }))
  190. }
  191. }
  192. #[cfg(test)]
  193. mod tests {
  194. use bitcoin::hashes::sha256::Hash as Sha256Hash;
  195. use bitcoin::hashes::Hash;
  196. use super::*;
  197. use crate::nuts::nut00::Witness;
  198. use crate::nuts::nut10::Kind;
  199. use crate::nuts::Nut10Secret;
  200. use crate::secret::Secret as SecretString;
  201. /// Tests that verify_htlc correctly accepts a valid HTLC with the correct preimage.
  202. ///
  203. /// This test ensures that a properly formed HTLC proof with the correct preimage
  204. /// passes verification.
  205. ///
  206. /// Mutant testing: Combined with negative tests, this catches mutations that
  207. /// replace verify_htlc with Ok(()) since the negative tests will fail.
  208. #[test]
  209. fn test_verify_htlc_valid() {
  210. // Create a valid HTLC secret with a known preimage (32 bytes)
  211. let preimage_bytes = [42u8; 32]; // 32-byte preimage
  212. let hash = Sha256Hash::hash(&preimage_bytes);
  213. let hash_str = hash.to_string();
  214. let nut10_secret = Nut10Secret::new(Kind::HTLC, hash_str, None::<Vec<Vec<String>>>);
  215. let secret: SecretString = nut10_secret.try_into().unwrap();
  216. let htlc_witness = HTLCWitness {
  217. preimage: hex::encode(&preimage_bytes),
  218. signatures: None,
  219. };
  220. let proof = Proof {
  221. amount: crate::Amount::from(1),
  222. keyset_id: crate::nuts::nut02::Id::from_str("00deadbeef123456").unwrap(),
  223. secret,
  224. c: crate::nuts::nut01::PublicKey::from_hex(
  225. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  226. )
  227. .unwrap(),
  228. witness: Some(Witness::HTLCWitness(htlc_witness)),
  229. dleq: None,
  230. };
  231. // Valid HTLC should verify successfully
  232. assert!(proof.verify_htlc().is_ok());
  233. }
  234. /// Tests that verify_htlc correctly rejects an HTLC with a wrong preimage.
  235. ///
  236. /// This test is critical for security - if the verification function doesn't properly
  237. /// check the preimage against the hash, an attacker could spend HTLC-locked funds
  238. /// without knowing the correct preimage.
  239. ///
  240. /// Mutant testing: Catches mutations that replace verify_htlc with Ok(()) or remove
  241. /// the preimage verification logic.
  242. #[test]
  243. fn test_verify_htlc_wrong_preimage() {
  244. // Create an HTLC secret with a specific hash (32 bytes)
  245. let correct_preimage_bytes = [42u8; 32];
  246. let hash = Sha256Hash::hash(&correct_preimage_bytes);
  247. let hash_str = hash.to_string();
  248. let nut10_secret = Nut10Secret::new(Kind::HTLC, hash_str, None::<Vec<Vec<String>>>);
  249. let secret: SecretString = nut10_secret.try_into().unwrap();
  250. // Use a different preimage in the witness
  251. let wrong_preimage_bytes = [99u8; 32]; // Different from correct preimage
  252. let htlc_witness = HTLCWitness {
  253. preimage: hex::encode(&wrong_preimage_bytes),
  254. signatures: None,
  255. };
  256. let proof = Proof {
  257. amount: crate::Amount::from(1),
  258. keyset_id: crate::nuts::nut02::Id::from_str("00deadbeef123456").unwrap(),
  259. secret,
  260. c: crate::nuts::nut01::PublicKey::from_hex(
  261. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  262. )
  263. .unwrap(),
  264. witness: Some(Witness::HTLCWitness(htlc_witness)),
  265. dleq: None,
  266. };
  267. // Verification should fail with wrong preimage
  268. let result = proof.verify_htlc();
  269. assert!(result.is_err());
  270. assert!(matches!(result.unwrap_err(), Error::Preimage));
  271. }
  272. /// Tests that verify_htlc correctly rejects an HTLC with an invalid hash format.
  273. ///
  274. /// This test ensures that the verification function properly validates that the
  275. /// hash in the secret data is a valid SHA256 hash.
  276. ///
  277. /// Mutant testing: Catches mutations that replace verify_htlc with Ok(()) or
  278. /// remove the hash validation logic.
  279. #[test]
  280. fn test_verify_htlc_invalid_hash() {
  281. // Create an HTLC secret with an invalid hash (not a valid hex string)
  282. let invalid_hash = "not_a_valid_hash";
  283. let nut10_secret = Nut10Secret::new(
  284. Kind::HTLC,
  285. invalid_hash.to_string(),
  286. None::<Vec<Vec<String>>>,
  287. );
  288. let secret: SecretString = nut10_secret.try_into().unwrap();
  289. let preimage_bytes = [42u8; 32]; // Valid 32-byte preimage
  290. let htlc_witness = HTLCWitness {
  291. preimage: hex::encode(&preimage_bytes),
  292. signatures: None,
  293. };
  294. let proof = Proof {
  295. amount: crate::Amount::from(1),
  296. keyset_id: crate::nuts::nut02::Id::from_str("00deadbeef123456").unwrap(),
  297. secret,
  298. c: crate::nuts::nut01::PublicKey::from_hex(
  299. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  300. )
  301. .unwrap(),
  302. witness: Some(Witness::HTLCWitness(htlc_witness)),
  303. dleq: None,
  304. };
  305. // Verification should fail with invalid hash
  306. let result = proof.verify_htlc();
  307. assert!(result.is_err());
  308. assert!(matches!(result.unwrap_err(), Error::InvalidHash));
  309. }
  310. /// Tests that verify_htlc correctly rejects an HTLC with the wrong witness type.
  311. ///
  312. /// This test ensures that the verification function checks that the witness is
  313. /// of the correct type (HTLCWitness) and not some other witness type.
  314. ///
  315. /// Mutant testing: Catches mutations that replace verify_htlc with Ok(()) or
  316. /// remove the witness type check.
  317. #[test]
  318. fn test_verify_htlc_wrong_witness_type() {
  319. // Create an HTLC secret
  320. let preimage = "test_preimage";
  321. let hash = Sha256Hash::hash(preimage.as_bytes());
  322. let hash_str = hash.to_string();
  323. let nut10_secret = Nut10Secret::new(Kind::HTLC, hash_str, None::<Vec<Vec<String>>>);
  324. let secret: SecretString = nut10_secret.try_into().unwrap();
  325. // Create proof with wrong witness type (P2PKWitness instead of HTLCWitness)
  326. let proof = Proof {
  327. amount: crate::Amount::from(1),
  328. keyset_id: crate::nuts::nut02::Id::from_str("00deadbeef123456").unwrap(),
  329. secret,
  330. c: crate::nuts::nut01::PublicKey::from_hex(
  331. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  332. )
  333. .unwrap(),
  334. witness: Some(Witness::P2PKWitness(super::super::nut11::P2PKWitness {
  335. signatures: vec![],
  336. })),
  337. dleq: None,
  338. };
  339. // Verification should fail with wrong witness type
  340. let result = proof.verify_htlc();
  341. assert!(result.is_err());
  342. assert!(matches!(result.unwrap_err(), Error::IncorrectSecretKind));
  343. }
  344. /// Tests that add_preimage correctly adds a preimage to the proof.
  345. ///
  346. /// This test ensures that add_preimage actually modifies the witness and doesn't
  347. /// just return without doing anything.
  348. ///
  349. /// Mutant testing: Catches mutations that replace add_preimage with () without
  350. /// actually adding the preimage.
  351. #[test]
  352. fn test_add_preimage() {
  353. let preimage_bytes = [42u8; 32]; // 32-byte preimage
  354. let hash = Sha256Hash::hash(&preimage_bytes);
  355. let hash_str = hash.to_string();
  356. let nut10_secret = Nut10Secret::new(Kind::HTLC, hash_str, None::<Vec<Vec<String>>>);
  357. let secret: SecretString = nut10_secret.try_into().unwrap();
  358. let mut proof = Proof {
  359. amount: crate::Amount::from(1),
  360. keyset_id: crate::nuts::nut02::Id::from_str("00deadbeef123456").unwrap(),
  361. secret,
  362. c: crate::nuts::nut01::PublicKey::from_hex(
  363. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  364. )
  365. .unwrap(),
  366. witness: None,
  367. dleq: None,
  368. };
  369. // Initially, witness should be None
  370. assert!(proof.witness.is_none());
  371. // Add preimage (hex-encoded)
  372. let preimage_hex = hex::encode(&preimage_bytes);
  373. proof.add_preimage(preimage_hex.clone());
  374. // After adding, witness should be Some with HTLCWitness
  375. assert!(proof.witness.is_some());
  376. if let Some(Witness::HTLCWitness(witness)) = &proof.witness {
  377. assert_eq!(witness.preimage, preimage_hex);
  378. } else {
  379. panic!("Expected HTLCWitness");
  380. }
  381. // The proof with added preimage should verify successfully
  382. assert!(proof.verify_htlc().is_ok());
  383. }
  384. /// Tests that verify_htlc requires BOTH locktime expired AND no refund keys for "anyone can spend".
  385. ///
  386. /// This test verifies that when locktime has passed and refund keys are present,
  387. /// a signature from the refund keys is required (not anyone-can-spend).
  388. ///
  389. /// Per NUT-14: After locktime, the refund path requires signatures from refund keys.
  390. /// The "anyone can spend" case only applies when locktime passed AND no refund keys.
  391. #[test]
  392. fn test_htlc_locktime_and_refund_keys_logic() {
  393. use crate::nuts::nut01::PublicKey;
  394. use crate::nuts::nut11::Conditions;
  395. let correct_preimage_bytes = [42u8; 32]; // 32-byte preimage
  396. let hash = Sha256Hash::hash(&correct_preimage_bytes);
  397. let hash_str = hash.to_string();
  398. // Use WRONG preimage to force using refund path (not receiver path)
  399. let wrong_preimage_bytes = [99u8; 32];
  400. // Test: Locktime has passed (locktime=1) but refund keys ARE present
  401. // Since we provide wrong preimage, receiver path fails, so we try refund path.
  402. // Refund path with refund keys present should require a signature.
  403. let refund_pubkey = PublicKey::from_hex(
  404. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  405. )
  406. .unwrap();
  407. let conditions_with_refund = Conditions {
  408. locktime: Some(1), // Locktime in past (current time is much larger)
  409. pubkeys: None,
  410. refund_keys: Some(vec![refund_pubkey]), // Refund key present
  411. num_sigs: None,
  412. sig_flag: crate::nuts::nut11::SigFlag::default(),
  413. num_sigs_refund: None,
  414. };
  415. let nut10_secret = Nut10Secret::new(Kind::HTLC, hash_str, Some(conditions_with_refund));
  416. let secret: SecretString = nut10_secret.try_into().unwrap();
  417. let htlc_witness = HTLCWitness {
  418. preimage: hex::encode(&wrong_preimage_bytes), // Wrong preimage!
  419. signatures: None, // No signature provided
  420. };
  421. let proof = Proof {
  422. amount: crate::Amount::from(1),
  423. keyset_id: crate::nuts::nut02::Id::from_str("00deadbeef123456").unwrap(),
  424. secret,
  425. c: crate::nuts::nut01::PublicKey::from_hex(
  426. "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
  427. )
  428. .unwrap(),
  429. witness: Some(Witness::HTLCWitness(htlc_witness)),
  430. dleq: None,
  431. };
  432. // Should FAIL because:
  433. // 1. Wrong preimage means receiver path fails
  434. // 2. Falls back to refund path (locktime passed)
  435. // 3. Refund keys are present, so signature is required
  436. // 4. No signature provided
  437. let result = proof.verify_htlc();
  438. assert!(
  439. result.is_err(),
  440. "Should fail when using refund path with refund keys but no signature"
  441. );
  442. }
  443. }