feat: works
This commit is contained in:
38
README.md
38
README.md
@@ -1,3 +1,41 @@
|
||||
# chacha20-poly1305-stream
|
||||
|
||||
ChaCha20 Poly1305 stream encrypt and decrypt library
|
||||
|
||||
## Encrypt
|
||||
|
||||
```rust
|
||||
// IMPORTANT! key and nonce SHOULD generate by random
|
||||
let key = [0u8; 32];
|
||||
let nonce = [0; 12];
|
||||
|
||||
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce).unwrap();
|
||||
|
||||
let mut ciphertext = vec![];
|
||||
ciphertext.extend_from_slice(&encryptor.update(b"Hello "));
|
||||
ciphertext.extend_from_slice(&encryptor.update(b" World"));
|
||||
ciphertext.extend_from_slice(&encryptor.update(b"!"));
|
||||
let (last_block, tag) = encryptor.finalize();
|
||||
ciphertext.extend_from_slice(&last_block);
|
||||
ciphertext.extend_from_slice(&tag);
|
||||
|
||||
println!("Ciphertext: {}", hex::encode(&ciphertext));
|
||||
```
|
||||
|
||||
## Run Example
|
||||
|
||||
```shell
|
||||
$ cargo run --example encrypt_and_decrypt
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
|
||||
Running `target/debug/examples/encrypt_and_decrypt`
|
||||
Ciphertext: d7628bd23a71182df7c8fb1852d3dc42b88a61e2fce340d9c5b323884d
|
||||
Plaintext : Hello World!
|
||||
```
|
||||
|
||||
Benchmark @MacBook Pro (Retina, 15-inch, Late 2013/2 GHz Quad-Core Intel Core i7)
|
||||
|
||||
```shell
|
||||
$ cargo r --release --example bench
|
||||
ChaCha20Poly1305 encrypt : 287.06 M/s
|
||||
ChaCha20Poly1305 encrypt/decrypt : 144.93 M/s
|
||||
```
|
||||
|
||||
39
examples/encrypt_and_decrypt.rs
Normal file
39
examples/encrypt_and_decrypt.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use chacha20_poly1305_stream::{ChaCha20Poly1305StreamDecryptor, ChaCha20Poly1305StreamEncryptor};
|
||||
|
||||
fn main() {
|
||||
encrypt();
|
||||
decrypt();
|
||||
}
|
||||
|
||||
fn encrypt() {
|
||||
// IMPORTANT! key and nonce SHOULD generate by random
|
||||
let key = [0u8; 32];
|
||||
let nonce = [0; 12];
|
||||
|
||||
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce).unwrap();
|
||||
|
||||
let mut ciphertext = vec![];
|
||||
ciphertext.extend_from_slice(&encryptor.update(b"Hello "));
|
||||
ciphertext.extend_from_slice(&encryptor.update(b" World"));
|
||||
ciphertext.extend_from_slice(&encryptor.update(b"!"));
|
||||
let (last_block, tag) = encryptor.finalize();
|
||||
ciphertext.extend_from_slice(&last_block);
|
||||
ciphertext.extend_from_slice(&tag);
|
||||
|
||||
println!("Ciphertext: {}", hex::encode(&ciphertext));
|
||||
}
|
||||
|
||||
fn decrypt() {
|
||||
// IMPORTANT! key and nonce SHOULD generate by random
|
||||
let key = [0u8; 32];
|
||||
let nonce = [0; 12];
|
||||
let cipher_text = hex::decode("d7628bd23a71182df7c8fb1852d3dc42b88a61e2fce340d9c5b323884d").unwrap();
|
||||
|
||||
let mut decryptor = ChaCha20Poly1305StreamDecryptor::new(&key, &nonce).unwrap();
|
||||
|
||||
let mut plaintext = decryptor.update(&cipher_text);
|
||||
let last_block = decryptor.finalize().unwrap();
|
||||
plaintext.extend_from_slice(&last_block);
|
||||
|
||||
println!("Plaintext : {}", String::from_utf8_lossy(&plaintext));
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe trait Safe {}
|
||||
|
||||
pub trait AsBytes {
|
||||
@@ -19,6 +20,7 @@ impl<T: Safe> AsBytes for [T] {
|
||||
#[inline]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
#[allow(clippy::manual_slice_size_calculation)]
|
||||
slice::from_raw_parts(self.as_ptr() as *const u8,
|
||||
self.len() * mem::size_of::<T>())
|
||||
}
|
||||
@@ -27,6 +29,7 @@ impl<T: Safe> AsBytes for [T] {
|
||||
#[inline]
|
||||
fn as_mut_bytes(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
#[allow(clippy::manual_slice_size_calculation)]
|
||||
slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8,
|
||||
self.len() * mem::size_of::<T>())
|
||||
}
|
||||
|
||||
28
src/lib.rs
28
src/lib.rs
@@ -1,4 +1,5 @@
|
||||
// Copyright 2016 chacha20-poly1305-aead Developers
|
||||
// Copyright 2016 chacha20-poly1305-stream 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
|
||||
@@ -112,3 +113,30 @@ fn test_enc_dec() {
|
||||
let plaintext_decrypted = chacha20_poly1305_aad_decrypt(&key, &nonce, aad, &ciphertext).unwrap();
|
||||
assert_eq!(plaintext, plaintext_decrypted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rfc_7539() {
|
||||
let key = hex::decode(
|
||||
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f").unwrap();
|
||||
let nonce = hex::decode("070000004041424344454647").unwrap();
|
||||
let aad = hex::decode("50515253c0c1c2c3c4c5c6c7").unwrap();
|
||||
let plaintext = hex::decode(
|
||||
"4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f6620\
|
||||
2739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666\
|
||||
f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e").unwrap();
|
||||
|
||||
let expected_ciphertext = hex::decode(
|
||||
"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca\
|
||||
9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091\
|
||||
b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116").unwrap();
|
||||
let expected_tag = hex::decode("1ae10b594f09e26a7e902ecbd0600691").unwrap();
|
||||
|
||||
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key, &nonce).unwrap();
|
||||
encryptor.init_adata(&aad);
|
||||
let mut ciphertext = encryptor.update(&plaintext);
|
||||
let (last_block, tag) = encryptor.finalize();
|
||||
ciphertext.extend_from_slice(&last_block);
|
||||
|
||||
assert_eq!(expected_ciphertext, ciphertext);
|
||||
assert_eq!(expected_tag, tag);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use crate::simd_opt;
|
||||
pub use crate::simdty::u32x4;
|
||||
|
||||
pub trait Vector4<T>: Copy {
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_le(self) -> Self;
|
||||
fn to_le(self) -> Self;
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ pub struct ChaCha20Poly1305StreamDecryptor {
|
||||
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()); }
|
||||
stream_util::verify_key_nonce_length(key, nonce)?;
|
||||
|
||||
let mut chacha20 = ChaCha20::new(key, nonce);
|
||||
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
|
||||
Ok(Self {
|
||||
@@ -43,17 +43,17 @@ impl ChaCha20Poly1305StreamDecryptor {
|
||||
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;
|
||||
let payload_len = if self.message_buffer.len() >= 16 { self.message_buffer.len() - 16 } else { 0 };
|
||||
let b_count = payload_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);
|
||||
let b_encrypted = &self.message_buffer[(b_len * i)..(b_len * (i + 1))];
|
||||
self.poly1305.padded_blocks(b_encrypted);
|
||||
buf.as_mut_bytes().copy_from_slice(b_encrypted);
|
||||
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
|
||||
decrypted.extend_from_slice(buf.as_bytes());
|
||||
}
|
||||
@@ -64,33 +64,33 @@ impl ChaCha20Poly1305StreamDecryptor {
|
||||
|
||||
/// 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 last_decrypted_block = vec![];
|
||||
let last_payload_len = self.message_buffer.len() - 16;
|
||||
if last_payload_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);
|
||||
let last_b_encrypted = &self.message_buffer[..last_payload_len];
|
||||
buf_bytes[..last_payload_len].copy_from_slice(last_b_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 last_b_decrypted = &buf.as_bytes()[..last_payload_len];
|
||||
self.poly1305.padded_blocks(last_b_encrypted);
|
||||
last_decrypted_block.extend_from_slice(last_b_decrypted);
|
||||
self.message_len += last_decrypted_block.len() as u64;
|
||||
}
|
||||
let message_tag = &self.message_buffer[message_buffer_len..];
|
||||
|
||||
let message_tag = &self.message_buffer[last_payload_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());
|
||||
let poly1305_tag = self.poly1305.tag();
|
||||
let tag = 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)
|
||||
Ok(last_decrypted_block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ pub struct ChaCha20Poly1305StreamEncryptor {
|
||||
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()); }
|
||||
stream_util::verify_key_nonce_length(key, nonce)?;
|
||||
|
||||
let mut chacha20 = ChaCha20::new(key, nonce);
|
||||
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
|
||||
Ok(Self {
|
||||
@@ -76,10 +76,9 @@ impl ChaCha20Poly1305StreamEncryptor {
|
||||
}
|
||||
|
||||
self.poly1305.block([self.adata_len.to_le(), self.message_len.to_le()].as_bytes());
|
||||
let tag = self.poly1305.tag().as_bytes().to_vec();
|
||||
|
||||
let mut tag = [0; 16];
|
||||
tag.clone_from_slice(self.poly1305.tag().as_bytes());
|
||||
(last_block, tag.to_vec())
|
||||
(last_block, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::chacha20::ChaCha20;
|
||||
use crate::simdty::Simd4;
|
||||
|
||||
const CHACHA20_POLY1305_KEY_LEN: usize = 32;
|
||||
const CHACHA20_POLY1305_NONCE_LEN: usize = 12;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_chacha20_xor(chacha20: &mut ChaCha20, buf: &mut [Simd4<u32>; 4]) {
|
||||
let block = chacha20.next();
|
||||
buf[0] = buf[0] ^ block[0];
|
||||
@@ -8,3 +12,13 @@ pub(crate) fn next_chacha20_xor(chacha20: &mut ChaCha20, buf: &mut [Simd4<u32>;
|
||||
buf[2] = buf[2] ^ block[2];
|
||||
buf[3] = buf[3] ^ block[3];
|
||||
}
|
||||
|
||||
pub(crate) fn verify_key_nonce_length(key: &[u8], nonce: &[u8]) -> Result<(), String> {
|
||||
if key.len() != CHACHA20_POLY1305_KEY_LEN {
|
||||
return Err("Bad key length".to_string());
|
||||
}
|
||||
if nonce.len() != CHACHA20_POLY1305_NONCE_LEN {
|
||||
return Err("Bad nonce length".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user