Skip to content

implement close-semantic for EncWriter / DecWriter #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 31 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,20 @@
//!
//! There is also an optional fourth parameter (buffer/fragment size) which we will cover later.
//!
//! Now, there are two important rules you have to ensure are never violated by your code:
//! 1. **Correctness:**
//! The parameters (`key'`, `nonce'` `aad'`) for decryption must match exactly the parameters
//! (`key`, `nonce` `aad`) used when encrypting the data.
//! 2. **Freshness:**
//! When encrypting a data stream, you must use a new `key` **or** a new `nonce` value that
//! has **never** been used before.
//!
//! The 1st rule is pretty obvious. If you, for example, use different keys for encryption and
//! decryption then the decryption will fail. The same applies to the nonce and associated data.
//!
//! The 2nd rule may be less obvious but it is at least as important as the 1st one since security
//! crucially depends on it. In general, the authenticated encryption algorithm (used by the
//! channel construction) assumes that, given the same key, the nonce value does never repeat.
//! Violating this assumption breaks the security properties and potentially allows decrypting
//! or forging data without knowing the secret key. But don't worry, there are best practices for
//! dealing with keys and nonce values which can help here.
//! Now, there is one important rule that must never be violated by your code since security
//! crucially depends on it:
//! <p style="margin-left: 40px; margin-right: 50px; border:1px; border-style:solid; border-color:#000000; padding: 0.3em">
//! When encrypting a data stream you must use a new <code>key</code> <b>or</b> a new
//! <code>nonce</code> value such that this particular <code>key</code>-<code>nonce</code>
//! combination has <b>never</b> been used before.
//! </p>
//!
//! In general, the authenticated encryption algorithm (used by the channel construction) assumes
//! that, given the same key, the nonce value does never repeat. Violating this assumption breaks
//! the security properties and potentially allows decrypting or forging data without knowing the
//! secret key. Therefore, you have to make sure that you use a key-nonce combination only once.
//! But don't worry, there are best practices for dealing with keys and nonce values which can help
//! here.
//!
//! Next, we will take a look at some examples for encryption and decryption.
//!
Expand All @@ -76,14 +73,14 @@
//! use std::io;
//! use std::io::Write;
//! use std::fs::File;
//! use sio::{EncWriter, Key, Nonce, Aad, AES_256_GCM, Close};
//! use sio::{EncWriter, Key, Nonce, Aad, AES_256_GCM, NopCloser};
//!
//! fn main() -> io::Result<()> {
//! // Obviously, do NOT use this demo key for anything real!
//! let secret_key: Key::<AES_256_GCM> = Key::new([0; Key::<AES_256_GCM>::SIZE]);
//!
//! let mut f = EncWriter::new(
//! File::create("foo.txt")?,
//! NopCloser::wrap(File::create("foo.txt")?),
//! &secret_key,
//! Nonce::new([0; Nonce::<AES_256_GCM>::SIZE]),
//! Aad::empty(),
Expand All @@ -96,26 +93,26 @@
//! Here, we try to create and wrap the file `foo.txt` and encrypt the string
//! `"Hello World"` using the [`AES_256_GCM`](https://en.wikipedia.org/wiki/Galois/Counter_Mode)
//! algorithm before writing it to the file. Note that we call a `close` method
//! after writing. This is *very important* and you should take a look at the
//! `Close` trait for a detailed explanation.
//! after writing. This is very important and you should take a look at the
//! `Close` trait for a detailed explanation about why this call is necessary.
//!
//! # Decryption
//!
//! Similarly, you can decrypt data by wrapping a writer with a `DecWriter`. The `DecWriter`
//! is also generic over an authenticated encryption algorithm and expects the same
//! `Key`, `Nonce` and `Aad` used to encrypt the data.
//! Similarly, you can decrypt data by using a `DecWriter` instead of an `EncWriter`. The
//! `DecWriter` is also generic over an authenticated encryption algorithm and expects the
//! same `Key`, `Nonce` and `Aad` used before to encrypt the data.
//! ```norun
//! use std::io;
//! use std::io::{Read, Write};
//! use std::fs::File;
//! use sio::{DecWriter, Key, Nonce, Aad, AES_256_GCM, Close};
//! use sio::{DecWriter, Key, Nonce, Aad, AES_256_GCM, NopCloser};
//!
//! fn main() -> io::Result<()> {
//! // Obviously, do NOT use this demo key for anything real!
//! let secret_key: Key::<AES_256_GCM> = Key::new([0; Key::<AES_256_GCM>::SIZE]);
//!
//! let mut out = DecWriter::new(
//! io::stdout(),
//! NopCloser::wrap(io::stdout()),
//! &secret_key,
//! Nonce::new([0; Nonce::<AES_256_GCM>::SIZE]),
//! Aad::empty(),
Expand All @@ -124,13 +121,21 @@
//! io::copy(&mut File::open("foo.txt")?, &mut out)?;
//! out.close()
//! }
//! ```
//! Here, we wrap the standard output file descriptor (STDOUT) with a `DecWriter`
//! such that everything written to `out` gets decrypted and verified before passed to
//! STDOUT. Than, we open the `foo.txt` file again and copy its content to `out`. Observe
//! that we invoke a `close` method at the end again. Refer to the `Close` trait for an
//! explanation about why this call is necessary.

pub use self::aead::{Aad, Algorithm, Key, Nonce};
pub use self::error::{Invalid, NotAuthentic};
pub use self::utils::NopCloser;
pub use self::writer::{Close, DecWriter, EncWriter};

mod aead;
mod error;
mod utils;
mod writer;

#[cfg(feature = "ring")]
Expand Down
136 changes: 136 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use super::writer::Close;
use std::io;
use std::io::Write;

impl<T: Close + ?Sized> Close for &mut T {
#[inline(always)]
fn close(&mut self) -> io::Result<()> {
Close::close(*self)
}
}

impl Close for Vec<u8> {
#[inline(always)]
fn close(&mut self) -> io::Result<()> {
self.flush()
}
}

impl Close for io::Sink {
#[inline(always)]
fn close(&mut self) -> io::Result<()> {
self.flush()
}
}

impl<W: Close + ?Sized> Close for Box<W> {
#[inline(always)]
fn close(&mut self) -> io::Result<()> {
self.as_mut().close()
}
}

impl<W: Write + Close> Close for io::BufWriter<W> {
#[inline]
fn close(&mut self) -> io::Result<()> {
self.flush().and_then(|_| self.get_mut().close())
}
}

impl<W: Write + Close> Close for io::LineWriter<W> {
#[inline]
fn close(&mut self) -> io::Result<()> {
self.flush().and_then(|_| self.get_mut().close())
}
}

/// NopCloser wraps a writer and implements the `Close` trait by
/// performing a `flush` when the `close` method is called. It should
/// only be used to wrap a writer which does not implement the `Close`
/// trait.
///
/// # Examples
///
/// ```
/// use std::{io, io::Write};
/// use sio::{Key, Nonce, Aad, EncWriter, AES_256_GCM, NopCloser};
///
/// // Load your secret keys from a secure location or derive
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
/// // Obviously, don't use this all-zeros key for anything real.
/// let key: Key<AES_256_GCM> = Key::new([0; Key::<AES_256_GCM>::SIZE]);
///
/// // Make sure you use an unique key-nonce combination!
/// // Reusing a nonce value for the same secret key breaks
/// // the security of the encryption algorithm.
/// let nonce = Nonce::new([0; Nonce::<AES_256_GCM>::SIZE]);
///
/// // You must be able to re-generate this aad to decrypt
/// // the ciphertext again. Usually, it's stored together with
/// // the encrypted data.
/// let aad = Aad::from("Some authenticated but not encrypted data".as_bytes());
///
/// let plaintext = "Some example plaintext".as_bytes();
///
/// let mut ciphertext: Vec<u8> = Vec::default(); // Store the ciphertext in memory.
/// let mut writer = EncWriter::new(
/// NopCloser::wrap(io::stdout()), // Without wrapping STDOUT the code would not compile.
/// &key,
/// nonce,
/// aad,
/// );
///
/// writer.write_all(plaintext).expect("There could be your error handling");
///
/// // Complete the encryption process explicitly.
/// writer.close().expect("There could be your error handling");
/// ```
pub struct NopCloser<W: Write>(W);

impl<W: Write> NopCloser<W> {
/// Wraps a writer.
#[inline(always)]
pub fn wrap(w: W) -> Self {
Self(w)
}
}

impl<W: Write> From<W> for NopCloser<W> {
#[inline(always)]
fn from(w: W) -> Self {
Self::wrap(w)
}
}

impl<W: Write> Write for NopCloser<W> {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}

#[inline(always)]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}

impl<W: Write> Close for NopCloser<W> {
#[inline(always)]
fn close(&mut self) -> io::Result<()> {
self.flush()
}
}

impl<W: Write> AsRef<W> for NopCloser<W> {
#[inline(always)]
fn as_ref(&self) -> &W {
&self.0
}
}

impl<W: Write> AsMut<W> for NopCloser<W> {
#[inline(always)]
fn as_mut(&mut self) -> &mut W {
&mut self.0
}
}
Loading