diff options
author | Daniel Schadt <kingdread@gmx.de> | 2025-04-08 22:08:05 +0200 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2025-04-08 22:08:05 +0200 |
commit | d4ad1672404745c68096c700edc0816051f6db3f (patch) | |
tree | 1b84ef30bec06aafbdf03bad99e10d68c7bdeaff /src | |
parent | 71cdf50525f0cbb70673477510050669206df7f2 (diff) | |
download | zears-d4ad1672404745c68096c700edc0816051f6db3f.tar.gz zears-d4ad1672404745c68096c700edc0816051f6db3f.tar.bz2 zears-d4ad1672404745c68096c700edc0816051f6db3f.zip |
add documentation
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 117 |
1 files changed, 117 insertions, 0 deletions
@@ -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], |