noise_rust_crypto/
lib.rs

1//! This crate provides wrappers around pure rust implementations of the crypto
2//! primitives used in `noise-protocol`.
3//
4//! The underlying implementations are:
5//
6//! * [`x25519-dalek`](https://crates.io/crates/x25519-dalek)
7//! * [`chacha20poly1305`](https://crates.io/crates/chacha20poly1305)
8//! * [`aes-gcm`](https://crates.io/crates/aes-gcm)
9//! * [`sha2`](https://crates.io/crates/sha2)
10//! * [`blake2`](https://crates.io/crates/blake2)
11
12#![no_std]
13
14pub mod sensitive;
15
16use sensitive::Sensitive;
17
18use noise_protocol::*;
19#[cfg(feature = "x25519")]
20use x25519_dalek::{PublicKey, StaticSecret};
21
22#[cfg(feature = "x25519")]
23pub enum X25519 {}
24
25#[cfg(feature = "x25519")]
26impl DH for X25519 {
27    type Key = Sensitive<[u8; 32]>;
28    type Pubkey = [u8; 32];
29    type Output = Sensitive<[u8; 32]>;
30
31    fn name() -> &'static str {
32        "25519"
33    }
34
35    fn genkey() -> Self::Key {
36        Self::Key::from_slice(StaticSecret::random().as_bytes())
37    }
38
39    fn pubkey(k: &Self::Key) -> Self::Pubkey {
40        let static_secret = StaticSecret::from(**k);
41        *PublicKey::from(&static_secret).as_bytes()
42    }
43
44    fn dh(k: &Self::Key, pk: &Self::Pubkey) -> Result<Self::Output, ()> {
45        let k = StaticSecret::from(**k);
46        let pk = PublicKey::from(*pk);
47        Ok(Self::Output::from_slice(k.diffie_hellman(&pk).as_bytes()))
48    }
49}
50
51#[cfg(feature = "use-chacha20poly1305")]
52pub enum ChaCha20Poly1305 {}
53
54#[cfg(feature = "use-chacha20poly1305")]
55impl Cipher for ChaCha20Poly1305 {
56    fn name() -> &'static str {
57        "ChaChaPoly"
58    }
59
60    type Key = Sensitive<[u8; 32]>;
61
62    fn encrypt(k: &Self::Key, nonce: u64, ad: &[u8], plaintext: &[u8], out: &mut [u8]) {
63        assert!(plaintext.len().checked_add(16) == Some(out.len()));
64
65        let mut full_nonce = [0u8; 12];
66        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());
67
68        let (in_out, tag_out) = out.split_at_mut(plaintext.len());
69        in_out.copy_from_slice(plaintext);
70
71        use chacha20poly1305::{AeadInPlace, KeyInit};
72        let tag = chacha20poly1305::ChaCha20Poly1305::new(&(**k).into())
73            .encrypt_in_place_detached(&full_nonce.into(), ad, in_out)
74            .unwrap();
75
76        tag_out.copy_from_slice(tag.as_ref())
77    }
78
79    fn encrypt_in_place(
80        k: &Self::Key,
81        nonce: u64,
82        ad: &[u8],
83        in_out: &mut [u8],
84        plaintext_len: usize,
85    ) -> usize {
86        assert!(plaintext_len
87            .checked_add(16)
88            .map_or(false, |l| l <= in_out.len()));
89
90        let mut full_nonce = [0u8; 12];
91        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());
92
93        let (in_out, tag_out) = in_out[..plaintext_len + 16].split_at_mut(plaintext_len);
94
95        use chacha20poly1305::{AeadInPlace, KeyInit};
96        let tag = chacha20poly1305::ChaCha20Poly1305::new(&(**k).into())
97            .encrypt_in_place_detached(&full_nonce.into(), ad, in_out)
98            .unwrap();
99        tag_out.copy_from_slice(tag.as_ref());
100
101        plaintext_len + 16
102    }
103
104    fn decrypt(
105        k: &Self::Key,
106        nonce: u64,
107        ad: &[u8],
108        ciphertext: &[u8],
109        out: &mut [u8],
110    ) -> Result<(), ()> {
111        assert!(ciphertext.len().checked_sub(16) == Some(out.len()));
112
113        let mut full_nonce = [0u8; 12];
114        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());
115
116        out.copy_from_slice(&ciphertext[..out.len()]);
117        let tag = &ciphertext[out.len()..];
118
119        use chacha20poly1305::{AeadInPlace, KeyInit};
120        chacha20poly1305::ChaCha20Poly1305::new(&(**k).into())
121            .decrypt_in_place_detached(&full_nonce.into(), ad, out, tag.into())
122            .map_err(|_| ())
123    }
124
125    fn decrypt_in_place(
126        k: &Self::Key,
127        nonce: u64,
128        ad: &[u8],
129        in_out: &mut [u8],
130        ciphertext_len: usize,
131    ) -> Result<usize, ()> {
132        assert!(ciphertext_len <= in_out.len());
133        assert!(ciphertext_len >= 16);
134
135        let mut full_nonce = [0u8; 12];
136        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());
137
138        let (in_out, tag) = in_out[..ciphertext_len].split_at_mut(ciphertext_len - 16);
139
140        use chacha20poly1305::{AeadInPlace, KeyInit};
141        chacha20poly1305::ChaCha20Poly1305::new(&(**k).into())
142            .decrypt_in_place_detached(&full_nonce.into(), ad, in_out, tag.as_ref().into())
143            .map_err(|_| ())?;
144
145        Ok(in_out.len())
146    }
147}
148
149#[cfg(feature = "use-aes-256-gcm")]
150pub enum Aes256Gcm {}
151
152#[cfg(feature = "use-aes-256-gcm")]
153impl Cipher for Aes256Gcm {
154    fn name() -> &'static str {
155        "AESGCM"
156    }
157
158    type Key = Sensitive<[u8; 32]>;
159
160    fn encrypt(k: &Self::Key, nonce: u64, ad: &[u8], plaintext: &[u8], out: &mut [u8]) {
161        assert!(plaintext.len().checked_add(16) == Some(out.len()));
162
163        let mut full_nonce = [0u8; 12];
164        full_nonce[4..].copy_from_slice(&nonce.to_be_bytes());
165
166        let (in_out, tag_out) = out.split_at_mut(plaintext.len());
167        in_out.copy_from_slice(plaintext);
168
169        use aes_gcm::{AeadInPlace, KeyInit};
170        let tag = aes_gcm::Aes256Gcm::new(&(**k).into())
171            .encrypt_in_place_detached(&full_nonce.into(), ad, in_out)
172            .unwrap();
173
174        tag_out.copy_from_slice(tag.as_ref())
175    }
176
177    fn encrypt_in_place(
178        k: &Self::Key,
179        nonce: u64,
180        ad: &[u8],
181        in_out: &mut [u8],
182        plaintext_len: usize,
183    ) -> usize {
184        assert!(plaintext_len
185            .checked_add(16)
186            .map_or(false, |l| l <= in_out.len()));
187
188        let mut full_nonce = [0u8; 12];
189        full_nonce[4..].copy_from_slice(&nonce.to_be_bytes());
190
191        let (in_out, tag_out) = in_out[..plaintext_len + 16].split_at_mut(plaintext_len);
192
193        use aes_gcm::{AeadInPlace, KeyInit};
194        let tag = aes_gcm::Aes256Gcm::new(&(**k).into())
195            .encrypt_in_place_detached(&full_nonce.into(), ad, in_out)
196            .unwrap();
197        tag_out.copy_from_slice(tag.as_ref());
198
199        plaintext_len + 16
200    }
201
202    fn decrypt(
203        k: &Self::Key,
204        nonce: u64,
205        ad: &[u8],
206        ciphertext: &[u8],
207        out: &mut [u8],
208    ) -> Result<(), ()> {
209        assert!(ciphertext.len().checked_sub(16) == Some(out.len()));
210
211        let mut full_nonce = [0u8; 12];
212        full_nonce[4..].copy_from_slice(&nonce.to_be_bytes());
213
214        out.copy_from_slice(&ciphertext[..out.len()]);
215        let tag = &ciphertext[out.len()..];
216
217        use aes_gcm::{AeadInPlace, KeyInit};
218        aes_gcm::Aes256Gcm::new(&(**k).into())
219            .decrypt_in_place_detached(&full_nonce.into(), ad, out, tag.into())
220            .map_err(|_| ())
221    }
222
223    fn decrypt_in_place(
224        k: &Self::Key,
225        nonce: u64,
226        ad: &[u8],
227        in_out: &mut [u8],
228        ciphertext_len: usize,
229    ) -> Result<usize, ()> {
230        assert!(ciphertext_len <= in_out.len());
231        assert!(ciphertext_len >= 16);
232
233        let mut full_nonce = [0u8; 12];
234        full_nonce[4..].copy_from_slice(&nonce.to_be_bytes());
235
236        let (in_out, tag) = in_out[..ciphertext_len].split_at_mut(ciphertext_len - 16);
237
238        use aes_gcm::{AeadInPlace, KeyInit};
239        aes_gcm::Aes256Gcm::new(&(**k).into())
240            .decrypt_in_place_detached(&full_nonce.into(), ad, in_out, tag.as_ref().into())
241            .map_err(|_| ())?;
242
243        Ok(in_out.len())
244    }
245}
246
247#[cfg(feature = "use-sha2")]
248#[derive(Default, Clone)]
249pub struct Sha256(sha2::Sha256);
250
251#[cfg(feature = "use-sha2")]
252impl Hash for Sha256 {
253    fn name() -> &'static str {
254        "SHA256"
255    }
256
257    type Block = [u8; 64];
258    type Output = Sensitive<[u8; 32]>;
259
260    fn input(&mut self, data: &[u8]) {
261        use sha2::Digest;
262        self.0.update(data);
263    }
264
265    fn result(&mut self) -> Self::Output {
266        use sha2::Digest;
267        Self::Output::from_slice(self.0.finalize_reset().as_ref())
268    }
269}
270
271#[cfg(feature = "use-sha2")]
272#[derive(Default, Clone)]
273pub struct Sha512(sha2::Sha512);
274
275#[cfg(feature = "use-sha2")]
276impl Hash for Sha512 {
277    fn name() -> &'static str {
278        "SHA512"
279    }
280
281    type Block = [u8; 128];
282    type Output = Sensitive<[u8; 64]>;
283
284    fn input(&mut self, data: &[u8]) {
285        use sha2::Digest;
286        self.0.update(data);
287    }
288
289    fn result(&mut self) -> Self::Output {
290        use sha2::Digest;
291        Self::Output::from_slice(self.0.finalize_reset().as_ref())
292    }
293}
294
295#[cfg(feature = "use-blake2")]
296#[derive(Default, Clone)]
297pub struct Blake2s(blake2::Blake2s256);
298
299#[cfg(feature = "use-blake2")]
300impl Hash for Blake2s {
301    fn name() -> &'static str {
302        "BLAKE2s"
303    }
304
305    type Block = [u8; 64];
306    type Output = Sensitive<[u8; 32]>;
307
308    fn input(&mut self, data: &[u8]) {
309        use blake2::Digest;
310        self.0.update(data);
311    }
312
313    fn result(&mut self) -> Self::Output {
314        use blake2::Digest;
315        Self::Output::from_slice(self.0.finalize_reset().as_ref())
316    }
317}
318
319#[cfg(feature = "use-blake2")]
320#[derive(Default, Clone)]
321pub struct Blake2b(blake2::Blake2b512);
322
323#[cfg(feature = "use-blake2")]
324impl Hash for Blake2b {
325    fn name() -> &'static str {
326        "BLAKE2b"
327    }
328
329    type Block = [u8; 128];
330    type Output = Sensitive<[u8; 64]>;
331
332    fn input(&mut self, data: &[u8]) {
333        use blake2::Digest;
334        self.0.update(data);
335    }
336
337    fn result(&mut self) -> Self::Output {
338        use blake2::Digest;
339        Self::Output::from_slice(self.0.finalize_reset().as_ref())
340    }
341}