libzeropool/native/
cipher.rs1use crate::{
2 fawkes_crypto::{
3 ff_uint::{Num, seedbox::{SeedboxChaCha20, SeedBox, SeedBoxGen}},
4 borsh::{BorshSerialize, BorshDeserialize},
5 native::ecc::{EdwardsPoint},
6
7 },
8 native::{
9 account::Account,
10 note::Note,
11 params::PoolParams,
12 key::{derive_key_a, derive_key_p_d}
13 },
14 constants
15};
16
17use sha3::{Digest, Keccak256};
18
19use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
20use chacha20poly1305::aead::{Aead, NewAead};
21
22fn keccak256(data:&[u8])->[u8;constants::U256_SIZE] {
23 let mut hasher = Keccak256::new();
24 hasher.update(data);
25 let mut res = [0u8;constants::U256_SIZE];
26 res.iter_mut().zip(hasher.finalize().into_iter()).for_each(|(l,r)| *l=r);
27 res
28}
29
30fn symcipher_encode(key:&[u8], data:&[u8])->Vec<u8> {
32 assert!(key.len()==constants::U256_SIZE);
33 let nonce = Nonce::from_slice(&constants::ENCRYPTION_NONCE);
34 let cipher = ChaCha20Poly1305::new(Key::from_slice(key));
35 cipher.encrypt(nonce, data.as_ref()).unwrap()
36}
37
38fn symcipher_decode(key:&[u8], data:&[u8])->Option<Vec<u8>> {
40 assert!(key.len()==constants::U256_SIZE);
41 let nonce = Nonce::from_slice(&constants::ENCRYPTION_NONCE);
42 let cipher = ChaCha20Poly1305::new(Key::from_slice(key));
43 cipher.decrypt(nonce, data).ok()
44
45}
46
47
48
49pub fn encrypt<P: PoolParams>(
50 entropy: &[u8],
51 eta:Num<P::Fr>,
52 account: Account<P::Fr>,
53 note: &[Note<P::Fr>],
54 params:&P
55) -> Vec<u8> {
56 let nozero_notes_num = note.len();
57 let nozero_items_num = nozero_notes_num+1;
58
59
60 let mut sb = SeedboxChaCha20::new_with_salt(entropy);
61
62 let account_data = {
63 let mut account_key = [0u8;constants::U256_SIZE];
64 sb.fill_bytes(&mut account_key);
65 let account_ciphertext = symcipher_encode(&account_key, &account.try_to_vec().unwrap());
66 (account_key, account_ciphertext)
67 };
68
69
70 let notes_data = note.iter().map(|e|{
71 let a:Num<P::Fs> = sb.gen();
72 let p_d = EdwardsPoint::subgroup_decompress(e.p_d, params.jubjub()).unwrap();
73 let ecdh = p_d.mul(a, params.jubjub());
74 let key = keccak256(&ecdh.x.try_to_vec().unwrap());
75 let ciphertext = symcipher_encode(&key, &e.try_to_vec().unwrap());
76 let a_pub = derive_key_p_d(e.d.to_num(), a, params);
77 (a_pub.x, key, ciphertext)
78
79 }).collect::<Vec<_>>();
80
81 let shared_secret_data = {
82 let a_p_pub = derive_key_a(sb.gen(), params);
83 let ecdh = a_p_pub.mul(eta.to_other_reduced(), params.jubjub());
84 let key = keccak256(&ecdh.x.try_to_vec().unwrap());
85 let text:Vec<u8> = core::iter::once(&account_data.0[..]).chain(notes_data.iter().map(|e| &e.1[..])).collect::<Vec<_>>().concat();
86 let ciphertext = symcipher_encode(&key, &text);
87 (a_p_pub.x, ciphertext)
88 };
89
90 let mut res = vec![];
91
92 (nozero_items_num as u32).serialize(&mut res).unwrap();
93 account.hash(params).serialize(&mut res).unwrap();
94
95 for e in note.iter() {
96 e.hash(params).serialize(&mut res).unwrap();
97 }
98 shared_secret_data.0.serialize(&mut res).unwrap();
99 res.extend(&shared_secret_data.1);
100
101 res.extend(&account_data.1);
102
103 notes_data.iter().for_each(|nd|{
104 nd.0.serialize(&mut res).unwrap();
105 res.extend(&nd.2);
106 });
107
108 res
109}
110
111
112fn buf_take<'a>(memo: &mut &'a[u8], size:usize) -> Option<&'a[u8]> {
113 if memo.len() < size {
114 None
115 } else {
116 let res = &memo[0..size];
117 *memo = &memo[size..];
118 Some(res)
119 }
120}
121
122pub fn decrypt_out<P: PoolParams>(eta:Num<P::Fr>, mut memo:&[u8], params:&P)->Option<(Account<P::Fr>, Vec<Note<P::Fr>>)> {
123 let num_size = constants::num_size_bits::<P::Fr>()/8;
124 let account_size = constants::account_size_bits::<P::Fr>()/8;
125 let note_size = constants::note_size_bits::<P::Fr>()/8;
126
127
128 let nozero_items_num = u32::deserialize(&mut memo).ok()? as usize;
129 if nozero_items_num == 0 {
130 return None;
131 }
132
133 let nozero_notes_num = nozero_items_num - 1;
134 let shared_secret_ciphertext_size = nozero_items_num * constants::U256_SIZE + constants::POLY_1305_TAG_SIZE;
135
136 let account_hash = Num::deserialize(&mut memo).ok()?;
137 let note_hash = (0..nozero_notes_num).map(|_| Num::deserialize(&mut memo)).collect::<Result<Vec<_>, _>>().ok()?;
138
139 let shared_secret_text = {
140 let a_p = EdwardsPoint::subgroup_decompress(Num::deserialize(&mut memo).ok()?, params.jubjub())?;
141 let ecdh = a_p.mul(eta.to_other_reduced(), params.jubjub());
142 let key = keccak256(&ecdh.x.try_to_vec().unwrap());
143 let ciphertext = buf_take(&mut memo, shared_secret_ciphertext_size)?;
144 symcipher_decode(&key, ciphertext)?
145 };
146 let mut shared_secret_text_ptr =&shared_secret_text[..];
147
148 let account_key= <[u8;constants::U256_SIZE]>::deserialize(&mut shared_secret_text_ptr).ok()?;
149 let note_key = (0..nozero_notes_num).map(|_| <[u8;constants::U256_SIZE]>::deserialize(&mut shared_secret_text_ptr)).collect::<Result<Vec<_>,_>>().ok()?;
150
151 let account_ciphertext = buf_take(&mut memo, account_size+constants::POLY_1305_TAG_SIZE)?;
152 let account_text = symcipher_decode(&account_key, account_ciphertext)?;
153 let account = Account::try_from_slice(&account_text).ok()?;
154
155 if account.hash(params)!= account_hash {
156 return None;
157 }
158
159 let note = (0..nozero_notes_num).map(|i| {
160 buf_take(&mut memo, num_size)?;
161 let ciphertext = buf_take(&mut memo, note_size+constants::POLY_1305_TAG_SIZE)?;
162 let text = symcipher_decode(¬e_key[i], ciphertext)?;
163 let note = Note::try_from_slice(&text).ok()?;
164 if note.hash(params) != note_hash[i] {
165 None
166 } else {
167 Some(note)
168 }
169 }).collect::<Option<Vec<_>>>()?;
170
171 Some((account, note))
172}
173
174fn _decrypt_in<P: PoolParams>(eta:Num<P::Fr>, mut memo:&[u8], params:&P)->Option<Vec<Option<Note<P::Fr>>>> {
175 let num_size = constants::num_size_bits::<P::Fr>()/8;
176 let account_size = constants::account_size_bits::<P::Fr>()/8;
177 let note_size = constants::note_size_bits::<P::Fr>()/8;
178
179
180 let nozero_items_num = u32::deserialize(&mut memo).ok()? as usize;
181 if nozero_items_num == 0 {
182 return None;
183 }
184
185 let nozero_notes_num = nozero_items_num - 1;
186 let shared_secret_ciphertext_size = nozero_items_num * constants::U256_SIZE + constants::POLY_1305_TAG_SIZE;
187
188 buf_take(&mut memo, num_size)?;
189 let note_hash = (0..nozero_notes_num).map(|_| Num::deserialize(&mut memo)).collect::<Result<Vec<_>, _>>().ok()?;
190
191 buf_take(&mut memo, num_size)?;
192 buf_take(&mut memo, shared_secret_ciphertext_size)?;
193 buf_take(&mut memo, account_size+constants::POLY_1305_TAG_SIZE)?;
194
195
196 let note = (0..nozero_notes_num).map(|i| {
197 let a_pub = EdwardsPoint::subgroup_decompress(Num::deserialize(&mut memo).ok()?, params.jubjub())?;
198 let ecdh = a_pub.mul(eta.to_other_reduced(), params.jubjub());
199 let key = keccak256(&ecdh.x.try_to_vec().unwrap());
200
201 let ciphertext = buf_take(&mut memo, note_size+constants::POLY_1305_TAG_SIZE)?;
202 let text = symcipher_decode(&key, ciphertext)?;
203 let note = Note::try_from_slice(&text).ok()?;
204 if note.hash(params) != note_hash[i] {
205 None
206 } else {
207 Some(note)
208 }
209 }).collect::<Vec<Option<_>>>();
210
211 Some(note)
212}
213
214pub fn decrypt_in<P: PoolParams>(eta:Num<P::Fr>, memo:&[u8], params:&P)->Vec<Option<Note<P::Fr>>> {
215 if let Some(res) = _decrypt_in(eta, memo, params) {
216 res
217 } else {
218 vec![]
219 }
220}