feat: works

This commit is contained in:
2023-10-22 00:42:30 +08:00
parent 745b192c20
commit 71b8f75417
7 changed files with 427 additions and 481 deletions

View File

@@ -18,3 +18,8 @@ simd_asm = ["simd_opt"]
[dependencies]
constant_time_eq = "0.1.0"
clippy = { version = "0.0.37", optional = true }
hex = "0.4.3"
[dev-dependencies]
chacha20-poly1305-aead = "0.1.2"
benchmark-simple = "0.1.8"

38
examples/bench.rs Normal file
View File

@@ -0,0 +1,38 @@
use benchmark_simple::{Bench, Options};
fn test_chacha20_poly1305_encrypt(m: &mut [u8]) {
let key = [0u8; 32];
let nonce = [0u8; 12];
chacha20_poly1305_stream::chacha20_poly1305_encrypt(&key, &nonce, m).unwrap();
}
fn test_chacha20_poly1305_encrypt_and_decrypt(m: &mut [u8]) {
let key = [0u8; 32];
let nonce = [0u8; 12];
let encrypted = chacha20_poly1305_stream::chacha20_poly1305_encrypt(&key, &nonce, m).unwrap();
let decrypted = chacha20_poly1305_stream::chacha20_poly1305_decrypt(&key, &nonce, &encrypted).unwrap();
assert_eq!(m, decrypted.as_slice());
}
fn main() {
let bench = Bench::new();
let mut m = vec![0xd0u8; 16384];
let options = &Options {
iterations: 1_000,
warmup_iterations: 1_00,
min_samples: 5,
max_samples: 10,
max_rsd: 1.0,
..Default::default()
};
let res = bench.run(options, || test_chacha20_poly1305_encrypt(&mut m));
println!("ChaCha20Poly1305 encrypt : {}", res.throughput(m.len() as _));
let res = bench.run(options, || test_chacha20_poly1305_encrypt_and_decrypt(&mut m));
println!("ChaCha20Poly1305 encrypt/decrypt : {}", res.throughput(m.len() as _));
}

View File

@@ -1,476 +0,0 @@
// Copyright 2016 chacha20-poly1305-aead Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::io::{self, ErrorKind, Read, Write};
use crate::as_bytes::AsBytes;
use crate::chacha20::ChaCha20;
use constant_time_eq::constant_time_eq;
use crate::poly1305::Poly1305;
use crate::simd::u32x4;
const CHACHA20_COUNTER_OVERFLOW: u64 = ((1 << 32) - 1) * 64;
/// Encrypts a byte slice and returns the authentication tag.
///
/// # Example
///
/// ```
/// use chacha20_poly1305_stream::encrypt;
///
/// let key = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
/// 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31];
/// let nonce = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// let aad = [1, 2, 3, 4];
///
/// let plaintext = b"hello, world";
///
/// // Vec implements the Write trait
/// let mut ciphertext = Vec::with_capacity(plaintext.len());
///
/// let tag = encrypt(&key, &nonce, &aad, plaintext, &mut ciphertext).unwrap();
///
/// assert_eq!(ciphertext, [0xfc, 0x5a, 0x17, 0x82,
/// 0xab, 0xcf, 0xbc, 0x5d, 0x18, 0x29, 0xbf, 0x97]);
/// assert_eq!(tag, [0xdb, 0xb7, 0x0d, 0xda, 0xbd, 0xfa, 0x8c, 0xa5,
/// 0x60, 0xa2, 0x30, 0x3d, 0xe6, 0x07, 0x92, 0x10]);
/// ```
pub fn encrypt<W: Write>(key: &[u8], nonce: &[u8],
aad: &[u8], mut input: &[u8],
output: &mut W) -> io::Result<[u8; 16]> {
encrypt_read(key, nonce, aad, &mut input, output)
}
/// Encrypts bytes from a reader and returns the authentication tag.
///
/// This function is identical to the `encrypt` function, the only
/// difference being that its input comes from a reader instead of a
/// byte slice.
pub fn encrypt_read<R: Read, W: Write>(key: &[u8], nonce: &[u8],
aad: &[u8], input: &mut R,
output: &mut W) -> io::Result<[u8; 16]> {
let mut chacha20 = ChaCha20::new(key, nonce);
let mut poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
let aad_len = aad.len() as u64;
let mut input_len = 0;
poly1305.padded_blocks(aad);
let mut buf = [u32x4::default(); 4];
loop {
let read = read_all(input, buf.as_mut_bytes())?;
if read == 0 { break; }
input_len += read as u64;
if input_len >= CHACHA20_COUNTER_OVERFLOW {
return Err(io::Error::new(ErrorKind::WriteZero,
"counter overflow"));
}
let block = chacha20.next();
buf[0] = buf[0] ^ block[0];
buf[1] = buf[1] ^ block[1];
buf[2] = buf[2] ^ block[2];
buf[3] = buf[3] ^ block[3];
poly1305.padded_blocks(&buf.as_bytes()[..read]);
output.write_all(&buf.as_bytes()[..read])?;
}
poly1305.block([aad_len.to_le(), input_len.to_le()].as_bytes());
let mut tag = [0; 16];
tag.clone_from_slice(poly1305.tag().as_bytes());
Ok(tag)
}
/// Verifies the authentication tag and decrypts a byte slice.
///
/// If the tag does not match, this function produces no output and
/// returns `Err(DecryptError::TagMismatch)`.
///
/// # Example
///
/// ```
/// # use chacha20_poly1305_stream::DecryptError;
/// # fn example() -> Result<(), DecryptError> {
/// use chacha20_poly1305_stream::decrypt;
///
/// let key = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
/// 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31];
/// let nonce = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// let aad = [1, 2, 3, 4];
///
/// let ciphertext = [0xfc, 0x5a, 0x17, 0x82, 0xab, 0xcf, 0xbc, 0x5d,
/// 0x18, 0x29, 0xbf, 0x97];
/// let tag = [0xdb, 0xb7, 0x0d, 0xda, 0xbd, 0xfa, 0x8c, 0xa5,
/// 0x60, 0xa2, 0x30, 0x3d, 0xe6, 0x07, 0x92, 0x10];
///
/// // Vec implements the Write trait
/// let mut plaintext = Vec::with_capacity(ciphertext.len());
///
/// decrypt(&key, &nonce, &aad, &ciphertext, &tag, &mut plaintext)?;
///
/// assert_eq!(plaintext, b"hello, world");
/// # Ok(())
/// # }
/// # example().unwrap();
/// ```
pub fn decrypt<W: Write>(key: &[u8], nonce: &[u8],
aad: &[u8], mut input: &[u8], tag: &[u8],
output: &mut W) -> Result<(), DecryptError> {
let mut chacha20 = ChaCha20::new(key, nonce);
let mut poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
let aad_len = aad.len() as u64;
let input_len = input.len() as u64;
assert!(tag.len() == 16);
if input_len >= CHACHA20_COUNTER_OVERFLOW {
return Err(io::Error::new(ErrorKind::WriteZero,
"counter overflow").into());
}
poly1305.padded_blocks(aad);
poly1305.padded_blocks(input);
poly1305.block([aad_len.to_le(), input_len.to_le()].as_bytes());
if !constant_time_eq(poly1305.tag().as_bytes(), tag) {
return Err(DecryptError::TagMismatch);
}
let mut buf = [u32x4::default(); 4];
loop {
let read = read_all(&mut input, buf.as_mut_bytes())?;
if read == 0 { break; }
let block = chacha20.next();
buf[0] = buf[0] ^ block[0];
buf[1] = buf[1] ^ block[1];
buf[2] = buf[2] ^ block[2];
buf[3] = buf[3] ^ block[3];
output.write_all(&buf.as_bytes()[..read])?;
}
Ok(())
}
fn read_all<R: Read>(reader: &mut R, mut buf: &mut [u8]) -> io::Result<usize> {
let mut read = 0;
while !buf.is_empty() {
match reader.read(buf) {
Ok(0) => break,
Ok(n) => { read += n; let tmp = buf; buf = &mut tmp[n..]; }
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(read)
}
/// Error returned from the `decrypt` function.
#[derive(Debug)]
pub enum DecryptError {
/// The calculated Poly1305 tag did not match the given tag.
TagMismatch,
/// There was an error writing the output.
IoError(io::Error),
}
impl Display for DecryptError {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
match *self {
DecryptError::TagMismatch => fmt.write_str(self.description()),
DecryptError::IoError(ref e) => e.fmt(fmt),
}
}
}
impl Error for DecryptError {
fn description(&self) -> &str {
match *self {
DecryptError::TagMismatch => "authentication tag mismatch",
DecryptError::IoError(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&dyn Error> {
match *self {
DecryptError::TagMismatch => None,
DecryptError::IoError(ref e) => Some(e),
}
}
}
impl From<io::Error> for DecryptError {
fn from(error: io::Error) -> Self {
DecryptError::IoError(error)
}
}
impl From<DecryptError> for io::Error {
fn from(error: DecryptError) -> Self {
match error {
DecryptError::IoError(e) => e,
DecryptError::TagMismatch =>
io::Error::new(ErrorKind::InvalidData, error),
}
}
}
pub mod selftest {
use super::*;
static PLAINTEXT: &'static [u8] = b"\
Ladies and Gentlemen of the class of '99: If I could offer you o\
nly one tip for the future, sunscreen would be it.";
static AAD: &'static [u8] = &[0x50, 0x51, 0x52, 0x53,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7];
static KEY: &'static [u8] = &[
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f];
static NONCE: &'static [u8] = &[0x07, 0x00, 0x00, 0x00,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47];
static CIPHERTEXT: &'static [u8] = &[
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
0x61, 0x16];
static TAG: &'static [u8] = &[
0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91];
#[cold]
pub fn selftest() {
selftest_encrypt();
selftest_decrypt();
selftest_decrypt_mismatch();
}
#[cold]
pub fn selftest_encrypt() {
let mut output = Vec::with_capacity(PLAINTEXT.len());
let tag = encrypt(KEY, NONCE, AAD, PLAINTEXT, &mut output)
.expect("selftest failure");
assert_eq!(&output[..], CIPHERTEXT);
assert_eq!(tag, TAG);
}
#[cold]
pub fn selftest_decrypt() {
let mut output = Vec::with_capacity(CIPHERTEXT.len());
decrypt(KEY, NONCE, AAD, CIPHERTEXT, TAG, &mut output)
.expect("selftest failure");
assert_eq!(&output[..], PLAINTEXT);
}
#[cold]
pub fn selftest_decrypt_mismatch() {
let mut output = Vec::with_capacity(0);
let result = decrypt(KEY, NONCE, AAD, CIPHERTEXT, &[0; 16],
&mut output);
if let Err(DecryptError::TagMismatch) = result {
assert!(output.is_empty());
} else {
panic!("selftest failure");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn selftest_encrypt() {
selftest::selftest_encrypt();
}
#[test]
fn selftest_decrypt() {
selftest::selftest_decrypt();
}
#[test]
fn selftest_decrypt_mismatch() {
selftest::selftest_decrypt_mismatch();
}
#[test]
fn test_encrypt() {
let mut output = Vec::with_capacity(PLAINTEXT.len());
let tag = encrypt(KEY, NONCE, AAD, PLAINTEXT.as_bytes(),
&mut output).expect("test failed");
assert_eq!(&output[..], CIPHERTEXT);
assert_eq!(tag, TAG);
}
#[test]
fn test_decrypt() {
let mut output = Vec::with_capacity(CIPHERTEXT.len());
decrypt(KEY, NONCE, AAD, CIPHERTEXT, TAG,
&mut output).expect("test failed");
assert_eq!(&output[..], PLAINTEXT.as_bytes());
}
static KEY: &'static [u8] = &[
0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0];
static CIPHERTEXT: &'static [u8] = &[
0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4,
0x60, 0xf0, 0x62, 0xc7, 0x9b, 0xe6, 0x43, 0xbd,
0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89,
0xf1, 0x08, 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2,
0x4c, 0x6c, 0xfc, 0x18, 0x75, 0x5d, 0x43, 0xee,
0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0,
0xbd, 0xb7, 0xb7, 0x3c, 0x32, 0x1b, 0x01, 0x00,
0xd4, 0xf0, 0x3b, 0x7f, 0x35, 0x58, 0x94, 0xcf,
0x33, 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce,
0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b, 0x94, 0x81,
0x14, 0xad, 0x17, 0x6e, 0x00, 0x8d, 0x33, 0xbd,
0x60, 0xf9, 0x82, 0xb1, 0xff, 0x37, 0xc8, 0x55,
0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61,
0xc1, 0x86, 0x32, 0x4e, 0x2b, 0x35, 0x06, 0x38,
0x36, 0x06, 0x90, 0x7b, 0x6a, 0x7c, 0x02, 0xb0,
0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4,
0xb9, 0x16, 0x6c, 0x76, 0x7b, 0x80, 0x4d, 0x46,
0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4, 0xe9,
0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e,
0xe2, 0x82, 0xa1, 0xb0, 0xa0, 0x6c, 0x52, 0x3e,
0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15,
0x5b, 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a,
0x0d, 0x07, 0x2b, 0x04, 0xb3, 0x56, 0x4e, 0xea,
0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a,
0x0b, 0xb2, 0x31, 0x60, 0x53, 0xfa, 0x76, 0x99,
0x19, 0x55, 0xeb, 0xd6, 0x31, 0x59, 0x43, 0x4e,
0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10,
0x73, 0xa6, 0x72, 0x76, 0x27, 0x09, 0x7a, 0x10,
0x49, 0xe6, 0x17, 0xd9, 0x1d, 0x36, 0x10, 0x94,
0xfa, 0x68, 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30,
0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04, 0xdf,
0x99, 0x7b, 0x71, 0x4d, 0x6c, 0x6f, 0x2c, 0x29,
0xa6, 0xad, 0x5c, 0xb4, 0x02, 0x2b, 0x02, 0x70,
0x9b];
static NONCE: &'static [u8] = &[0x00, 0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
static AAD: &'static [u8] = &[0xf3, 0x33, 0x88, 0x86,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x91];
static TAG: &'static [u8] = &[
0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22,
0x39, 0x23, 0x36, 0xfe, 0xa1, 0x85, 0x1f, 0x38];
static PLAINTEXT: &'static str = "\
Internet-Drafts are draft documents valid for a maximum of six m\
onths and may be updated, replaced, or obsoleted by other docume\
nts at any time. It is inappropriate to use Internet-Drafts as r\
eference material or to cite them other than as /\u{201c}work in prog\
ress./\u{201d}";
}
#[cfg(all(feature = "bench", test))]
mod bench {
use test::{Bencher, black_box};
use super::*;
#[cfg_attr(feature = "clippy", allow(result_unwrap_used))]
fn bench_encrypt(b: &mut Bencher, aad: &[u8], data: &[u8]) {
let key = [!0; 32];
let nonce = [!0; 12];
let mut buf = Vec::with_capacity(data.len());
b.bytes = data.len() as u64;
b.iter(|| {
buf.clear();
encrypt(black_box(&key), black_box(&nonce),
black_box(aad), black_box(data),
black_box(&mut buf)).unwrap()
})
}
#[cfg_attr(feature = "clippy", allow(result_unwrap_used))]
fn bench_decrypt(b: &mut Bencher, aad: &[u8], data: &[u8]) {
let key = [!0; 32];
let nonce = [!0; 12];
let mut ciphertext = Vec::with_capacity(data.len());
let tag = encrypt(&key, &nonce, aad, data, &mut ciphertext).unwrap();
let input = &ciphertext[..];
let mut buf = Vec::with_capacity(data.len());
b.bytes = data.len() as u64;
b.iter(|| {
buf.clear();
decrypt(black_box(&key), black_box(&nonce),
black_box(aad), black_box(input), black_box(&tag),
black_box(&mut buf)).unwrap()
})
}
#[bench]
fn bench_encrypt_16(b: &mut Bencher) {
bench_encrypt(b, &[!0; 16], &[!0; 16])
}
#[bench]
fn bench_encrypt_4k(b: &mut Bencher) {
bench_encrypt(b, &[!0; 16], &[!0; 4096])
}
#[bench]
fn bench_encrypt_64k(b: &mut Bencher) {
bench_encrypt(b, &[!0; 16], &[!0; 65536])
}
#[bench]
fn bench_decrypt_16(b: &mut Bencher) {
bench_decrypt(b, &[!0; 16], &[!0; 16])
}
#[bench]
fn bench_decrypt_4k(b: &mut Bencher) {
bench_decrypt(b, &[!0; 16], &[!0; 4096])
}
#[bench]
fn bench_decrypt_64k(b: &mut Bencher) {
bench_decrypt(b, &[!0; 16], &[!0; 65536])
}
}

View File

@@ -32,10 +32,12 @@
#![cfg_attr(feature = "simd", feature(platform_intrinsics, repr_simd))]
#![cfg_attr(feature = "simd_opt", feature(cfg_target_feature))]
extern crate constant_time_eq;
#[cfg(all(feature = "bench", test))]
extern crate test;
extern crate constant_time_eq;
pub use stream_decryptor::ChaCha20Poly1305StreamDecryptor;
pub use stream_encryptor::ChaCha20Poly1305StreamEncryptor;
mod as_bytes;
@@ -47,14 +49,66 @@ mod simd;
mod chacha20;
mod poly1305;
mod aead;
// mod aead;
mod stream_util;
mod stream_encryptor;
mod stream_decryptor;
pub use aead::{DecryptError, decrypt, encrypt, encrypt_read};
/// ChaCha20Policy Encrypt
pub fn chacha20_poly1305_encrypt(key: &[u8], nonce: &[u8], message: &[u8]) -> Result<Vec<u8>, String> {
chacha20_poly1305_aad_encrypt(key, nonce, &[], message)
}
/// Runs the self-test for ChaCha20, Poly1305, and the AEAD.
/// ChaCha20Policy Decrypt
pub fn chacha20_poly1305_decrypt(key: &[u8], nonce: &[u8], message: &[u8]) -> Result<Vec<u8>, String> {
chacha20_poly1305_aad_decrypt(key, nonce, &[], message)
}
/// ChaCha20Policy Encrypt with AAD
pub fn chacha20_poly1305_aad_encrypt(key: &[u8], nonce: &[u8], aad: &[u8], message: &[u8]) -> Result<Vec<u8>, String> {
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce)?;
if !aad.is_empty() { encryptor.init_adata(aad); }
let mut b1 = encryptor.update(message);
let (last_block, tag) = encryptor.finalize();
b1.extend_from_slice(&last_block);
b1.extend_from_slice(&tag);
Ok(b1)
}
/// ChaCha20Policy Decrypt with AAD
pub fn chacha20_poly1305_aad_decrypt(key: &[u8], nonce: &[u8], aad: &[u8], message: &[u8]) -> Result<Vec<u8>, String> {
let mut decryptor = ChaCha20Poly1305StreamDecryptor::new(&key, &nonce)?;
if !aad.is_empty() { decryptor.init_adata(aad); }
let mut b1 = decryptor.update(message);
let last_block = decryptor.finalize()?;
b1.extend_from_slice(&last_block);
Ok(b1)
}
/// Runs the self-test for ChaCha20, Poly1305
#[cold]
pub fn selftest() {
chacha20::selftest();
poly1305::selftest();
aead::selftest::selftest();
}
#[test]
fn test_enc_dec() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let aad = b"hello world";
let plaintext = [0u8; 1000];
let ciphertext = chacha20_poly1305_aad_encrypt(&key, &nonce, aad, &plaintext).unwrap();
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &aad[..], &mut plaintext, &mut output).unwrap();
output.extend_from_slice(&tag);
assert_eq!(ciphertext, output);
let plaintext_decrypted = chacha20_poly1305_aad_decrypt(&key, &nonce, aad, &ciphertext).unwrap();
assert_eq!(plaintext, plaintext_decrypted);
}

167
src/stream_decryptor.rs Normal file
View File

@@ -0,0 +1,167 @@
use crate::as_bytes::AsBytes;
use crate::chacha20::ChaCha20;
use crate::poly1305::Poly1305;
use crate::simd::u32x4;
use crate::stream_util;
/// ChaCha20Poly1203 Stream Decryptor
pub struct ChaCha20Poly1305StreamDecryptor {
chacha20: ChaCha20,
message_buffer: Vec<u8>,
poly1305: Poly1305,
adata_len: u64,
message_len: u64,
}
impl ChaCha20Poly1305StreamDecryptor {
/// New ChaCha20Poly1305StreamDecryptor
pub fn new(key: &[u8], nonce: &[u8]) -> Result<Self, String> {
if key.len() != 32 { return Err("Bad key length".to_string()); }
if nonce.len() != 12 { return Err("Bad nonce length".to_string()); }
let mut chacha20 = ChaCha20::new(key, nonce);
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
Ok(Self {
chacha20,
message_buffer: vec![],
poly1305,
adata_len: 0,
message_len: 0,
})
}
/// Initialize AAD
pub fn init_adata(&mut self, adata: &[u8]) {
if !adata.is_empty() {
self.adata_len += adata.len() as u64;
self.poly1305.padded_blocks(adata);
}
}
/// Update encrypted message
pub fn update(&mut self, message: &[u8]) -> Vec<u8> {
self.message_buffer.extend_from_slice(message);
let mut buf = [u32x4::default(); 4];
let b_len = buf.as_bytes().len();
let valid_message_len = if self.message_buffer.len() >= 16 { self.message_buffer.len() - 16 } else { 0 };
let b_count = valid_message_len / b_len;
if b_count == 0 {
return vec![];
}
let mut decrypted = Vec::with_capacity(b_len * b_count);
for i in 0..b_count {
let encrypted = &self.message_buffer[(b_len * i)..(b_len * (i + 1))];
self.poly1305.padded_blocks(encrypted);
buf.as_mut_bytes().copy_from_slice(encrypted);
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
decrypted.extend_from_slice(buf.as_bytes());
}
self.message_buffer = self.message_buffer[(b_len * b_count)..].to_vec();
self.message_len += decrypted.len() as u64;
decrypted
}
/// Finalize decrypt
pub fn finalize(mut self) -> Result<Vec<u8>, String> {
let mut last_block = vec![];
if self.message_buffer.len() < 16 {
return Err("Bad tag length".to_string());
}
let message_buffer_len = self.message_buffer.len() - 16;
if message_buffer_len > 0 {
let mut buf = [u32x4::default(); 4];
let buf_bytes = buf.as_mut_bytes();
let encrypted = &self.message_buffer[..message_buffer_len];
buf_bytes[..message_buffer_len].copy_from_slice(encrypted);
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
let last_block_bytes = &buf.as_bytes()[..message_buffer_len];
self.poly1305.padded_blocks(encrypted);
last_block.extend_from_slice(last_block_bytes);
self.message_len += last_block.len() as u64;
}
let message_tag = &self.message_buffer[message_buffer_len..];
self.poly1305.block([self.adata_len.to_le(), self.message_len.to_le()].as_bytes());
let mut tag = [0; 16];
tag.clone_from_slice(self.poly1305.tag().as_bytes());
if message_tag != tag {
Err(format!("Tag mismatch, expected: {}, actual: {}",
hex::encode(tag), hex::encode(message_tag)))
} else {
Ok(last_block)
}
}
}
#[test]
fn test_stream_001() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let plaintext = [0u8; 1000];
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &[], &mut plaintext, &mut output).unwrap();
let mut decryptor = ChaCha20Poly1305StreamDecryptor::new(&key, &nonce).unwrap();
let mut m1 = decryptor.update(&output);
let m2 = decryptor.update(&tag);
let m3 = decryptor.finalize().unwrap();
m1.extend_from_slice(&m2);
m1.extend_from_slice(&m3);
assert_eq!(&plaintext[..], &m1);
}
#[test]
fn test_stream_002() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let aad = b"hello world";
let plaintext = [0u8; 1000];
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &aad[..], &mut plaintext, &mut output).unwrap();
let mut decryptor = ChaCha20Poly1305StreamDecryptor::new(&key, &nonce).unwrap();
decryptor.init_adata(&aad[..]);
let mut m1 = decryptor.update(&output);
let m2 = decryptor.update(&tag);
let m3 = decryptor.finalize().unwrap();
m1.extend_from_slice(&m2);
m1.extend_from_slice(&m3);
assert_eq!(&plaintext[..], &m1);
}
#[test]
fn test_stream_003() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let aad = b"hello world";
let plaintext = [0u8; 1000];
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &aad[..], &mut plaintext, &mut output).unwrap();
let mut decryptor = ChaCha20Poly1305StreamDecryptor::new(&key, &nonce).unwrap();
decryptor.init_adata(&aad[..]);
let mut m1 = vec![];
output.iter().for_each(|c| {
m1.extend_from_slice(&decryptor.update(&[*c]));
});
tag.iter().for_each(|c| {
m1.extend_from_slice(&decryptor.update(&[*c]));
});
let m2 = decryptor.finalize().unwrap();
m1.extend_from_slice(&m2);
assert_eq!(&plaintext[..], &m1);
}

148
src/stream_encryptor.rs Normal file
View File

@@ -0,0 +1,148 @@
use crate::as_bytes::AsBytes;
use crate::chacha20::ChaCha20;
use crate::poly1305::Poly1305;
use crate::simd::u32x4;
use crate::stream_util;
/// ChaCha20Poly1203 Stream Encryptor
pub struct ChaCha20Poly1305StreamEncryptor {
chacha20: ChaCha20,
message_buffer: Vec<u8>,
poly1305: Poly1305,
adata_len: u64,
message_len: u64,
}
impl ChaCha20Poly1305StreamEncryptor {
/// New ChaCha20Poly1305StreamEncryptor
pub fn new(key: &[u8], nonce: &[u8]) -> Result<Self, String> {
if key.len() != 32 { return Err("Bad key length".to_string()); }
if nonce.len() != 12 { return Err("Bad nonce length".to_string()); }
let mut chacha20 = ChaCha20::new(key, nonce);
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
Ok(Self {
chacha20,
message_buffer: vec![],
poly1305,
adata_len: 0,
message_len: 0,
})
}
/// Initialize AAD
pub fn init_adata(&mut self, adata: &[u8]) {
if !adata.is_empty() {
self.adata_len += adata.len() as u64;
self.poly1305.padded_blocks(adata);
}
}
/// Update plaintext message
pub fn update(&mut self, message: &[u8]) -> Vec<u8> {
self.message_buffer.extend_from_slice(message);
let mut buf = [u32x4::default(); 4];
let b_len = buf.as_bytes().len();
let b_count = self.message_buffer.len() / b_len;
if b_count == 0 {
return vec![];
}
let mut encrypted = Vec::with_capacity(b_len * b_count);
for i in 0..b_count {
buf.as_mut_bytes()
.copy_from_slice(&self.message_buffer[(b_len * i)..(b_len * (i + 1))]);
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
self.poly1305.padded_blocks(buf.as_bytes());
encrypted.extend_from_slice(buf.as_bytes());
}
self.message_buffer = self.message_buffer[(b_len * b_count)..].to_vec();
self.message_len += encrypted.len() as u64;
encrypted
}
/// Finalize encrypt
pub fn finalize(mut self) -> (Vec<u8>, Vec<u8>) {
let mut last_block = vec![];
if !self.message_buffer.is_empty() {
let mut buf = [u32x4::default(); 4];
let buf_bytes = buf.as_mut_bytes();
buf_bytes[..self.message_buffer.len()].copy_from_slice(&self.message_buffer[..]);
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
let last_block_bytes = &buf.as_bytes()[0..self.message_buffer.len()];
self.poly1305.padded_blocks(last_block_bytes);
last_block.extend_from_slice(last_block_bytes);
self.message_len += last_block.len() as u64;
}
self.poly1305.block([self.adata_len.to_le(), self.message_len.to_le()].as_bytes());
let mut tag = [0; 16];
tag.clone_from_slice(self.poly1305.tag().as_bytes());
(last_block, tag.to_vec())
}
}
#[test]
fn test_stream_001() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let plaintext = [0u8; 1000];
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce).unwrap();
let mut b1 = encryptor.update(&plaintext);
let (b2, t) = encryptor.finalize();
b1.extend_from_slice(&b2);
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &[], &mut plaintext, &mut output).unwrap();
assert_eq!(b1, output);
assert_eq!(t, tag.to_vec());
}
#[test]
fn test_stream_002() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let aad = b"hello world";
let plaintext = [0u8; 1000];
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce).unwrap();
encryptor.init_adata(&aad[..]);
let mut b1 = encryptor.update(&plaintext);
let (b2, t) = encryptor.finalize();
b1.extend_from_slice(&b2);
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &aad[..], &mut plaintext, &mut output).unwrap();
assert_eq!(b1, output);
assert_eq!(t, tag.to_vec());
}
#[test]
fn test_stream_003() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let aad = b"hello world";
let plaintext = [0u8; 1000];
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce).unwrap();
encryptor.init_adata(&aad[..]);
let mut b1 = vec![];
for _ in 0..1000 {
b1.extend_from_slice(&encryptor.update(&[0u8]));
}
let (b2, t) = encryptor.finalize();
b1.extend_from_slice(&b2);
let mut output = vec![];
let mut plaintext = plaintext.to_vec();
let tag = chacha20_poly1305_aead::encrypt(
&key, &nonce, &aad[..], &mut plaintext, &mut output).unwrap();
assert_eq!(b1, output);
assert_eq!(t, tag.to_vec());
}

10
src/stream_util.rs Normal file
View File

@@ -0,0 +1,10 @@
use crate::chacha20::ChaCha20;
use crate::simdty::Simd4;
pub(crate) fn next_chacha20_xor(chacha20: &mut ChaCha20, buf: &mut [Simd4<u32>; 4]) {
let block = chacha20.next();
buf[0] = buf[0] ^ block[0];
buf[1] = buf[1] ^ block[1];
buf[2] = buf[2] ^ block[2];
buf[3] = buf[3] ^ block[3];
}