nut02.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. //! Keysets and keyset ID
  2. // https://github.com/cashubtc/nuts/blob/main/02.md
  3. use core::fmt;
  4. use std::str::FromStr;
  5. use bitcoin::hashes::{sha256, Hash};
  6. use itertools::Itertools;
  7. use serde::{Deserialize, Serialize};
  8. use serde_with::{serde_as, VecSkipError};
  9. use thiserror::Error;
  10. use super::nut01::Keys;
  11. use super::CurrencyUnit;
  12. #[derive(Debug, Error, PartialEq)]
  13. pub enum Error {
  14. #[error("`{0}`")]
  15. HexError(#[from] hex::FromHexError),
  16. #[error("NUT02: ID length invalid")]
  17. Length,
  18. }
  19. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  20. pub enum KeySetVersion {
  21. Version00,
  22. }
  23. impl std::fmt::Display for KeySetVersion {
  24. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  25. match self {
  26. KeySetVersion::Version00 => f.write_str("00"),
  27. }
  28. }
  29. }
  30. /// A keyset ID is an identifier for a specific keyset. It can be derived by
  31. /// anyone who knows the set of public keys of a mint. The keyset ID **CAN**
  32. /// be stored in a Cashu token such that the token can be used to identify
  33. /// which mint or keyset it was generated from.
  34. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  35. pub struct Id {
  36. version: KeySetVersion,
  37. id: [u8; Self::STRLEN],
  38. }
  39. impl Id {
  40. const STRLEN: usize = 14;
  41. }
  42. impl From<Id> for u64 {
  43. fn from(value: Id) -> Self {
  44. value
  45. .id
  46. .iter()
  47. .fold(0, |acc, &byte| (acc << 8) | u64::from(byte))
  48. }
  49. }
  50. impl std::fmt::Display for Id {
  51. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  52. f.write_str(&format!(
  53. "{}{}",
  54. self.version,
  55. String::from_utf8(self.id.to_vec()).map_err(|_| fmt::Error)?
  56. ))
  57. }
  58. }
  59. impl FromStr for Id {
  60. type Err = Error;
  61. fn from_str(s: &str) -> Result<Self, Self::Err> {
  62. // Check if the string length is valid
  63. if s.len() != 16 {
  64. return Err(Error::Length);
  65. }
  66. Ok(Self {
  67. version: KeySetVersion::Version00,
  68. id: s[2..].as_bytes().try_into().map_err(|_| Error::Length)?,
  69. })
  70. }
  71. }
  72. impl serde::ser::Serialize for Id {
  73. fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  74. where
  75. S: serde::Serializer,
  76. {
  77. serializer.serialize_str(&self.to_string())
  78. }
  79. }
  80. impl<'de> serde::de::Deserialize<'de> for Id {
  81. fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  82. where
  83. D: serde::Deserializer<'de>,
  84. {
  85. struct IdVisitor;
  86. impl<'de> serde::de::Visitor<'de> for IdVisitor {
  87. type Value = Id;
  88. fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
  89. formatter.write_str("Expecting a 14 char hex string")
  90. }
  91. fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
  92. where
  93. E: serde::de::Error,
  94. {
  95. Id::from_str(v).map_err(|e| match e {
  96. Error::Length => E::custom(format!(
  97. "Invalid Length: Expected {}, got {}",
  98. Id::STRLEN,
  99. v.len()
  100. )),
  101. Error::HexError(e) => E::custom(e),
  102. })
  103. }
  104. }
  105. deserializer.deserialize_str(IdVisitor)
  106. }
  107. }
  108. impl From<&Keys> for Id {
  109. fn from(map: &Keys) -> Self {
  110. // REVIEW: Is it 16 or 14 bytes
  111. /* NUT-02
  112. 1 - sort public keys by their amount in ascending order
  113. 2 - concatenate all public keys to one string
  114. 3 - HASH_SHA256 the concatenated public keys
  115. 4 - take the first 14 characters of the hex-encoded hash
  116. 5 - prefix it with a keyset ID version byte
  117. */
  118. let pubkeys_concat = map
  119. .iter()
  120. .sorted_by(|(amt_a, _), (amt_b, _)| amt_a.cmp(amt_b))
  121. .map(|(_, pubkey)| pubkey.to_bytes())
  122. .collect::<Box<[Box<[u8]>]>>()
  123. .concat();
  124. let hash = sha256::Hash::hash(&pubkeys_concat);
  125. let hex_of_hash = hex::encode(hash.to_byte_array());
  126. // First 9 bytes of hash will encode as the first 12 Base64 characters later
  127. Self {
  128. version: KeySetVersion::Version00,
  129. id: hex_of_hash[0..Self::STRLEN]
  130. .as_bytes()
  131. .to_owned()
  132. .try_into()
  133. .unwrap(),
  134. }
  135. }
  136. }
  137. /// Mint Keysets [NUT-02]
  138. /// Ids of mints keyset ids
  139. #[serde_as]
  140. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  141. pub struct KeysetResponse {
  142. /// set of public key ids that the mint generates
  143. #[serde_as(as = "VecSkipError<_>")]
  144. pub keysets: Vec<KeySetInfo>,
  145. }
  146. impl KeysetResponse {
  147. pub fn new(keysets: Vec<KeySet>) -> Self {
  148. Self {
  149. keysets: keysets.into_iter().map(|keyset| keyset.into()).collect(),
  150. }
  151. }
  152. }
  153. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
  154. pub struct KeySet {
  155. pub id: Id,
  156. pub unit: CurrencyUnit,
  157. pub keys: Keys,
  158. }
  159. impl From<mint::KeySet> for KeySet {
  160. fn from(keyset: mint::KeySet) -> Self {
  161. Self {
  162. id: keyset.id,
  163. unit: keyset.unit,
  164. keys: Keys::from(keyset.keys),
  165. }
  166. }
  167. }
  168. #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
  169. pub struct KeySetInfo {
  170. pub id: Id,
  171. pub unit: CurrencyUnit,
  172. pub active: bool,
  173. }
  174. impl From<KeySet> for KeySetInfo {
  175. fn from(keyset: KeySet) -> KeySetInfo {
  176. Self {
  177. id: keyset.id,
  178. unit: keyset.unit,
  179. active: false,
  180. }
  181. }
  182. }
  183. pub mod mint {
  184. use std::collections::BTreeMap;
  185. use bitcoin::hashes::sha256::Hash as Sha256;
  186. use bitcoin::hashes::{Hash, HashEngine};
  187. use k256::SecretKey;
  188. use serde::{Deserialize, Serialize};
  189. use super::Id;
  190. use crate::nuts::nut01::mint::{KeyPair, Keys};
  191. use crate::nuts::CurrencyUnit;
  192. use crate::Amount;
  193. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  194. pub struct KeySet {
  195. pub id: Id,
  196. pub unit: CurrencyUnit,
  197. pub keys: Keys,
  198. }
  199. impl KeySet {
  200. pub fn generate(
  201. secret: &[u8],
  202. unit: CurrencyUnit,
  203. derivation_path: &str,
  204. max_order: u8,
  205. ) -> Self {
  206. // Elliptic curve math context
  207. /* NUT-02 § 2.1
  208. for i in range(MAX_ORDER):
  209. k_i = HASH_SHA256(s + D + i)[:32]
  210. */
  211. let mut map = BTreeMap::new();
  212. // SHA-256 midstate, for quicker hashing
  213. let mut engine = Sha256::engine();
  214. engine.input(secret);
  215. engine.input(derivation_path.as_bytes());
  216. for i in 0..max_order {
  217. let amount = Amount::from(2_u64.pow(i as u32));
  218. // Reuse midstate
  219. let mut e = engine.clone();
  220. e.input(i.to_string().as_bytes());
  221. let hash = Sha256::from_engine(e);
  222. let secret_key = SecretKey::from_slice(&hash.to_byte_array()).unwrap();
  223. let keypair = KeyPair::from_secret_key(secret_key.into());
  224. map.insert(amount, keypair);
  225. }
  226. let keys = Keys(map);
  227. Self {
  228. id: (&keys).into(),
  229. unit,
  230. keys,
  231. }
  232. }
  233. }
  234. impl From<KeySet> for Id {
  235. fn from(keyset: KeySet) -> Id {
  236. let keys: super::KeySet = keyset.into();
  237. Id::from(&keys.keys)
  238. }
  239. }
  240. impl From<&Keys> for Id {
  241. fn from(map: &Keys) -> Self {
  242. let keys: super::Keys = map.clone().into();
  243. Id::from(&keys)
  244. }
  245. }
  246. }
  247. #[cfg(test)]
  248. mod test {
  249. use std::str::FromStr;
  250. use super::{KeySetInfo, Keys, KeysetResponse};
  251. use crate::nuts::nut02::Id;
  252. const SHORT_KEYSET_ID: &str = "00456a94ab4e1c46";
  253. const SHORT_KEYSET: &str = r#"
  254. {
  255. "1":"03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc",
  256. "2":"03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de",
  257. "4":"02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303",
  258. "8":"02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528"
  259. }
  260. "#;
  261. const KEYSET_ID: &str = "000f01df73ea149a";
  262. const KEYSET: &str = r#"
  263. {
  264. "1":"03ba786a2c0745f8c30e490288acd7a72dd53d65afd292ddefa326a4a3fa14c566",
  265. "2":"03361cd8bd1329fea797a6add1cf1990ffcf2270ceb9fc81eeee0e8e9c1bd0cdf5",
  266. "4":"036e378bcf78738ddf68859293c69778035740e41138ab183c94f8fee7572214c7",
  267. "8":"03909d73beaf28edfb283dbeb8da321afd40651e8902fcf5454ecc7d69788626c0",
  268. "16":"028a36f0e6638ea7466665fe174d958212723019ec08f9ce6898d897f88e68aa5d",
  269. "32":"03a97a40e146adee2687ac60c2ba2586a90f970de92a9d0e6cae5a4b9965f54612",
  270. "64":"03ce86f0c197aab181ddba0cfc5c5576e11dfd5164d9f3d4a3fc3ffbbf2e069664",
  271. "128":"0284f2c06d938a6f78794814c687560a0aabab19fe5e6f30ede38e113b132a3cb9",
  272. "256":"03b99f475b68e5b4c0ba809cdecaae64eade2d9787aa123206f91cd61f76c01459",
  273. "512":"03d4db82ea19a44d35274de51f78af0a710925fe7d9e03620b84e3e9976e3ac2eb",
  274. "1024":"031fbd4ba801870871d46cf62228a1b748905ebc07d3b210daf48de229e683f2dc",
  275. "2048":"0276cedb9a3b160db6a158ad4e468d2437f021293204b3cd4bf6247970d8aff54b",
  276. "4096":"02fc6b89b403ee9eb8a7ed457cd3973638080d6e04ca8af7307c965c166b555ea2",
  277. "8192":"0320265583e916d3a305f0d2687fcf2cd4e3cd03a16ea8261fda309c3ec5721e21",
  278. "16384":"036e41de58fdff3cb1d8d713f48c63bc61fa3b3e1631495a444d178363c0d2ed50",
  279. "32768":"0365438f613f19696264300b069d1dad93f0c60a37536b72a8ab7c7366a5ee6c04",
  280. "65536":"02408426cfb6fc86341bac79624ba8708a4376b2d92debdf4134813f866eb57a8d",
  281. "131072":"031063e9f11c94dc778c473e968966eac0e70b7145213fbaff5f7a007e71c65f41",
  282. "262144":"02f2a3e808f9cd168ec71b7f328258d0c1dda250659c1aced14c7f5cf05aab4328",
  283. "524288":"038ac10de9f1ff9395903bb73077e94dbf91e9ef98fd77d9a2debc5f74c575bc86",
  284. "1048576":"0203eaee4db749b0fc7c49870d082024b2c31d889f9bc3b32473d4f1dfa3625788",
  285. "2097152":"033cdb9d36e1e82ae652b7b6a08e0204569ec7ff9ebf85d80a02786dc7fe00b04c",
  286. "4194304":"02c8b73f4e3a470ae05e5f2fe39984d41e9f6ae7be9f3b09c9ac31292e403ac512",
  287. "8388608":"025bbe0cfce8a1f4fbd7f3a0d4a09cb6badd73ef61829dc827aa8a98c270bc25b0",
  288. "16777216":"037eec3d1651a30a90182d9287a5c51386fe35d4a96839cf7969c6e2a03db1fc21",
  289. "33554432":"03280576b81a04e6abd7197f305506476f5751356b7643988495ca5c3e14e5c262",
  290. "67108864":"03268bfb05be1dbb33ab6e7e00e438373ca2c9b9abc018fdb452d0e1a0935e10d3",
  291. "134217728":"02573b68784ceba9617bbcc7c9487836d296aa7c628c3199173a841e7a19798020",
  292. "268435456":"0234076b6e70f7fbf755d2227ecc8d8169d662518ee3a1401f729e2a12ccb2b276",
  293. "536870912":"03015bd88961e2a466a2163bd4248d1d2b42c7c58a157e594785e7eb34d880efc9",
  294. "1073741824":"02c9b076d08f9020ebee49ac8ba2610b404d4e553a4f800150ceb539e9421aaeee",
  295. "2147483648":"034d592f4c366afddc919a509600af81b489a03caf4f7517c2b3f4f2b558f9a41a",
  296. "4294967296":"037c09ecb66da082981e4cbdb1ac65c0eb631fc75d85bed13efb2c6364148879b5",
  297. "8589934592":"02b4ebb0dda3b9ad83b39e2e31024b777cc0ac205a96b9a6cfab3edea2912ed1b3",
  298. "17179869184":"026cc4dacdced45e63f6e4f62edbc5779ccd802e7fabb82d5123db879b636176e9",
  299. "34359738368":"02b2cee01b7d8e90180254459b8f09bbea9aad34c3a2fd98c85517ecfc9805af75",
  300. "68719476736":"037a0c0d564540fc574b8bfa0253cca987b75466e44b295ed59f6f8bd41aace754",
  301. "137438953472":"021df6585cae9b9ca431318a713fd73dbb76b3ef5667957e8633bca8aaa7214fb6",
  302. "274877906944":"02b8f53dde126f8c85fa5bb6061c0be5aca90984ce9b902966941caf963648d53a",
  303. "549755813888":"029cc8af2840d59f1d8761779b2496623c82c64be8e15f9ab577c657c6dd453785",
  304. "1099511627776":"03e446fdb84fad492ff3a25fc1046fb9a93a5b262ebcd0151caa442ea28959a38a",
  305. "2199023255552":"02d6b25bd4ab599dd0818c55f75702fde603c93f259222001246569018842d3258",
  306. "4398046511104":"03397b522bb4e156ec3952d3f048e5a986c20a00718e5e52cd5718466bf494156a",
  307. "8796093022208":"02d1fb9e78262b5d7d74028073075b80bb5ab281edcfc3191061962c1346340f1e",
  308. "17592186044416":"030d3f2ad7a4ca115712ff7f140434f802b19a4c9b2dd1c76f3e8e80c05c6a9310",
  309. "35184372088832":"03e325b691f292e1dfb151c3fb7cad440b225795583c32e24e10635a80e4221c06",
  310. "70368744177664":"03bee8f64d88de3dee21d61f89efa32933da51152ddbd67466bef815e9f93f8fd1",
  311. "140737488355328":"0327244c9019a4892e1f04ba3bf95fe43b327479e2d57c25979446cc508cd379ed",
  312. "281474976710656":"02fb58522cd662f2f8b042f8161caae6e45de98283f74d4e99f19b0ea85e08a56d",
  313. "562949953421312":"02adde4b466a9d7e59386b6a701a39717c53f30c4810613c1b55e6b6da43b7bc9a",
  314. "1125899906842624":"038eeda11f78ce05c774f30e393cda075192b890d68590813ff46362548528dca9",
  315. "2251799813685248":"02ec13e0058b196db80f7079d329333b330dc30c000dbdd7397cbbc5a37a664c4f",
  316. "4503599627370496":"02d2d162db63675bd04f7d56df04508840f41e2ad87312a3c93041b494efe80a73",
  317. "9007199254740992":"0356969d6aef2bb40121dbd07c68b6102339f4ea8e674a9008bb69506795998f49",
  318. "18014398509481984":"02f4e667567ebb9f4e6e180a4113bb071c48855f657766bb5e9c776a880335d1d6",
  319. "36028797018963968":"0385b4fe35e41703d7a657d957c67bb536629de57b7e6ee6fe2130728ef0fc90b0",
  320. "72057594037927936":"02b2bc1968a6fddbcc78fb9903940524824b5f5bed329c6ad48a19b56068c144fd",
  321. "144115188075855872":"02e0dbb24f1d288a693e8a49bc14264d1276be16972131520cf9e055ae92fba19a",
  322. "288230376151711744":"03efe75c106f931a525dc2d653ebedddc413a2c7d8cb9da410893ae7d2fa7d19cc",
  323. "576460752303423488":"02c7ec2bd9508a7fc03f73c7565dc600b30fd86f3d305f8f139c45c404a52d958a",
  324. "1152921504606846976":"035a6679c6b25e68ff4e29d1c7ef87f21e0a8fc574f6a08c1aa45ff352c1d59f06",
  325. "2305843009213693952":"033cdc225962c052d485f7cfbf55a5b2367d200fe1fe4373a347deb4cc99e9a099",
  326. "4611686018427387904":"024a4b806cf413d14b294719090a9da36ba75209c7657135ad09bc65328fba9e6f",
  327. "9223372036854775808":"0377a6fe114e291a8d8e991627c38001c8305b23b9e98b1c7b1893f5cd0dda6cad"
  328. }
  329. "#;
  330. #[test]
  331. fn deserialization_and_id_generation() {
  332. let _id = Id::from_str("009a1f293253e41e").unwrap();
  333. let keys: Keys = serde_json::from_str(SHORT_KEYSET).unwrap();
  334. let id: Id = (&keys).into();
  335. assert_eq!(id, Id::from_str(SHORT_KEYSET_ID).unwrap());
  336. let keys: Keys = serde_json::from_str(KEYSET).unwrap();
  337. let id: Id = (&keys).into();
  338. assert_eq!(id, Id::from_str(KEYSET_ID).unwrap());
  339. }
  340. #[test]
  341. fn de_keyset_info() {
  342. let h = r#"{"id":"009a1f293253e41e","unit":"sat","active":true}"#;
  343. let _keyset_response: KeySetInfo = serde_json::from_str(h).unwrap();
  344. }
  345. #[test]
  346. fn test_deserialization_of_keyset_response() {
  347. let h = r#"{"keysets":[{"id":"009a1f293253e41e","unit":"sat","active":true},{"id":"eGnEWtdJ0PIM","unit":"sat","active":true},{"id":"003dfdf4e5e35487","unit":"sat","active":true},{"id":"0066ad1a4b6fc57c","unit":"sat","active":true},{"id":"00f7ca24d44c3e5e","unit":"sat","active":true},{"id":"001fcea2931f2d85","unit":"sat","active":true},{"id":"00d095959d940edb","unit":"sat","active":true},{"id":"000d7f730d657125","unit":"sat","active":true},{"id":"0007208d861d7295","unit":"sat","active":true},{"id":"00bfdf8889b719dd","unit":"sat","active":true},{"id":"00ca9b17da045f21","unit":"sat","active":true}]}"#;
  348. let _keyset_response: KeysetResponse = serde_json::from_str(h).unwrap();
  349. }
  350. }