use {chacha, error, poly1305};
pub struct SealingKey {
key: Key,
}
impl SealingKey {
pub fn new(key_material: &[u8; KEY_LEN]) -> SealingKey {
SealingKey { key: Key::new(key_material) }
}
pub fn seal_in_place(&self, sequence_number: u32,
plaintext_in_ciphertext_out: &mut [u8],
tag_out: &mut [u8; TAG_LEN]) {
let mut counter = make_counter(sequence_number);
{
let (len_in_out, data_and_padding_in_out) =
plaintext_in_ciphertext_out.split_at_mut(PACKET_LENGTH_LEN);
chacha::chacha20_xor_in_place(&self.key.k_1, &counter, len_in_out);
counter[0] = 1;
chacha::chacha20_xor_in_place(&self.key.k_2, &counter,
data_and_padding_in_out);
}
counter[0] = 0;
let poly_key = poly1305::Key::derive_using_chacha(&self.key.k_2,
&counter);
poly1305::sign(poly_key, plaintext_in_ciphertext_out, tag_out);
}
}
pub struct OpeningKey {
key: Key,
}
impl OpeningKey {
pub fn new(key_material: &[u8; KEY_LEN]) -> OpeningKey {
OpeningKey { key: Key::new(key_material) }
}
pub fn decrypt_packet_length(
&self, sequence_number: u32,
encrypted_packet_length: [u8; PACKET_LENGTH_LEN])
-> [u8; PACKET_LENGTH_LEN] {
let mut packet_length = encrypted_packet_length;
let counter = make_counter(sequence_number);
chacha::chacha20_xor_in_place(&self.key.k_1, &counter,
&mut packet_length);
packet_length
}
pub fn open_in_place<'a>(&self, sequence_number: u32,
ciphertext_in_plaintext_out: &'a mut [u8],
tag: &[u8; TAG_LEN])
-> Result<&'a [u8], error::Unspecified> {
let mut counter = make_counter(sequence_number);
let poly_key = poly1305::Key::derive_using_chacha(&self.key.k_2,
&counter);
poly1305::verify(poly_key, ciphertext_in_plaintext_out, tag)?;
let plaintext_in_ciphertext_out =
&mut ciphertext_in_plaintext_out[PACKET_LENGTH_LEN..];
counter[0] = 1;
chacha::chacha20_xor_in_place(&self.key.k_2, &counter,
plaintext_in_ciphertext_out);
Ok(plaintext_in_ciphertext_out)
}
}
struct Key {
k_1: chacha::Key,
k_2: chacha::Key,
}
impl Key {
pub fn new(key_material: &[u8; KEY_LEN]) -> Key {
Key {
k_1: chacha::key_from_bytes(
slice_as_array_ref!(
&key_material[chacha::KEY_LEN_IN_BYTES..],
chacha::KEY_LEN_IN_BYTES).unwrap()),
k_2: chacha::key_from_bytes(
slice_as_array_ref!(
&key_material[..chacha::KEY_LEN_IN_BYTES],
chacha::KEY_LEN_IN_BYTES).unwrap()),
}
}
}
fn make_counter(sequence_number: u32) -> chacha::Counter {
let mut sequence_number = sequence_number;
let mut nonce = [0; chacha::NONCE_LEN];
for i in 0..4 {
nonce[chacha::NONCE_LEN - 1 - i] = (sequence_number % 0x100) as u8;
sequence_number /= 0x100;
}
chacha::make_counter(&nonce, 0)
}
pub const KEY_LEN: usize = chacha::KEY_LEN_IN_BYTES * 2;
pub const TAG_LEN: usize = poly1305::TAG_LEN;
pub const PACKET_LENGTH_LEN: usize = 4;