feat: works
This commit is contained in:
38
README.md
38
README.md
@@ -1,3 +1,41 @@
|
|||||||
# chacha20-poly1305-stream
|
# chacha20-poly1305-stream
|
||||||
|
|
||||||
ChaCha20 Poly1305 stream encrypt and decrypt library
|
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::mem;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
#[allow(clippy::missing_safety_doc)]
|
||||||
pub unsafe trait Safe {}
|
pub unsafe trait Safe {}
|
||||||
|
|
||||||
pub trait AsBytes {
|
pub trait AsBytes {
|
||||||
@@ -19,6 +20,7 @@ impl<T: Safe> AsBytes for [T] {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn as_bytes(&self) -> &[u8] {
|
fn as_bytes(&self) -> &[u8] {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
#[allow(clippy::manual_slice_size_calculation)]
|
||||||
slice::from_raw_parts(self.as_ptr() as *const u8,
|
slice::from_raw_parts(self.as_ptr() as *const u8,
|
||||||
self.len() * mem::size_of::<T>())
|
self.len() * mem::size_of::<T>())
|
||||||
}
|
}
|
||||||
@@ -27,6 +29,7 @@ impl<T: Safe> AsBytes for [T] {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn as_mut_bytes(&mut self) -> &mut [u8] {
|
fn as_mut_bytes(&mut self) -> &mut [u8] {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
#[allow(clippy::manual_slice_size_calculation)]
|
||||||
slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8,
|
slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8,
|
||||||
self.len() * mem::size_of::<T>())
|
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-aead Developers
|
||||||
|
// Copyright 2016 chacha20-poly1305-stream Developers
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
// 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://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();
|
let plaintext_decrypted = chacha20_poly1305_aad_decrypt(&key, &nonce, aad, &ciphertext).unwrap();
|
||||||
assert_eq!(plaintext, plaintext_decrypted);
|
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 use crate::simdty::u32x4;
|
||||||
|
|
||||||
pub trait Vector4<T>: Copy {
|
pub trait Vector4<T>: Copy {
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
fn from_le(self) -> Self;
|
fn from_le(self) -> Self;
|
||||||
fn to_le(self) -> Self;
|
fn to_le(self) -> Self;
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ pub struct ChaCha20Poly1305StreamDecryptor {
|
|||||||
impl ChaCha20Poly1305StreamDecryptor {
|
impl ChaCha20Poly1305StreamDecryptor {
|
||||||
/// New ChaCha20Poly1305StreamDecryptor
|
/// New ChaCha20Poly1305StreamDecryptor
|
||||||
pub fn new(key: &[u8], nonce: &[u8]) -> Result<Self, String> {
|
pub fn new(key: &[u8], nonce: &[u8]) -> Result<Self, String> {
|
||||||
if key.len() != 32 { return Err("Bad key length".to_string()); }
|
stream_util::verify_key_nonce_length(key, nonce)?;
|
||||||
if nonce.len() != 12 { return Err("Bad nonce length".to_string()); }
|
|
||||||
let mut chacha20 = ChaCha20::new(key, nonce);
|
let mut chacha20 = ChaCha20::new(key, nonce);
|
||||||
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
|
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -43,17 +43,17 @@ impl ChaCha20Poly1305StreamDecryptor {
|
|||||||
let mut buf = [u32x4::default(); 4];
|
let mut buf = [u32x4::default(); 4];
|
||||||
let b_len = buf.as_bytes().len();
|
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 payload_len = if self.message_buffer.len() >= 16 { self.message_buffer.len() - 16 } else { 0 };
|
||||||
let b_count = valid_message_len / b_len;
|
let b_count = payload_len / b_len;
|
||||||
if b_count == 0 {
|
if b_count == 0 {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decrypted = Vec::with_capacity(b_len * b_count);
|
let mut decrypted = Vec::with_capacity(b_len * b_count);
|
||||||
for i in 0..b_count {
|
for i in 0..b_count {
|
||||||
let encrypted = &self.message_buffer[(b_len * i)..(b_len * (i + 1))];
|
let b_encrypted = &self.message_buffer[(b_len * i)..(b_len * (i + 1))];
|
||||||
self.poly1305.padded_blocks(encrypted);
|
self.poly1305.padded_blocks(b_encrypted);
|
||||||
buf.as_mut_bytes().copy_from_slice(encrypted);
|
buf.as_mut_bytes().copy_from_slice(b_encrypted);
|
||||||
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
|
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
|
||||||
decrypted.extend_from_slice(buf.as_bytes());
|
decrypted.extend_from_slice(buf.as_bytes());
|
||||||
}
|
}
|
||||||
@@ -64,33 +64,33 @@ impl ChaCha20Poly1305StreamDecryptor {
|
|||||||
|
|
||||||
/// Finalize decrypt
|
/// Finalize decrypt
|
||||||
pub fn finalize(mut self) -> Result<Vec<u8>, String> {
|
pub fn finalize(mut self) -> Result<Vec<u8>, String> {
|
||||||
let mut last_block = vec![];
|
|
||||||
if self.message_buffer.len() < 16 {
|
if self.message_buffer.len() < 16 {
|
||||||
return Err("Bad tag length".to_string());
|
return Err("Bad tag length".to_string());
|
||||||
}
|
}
|
||||||
let message_buffer_len = self.message_buffer.len() - 16;
|
let mut last_decrypted_block = vec![];
|
||||||
if message_buffer_len > 0 {
|
let last_payload_len = self.message_buffer.len() - 16;
|
||||||
|
if last_payload_len > 0 {
|
||||||
let mut buf = [u32x4::default(); 4];
|
let mut buf = [u32x4::default(); 4];
|
||||||
let buf_bytes = buf.as_mut_bytes();
|
let buf_bytes = buf.as_mut_bytes();
|
||||||
let encrypted = &self.message_buffer[..message_buffer_len];
|
let last_b_encrypted = &self.message_buffer[..last_payload_len];
|
||||||
buf_bytes[..message_buffer_len].copy_from_slice(encrypted);
|
buf_bytes[..last_payload_len].copy_from_slice(last_b_encrypted);
|
||||||
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
|
stream_util::next_chacha20_xor(&mut self.chacha20, &mut buf);
|
||||||
let last_block_bytes = &buf.as_bytes()[..message_buffer_len];
|
let last_b_decrypted = &buf.as_bytes()[..last_payload_len];
|
||||||
self.poly1305.padded_blocks(encrypted);
|
self.poly1305.padded_blocks(last_b_encrypted);
|
||||||
last_block.extend_from_slice(last_block_bytes);
|
last_decrypted_block.extend_from_slice(last_b_decrypted);
|
||||||
self.message_len += last_block.len() as u64;
|
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());
|
self.poly1305.block([self.adata_len.to_le(), self.message_len.to_le()].as_bytes());
|
||||||
|
let poly1305_tag = self.poly1305.tag();
|
||||||
let mut tag = [0; 16];
|
let tag = poly1305_tag.as_bytes();
|
||||||
tag.clone_from_slice(self.poly1305.tag().as_bytes());
|
|
||||||
if message_tag != tag {
|
if message_tag != tag {
|
||||||
Err(format!("Tag mismatch, expected: {}, actual: {}",
|
Err(format!("Tag mismatch, expected: {}, actual: {}",
|
||||||
hex::encode(tag), hex::encode(message_tag)))
|
hex::encode(tag), hex::encode(message_tag)))
|
||||||
} else {
|
} else {
|
||||||
Ok(last_block)
|
Ok(last_decrypted_block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ pub struct ChaCha20Poly1305StreamEncryptor {
|
|||||||
impl ChaCha20Poly1305StreamEncryptor {
|
impl ChaCha20Poly1305StreamEncryptor {
|
||||||
/// New ChaCha20Poly1305StreamEncryptor
|
/// New ChaCha20Poly1305StreamEncryptor
|
||||||
pub fn new(key: &[u8], nonce: &[u8]) -> Result<Self, String> {
|
pub fn new(key: &[u8], nonce: &[u8]) -> Result<Self, String> {
|
||||||
if key.len() != 32 { return Err("Bad key length".to_string()); }
|
stream_util::verify_key_nonce_length(key, nonce)?;
|
||||||
if nonce.len() != 12 { return Err("Bad nonce length".to_string()); }
|
|
||||||
let mut chacha20 = ChaCha20::new(key, nonce);
|
let mut chacha20 = ChaCha20::new(key, nonce);
|
||||||
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
|
let poly1305 = Poly1305::new(&chacha20.next().as_bytes()[..32]);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -76,10 +76,9 @@ impl ChaCha20Poly1305StreamEncryptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.poly1305.block([self.adata_len.to_le(), self.message_len.to_le()].as_bytes());
|
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];
|
(last_block, tag)
|
||||||
tag.clone_from_slice(self.poly1305.tag().as_bytes());
|
|
||||||
(last_block, tag.to_vec())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
use crate::chacha20::ChaCha20;
|
use crate::chacha20::ChaCha20;
|
||||||
use crate::simdty::Simd4;
|
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]) {
|
pub(crate) fn next_chacha20_xor(chacha20: &mut ChaCha20, buf: &mut [Simd4<u32>; 4]) {
|
||||||
let block = chacha20.next();
|
let block = chacha20.next();
|
||||||
buf[0] = buf[0] ^ block[0];
|
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[2] = buf[2] ^ block[2];
|
||||||
buf[3] = buf[3] ^ block[3];
|
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