Requesting an example
Hi! Does anyone have an example of basic operation, that includes a function or struct field containing each relevant type (key, poly, nonce)? Thank you!
Here is my non-compiling attempt:
Cargo.toml:
crypto = { package = "chacha20poly1305", version = "^0.10.1", default-features = false, features = ["rand_core"] }
rand_core = "^0.6.4"
generic-array = "^1.0.0"
Program:
use chacha20poly1305::{
aead::{AeadCore, KeyInit},
ChaCha20Poly1305 as Poly, Nonce
};
use generic_array::GenericArray;
type Key = GenericArray<u8, 12>;
type Nonce2 = GenericArray<u8, 12>;
pub fn make_cipher(rng: &mut Rng) -> (Key, Poly, Nonce) {
let key = Poly::generate_key(rng);
let cipher = Poly::new(&key);
// The nonce is unique per message. 96-bits.
let nonce = Poly::generate_nonce(rng);
(key, cipher, nonce)
}
pub fn encrypt(data: &mut [u8], cipher: &Poly, nonce: &Nonce) {
let ciphertext = cipher.encrypt(&nonce, data).unwrap();
}
pub fn decrypt(data: &mut [u8], cipher: &Poly, nonce: &Nonce) {
let ciphertext = cipher.decrypt(&nonce, data).unwrap();
}
Haven't cracked into this as I'm currently working on another PR rn but it would really help if you could post a dump of your specific compiler errors and warnings. I do think that more/better examples would definitely be a good thing though.
Hey! I got it working on my end. Important parts of the code. I think my biggest confusion point was on what a Generic Array is, and how to construct it.
use crypto::{
aead::{heapless::Vec, AeadCore, AeadInPlace, KeyInit},
ChaCha20Poly1305 as Poly, Key, Nonce,
};
pub struct CipherError {}
/// Generate a key; provide this as an option for the user to run, eg in the PC config.
pub fn genkey() -> Key {
let mut rng = Rng {};
Poly::generate_key(&mut rng)
}
pub struct Cipher {
/// A 256-bit key. We share this between the radios.
pub key: Key,
pub cipher: Poly,
/// The nonce is unique per message. 96-bits.
pub nonce: Nonce,
}
impl Cipher {
pub fn new(key: &Key) -> Self {
let mut rng = Rng {};
Self {
key: key.clone(),
cipher: Poly::new(&key),
nonce: Poly::generate_nonce(&mut rng),
}
}
/// Update the nonce; run this prior to sending each message. Send the nonce with the message.
pub fn update_nonce(&mut self) {
let mut rng = Rng {};
self.nonce = Poly::generate_nonce(&mut rng)
}
}
/// Data must include space for the auth. It will extend the effective message length.
pub fn encrypt(data: &mut [u8], cipher: &Cipher) -> Result<(), CipherError> {
// We must use heapless here, or else implement a trait like it for an array.
// Note: buffer needs 16-bytes overhead for auth tag
let len = data.len();
let mut buffer: Vec<u8, { RADIO_BUF_SIZE as usize }> = Vec::new();
buffer.extend_from_slice(&data[..len - AUTH_SIZE]).ok();
// "associated data is the AD in AEAD. basically its an arbitrary value what you can "mix" a MAC of
// into the AEAD's cipherext + authentication tag output. this lets you transmit the AEAD in
// plaintext but still validate it was the right one upon later decryption of the ciphertext.
// they are also used to add more context into an encryption operation for cotext binding etc
// since the wrong AD when decrypting will fail the whole operation"
let associated_data = [];
// Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
if cipher
.cipher
.encrypt_in_place(&cipher.nonce, &associated_data, &mut buffer)
.is_err()
{
return Err(CipherError {});
}
data.clone_from_slice(&buffer[..len]);
Ok(())
}
/// The decrypted data will be of the unencrypted (shorter) length. The input data must
/// not include the nonce.
pub fn decrypt(data: &mut [u8], cipher: &Cipher) -> Result<(), CipherError> {
let len = data.len(); // includes payload and auth; no nonce.
let mut buffer: Vec<u8, { RADIO_BUF_SIZE as usize }> = Vec::new();
buffer.extend_from_slice(data).ok();
let associated_data = [];
// Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
if cipher
.cipher
.decrypt_in_place(&cipher.nonce, &associated_data, &mut buffer)
.is_err()
{
return Err(CipherError {});
}
// Write the decrypted message to the relevant (first) part of the data.
data[..len - AUTH_SIZE].clone_from_slice(&buffer[..len - AUTH_SIZE]);
// Write 0s to the part of the buffer taken up by the auth.
data[len - AUTH_SIZE..len].clone_from_slice(&[0; AUTH_SIZE]);
Ok(())
}
Yeah that's actually a major criticism of mine lol. See the issue below yours in the issues list. I'm working on a PR that adds documentation that spells out this connection more explicitly.