wallet.rs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // We want Scalars to be lowercase letters, and Points and credentials
  2. // to be capital letters
  3. #![allow(non_snake_case)]
  4. use cmz::*;
  5. use curve25519_dalek::ristretto::RistrettoPoint as G;
  6. use group::Group;
  7. use rand::{CryptoRng, RngCore};
  8. use sha2::Sha512;
  9. CMZ! { Wallet: randid, balance }
  10. CMZ! { Item: serialno, price }
  11. // Define two versions of each protocol and test: one for CMZ14 and one for µCMZ
  12. macro_rules! protos_def {
  13. ($proto_macro:tt, $wallet_issue:ident, $item_issue:ident,
  14. $wallet_spend:ident, $wallet_spend_with_fee:ident,
  15. $gen_keys:ident, $test_wallet: ident) => {
  16. $proto_macro! { $wallet_issue,
  17. ,
  18. W: Wallet { randid: J, balance: S },
  19. }
  20. $proto_macro! { $item_issue,
  21. ,
  22. I: Item { serialno: S, price: S },
  23. }
  24. $proto_macro! { $wallet_spend,
  25. [ W: Wallet { randid: R, balance: H },
  26. I: Item { serialno: H, price: H } ],
  27. N: Wallet { randid: J, balance: H },
  28. (0..=100000000).contains(N.balance),
  29. W.balance = N.balance + I.price
  30. }
  31. $proto_macro! { $wallet_spend_with_fee<fee>,
  32. [ W: Wallet { randid: R, balance: H },
  33. I: Item { serialno: H, price: H } ],
  34. N: Wallet { randid: J, balance: H },
  35. (0..=100000000).contains(N.balance),
  36. W.balance = N.balance + I.price + fee
  37. }
  38. #[test]
  39. fn $test_wallet() -> Result<(), CMZError> {
  40. // The issuer runs this on its own to create an Item credential for a
  41. // particular item (specified by a serial number) with a given price.
  42. fn issue_item(
  43. rng: &mut (impl CryptoRng + RngCore),
  44. serialno: u128,
  45. price: u128,
  46. privkey: &CMZPrivkey<G>,
  47. public: &CMZPubkey<G>,
  48. ) -> Result<Item, CMZError> {
  49. let (request, state) =
  50. $item_issue::prepare(&mut *rng, b"issue_item", Item::using_pubkey(public))?;
  51. let (reply, _) = $item_issue::handle(
  52. &mut *rng,
  53. b"issue_item",
  54. request,
  55. |I: &mut Item| {
  56. I.set_privkey(privkey);
  57. I.serialno = Some(serialno.into());
  58. I.price = Some(price.into());
  59. Ok(())
  60. },
  61. |_I: &Item| Ok(()),
  62. )?;
  63. let res = state.finalize(reply);
  64. match res {
  65. Ok(c) => Ok(c),
  66. Err((err, _state)) => Err(err),
  67. }
  68. }
  69. // The issuer runs this on its own to create an initial wallet loaded
  70. // with funds, to sent to a client. The issuer will presumably charge
  71. // the client out of band for this loaded wallet.
  72. fn issue_wallet(
  73. rng: &mut (impl CryptoRng + RngCore),
  74. balance: u128,
  75. privkey: &CMZPrivkey<G>,
  76. public: &CMZPubkey<G>,
  77. ) -> Result<Wallet, CMZError> {
  78. let (request, state) = $wallet_issue::prepare(
  79. &mut *rng,
  80. b"issue_wallet",
  81. Wallet::using_pubkey(public),
  82. )?;
  83. let (reply, _) = $wallet_issue::handle(
  84. &mut *rng,
  85. b"issue_wallet",
  86. request,
  87. |W: &mut Wallet| {
  88. W.set_privkey(privkey);
  89. W.balance = Some(balance.into());
  90. Ok(())
  91. },
  92. |_W: &Wallet| Ok(()),
  93. )?;
  94. let res = state.finalize(reply);
  95. match res {
  96. Ok(c) => Ok(c),
  97. Err((err, _state)) => Err(err),
  98. }
  99. }
  100. // Initialization
  101. let mut rng = rand::thread_rng();
  102. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  103. // Issuer: generate private and public keys for each type of
  104. // credential. (The client gets a copy of the public keys.)
  105. let (wallet_priv, wallet_pub) = Wallet::$gen_keys(&mut rng);
  106. let (item_priv, item_pub) = Item::$gen_keys(&mut rng);
  107. // The issuer makes some Item credentials for various items by just
  108. // executing the issuing protocol with itself
  109. let ebook_item = issue_item(&mut rng, 100, 2995, &item_priv, &item_pub)?;
  110. let album_item = issue_item(&mut rng, 200, 995, &item_priv, &item_pub)?;
  111. // The verify_MAC function is only usable for debugging, since the
  112. // client will not have the private key and the issuer will
  113. // typically not have the complete credential. But we'll use it
  114. // here to check that the credentials we get back are correct.
  115. ebook_item.verify_MAC(&item_priv).unwrap();
  116. album_item.verify_MAC(&item_priv).unwrap();
  117. // In exchange for out of band funds, the issuer generates a loaded
  118. // wallet and sends it to the client.
  119. let initial_wallet = issue_wallet(&mut rng, 10000, &wallet_priv, &wallet_pub)?;
  120. initial_wallet.verify_MAC(&wallet_priv).unwrap();
  121. // Buy an item (no fee version)
  122. // client actions
  123. let mut N = Wallet::using_pubkey(&wallet_pub);
  124. N.balance = Some(initial_wallet.balance.unwrap() - ebook_item.price.unwrap());
  125. let (request, state) =
  126. $wallet_spend::prepare(&mut rng, b"test_wallet", &initial_wallet, &ebook_item, N)?;
  127. let reqbytes = request.as_bytes();
  128. // issuer actions
  129. let recvreq = $wallet_spend::Request::try_from(&reqbytes[..]).unwrap();
  130. let (reply, (_W_issuer, _I_issuer, _N_issuer)) = $wallet_spend::handle(
  131. &mut rng,
  132. b"test_wallet",
  133. recvreq,
  134. |W: &mut Wallet, I: &mut Item, N: &mut Wallet| {
  135. W.set_privkey(&wallet_priv);
  136. I.set_privkey(&item_priv);
  137. N.set_privkey(&wallet_priv);
  138. Ok(())
  139. },
  140. // This callback should test that W.randid has never been seen
  141. // before, but it doesn't currently do that.
  142. |_W: &Wallet, _I: &Item, _N: &Wallet| Ok(()),
  143. )?;
  144. let replybytes = reply.as_bytes();
  145. // client actions
  146. let recvreply = $wallet_spend::Reply::try_from(&replybytes[..]).unwrap();
  147. let W_issued = state.finalize(recvreply).unwrap();
  148. W_issued.verify_MAC(&wallet_priv).unwrap();
  149. // The version of the protocol parameterized by a fee. The client
  150. // and issue must agree on the params.
  151. let params = $wallet_spend_with_fee::Params { fee: 5u128.into() };
  152. // client actions
  153. let mut N_fee = Wallet::using_pubkey(&wallet_pub);
  154. N_fee.balance =
  155. Some(W_issued.balance.unwrap() - album_item.price.unwrap() - params.fee);
  156. let (request_fee, state_fee) = $wallet_spend_with_fee::prepare(
  157. &mut rng,
  158. b"test_wallet_fee",
  159. &W_issued,
  160. &album_item,
  161. N_fee,
  162. &params,
  163. )?;
  164. let reqbytes_fee = request_fee.as_bytes();
  165. // issuer actions
  166. let recvreq_fee = $wallet_spend_with_fee::Request::try_from(&reqbytes_fee[..]).unwrap();
  167. let (reply_fee, (_W_fee_issuer, _I_fee_isser, _N_fee_issuer)) =
  168. $wallet_spend_with_fee::handle(
  169. &mut rng,
  170. b"test_wallet_fee",
  171. recvreq_fee,
  172. |W: &mut Wallet, I: &mut Item, N: &mut Wallet| {
  173. W.set_privkey(&wallet_priv);
  174. I.set_privkey(&item_priv);
  175. N.set_privkey(&wallet_priv);
  176. Ok($wallet_spend_with_fee::Params { fee: params.fee })
  177. },
  178. // This callback should test that W.randid has never been seen
  179. // before, but it doesn't currently do that.
  180. |_W: &Wallet, _I: &Item, _N: &Wallet| Ok(()),
  181. )?;
  182. let replybytes_fee = reply_fee.as_bytes();
  183. // client actions
  184. let recvreply_fee =
  185. $wallet_spend_with_fee::Reply::try_from(&replybytes_fee[..]).unwrap();
  186. let W_issued_fee = state_fee.finalize(recvreply_fee).unwrap();
  187. W_issued_fee.verify_MAC(&wallet_priv).unwrap();
  188. Ok(())
  189. }
  190. };
  191. }
  192. protos_def! {CMZ14Protocol, cmz14_wallet_issue, cmz14_item_issue, cmz14_wallet_spend,
  193. cmz14_wallet_spend_with_fee, cmz14_gen_keys, test_cmz14_wallet}
  194. protos_def! {muCMZProtocol, mu_wallet_issue, mu_item_issue,
  195. mu_wallet_spend, mu_wallet_spend_with_fee, mucmz_gen_keys, test_mucmz_wallet}