softaes
A fast software implementation of the AES round function in pure Rust.
This crate is not a high-level "encrypt my buffer" library. It exposes the AES round function itself as a building block, the same primitive that backs constructions such as AEGIS, AES-based PRFs, and other designs that lean on the AES round for mixing. If that is what you need, you get it here without pulling in hardware intrinsics or unsafe assembly.
The code is no_std, has no dependencies, and runs anywhere Rust runs.
Two implementations
The crate ships two round functions and lets you pick the trade-off explicitly.
The default path, at the crate root, is constant-time. The round is computed with a bitsliced representation (called SRM-1R) that holds the block as eight 32-bit bit planes. ShiftRows is folded into the input packing, SubBytes is a gate-only Boolean S-box circuit, and MixColumns is a fixed sequence of rotations and XORs. No step ever indexes memory with secret data, so the round runs in constant time on every platform. This is the one to reach for when the input is secret.
The unprotected module is a classic table-based implementation. It is faster
on machines with good caches, but the table lookups are indexed by secret data,
so it leaks through cache-timing side channels. Use it only when the data is not
secret, or when you have already accepted that risk.
Installation
[]
= "0.1"
Using the constant-time round
SoftAes::block_encrypt applies one full forward round: SubBytes, ShiftRows,
MixColumns, and then AddRoundKey with the round key you pass in.
use ;
let state = from_bytes;
let round_key = from_bytes;
let next = block_encrypt;
let bytes = next.to_bytes;
For historical reasons the crate also exports SoftAesFast, SoftAesModerate,
and SoftAesSlow. These used to select between table variants with different
amounts of cache-timing hardening; they are now all aliases for the single
constant-time SoftAes, kept so existing code keeps compiling.
Building the full cipher
A standard AES encryption is the initial AddRoundKey, a number of full rounds,
and a final round that omits MixColumns. The constant-time path deliberately
exposes only the full round, which is all the AES-round constructions need. To
assemble the complete cipher, including the final round and decryption, use the
unprotected module, which provides block_encrypt, block_encrypt_last,
block_decrypt, and block_decrypt_last.
use ;
use key_expansion_128;
let key = ;
let plaintext = ;
let round_keys = key_expansion_128;
let mut state = from_bytes.xor;
for rk in &round_keys
let ciphertext = block_encrypt_last.to_bytes;
Key schedule
Both paths carry a key_schedule module with the same interface:
key_expansion_128,key_expansion_192,key_expansion_256expand a key into the encryption round keys.inverse_key_schedule_128,inverse_key_schedule_192,inverse_key_schedule_256turn an encryption schedule into the matching decryption schedule, reversing the round-key order and applying InvMixColumns to every key except the first and last.
The constant-time key_schedule runs its S-box lookups in constant time; the one
under unprotected uses a direct table. Both produce identical round keys, and
the expansions are checked against the FIPS 197 Appendix C test vectors.
License
MIT. See LICENSE.