libzeropool/native/
cipher.rs

1use 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
30//key stricly assumed to be unique for all messages. Using this function with multiple messages and one key is insecure!
31fn 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
38//key stricly assumed to be unique for all messages. Using this function with multiple messages and one key is insecure!
39fn 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(&note_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}