2017-12-25 01:05:27 -05:00
|
|
|
// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
|
|
|
|
// Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
|
2020-08-10 15:00:09 -04:00
|
|
|
//
|
|
|
|
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
|
|
|
|
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
|
|
|
|
// You may not use this file except in accordance with one or both of these
|
|
|
|
// licenses.
|
|
|
|
//
|
2017-12-25 01:05:27 -05:00
|
|
|
// This is a port of Andrew Moons poly1305-donna
|
|
|
|
// https://github.com/floodyberry/poly1305-donna
|
|
|
|
|
2022-10-13 02:35:48 -04:00
|
|
|
use crate::ln::msgs::DecodeError;
|
|
|
|
use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer};
|
|
|
|
use crate::io::{self, Read, Write};
|
2022-06-14 16:13:34 -04:00
|
|
|
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(not(fuzzing))]
|
2018-03-19 17:34:51 -04:00
|
|
|
mod real_chachapoly {
|
2022-10-13 02:35:48 -04:00
|
|
|
use crate::util::chacha20::ChaCha20;
|
|
|
|
use crate::util::poly1305::Poly1305;
|
2020-04-27 16:41:54 +02:00
|
|
|
use bitcoin::hashes::cmp::fixed_time_eq;
|
2018-08-02 20:05:14 -04:00
|
|
|
|
2018-03-19 17:34:51 -04:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct ChaCha20Poly1305RFC {
|
2018-12-13 17:18:31 -05:00
|
|
|
cipher: ChaCha20,
|
2018-03-19 17:34:51 -04:00
|
|
|
mac: Poly1305,
|
|
|
|
finished: bool,
|
|
|
|
data_len: usize,
|
|
|
|
aad_len: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChaCha20Poly1305RFC {
|
|
|
|
#[inline]
|
|
|
|
fn pad_mac_16(mac: &mut Poly1305, len: usize) {
|
|
|
|
if len % 16 != 0 {
|
|
|
|
mac.input(&[0; 16][0..16 - (len % 16)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
|
|
|
|
assert!(key.len() == 16 || key.len() == 32);
|
|
|
|
assert!(nonce.len() == 12);
|
|
|
|
|
|
|
|
// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
|
|
|
|
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
|
|
|
|
|
|
|
|
let mut cipher = ChaCha20::new(key, &nonce[4..]);
|
|
|
|
let mut mac_key = [0u8; 64];
|
|
|
|
let zero_key = [0u8; 64];
|
|
|
|
cipher.process(&zero_key, &mut mac_key);
|
|
|
|
|
|
|
|
let mut mac = Poly1305::new(&mac_key[..32]);
|
|
|
|
mac.input(aad);
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
|
|
|
|
|
|
|
|
ChaCha20Poly1305RFC {
|
2020-10-06 16:47:23 -07:00
|
|
|
cipher,
|
|
|
|
mac,
|
2018-03-19 17:34:51 -04:00
|
|
|
finished: false,
|
|
|
|
data_len: 0,
|
|
|
|
aad_len: aad.len() as u64,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
self.cipher.process(input, output);
|
|
|
|
self.data_len += input.len();
|
|
|
|
self.mac.input(output);
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
|
|
|
self.finished = true;
|
2021-10-08 22:54:32 +00:00
|
|
|
self.mac.input(&self.aad_len.to_le_bytes());
|
|
|
|
self.mac.input(&(self.data_len as u64).to_le_bytes());
|
2018-03-19 17:34:51 -04:00
|
|
|
self.mac.raw_result(out_tag);
|
|
|
|
}
|
|
|
|
|
2022-09-12 15:20:37 +00:00
|
|
|
pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
|
|
|
|
self.encrypt_in_place(input_output);
|
|
|
|
self.finish_and_get_tag(out_tag);
|
|
|
|
}
|
|
|
|
|
2022-06-14 16:13:34 -04:00
|
|
|
// Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
|
|
|
|
// below.
|
|
|
|
pub(super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
|
|
|
|
debug_assert!(self.finished == false);
|
|
|
|
self.cipher.process_in_place(input_output);
|
|
|
|
self.data_len += input_output.len();
|
|
|
|
self.mac.input(input_output);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
|
|
|
|
// encrypting and calculate the tag.
|
|
|
|
pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
|
|
|
|
debug_assert!(self.finished == false);
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
|
|
|
self.finished = true;
|
|
|
|
self.mac.input(&self.aad_len.to_le_bytes());
|
|
|
|
self.mac.input(&(self.data_len as u64).to_le_bytes());
|
|
|
|
self.mac.raw_result(out_tag);
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
|
|
|
|
self.finished = true;
|
|
|
|
|
|
|
|
self.mac.input(input);
|
|
|
|
|
|
|
|
self.data_len += input.len();
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
2021-10-08 22:54:32 +00:00
|
|
|
self.mac.input(&self.aad_len.to_le_bytes());
|
|
|
|
self.mac.input(&(self.data_len as u64).to_le_bytes());
|
2018-03-19 17:34:51 -04:00
|
|
|
|
|
|
|
let mut calc_tag = [0u8; 16];
|
|
|
|
self.mac.raw_result(&mut calc_tag);
|
|
|
|
if fixed_time_eq(&calc_tag, tag) {
|
|
|
|
self.cipher.process(input, output);
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2022-06-14 16:28:45 -04:00
|
|
|
|
2023-11-04 20:39:03 +00:00
|
|
|
pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
|
|
|
|
self.decrypt_in_place(input_output);
|
|
|
|
if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
|
|
|
|
/// later when decryption finishes.
|
|
|
|
///
|
|
|
|
/// Should never be `pub` because the public API should always enforce tag checking.
|
2022-06-14 16:28:45 -04:00
|
|
|
pub(super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
|
|
|
|
debug_assert!(self.finished == false);
|
|
|
|
self.mac.input(input_output);
|
|
|
|
self.data_len += input_output.len();
|
|
|
|
self.cipher.process_in_place(input_output);
|
|
|
|
}
|
|
|
|
|
2023-11-04 20:39:03 +00:00
|
|
|
/// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
|
|
|
|
/// to check the tag. Returns whether or not the tag is valid.
|
2022-06-14 16:28:45 -04:00
|
|
|
pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
|
|
|
|
debug_assert!(self.finished == false);
|
|
|
|
self.finished = true;
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
|
|
|
self.mac.input(&self.aad_len.to_le_bytes());
|
|
|
|
self.mac.input(&(self.data_len as u64).to_le_bytes());
|
|
|
|
|
|
|
|
let mut calc_tag = [0u8; 16];
|
|
|
|
self.mac.raw_result(&mut calc_tag);
|
|
|
|
if fixed_time_eq(&calc_tag, tag) {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2018-03-19 17:34:51 -04:00
|
|
|
}
|
2017-12-25 01:05:27 -05:00
|
|
|
}
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(not(fuzzing))]
|
2018-12-13 17:18:31 -05:00
|
|
|
pub use self::real_chachapoly::ChaCha20Poly1305RFC;
|
2018-03-19 17:34:51 -04:00
|
|
|
|
2022-06-14 16:28:45 -04:00
|
|
|
/// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read.
|
|
|
|
struct ChaChaPolyReader<'a, R: Read> {
|
|
|
|
pub chacha: &'a mut ChaCha20Poly1305RFC,
|
|
|
|
pub read: R,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> {
|
|
|
|
// Decrypt bytes from Self::read into `dest`.
|
|
|
|
// `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads
|
|
|
|
// complete.
|
|
|
|
fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
|
|
|
|
let res = self.read.read(dest)?;
|
|
|
|
if res > 0 {
|
|
|
|
self.chacha.decrypt_in_place(&mut dest[0..res]);
|
|
|
|
}
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-14 16:13:34 -04:00
|
|
|
/// Enables simultaneously writing and encrypting a byte stream into a Writer.
|
|
|
|
struct ChaChaPolyWriter<'a, W: Writer> {
|
|
|
|
pub chacha: &'a mut ChaCha20Poly1305RFC,
|
|
|
|
pub write: &'a mut W,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> {
|
|
|
|
// Encrypt then write bytes from `src` into Self::write.
|
|
|
|
// `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes
|
|
|
|
// complete.
|
|
|
|
fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> {
|
|
|
|
let mut src_idx = 0;
|
|
|
|
while src_idx < src.len() {
|
|
|
|
let mut write_buffer = [0; 8192];
|
|
|
|
let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail");
|
|
|
|
self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]);
|
|
|
|
self.write.write_all(&write_buffer[..bytes_written])?;
|
|
|
|
src_idx += bytes_written;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and
|
|
|
|
/// serialized. This allows us to avoid an intermediate Vec allocation.
|
|
|
|
pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> {
|
|
|
|
pub rho: [u8; 32],
|
|
|
|
pub writeable: &'a W,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> {
|
|
|
|
#[allow(unused)] // This will be used for onion messages soon
|
|
|
|
pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> {
|
|
|
|
Self { rho, writeable }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
|
|
|
|
// Simultaneously write and encrypt Self::writeable.
|
|
|
|
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
|
|
|
let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]);
|
|
|
|
let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w };
|
|
|
|
self.writeable.write(&mut chacha_stream)?;
|
|
|
|
let mut tag = [0 as u8; 16];
|
|
|
|
chacha.finish_and_get_tag(&mut tag);
|
|
|
|
tag.write(w)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-14 16:28:45 -04:00
|
|
|
/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and
|
|
|
|
/// deserialized. This allows us to avoid an intermediate Vec allocation.
|
|
|
|
pub(crate) struct ChaChaPolyReadAdapter<R: Readable> {
|
|
|
|
pub readable: R,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Readable> LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter<T> {
|
|
|
|
// Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable.
|
|
|
|
// LengthRead must be used instead of std::io::Read because we need the total length to separate
|
|
|
|
// out the tag at the end.
|
|
|
|
fn read<R: LengthRead>(mut r: &mut R, secret: [u8; 32]) -> Result<Self, DecodeError> {
|
|
|
|
if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) }
|
|
|
|
|
|
|
|
let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]);
|
|
|
|
let decrypted_len = r.total_bytes() - 16;
|
|
|
|
let s = FixedLengthReader::new(&mut r, decrypted_len);
|
|
|
|
let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s };
|
|
|
|
let readable: T = Readable::read(&mut chacha_stream)?;
|
|
|
|
chacha_stream.read.eat_remaining()?;
|
|
|
|
|
|
|
|
let mut tag = [0 as u8; 16];
|
|
|
|
r.read_exact(&mut tag)?;
|
|
|
|
if !chacha.finish_and_check_tag(&tag) {
|
|
|
|
return Err(DecodeError::InvalidValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Self { readable })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(fuzzing)]
|
2018-03-19 17:34:51 -04:00
|
|
|
mod fuzzy_chachapoly {
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct ChaCha20Poly1305RFC {
|
|
|
|
tag: [u8; 16],
|
|
|
|
finished: bool,
|
|
|
|
}
|
|
|
|
impl ChaCha20Poly1305RFC {
|
|
|
|
pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
|
|
|
|
assert!(key.len() == 16 || key.len() == 32);
|
|
|
|
assert!(nonce.len() == 12);
|
|
|
|
|
|
|
|
// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
|
|
|
|
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
|
|
|
|
|
|
|
|
let mut tag = [0; 16];
|
|
|
|
tag.copy_from_slice(&key[0..16]);
|
|
|
|
|
|
|
|
ChaCha20Poly1305RFC {
|
|
|
|
tag,
|
|
|
|
finished: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
|
|
|
|
output.copy_from_slice(&input);
|
|
|
|
out_tag.copy_from_slice(&self.tag);
|
|
|
|
self.finished = true;
|
|
|
|
}
|
|
|
|
|
2022-09-12 15:20:37 +00:00
|
|
|
pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
|
|
|
|
self.encrypt_in_place(input_output);
|
|
|
|
self.finish_and_get_tag(out_tag);
|
|
|
|
}
|
|
|
|
|
2022-06-14 16:13:34 -04:00
|
|
|
pub(super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
|
|
|
|
assert!(self.finished == false);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
|
2022-07-24 14:47:31 -04:00
|
|
|
assert!(self.finished == false);
|
2022-06-14 16:13:34 -04:00
|
|
|
out_tag.copy_from_slice(&self.tag);
|
|
|
|
self.finished = true;
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
|
|
|
|
if tag[..] != self.tag[..] { return false; }
|
|
|
|
output.copy_from_slice(input);
|
|
|
|
self.finished = true;
|
|
|
|
true
|
|
|
|
}
|
2022-06-14 16:28:45 -04:00
|
|
|
|
2023-11-04 20:39:03 +00:00
|
|
|
pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
|
|
|
|
self.decrypt_in_place(input_output);
|
|
|
|
if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
|
|
|
|
}
|
|
|
|
|
2022-06-14 16:28:45 -04:00
|
|
|
pub(super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
|
|
|
|
assert!(self.finished == false);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
|
|
|
|
if tag[..] != self.tag[..] { return false; }
|
|
|
|
self.finished = true;
|
|
|
|
true
|
|
|
|
}
|
2018-03-19 17:34:51 -04:00
|
|
|
}
|
2017-12-25 01:05:27 -05:00
|
|
|
}
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(fuzzing)]
|
2018-12-13 17:18:31 -05:00
|
|
|
pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
|
2022-06-14 16:28:45 -04:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-10-13 02:35:48 -04:00
|
|
|
use crate::ln::msgs::DecodeError;
|
2022-06-14 16:28:45 -04:00
|
|
|
use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
|
2022-10-13 02:35:48 -04:00
|
|
|
use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable};
|
2022-06-14 16:28:45 -04:00
|
|
|
|
|
|
|
// Used for for testing various lengths of serialization.
|
2022-10-14 13:24:02 +02:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2022-06-14 16:28:45 -04:00
|
|
|
struct TestWriteable {
|
|
|
|
field1: Vec<u8>,
|
|
|
|
field2: Vec<u8>,
|
|
|
|
field3: Vec<u8>,
|
|
|
|
}
|
|
|
|
impl_writeable_tlv_based!(TestWriteable, {
|
2023-07-07 18:44:24 +00:00
|
|
|
(1, field1, required_vec),
|
|
|
|
(2, field2, required_vec),
|
|
|
|
(3, field3, required_vec),
|
2022-06-14 16:28:45 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_chacha_stream_adapters() {
|
|
|
|
// Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
|
|
|
|
// encrypted object.
|
|
|
|
macro_rules! check_object_read_write {
|
|
|
|
($obj: expr) => {
|
|
|
|
// First, serialize the object, encrypted with ChaCha20Poly1305.
|
|
|
|
let rho = [42; 32];
|
|
|
|
let writeable_len = $obj.serialized_length() as u64 + 16;
|
|
|
|
let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj);
|
|
|
|
let encrypted_writeable_bytes = write_adapter.encode();
|
|
|
|
let encrypted_writeable = &encrypted_writeable_bytes[..];
|
|
|
|
|
|
|
|
// Now deserialize the object back and make sure it matches the original.
|
|
|
|
let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len);
|
|
|
|
let read_adapter = <ChaChaPolyReadAdapter<TestWriteable>>::read(&mut rd, rho).unwrap();
|
|
|
|
assert_eq!($obj, read_adapter.readable);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try a big object that will require multiple write buffers.
|
|
|
|
let big_writeable = TestWriteable {
|
|
|
|
field1: vec![43],
|
|
|
|
field2: vec![44; 4192],
|
|
|
|
field3: vec![45; 4192 + 1],
|
|
|
|
};
|
|
|
|
check_object_read_write!(big_writeable);
|
|
|
|
|
|
|
|
// Try a small object that fits into one write buffer.
|
|
|
|
let small_writeable = TestWriteable {
|
|
|
|
field1: vec![43],
|
|
|
|
field2: vec![44],
|
|
|
|
field3: vec![45],
|
|
|
|
};
|
|
|
|
check_object_read_write!(small_writeable);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> {
|
|
|
|
let writeable = TestWriteable {
|
|
|
|
field1: vec![43],
|
|
|
|
field2: vec![44; 4192],
|
|
|
|
field3: vec![45; 4192 + 1],
|
|
|
|
};
|
|
|
|
|
|
|
|
// First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305.
|
|
|
|
let rho = [42; 32];
|
|
|
|
let write_adapter = ChaChaPolyWriteAdapter::new(rho, &writeable);
|
|
|
|
let mut writer = ser::VecWriter(Vec::new());
|
|
|
|
encode_tlv_stream!(&mut writer, {
|
|
|
|
(1, write_adapter, required),
|
|
|
|
});
|
|
|
|
|
|
|
|
// Now deserialize the object back and make sure it matches the original.
|
|
|
|
let mut read_adapter: Option<ChaChaPolyReadAdapter<TestWriteable>> = None;
|
|
|
|
decode_tlv_stream!(&writer.0[..], {
|
|
|
|
(1, read_adapter, (option: LengthReadableArgs, rho)),
|
|
|
|
});
|
|
|
|
assert_eq!(writeable, read_adapter.unwrap().readable);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn chacha_stream_adapters_ser_macros() {
|
|
|
|
// Test that our stream adapters work as expected with the TLV macros.
|
2023-01-06 10:18:26 +02:00
|
|
|
// This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro.
|
2022-06-14 16:28:45 -04:00
|
|
|
do_chacha_stream_adapters_ser_macros().unwrap()
|
|
|
|
}
|
|
|
|
}
|