aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index f5dd3ab..20ee5a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,87 @@
+//! AEZ *\[sic!\]* v5 encryption implemented in Rust.
+//!
+//! # ☣️ Cryptographic hazmat ☣️
+//!
+//! This crate is not battle tested, nor is it audited. Its usage for critical systems is strongly
+//! discouraged. It mainly exists as a learning exercise.
+//!
+//! # AEZ encryption
+//!
+//! [AEZ](https://www.cs.ucdavis.edu/~rogaway/aez/index.html) is an authenticated encryption
+//! scheme. It works in two steps:
+//!
+//! * First, a known authentication block (a fixed number of zeroes) is appended to the message.
+//! * Second, the message is enciphered with an arbitrary-length blockcipher.
+//!
+//! The blockcipher is tweaked with the key, the nonce and additional data.
+//!
+//! The [paper](https://www.cs.ucdavis.edu/~rogaway/aez/aez.pdf) explains the security concepts of
+//! AEZ in more detail.
+//!
+//! # AEZ encryption (for laypeople)
+//!
+//! The security property of encryption schemes says that an adversary without key must not learn
+//! the content of a message, but the adversary might still be able to modify the message. For
+//! example, in AES-CTR, flipping a bit in the ciphertext means that the same bit will be flipped
+//! in the plaintext once the message is decrypted.
+//!
+//! Authenticated encryption solves this problem by including a mechanism to detect changes. This
+//! can be done for example by including a MAC, or using a mode like GCM (Galois counter mode). In
+//! many cases, not only the integrity of the ciphertext can be verified, but additional data can
+//! be provided during encryption and decryption which will also be included in the integrity
+//! check. This results in an *authenticated encryption with associated data* scheme, AEAD for
+//! short.
+//!
+//! AEZ employs a nifty technique in order to realize an AEAD scheme: The core of AEZ is an
+//! enciphering scheme, which in addition to "hiding" its input is also very "unpredictable",
+//! similar to a hash function. That means that if a ciphertext is changed slightly (by flipping a
+//! bit), the resulting plaintext will be unpredictably and completely different.
+//!
+//! With this property, authenticated encryption can be realized implicitly: The message is padded
+//! with a known string before enciphering it. If, after deciphering, this known string is not
+//! present, the message has been tampered with. Since the enciphering scheme is parametrized by
+//! the key, a nonce and arbitrary additional data, we can verify the integrity of associated data
+//! as well.
+//!
+//! # Other implementations
+//!
+//! As this library is a learning exercise, if you want to use AEZ in practice, it is suggested to
+//! use the [`aez`](https://crates.io/crates/aez) crate which provides bindings to the C reference
+//! implementation of AEZ.
+//!
+//! `zears` differs from `aez` in that ...
+//!
+//! * it works on platforms without hardware AES support, using the "soft" backend of
+//! [`aes`](https://crates.io/crates/aes).
+//! * it does not inherit the limitations of the reference implementation in regards to nonce
+//! length, authentication tag length, or the maximum of one associated data item.
+//!
+//! `zears` is tested with test vectors generated from the reference implementation using [Nick
+//! Mathewson's tool](https://github.com/nmathewson/aez_test_vectors).
+//!
+//! # Example usage
+//!
+//! The core of this crate is the [Aez] struct, which provides the high-level API. There is usually
+//! not a lot more that you need:
+//!
+//! ```
+//! # use zears::*;
+//! let aez = Aez::new(b"my secret key!");
+//! let cipher = aez.encrypt(b"nonce", &[b"associated data"], 16, b"message");
+//! let plaintext = aez.decrypt(b"nonce", &[b"associated data"], 16, &cipher);
+//! assert_eq!(plaintext.unwrap(), b"message");
+//!
+//! // Flipping a bit leads to decryption failure
+//! let mut cipher = aez.encrypt(b"nonce", &[], 16, b"message");
+//! cipher[0] ^= 0x02;
+//! let plaintext = aez.decrypt(b"nonce", &[], 16, &cipher);
+//! assert!(plaintext.is_none());
+//!
+//! // Similarly, modifying the associated data leads to failure
+//! let cipher = aez.encrypt(b"nonce", &[b"foo"], 16, b"message");
+//! let plaintext = aez.decrypt(b"nonce", &[b"bar"], 16, &cipher);
+//! assert!(plaintext.is_none());
+//! ```
use std::iter;
mod block;
@@ -8,15 +92,39 @@ use block::Block;
type Key = [u8; 48];
type Tweak<'a> = &'a [&'a [u8]];
+/// AEZ encryption scheme.
pub struct Aez {
key: Key,
}
impl Aez {
+ /// Create a new AEZ instance.
+ ///
+ /// The key is expanded using Blake2b, according to the AEZ specification.
+ ///
+ /// If you provide a key of the correct length (48 bytes), no expansion is done and the key is
+ /// taken as-is.
pub fn new(key: &[u8]) -> Self {
Aez { key: extract(key) }
}
+ /// Encrypt the given data.
+ ///
+ /// Parameters:
+ ///
+ /// * `nonce` -- the nonce to use. Each nonce should only be used once, as re-using the nonce
+ /// (without chaning the key) will lead to the same ciphertext being produced, potentially
+ /// making it re-identifiable.
+ /// * `associated_data` -- additional data to be included in the integrity check. Note that
+ /// this data will *not* be contained in the ciphertext, but it must be provided on
+ /// decryption.
+ /// * `tau` -- number of *bytes* (not bits) to use for integrity checking. A value of `tau =
+ /// 16` gives 128 bits of security. Passing a value of 0 is valid and leads to no integrity
+ /// checking.
+ /// * `data` -- actual data to encrypt. Can be empty, in which case the returned ciphertext
+ /// provides a "hash" that verifies the integrity of the associated data will be
+ ///
+ /// Returns the ciphertext, which will be of length `data.len() + tau`.
pub fn encrypt(
&self,
nonce: &[u8],
@@ -27,6 +135,15 @@ impl Aez {
encrypt(&self.key, nonce, associated_data, tau, data)
}
+ /// Decrypts the given ciphertext.
+ ///
+ /// Parameters:
+ ///
+ /// * `nonce`, `associated_data` and `tau` are as for [`Aez::encrypt`].
+ /// * `data` -- the ciphertext to decrypt.
+ ///
+ /// Returns the decrypted content. If the authentication check fails, returns `None` instead.
+ /// The returned vector has length `data.len() - tau`.
pub fn decrypt(
&self,
nonce: &[u8],