note.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. use crypto_api_chachapoly::ChachaPolyIetf;
  2. use ff::Field;
  3. use rand::rngs::OsRng;
  4. use std::io;
  5. use super::diffie_hellman::{kdf_sapling, sapling_ka_agree};
  6. use crate::error::{Error, Result};
  7. use crate::serial::{Decodable, Encodable, ReadExt, WriteExt};
  8. pub const NOTE_PLAINTEXT_SIZE: usize = 32 + // serial
  9. 8 + // value
  10. 8 + // asset_id
  11. 32 + // coin_blind
  12. 32; // valcom_blind
  13. pub const AEAD_TAG_SIZE: usize = 16;
  14. pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
  15. #[derive(Clone)]
  16. pub struct Note {
  17. pub serial: jubjub::Fr,
  18. pub value: u64,
  19. pub asset_id: jubjub::Fr,
  20. pub coin_blind: jubjub::Fr,
  21. pub valcom_blind: jubjub::Fr,
  22. }
  23. impl Encodable for Note {
  24. fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
  25. let mut len = 0;
  26. len += self.serial.encode(&mut s)?;
  27. len += self.value.encode(&mut s)?;
  28. len += self.asset_id.encode(&mut s)?;
  29. len += self.coin_blind.encode(&mut s)?;
  30. len += self.valcom_blind.encode(&mut s)?;
  31. Ok(len)
  32. }
  33. }
  34. impl Decodable for Note {
  35. fn decode<D: io::Read>(mut d: D) -> Result<Self> {
  36. Ok(Self {
  37. serial: Decodable::decode(&mut d)?,
  38. value: Decodable::decode(&mut d)?,
  39. asset_id: Decodable::decode(&mut d)?,
  40. coin_blind: Decodable::decode(&mut d)?,
  41. valcom_blind: Decodable::decode(d)?,
  42. })
  43. }
  44. }
  45. impl Note {
  46. pub fn encrypt(&self, public: &jubjub::SubgroupPoint) -> Result<EncryptedNote> {
  47. let ephem_secret = jubjub::Fr::random(&mut OsRng);
  48. let ephem_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * ephem_secret;
  49. let shared_secret = sapling_ka_agree(&ephem_secret, public.into());
  50. let key = kdf_sapling(shared_secret, &ephem_public.into());
  51. let mut input = Vec::new();
  52. self.encode(&mut input)?;
  53. let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
  54. assert_eq!(
  55. ChachaPolyIetf::aead_cipher()
  56. .seal_to(&mut ciphertext, &input, &[], key.as_ref(), &[0u8; 12])
  57. .unwrap(),
  58. ENC_CIPHERTEXT_SIZE
  59. );
  60. Ok(EncryptedNote {
  61. ciphertext,
  62. ephem_public,
  63. })
  64. }
  65. }
  66. pub struct EncryptedNote {
  67. ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
  68. ephem_public: jubjub::SubgroupPoint,
  69. }
  70. impl Encodable for EncryptedNote {
  71. fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
  72. let mut len = 0;
  73. s.write_slice(&self.ciphertext)?;
  74. len += ENC_CIPHERTEXT_SIZE;
  75. len += self.ephem_public.encode(&mut s)?;
  76. Ok(len)
  77. }
  78. }
  79. impl Decodable for EncryptedNote {
  80. fn decode<D: io::Read>(mut d: D) -> Result<Self> {
  81. let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
  82. d.read_slice(&mut ciphertext[..])?;
  83. Ok(Self {
  84. ciphertext,
  85. ephem_public: Decodable::decode(d)?,
  86. })
  87. }
  88. }
  89. impl EncryptedNote {
  90. pub fn decrypt(&self, secret: &jubjub::Fr) -> Result<Note> {
  91. let shared_secret = sapling_ka_agree(&secret, &self.ephem_public.into());
  92. let key = kdf_sapling(shared_secret, &self.ephem_public.into());
  93. let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
  94. assert_eq!(
  95. ChachaPolyIetf::aead_cipher()
  96. .open_to(
  97. &mut plaintext,
  98. &self.ciphertext,
  99. &[],
  100. key.as_ref(),
  101. &[0u8; 12]
  102. )
  103. .map_err(|_| Error::NoteDecryptionFailed)?,
  104. NOTE_PLAINTEXT_SIZE
  105. );
  106. Note::decode(&plaintext[..])
  107. }
  108. }
  109. #[test]
  110. fn test_note_encdec() {
  111. let note = Note {
  112. serial: jubjub::Fr::random(&mut OsRng),
  113. value: 110,
  114. asset_id: 1,
  115. coin_blind: jubjub::Fr::random(&mut OsRng),
  116. valcom_blind: jubjub::Fr::random(&mut OsRng),
  117. };
  118. let secret = jubjub::Fr::random(&mut OsRng);
  119. let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
  120. let encrypted_note = note.encrypt(&public).unwrap();
  121. let note2 = encrypted_note.decrypt(&secret).unwrap();
  122. assert_eq!(note.value, note2.value);
  123. assert_eq!(note.asset_id, note2.asset_id);
  124. }