eme2 0.2.1

EME2 (ECB-Mask-ECB) wide-block cipher mode of operation
Documentation

eme2

Docs Apache2/MIT licensed Rust Version

Generic implementation of the EME2 (ECB-Mask-ECB) wide-block cipher mode of operation.

Mode functionality is accessed using traits from the re-exported cipher crate.

Standards and Specifications

This crate implements IEEE Std 1619.2 EME2-AES, which is the original academic EME* specification instantiated with a 128-bit block cipher and a concrete key schedule (one master key split into Key1/Key2/Key3) in place of EME*'s independent masks L and R. The two specs describe the same algorithm, so there is a single implementation ([Eme2::hash_ad], encrypt_with_ad, decrypt_with_ad); the _eme2/_emestar-suffixed method names are kept as aliases only so callers can cite whichever spec they care about.

Reference Documents

Cipher Compatibility

This crate ONLY supports block ciphers with a 16-byte (128-bit) block size.

EME2 mathematically requires a 128-bit polynomial ($x{128} + x7 + x^2 + x + 1$) for its internal tweak processing. Ciphers with larger block sizes (such as Threefish256, Threefish512, or Threefish1024) are structurally incompatible with EME2 and are safely rejected at compile time by the crate's generic trait bounds (BlockSizeUser<BlockSize = U16>).

The implementation is explictly tested against standard 128-bit block ciphers to guarantee compatibility across the ecosystem:

  • aes-128
  • aes-256
  • serpent

Migrating from Stream Ciphers (e.g., CTR Mode)

Because eme2 strictly implements RustCrypto's KeyIvInit traits, instantiating the cipher is a 1:1 drop-in replacement for stream ciphers like ctr:

// CTR Mode Initialization
let cipher = Ctr128BE::<Aes128>::new(&key.into(), &nonce.into());

// EME2 Mode Initialization
let cipher = Eme2::<Aes128>::new(&key.into(), &tweak.into());

However, execution differs. Stream ciphers use the StreamCipher trait because their encryption and decryption processes are mathematically identical (XORing a keystream). EME2 is a Wide-Block Cipher, meaning its encryption and decryption passes are mathematically asymmetric. Therefore, it does not implement StreamCipher and instead exposes explicit .encrypt() and .decrypt() methods:

// Stream Ciphers (CTR)
cipher.apply_keystream(&mut data); // Handles both encryption and decryption

// Wide-Block Ciphers (EME2)
cipher.encrypt(&mut data).expect("encryption succeeded");
cipher.decrypt(&mut data).expect("decryption succeeded");

Usage Examples

1. Encryption with a tweak

use aes::Aes256;
use cipher::KeyIvInit;
use eme2::Eme2;

type Aes256Eme2 = Eme2<Aes256>;

let key = [0x42; 64]; // Partitioned internally into Key1, Key2, and Key3
let tweak = [0x11; 16];
let mut data = vec![0u8; 64];

// Initialization using KeyIvInit trait (hashes the tweak via `Eme2::hash_ad`)
let cipher = Aes256Eme2::new(&key.into(), &tweak.into());

cipher.encrypt(&mut data).expect("encryption succeeded");
cipher.decrypt(&mut data).expect("decryption succeeded");

2. Encryption with associated data

use aes::Aes256;
use cipher::KeyInit;
use eme2::Eme2;

type Aes256Eme2 = Eme2<Aes256>;

let key = [0x42; 64];
let mut data = vec![0u8; 64];

let cipher = <Aes256Eme2 as cipher::KeyInit>::new(&key.into());

let associated_data = b"associated data";
cipher.encrypt_with_ad(associated_data, &mut data).expect("encryption succeeded");
cipher.decrypt_with_ad(associated_data, &mut data).expect("decryption succeeded");

⚠️ Security Warning: Hazmat!

This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity is not verified, which can lead to serious vulnerabilities. It is highly recommended to use EME2 in combination with a strong MAC to provide robust authenticated encryption.

License

Licensed under either of:

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.