fire_crypto/cipher/
key.rs1use super::{Mac, MacNotEqual};
2use crate::xor;
3
4use std::sync::atomic::{AtomicU64, Ordering};
5
6use std::fmt;
7
8use zeroize::Zeroize;
9
10use chacha20::cipher::typenum::U10;
11use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
12use chacha20::{hchacha, XChaCha20};
13
14use poly1305::Poly1305;
15use universal_hash::{KeyInit, UniversalHash};
16
17use generic_array::GenericArray;
18
19const BLOCK_SIZE: u64 = 64;
22
23pub struct Key {
25 shared_secret: [u8; 32],
26 initial_nonce: [u8; 24],
27 count: u64,
28}
29
30impl Key {
31 pub(crate) fn new(
34 shared_secret: [u8; 32],
35 initial_nonce: [u8; 24],
36 ) -> Self {
37 let shared_secret = hchacha::<U10>(
39 shared_secret.as_ref().into(),
40 &GenericArray::default(),
41 )
42 .into();
43
44 Self {
45 shared_secret,
46 initial_nonce,
47 count: 0,
48 }
49 }
50
51 pub fn encrypt(&mut self, msg: &mut [u8]) -> Mac {
53 self.new_cipher().encrypt(msg)
54 }
55
56 pub fn decrypt(
59 &mut self,
60 msg: &mut [u8],
61 recv_mac: &Mac,
62 ) -> Result<(), MacNotEqual> {
63 self.new_cipher().decrypt(msg, recv_mac)
64 }
65
66 fn new_cipher(&mut self) -> Cipher {
68 self.count += 1;
69 Cipher::new(&self.shared_secret, &self.initial_nonce, self.count)
70 }
71
72 pub fn into_sync(self) -> SyncKey {
73 SyncKey::new(self.shared_secret, self.initial_nonce, self.count)
74 }
75
76 pub fn dublicate(&self) -> Self {
82 Self {
83 shared_secret: self.shared_secret,
84 initial_nonce: self.initial_nonce,
85 count: self.count,
86 }
87 }
88}
89
90impl fmt::Debug for Key {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 f.write_str("Key")
93 }
94}
95
96impl Drop for Key {
97 fn drop(&mut self) {
98 self.shared_secret.zeroize();
99 self.initial_nonce.zeroize();
100 }
101}
102
103pub struct SyncKey {
106 shared_secret: [u8; 32],
107 initial_nonce: [u8; 24],
108 count: AtomicU64,
109}
110
111impl SyncKey {
112 fn new(
115 shared_secret: [u8; 32],
116 initial_nonce: [u8; 24],
117 count: u64,
118 ) -> Self {
119 Self {
120 shared_secret,
121 initial_nonce,
122 count: AtomicU64::new(count + 1),
124 }
125 }
126
127 pub fn encrypt(&self, msg: &mut [u8]) -> Mac {
129 self.new_cipher().encrypt(msg)
130 }
131
132 pub fn decrypt(
135 &self,
136 msg: &mut [u8],
137 recv_mac: &Mac,
138 ) -> Result<(), MacNotEqual> {
139 self.new_cipher().decrypt(msg, recv_mac)
140 }
141
142 fn new_cipher(&self) -> Cipher {
144 Cipher::new(
145 &self.shared_secret,
146 &self.initial_nonce,
147 self.count.fetch_add(1, Ordering::Relaxed),
149 )
150 }
151}
152
153impl fmt::Debug for SyncKey {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 f.write_str("SyncKey")
156 }
157}
158
159impl Drop for SyncKey {
160 fn drop(&mut self) {
161 self.shared_secret.zeroize();
162 self.initial_nonce.zeroize();
163 }
164}
165
166trait ToMac {
167 fn to_mac(self, msg_len: usize) -> Mac;
168}
169
170impl ToMac for Poly1305 {
171 fn to_mac(self, msg_len: usize) -> Mac {
172 let bytes = (msg_len as u64).to_be_bytes();
174
175 Mac::new(self.compute_unpadded(&bytes))
177 }
178}
179
180fn xor_nonce_with_u64(nonce: &mut [u8; 24], count: u64) {
181 let bytes = count.to_be_bytes();
182 xor(&mut nonce[..8], &bytes);
183 xor(&mut nonce[8..16], &bytes);
184 xor(&mut nonce[16..], &bytes);
185}
186
187struct Cipher {
188 cipher: XChaCha20,
189 poly: Poly1305,
190}
191
192impl Cipher {
193 fn new(
194 shared_secret: &[u8; 32],
195 initial_nonce: &[u8; 24],
196 count: u64,
197 ) -> Self {
198 let mut iv = *initial_nonce;
200 xor_nonce_with_u64(&mut iv, count);
201
202 let mut cipher =
203 XChaCha20::new(shared_secret.into(), iv.as_ref().into());
204
205 let mut mac_key = [0u8; 32];
207 cipher.apply_keystream(&mut mac_key);
208
209 let poly = Poly1305::new(mac_key.as_ref().into());
210
211 mac_key.zeroize();
212
213 cipher.seek(BLOCK_SIZE);
215
216 Self { cipher, poly }
217 }
218
219 fn encrypt(mut self, msg: &mut [u8]) -> Mac {
221 self.cipher.apply_keystream(msg);
222 self.poly.update_padded(msg);
223 self.poly.to_mac(msg.len())
224 }
225
226 fn decrypt(
227 mut self,
228 msg: &mut [u8],
229 recv_mac: &Mac,
230 ) -> Result<(), MacNotEqual> {
231 self.poly.update_padded(msg);
232 let mac = self.poly.to_mac(msg.len());
233
234 if recv_mac == &mac {
237 self.cipher.apply_keystream(msg);
238
239 Ok(())
240 } else {
241 Err(MacNotEqual)
242 }
243 }
244}