chuchi_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::{KeyIvInit, StreamCipher, StreamCipherSeek};
11use chacha20::{Rounds, XChaCha20, hchacha};
12
13use poly1305::Poly1305;
14use universal_hash::{KeyInit, UniversalHash};
15
16const BLOCK_SIZE: u64 = 64;
19
20#[derive(Debug, Clone, Copy)]
24pub struct R10;
25
26impl Rounds for R10 {
27 const COUNT: usize = 10;
28}
29
30pub struct Key {
32 shared_secret: [u8; 32],
33 initial_nonce: [u8; 24],
34 count: u64,
35}
36
37impl Key {
38 pub(crate) fn new(
41 shared_secret: [u8; 32],
42 initial_nonce: [u8; 24],
43 ) -> Self {
44 let shared_secret =
46 hchacha::<R10>(&shared_secret.into(), &Default::default()).into();
47
48 Self {
49 shared_secret,
50 initial_nonce,
51 count: 0,
52 }
53 }
54
55 pub fn encrypt(&mut self, msg: &mut [u8]) -> Mac {
57 self.new_cipher().encrypt(msg)
58 }
59
60 pub fn decrypt(
63 &mut self,
64 msg: &mut [u8],
65 recv_mac: &Mac,
66 ) -> Result<(), MacNotEqual> {
67 self.new_cipher().decrypt(msg, recv_mac)
68 }
69
70 fn new_cipher(&mut self) -> Cipher {
72 self.count += 1;
73 Cipher::new(&self.shared_secret, &self.initial_nonce, self.count)
74 }
75
76 pub fn into_sync(self) -> SyncKey {
77 SyncKey::new(self.shared_secret, self.initial_nonce, self.count)
78 }
79
80 pub fn dublicate(&self) -> Self {
86 Self {
87 shared_secret: self.shared_secret,
88 initial_nonce: self.initial_nonce,
89 count: self.count,
90 }
91 }
92}
93
94impl fmt::Debug for Key {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 f.write_str("Key")
97 }
98}
99
100impl Drop for Key {
101 fn drop(&mut self) {
102 self.shared_secret.zeroize();
103 self.initial_nonce.zeroize();
104 }
105}
106
107pub struct SyncKey {
110 shared_secret: [u8; 32],
111 initial_nonce: [u8; 24],
112 count: AtomicU64,
113}
114
115impl SyncKey {
116 fn new(
119 shared_secret: [u8; 32],
120 initial_nonce: [u8; 24],
121 count: u64,
122 ) -> Self {
123 Self {
124 shared_secret,
125 initial_nonce,
126 count: AtomicU64::new(count + 1),
128 }
129 }
130
131 pub fn encrypt(&self, msg: &mut [u8]) -> Mac {
133 self.new_cipher().encrypt(msg)
134 }
135
136 pub fn decrypt(
139 &self,
140 msg: &mut [u8],
141 recv_mac: &Mac,
142 ) -> Result<(), MacNotEqual> {
143 self.new_cipher().decrypt(msg, recv_mac)
144 }
145
146 fn new_cipher(&self) -> Cipher {
148 Cipher::new(
149 &self.shared_secret,
150 &self.initial_nonce,
151 self.count.fetch_add(1, Ordering::Relaxed),
153 )
154 }
155}
156
157impl fmt::Debug for SyncKey {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 f.write_str("SyncKey")
160 }
161}
162
163impl Drop for SyncKey {
164 fn drop(&mut self) {
165 self.shared_secret.zeroize();
166 self.initial_nonce.zeroize();
167 }
168}
169
170trait ToMac {
171 fn to_mac(self, msg_len: usize) -> Mac;
172}
173
174impl ToMac for Poly1305 {
175 fn to_mac(self, msg_len: usize) -> Mac {
176 let bytes = (msg_len as u64).to_be_bytes();
178
179 Mac::new(self.compute_unpadded(&bytes))
181 }
182}
183
184fn xor_nonce_with_u64(nonce: &mut [u8; 24], count: u64) {
185 let bytes = count.to_be_bytes();
186 xor(&mut nonce[..8], &bytes);
187 xor(&mut nonce[8..16], &bytes);
188 xor(&mut nonce[16..], &bytes);
189}
190
191struct Cipher {
192 cipher: XChaCha20,
193 poly: Poly1305,
194}
195
196impl Cipher {
197 fn new(
198 shared_secret: &[u8; 32],
199 initial_nonce: &[u8; 24],
200 count: u64,
201 ) -> Self {
202 let mut iv = *initial_nonce;
204 xor_nonce_with_u64(&mut iv, count);
205
206 let mut cipher =
207 <XChaCha20 as KeyIvInit>::new(shared_secret.into(), &iv.into());
208
209 let mut mac_key = [0u8; 32];
211 cipher.apply_keystream(&mut mac_key);
212
213 let poly = Poly1305::new(&mac_key.into());
214
215 mac_key.zeroize();
216
217 cipher.seek(BLOCK_SIZE);
219
220 Self { cipher, poly }
221 }
222
223 fn encrypt(mut self, msg: &mut [u8]) -> Mac {
225 self.cipher.apply_keystream(msg);
226 self.poly.update_padded(msg);
227 self.poly.to_mac(msg.len())
228 }
229
230 fn decrypt(
231 mut self,
232 msg: &mut [u8],
233 recv_mac: &Mac,
234 ) -> Result<(), MacNotEqual> {
235 self.poly.update_padded(msg);
236 let mac = self.poly.to_mac(msg.len());
237
238 if recv_mac == &mac {
241 self.cipher.apply_keystream(msg);
242
243 Ok(())
244 } else {
245 Err(MacNotEqual)
246 }
247 }
248}