commonware_cryptography/handshake/
cipher.rs

1// Intentionally avoid depending directly on super, to depend on the sibling.
2use super::error::Error;
3use chacha20poly1305::{
4    aead::{generic_array::typenum::Unsigned, Aead},
5    AeadCore, ChaCha20Poly1305, KeyInit as _,
6};
7use rand_core::CryptoRngCore;
8use std::vec::Vec;
9use zeroize::ZeroizeOnDrop;
10
11/// The amount of overhead in a ciphertext, compared to the plain message.
12pub const CIPHERTEXT_OVERHEAD: usize = <ChaCha20Poly1305 as AeadCore>::TagSize::USIZE;
13
14/// How many bytes are in a nonce.
15const NONCE_SIZE_BYTES: usize = <ChaCha20Poly1305 as AeadCore>::NonceSize::USIZE;
16
17struct CounterNonce {
18    inner: u128,
19}
20
21// We don't need to zeroize nonces.
22impl ZeroizeOnDrop for CounterNonce {}
23
24impl CounterNonce {
25    /// Creates a new counter nonce starting at zero.
26    pub fn new() -> Self {
27        Self { inner: 0 }
28    }
29
30    /// Increments the counter and returns the current value as bytes.
31    /// Returns an error if the counter would overflow.
32    pub fn inc(&mut self) -> Result<[u8; 128 / 8], Error> {
33        if self.inner >= 1 << (8 * NONCE_SIZE_BYTES) {
34            return Err(Error::MessageLimitReached);
35        }
36        let out = self.inner.to_le_bytes();
37        self.inner += 1;
38        Ok(out)
39    }
40}
41
42#[derive(ZeroizeOnDrop)]
43pub struct SendCipher {
44    nonce: CounterNonce,
45    inner: ChaCha20Poly1305,
46}
47
48impl SendCipher {
49    /// Creates a new sending cipher with a random key.
50    pub fn new(mut rng: impl CryptoRngCore) -> Self {
51        let mut key = [0u8; 32];
52        rng.fill_bytes(&mut key[..]);
53        Self {
54            nonce: CounterNonce::new(),
55            inner: ChaCha20Poly1305::new(&key.into()),
56        }
57    }
58
59    /// Encrypts data and returns the ciphertext.
60    pub fn send(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
61        self.inner
62            .encrypt((&self.nonce.inc()?[..NONCE_SIZE_BYTES]).into(), data)
63            .map_err(|_| Error::EncryptionFailed)
64    }
65}
66
67#[derive(ZeroizeOnDrop)]
68pub struct RecvCipher {
69    nonce: CounterNonce,
70    inner: ChaCha20Poly1305,
71}
72
73impl RecvCipher {
74    /// Creates a new receiving cipher with a random key.
75    pub fn new(mut rng: impl CryptoRngCore) -> Self {
76        let mut key = [0u8; 32];
77        rng.fill_bytes(&mut key[..]);
78        Self {
79            nonce: CounterNonce::new(),
80            inner: ChaCha20Poly1305::new(&key.into()),
81        }
82    }
83
84    /// Decrypts ciphertext and returns the original data.
85    pub fn recv(&mut self, encrypted_data: &[u8]) -> Result<Vec<u8>, Error> {
86        self.inner
87            .decrypt(
88                (&self.nonce.inc()?[..NONCE_SIZE_BYTES]).into(),
89                encrypted_data,
90            )
91            .map_err(|_| Error::DecryptionFailed)
92    }
93}