AES-XTS
Algorithm
XTS (XEX-based tweaked-codebook mode with ciphertext stealing), standardized in IEEE Std 1619, is meant to encrypt fixed data units such as disk sectors: each unit gets a distinct tweak so equal plaintext blocks in different positions or sectors do not yield identical ciphertext. It is a poor fit for arbitrary network messages; use an IV or nonce with a conventional mode or an AEAD instead.
In this library, XTS uses two expanded AES keys: cipher_a (data key) encrypts or decrypts payload blocks, and cipher_b (tweak key) derives the running tweak from a 16-byte tweak input tweak. The implementation first sets the working tweak to E_K2(tweak) (AES encrypt of the tweak block with the tweak key), then for each 16-byte block XORs the tweak, applies cipher_a in the appropriate direction, XORs the tweak again, and updates the tweak by multiplication by x in the binary field GF(2^128) (the usual XTS “alpha†step) before the next block.
Input length: the data unit must be at least 16 bytes. If the length is not a multiple of 16, ciphertext stealing handles the trailing short segment so ciphertext length equals plaintext length (no external padding layer).
Purpose
Document AES-XTS for sector-style encryption: callers supply the per-unit tweak (for example a sector index encoded in the tweak block), two AesCipher instances for the data and tweak keys, and a contiguous plaintext or ciphertext buffer.
Rust API
- Crate:
noxtls-crypto - Module path (conceptual):
noxtls_crypto::sym (re-exported at crate root) - Primary symbols:
AesCiphernoxtls_aes_xts_encryptnoxtls_aes_xts_decrypt
Functions and types:
noxtls_aes_xts_encrypt(cipher_a, cipher_b, tweak, plaintext) -> Result<Vec<u8>>- Parameters:cipher_ais the data AES key schedule;cipher_bis the tweak AES key schedule;tweakis a 16-byte per–data-unit value (for example a sector index) that seeds the tweak chain viaE_K2(tweak);plaintextis the unit to encrypt, length ≥ 16 (any length ≥ 16 is allowed; non–16-byte-aligned lengths use ciphertext stealing). Behavior: AES-XTS encryption over the whole buffer. Returns: ciphertextVec<u8>of the same length asplaintext, orInvalidLengthif shorter than one block.noxtls_aes_xts_decrypt(cipher_a, cipher_b, tweak, ciphertext) -> Result<Vec<u8>>- Parameters: same keys andtweakas used for that unit’s encryption;ciphertextis the XTS ciphertext, length ≥ 16. Behavior: inverse XTS transform, including ciphertext stealing when length is not a multiple of 16. Returns: plaintextVec<u8>of the same length, or errors on invalid length.
Feature flags and policy
Standard noxtls-crypto build.
Examples
use noxtls_crypto::{AesCipher, noxtls_aes_xts_decrypt, noxtls_aes_xts_encrypt};
let data_key = AesCipher::new(&[0x01u8; 32])?;
let tweak_key = AesCipher::new(&[0x02u8; 32])?;
let tweak = [0u8; 16];
let plaintext = [0xAAu8; 32];
let ciphertext = noxtls_aes_xts_encrypt(&data_key, &tweak_key, &tweak, &plaintext)?;
let recovered = noxtls_aes_xts_decrypt(&data_key, &tweak_key, &tweak, &ciphertext)?;
assert_eq!(recovered, plaintext);
// Lengths ≥ 16 that are not a multiple of 16 use ciphertext stealing (same length in/out).
# Ok::<(), noxtls_core::Error>(())
Security and compatibility
Use a fresh tweak for each data unit you must distinguish (for example each sector index within a key scope). Reusing the same (key pair, tweak) for different units weakens the intended domain separation. XTS provides confidentiality for whole-unit encryption, not authentication; an attacker can still flip ciphertext bits in many threat models unless integrity is handled elsewhere.
Follow your platform’s rules for how the tweak is derived (128-bit tweak field, endianness, and how partial sectors are handled). Typical XTS key bundles are 256-bit (two 128-bit AES keys) or 512-bit (two 256-bit keys), realized here as two separate AesCipher values from two key byte arrays.