better write efficiency

update README

update help message

remove planned formatting support in TODO in favour of outside tools
transfer
Avril 3 years ago
parent fbba88a4c9
commit 3e93f71547
Signed by: flanchan
GPG Key ID: 284488987C31F630

2
Cargo.lock generated

@ -34,7 +34,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chacha20"
version = "1.0.0"
version = "1.0.1"
dependencies = [
"base64",
"getrandom",

@ -1,18 +1,13 @@
[package]
name = "chacha20"
description = "chacha20_poly1305 encryption tool"
version = "1.0.0"
version = "1.0.1"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
license = "gpl-3.0-or-later"
[dependencies]
base64 = "0.13"
getrandom = "0.2"
openssl = "0.10"
smallvec = {version = "1.6", features=["write", "union"]}
[dev-dependencies]
#khash = {version = "2.0.4", default-features=false}
smallvec = {version = "1.6", features=["write", "union"]}

@ -2,7 +2,7 @@
A simple chacha20_poly1305 CLI encryption tool
## Building
Requires Rust and Cargo to build.
Requires Rust and Cargo to build; also requires OpenSSL v1.1.0 or higher.
Run `cargo build --release`, the binary will be built to `./target/release/chacha20`.
### Testing
@ -10,22 +10,37 @@ Run `cargo test && cargo build && ./test.sh debug` to test the program.
Alternatively, run `./test.sh` after building to test the release build's correctness.
# Usage
Copies stdin to stdout while encrypting or decrypting with the stream cipher.
Copies stdin to stdout while encrypting or decrypting with the stream cipher `chacha20_poly1305`.
## Modes
* Encrypt - Encrypt stdin to stdout
* Decrypt - Decrypt stdin to stdout
* Keygen - Generate a random key and IV and print them to stdout
To see a more detailed explenation run `chacha20` with no arguments.
To see a more detailed explenation run `chacha20 help`.
## Formats
The key and IV is expected/generated in base64 format.
The key and IV sizes respectively are 32 and 12 bytes.
The ciphertext input and output is raw binary data. You can encode this to text formats if you want with whatever tool you choose (Example with `base64` below.)
## Example
Encrypting and decrypting a string to binary with randomly generated keys
```shell
$ echo "Hello world!" | chacha20 e 2>keys.cck > output.cc20
$ chacha20 d $(cat keys.cck) < output.cc20
Hello world!
```
The same but with text instead of binary ciphertexts
``` shell
$ echo "Hello world!" | chacha20 e 2>keys.cck | base64 > output.cc20.b64
$ base64 --decode output.cc20.b64 | chacha20 d $(cat keys.cck)
Hello world!
```
# License
GPL'd with <3

@ -1 +0,0 @@
Add `b-encrypt` mode to write the outputted bytes in base64 instead of raw bytes, and `b-decrypt` mode to read the inputted bytes in base64 instead of raw bytes

@ -1,7 +1,10 @@
//#![feature(test)]
#![allow(dead_code)]
mod ext; #[macro_use] use ext::*;
//extern crate test;
#[macro_use] mod ext; #[allow(unused_imports)] use ext::*;
mod key;
mod cha;
@ -30,7 +33,7 @@ fn keys() -> Result<(Mode, Key, IV), base64::DecodeError>
let (key, iv) = cha::keygen();
return Ok((Mode::Keygen, key, iv));
},
_ => {
other => {
eprintln!("{} (v{}) - chacha20_poly1305 command line encryption tool",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"));
@ -40,12 +43,15 @@ fn keys() -> Result<(Mode, Key, IV), base64::DecodeError>
eprintln!("Usage: {} encrypt [<base64 key>] [<base64 iv>]", prog_name);
eprintln!("Usage: {} decrypt [<base64 key>] [<base64 iv>]", prog_name);
eprintln!("Usage: {} keygen", prog_name);
eprintln!("Usage: {} help", prog_name);
eprintln!();
eprintln!("(Key size is {}, IV size is {})", cha::KEY_SIZE, cha::IV_SIZE);
eprintln!("\nencrypt/decrypt:\n\tIf key and/or IV are not provided, they are generated randomly and printed to stderr in order on one line each");
eprintln!("\tIf the key and/or IV provided's size is lower than the cipher's key/IV size, the rest of the key/IV padded with 0s. If the size is higher, the extra bytes are ignored.");
eprintln!("\nkeygen:\n\tThe key/iv is printed in the same way as auto-generated keys for the en/decryption modes, but to stdout instead of stderr");
std::process::exit(1)
eprintln!("(requires OpenSSL 1.1.0 or newer)");
eprintln!("\nencrypt/decrypt:\n\tIf a key and/or IV are not provided, they are generated randomly and printed to stderr in order on one line each.");
eprintln!("\tIf the key and/or IV provided's size is lower than the cipher's key/IV size, the rest of the key/IV is padded with 0s. If the size is higher, the extra bytes are ignored.");
eprintln!("\nkeygen:\n\tThe key/iv is printed in the same way as auto-generated keys for the en/decryption modes, but to stdout instead of stderr.");
eprintln!("\nhelp:\n\tPrint this message to stderr then exit with code 0");
std::process::exit(if other == Some('h') {0} else {1})
}
};

@ -15,6 +15,10 @@ pub const BUFFER_SIZE: usize = 32;
pub type Error = ErrorStack;
/// ChaCha Sink
///
/// # Note
/// When writing, a temporary buffer stored in the structure is used. This buffer is **not** cleared after a write, for efficiency reasons. This may leave sensitive information in the buffer after the write operation.
/// The `flush()` implementation *does* clear this buffer.
//#[derive(Debug)]
pub struct Sink<W>
{
@ -36,7 +40,7 @@ impl<W> Sink<W>
where W: Write
{
/// Create a new Chacha Sink stream wrapper
pub fn new(stream: W, crypter: Crypter) -> Self
#[inline] fn new(stream: W, crypter: Crypter) -> Self
{
Self{stream, crypter, buffer: SmallVec::new()}
}
@ -55,69 +59,72 @@ where W: Write
/// Consume into the inner stream
pub fn into_inner(self) -> W
#[inline] pub fn into_inner(self) -> W
{
self.stream
}
/// Consume into the inner stream and crypter
pub fn into_parts(self) -> (W, Crypter)
#[inline] pub fn into_parts(self) -> (W, Crypter)
{
(self.stream, self.crypter)
}
/// The crypter of this instance
pub fn crypter(&self) -> &Crypter
#[inline] pub fn crypter(&self) -> &Crypter
{
&self.crypter
}
/// The crypter of this instance
pub fn crypter_mut(&mut self) -> &mut Crypter
#[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
{
&mut self.crypter
}
/// The inner stream
pub fn inner(&self) -> &W
#[inline] pub fn inner(&self) -> &W
{
&self.stream
}
/// The inner stream
pub fn inner_mut(&mut self) -> &mut W
#[inline] pub fn inner_mut(&mut self) -> &mut W
{
&mut self.stream
}
/// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated.
fn transform(&mut self, buf: &[u8]) -> Result<usize, ErrorStack>
{
if buf.len() > self.buffer.len() {
self.buffer.resize(buf.len(), 0);
}
let n = self.crypter.update(&buf[..], &mut self.buffer[..])?;
let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed.
debug_assert_eq!(_f, 0);
Ok(n)
}
}
impl<W: Write> Write for Sink<W>
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
prog1!{
{
self.buffer.write_all(buf).unwrap();
let n = self.crypter.update(&buf[..], &mut self.buffer[..])?;
self.crypter.finalize(&mut self.buffer[..n])?; // I don't think this is needed
self.stream.write(&self.buffer[..n])
},
self.buffer.clear();
}
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let n = self.transform(buf)?;
self.stream.write(&self.buffer[..n])
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
prog1!{
{
self.buffer.write_all(buf).unwrap();
let n = self.crypter.update(&buf[..], &mut self.buffer[..])?;
self.crypter.finalize(&mut self.buffer[..n])?;
#[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
let n = self.transform(buf)?;
self.stream.write_all(&self.buffer[..n])
},
self.buffer.clear();
}
self.stream.write_all(&self.buffer[..n])
}
#[inline] fn flush(&mut self) -> io::Result<()> {
self.buffer.clear();
self.stream.flush()
}
}
@ -175,3 +182,4 @@ mod tests
}
}

@ -17,7 +17,7 @@ else
echo ""
fi
INPUT_SIZE=4096
INPUT_SIZE=${INPUT_SIZE:-4096}
echo ">>> Generating random input ($INPUT_SIZE bytes)"
dd if=/dev/urandom "bs=$INPUT_SIZE" count=1 | base64 > test.txt

Loading…
Cancel
Save