feat: works
This commit is contained in:
@@ -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
38
examples/bench.rs
Normal 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 _));
|
||||
}
|
||||
476
src/aead.rs
476
src/aead.rs
@@ -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])
|
||||
}
|
||||
}
|
||||
64
src/lib.rs
64
src/lib.rs
@@ -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
167
src/stream_decryptor.rs
Normal 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
148
src/stream_encryptor.rs
Normal 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
10
src/stream_util.rs
Normal 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];
|
||||
}
|
||||
Reference in New Issue
Block a user