commonware_cryptography/handshake/
cipher.rs

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