Skip to content

Commit 1dec963

Browse files
author
Andreas Auernhammer
authored
restructure and document crate features
This commit restructures both crate features: - The `ring` feature does not include the `ring` module but export the AES_256_GCM and CHACHA20_POLY1305 directly in the `sio` namespace. - The `must_close` feature becomes `debug_panic` and is not a default feature anymore. Further this commit adds some (not yet complete) lib documentation and moves the name of the benchmark env. var. from a magic string to a constant.
1 parent 3fd1421 commit 1dec963

File tree

5 files changed

+161
-35
lines changed

5 files changed

+161
-35
lines changed

Cargo.toml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,22 @@ incremental = false
1818
overflow-checks = false
1919

2020
[features]
21-
default = ["ring", "must_close"]
21+
default = ["ring"]
2222

23-
# The 'must_close' feature ensures that an EncWriter
24-
# or DecWriter is actually closed by an explicit
25-
# invocation of the 'close()' method. If an EncWriter
26-
# or DecWriter gets dropped without being closed
27-
# explicitly, its destructor will panic at runtime.
23+
# The 'debug_panic' feature prevents panicking when
24+
# an 'EncWriter' or 'DecWriter' gets dropped without
25+
# being closed explicitly - in debug mode. It has no
26+
# effect in release mode. The purpose of this feature
27+
# is to debug a panic caused by some other code. If
28+
# that panic occurs while an 'EncWriter' or 'DecWriter'
29+
# has not been closed (yet) then the close call will be
30+
# skipped but the writer may get dropped (unwinding) -
31+
# which then causes another panic.
2832
#
29-
# This feature is enabled by default. Disabling it
30-
# only affects debug builds.
31-
must_close = []
33+
# This feature should only be enabled when debugging a
34+
# panic.
35+
debug_panic = []
36+
3237

3338
[dependencies]
3439
ring = { version = "0.14.6", optional = true }

benches/writer.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(test)]
22

3-
use sio::{ring::*, *};
3+
use sio::*;
44
use std::{io, io::Write};
55

66
extern crate test;
@@ -9,11 +9,12 @@ use test::Bencher;
99
type AEAD = AES_256_GCM;
1010

1111
fn buffer_size() -> usize {
12-
if let Ok(value) = std::env::var("SIO_BUF_SIZE") {
12+
const BUFFER_SIZE: &'static str = "SIO_BUF_SIZE";
13+
if let Ok(value) = std::env::var(BUFFER_SIZE) {
1314
let value: usize = value
1415
.as_str()
1516
.parse()
16-
.expect("'SIO_BUF_SIZE' is not a number");
17+
.expect(format!("'{}' is not a number", BUFFER_SIZE).as_str());
1718
1024 * value
1819
} else {
1920
sio::BUF_SIZE

src/lib.rs

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,140 @@
1+
//! Authenticated encryption for I/O streams.
2+
//!
3+
//! The `sio` crate implements authenticated encryption for the `Read` and `Write` traits.
4+
//! Therefore, it provides wrapper types for encryption (`EncWriter`) and decryption (`DecWriter`).
5+
//!
6+
//! The most core part of this crate is the `Algorithm` trait, which represents
7+
//! an authenticated encryption algorithm ([AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data_(AEAD))).
8+
//! However, you only have to care about it when providing your own algorithm implementations.
9+
//!
10+
//! # Feature Flags
11+
//!
12+
//! <table>
13+
//! <tr><th>Feature
14+
//! <th>Description
15+
//! <tr><td><code>ring (default)</code>
16+
//! <td>Use <a href="https://briansmith.org/rustdoc/ring/"><code>ring</code></a> to provide
17+
//! default implementations of AES-256-GCM and ChaCha20-Poly1305 based on Google's
18+
//! <a href="https://github.com/google/boringssl">BoringSSL</a> by implementing the
19+
//! <code>Algorithm</code> trait.
20+
//! <tr><td><code>debug_panic</code>
21+
//! <td>This feature only affects debug builds and should only be enabled when debugging a
22+
//! panic. Both, <code>EncWriter</code> and <code>DecWriter</code> must be closed explicitly.
23+
//! Otherwise, dropping them causes a panic. Take a look at the <code>Close</code> trait for
24+
//! more details. When this feature is enabled, dropping an <code>EncWriter</code> or
25+
//! <code>DecWriter</code> without closing it explicitly does not trigger a panic in debug mode.
26+
//! This may be useful when debugging a panic of some other code.
27+
//! </table>
28+
//!
29+
//! # Introduction
30+
//!
31+
//! The `sio` crate implements a (stream-based) secure channel construction to encrypt data.
32+
//! It splits a data stream into fixed-sized chunks (fragments) and encrypts each fragment
33+
//! separately using an authenticated encryption algorithm. The main advantage of using a
34+
//! channel construction instead of applying the authenticated encryption algorithm directly
35+
//! is that encryption as well as decryption can happen "online" and only requires a constant
36+
//! amount of RAM - even for very large data streams. Here, "online" means that you don't need
37+
//! to en/decrypt the entire data in one atomic operation but instead process it as continuous
38+
//! stream. In general, this cannot be done securely with an authenticated encryption algorithm.
39+
//!
40+
//! For encrypting a data stream you have to provide three parameters:
41+
//! - The secret key represented by the `Key` type.
42+
//! - A nonce value represented by the `Nonce` type.
43+
//! - Some associated data represented by the `Aad` type.
44+
//!
45+
//! There is also an optional fourth parameter (buffer/fragment size) which we will cover later.
46+
//!
47+
//! Now, there are two important rules you have to ensure are never violated by your code:
48+
//! 1. **Correctness:**
49+
//! The parameters (`key'`, `nonce'` `aad'`) for decryption must match exactly the parameters
50+
//! (`key`, `nonce` `aad`) used when encrypting the data.
51+
//! 2. **Freshness:**
52+
//! When encrypting a data stream, you must use a new `key` **or** a new `nonce` value that
53+
//! has **never** been used before.
54+
//!
55+
//! The 1st rule is pretty obvious. If you, for example, use different keys for encryption and
56+
//! decryption then the decryption will fail. The same applies to the nonce and associated data.
57+
//!
58+
//! The 2nd rule may be less obvious but it is at least as important as the 1st one since security
59+
//! crucially depends on it. In general, the authenticated encryption algorithm (used by the
60+
//! channel construction) assumes that, given the same key, the nonce value does never repeat.
61+
//! Violating this assumption breaks the security properties and potentially allows decrypting
62+
//! or forging data without knowing the secret key. But don't worry, there are best practices for
63+
//! dealing with keys and nonce values which can help here.
64+
//!
65+
//! Next, we will take a look at some examples for encryption and decryption.
66+
//!
67+
//! # Encryption
68+
//!
69+
//! You can encrypt data by wrapping a writer with an `EncWriter`. The `EncWriter` is generic over
70+
//! an authenticated encryption algorithm and takes a `Key`, a `Nonce` and some `Aad`.
71+
//! ```norun
72+
//! use std::io;
73+
//! use std::io::Write;
74+
//! use std::fs::File;
75+
//! use sio::{EncWriter, Key, Nonce, Aad, AES_256_GCM, Close};
76+
//!
77+
//! fn main() -> io::Result<()> {
78+
//! // Obviously, do NOT use this demo key for anything real!
79+
//! let secret_key: Key::<AES_256_GCM> = Key::new([0; Key::<AES_256_GCM>::SIZE]);
80+
//!
81+
//! let mut f = EncWriter::new(
82+
//! File::create("foo.txt")?,
83+
//! &secret_key,
84+
//! Nonce::new([0; Nonce::<AES_256_GCM>::SIZE]),
85+
//! Aad::empty(),
86+
//! );
87+
//!
88+
//! f.write_all(b"Hello World")?;
89+
//! f.close()
90+
//! }
91+
//! ```
92+
//! Here, we try to create and wrap the file `foo.txt` and encrypt the string
93+
//! `"Hello World"` using the [`AES_256_GCM`](https://en.wikipedia.org/wiki/Galois/Counter_Mode)
94+
//! algorithm before writing it to the file. Note that we call a `close` method
95+
//! after writing. This is *very important* and you should take a look at the
96+
//! `Close` trait for a detailed explanation.
97+
//!
98+
//! # Decryption
99+
//!
100+
//! Similarly, you can decrypt data by wrapping a writer with a `DecWriter`. The `DecWriter`
101+
//! is also generic over an authenticated encryption algorithm and expects the same
102+
//! `Key`, `Nonce` and `Aad` used to encrypt the data.
103+
//! ```norun
104+
//! use std::io;
105+
//! use std::io::{Read, Write};
106+
//! use std::fs::File;
107+
//! use sio::{DecWriter, Key, Nonce, Aad, AES_256_GCM, Close};
108+
//!
109+
//! fn main() -> io::Result<()> {
110+
//! // Obviously, do NOT use this demo key for anything real!
111+
//! let secret_key: Key::<AES_256_GCM> = Key::new([0; Key::<AES_256_GCM>::SIZE]);
112+
//!
113+
//! let mut out = DecWriter::new(
114+
//! io::stdout(),
115+
//! &secret_key,
116+
//! Nonce::new([0; Nonce::<AES_256_GCM>::SIZE]),
117+
//! Aad::empty(),
118+
//! );
119+
//!
120+
//! io::copy(&mut File::open("foo.txt")?, &mut out)?;
121+
//! out.close()
122+
//! }
123+
1124
pub use self::aead::{Aad, Algorithm, Key, Nonce};
2125
pub use self::error::{Invalid, NotAuthentic};
3126
pub use self::writer::{Close, DecWriter, EncWriter};
4127

5128
mod aead;
6129
mod error;
130+
mod ring;
7131
mod writer;
8132

9133
#[cfg(feature = "ring")]
10-
pub mod ring;
134+
pub use self::ring::AES_256_GCM;
135+
136+
#[cfg(feature = "ring")]
137+
pub use self::ring::CHACHA20_POLY1305;
11138

12139
pub const MAX_BUF_SIZE: usize = (1 << 24) - 1;
13140
pub const BUF_SIZE: usize = 1 << 14;

src/writer.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ use std::io::Write;
2525
///
2626
/// ```
2727
/// use std::io::Write;
28-
/// use sio::{Key, Nonce, Aad, EncWriter, Close};
29-
/// use sio::ring::AES_256_GCM;
28+
/// use sio::{Key, Nonce, Aad, EncWriter, Close, AES_256_GCM};
3029
///
3130
/// // Load your secret keys from a secure location or derive
3231
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
@@ -87,8 +86,7 @@ impl<A: Algorithm, W: Write> EncWriter<A, W> {
8786
///
8887
/// ```
8988
/// use std::io::Write;
90-
/// use sio::{Key, Nonce, Aad, EncWriter, Close};
91-
/// use sio::ring::AES_256_GCM;
89+
/// use sio::{Key, Nonce, Aad, EncWriter, Close, AES_256_GCM};
9290
///
9391
/// // Load your secret keys from a secure location or derive
9492
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
@@ -135,8 +133,7 @@ impl<A: Algorithm, W: Write> EncWriter<A, W> {
135133
///
136134
/// ```
137135
/// use std::io::Write;
138-
/// use sio::{Key, Nonce, Aad, EncWriter, Close};
139-
/// use sio::ring::AES_256_GCM;
136+
/// use sio::{Key, Nonce, Aad, EncWriter, Close, AES_256_GCM};
140137
///
141138
/// // Load your secret keys from a secure location or derive
142139
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
@@ -282,19 +279,19 @@ impl<A: Algorithm, W: Write> Drop for EncWriter<A, W> {
282279
// - we encountered an error during a write or flush call.
283280
if !self.panicked && !self.errored {
284281
// For debugging purposes, we allow disabling the panic
285-
// for debug builds - but only if the feature "must_close"
286-
// (enabled by default) is turned off.
287-
if !(cfg!(debug_assertions) && !cfg!(feature = "must_close")) {
282+
// for debug builds - but only if the feature "debug_panic"
283+
// is turned on.
284+
if !(cfg!(debug_assertions) && cfg!(feature = "debug_panic")) {
288285
// Actually, Drop implementations should not panic.
289286
// However, not closing the EncWriter (see: close())
290287
// implies not encrypting the entire plaintext such that
291288
// the ciphertext written to the inner writer cannot be
292289
// decrypted anymore. Consequently, we would "loose" data.
293290
//
294291
// We could call close() here if it hasn't been called explicitly
295-
// by callers but that would only succeed if the no other I/O error
292+
// by callers but that would only succeed if no other I/O error
296293
// occurs. Otherwise, we are in the same situation as before. Calling
297-
// close() here would an optimistic approach - while in cryptography
294+
// close() here would be an optimistic approach - while in cryptography
298295
// we have to be pessimistic.
299296
assert!(
300297
self.closed,
@@ -328,8 +325,7 @@ impl<A: Algorithm, W: Write> Drop for EncWriter<A, W> {
328325
///
329326
/// ```
330327
/// use std::io::Write;
331-
/// use sio::{Key, Nonce, Aad, DecWriter, Close};
332-
/// use sio::ring::AES_256_GCM;
328+
/// use sio::{Key, Nonce, Aad, DecWriter, Close, AES_256_GCM};
333329
///
334330
/// // Load your secret keys from a secure location or derive
335331
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
@@ -390,8 +386,7 @@ impl<A: Algorithm, W: Write> DecWriter<A, W> {
390386
///
391387
/// ```
392388
/// use std::io::Write;
393-
/// use sio::{Key, Nonce, Aad, DecWriter, Close};
394-
/// use sio::ring::AES_256_GCM;
389+
/// use sio::{Key, Nonce, Aad, DecWriter, Close, AES_256_GCM};
395390
///
396391
/// // Load your secret keys from a secure location or derive
397392
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
@@ -441,8 +436,7 @@ impl<A: Algorithm, W: Write> DecWriter<A, W> {
441436
///
442437
/// ```
443438
/// use std::io::Write;
444-
/// use sio::{Key, Nonce, Aad, DecWriter, Close};
445-
/// use sio::ring::AES_256_GCM;
439+
/// use sio::{Key, Nonce, Aad, DecWriter, Close, AES_256_GCM};
446440
///
447441
/// // Load your secret keys from a secure location or derive
448442
/// // them using a secure (password-based) key-derivation-function, like Argon2id.
@@ -588,9 +582,9 @@ impl<A: Algorithm, W: Write> Drop for DecWriter<A, W> {
588582
// - we encountered an error during a write or flush call.
589583
if !self.panicked && !self.errored {
590584
// For debugging purposes,we allow disabling the panic
591-
// for debug builds - but only if the feature "must_close"
592-
// (enabled by default) is turned off.
593-
if !(cfg!(debug_assertions) && !cfg!(feature = "must_close")) {
585+
// for debug builds - but only if the feature "debug_panic"
586+
// is turned on.
587+
if !(cfg!(debug_assertions) && cfg!(feature = "debug_panic")) {
594588
// Actually, Drop implementations should not panic.
595589
// However, not closing the DecWriter (see: close())
596590
// implies not decrypting the entire ciphertext and
@@ -602,7 +596,7 @@ impl<A: Algorithm, W: Write> Drop for DecWriter<A, W> {
602596
// by callers but that would only succeed if the ciphertext
603597
// is authentic and no other I/O error occurs. Otherwise, we
604598
// are in the same situation as before. Calling close() here
605-
// would an optimistic approach - while in cryptography we have
599+
// would be an optimistic approach - while in cryptography we have
606600
// to be pessimistic.
607601
assert!(
608602
self.closed,

tests/writer_tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use sio::ring::AES_256_GCM;
21
use sio::*;
32
use std::{io, io::Write};
43

0 commit comments

Comments
 (0)