softaes 0.1.5

Fast software implementation of the AES round function in pure Rust
Documentation

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

[dependencies]
softaes = "0.1"

The round functions

Both SoftAes (constant-time) and softaes::unprotected::SoftAes (table-based) expose the same four round functions, so you can switch paths by changing one import:

  • block_encrypt — a full forward round: SubBytes, ShiftRows, MixColumns, then AddRoundKey.
  • block_encrypt_last — the final forward round, which omits MixColumns.
  • block_decrypt — a full inverse round, expecting a round key from the inverse key schedule.
  • block_decrypt_last — the final inverse round.
use softaes::{Block, SoftAes};

let state = Block::from_bytes(&[0u8; 16]);
let round_key = Block::from_bytes(&[0u8; 16]);

let next = SoftAes::block_encrypt(&state, &round_key);
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 run of full rounds, and a final round that omits MixColumns; decryption mirrors it with the inverse rounds and the inverse key schedule. Either path can do the whole thing — the example below uses the constant-time one.

use softaes::key_schedule::{inverse_key_schedule_128, key_expansion_128};
use softaes::{Block, SoftAes};

let key = [0u8; 16];
let plaintext = [0u8; 16];

let round_keys = key_expansion_128(&key);

let mut state = Block::from_bytes(&plaintext).xor(&round_keys[0]);
for rk in &round_keys[1..10] {
    state = SoftAes::block_encrypt(&state, rk);
}
let ciphertext = SoftAes::block_encrypt_last(&state, &round_keys[10]);

let dec_keys = inverse_key_schedule_128(&round_keys);
let mut state = ciphertext.xor(&dec_keys[0]);
for rk in &dec_keys[1..10] {
    state = SoftAes::block_decrypt(&state, rk);
}
let recovered = SoftAes::block_decrypt_last(&state, &dec_keys[10]).to_bytes();
assert_eq!(recovered, plaintext);

Key schedule

Both paths carry a key_schedule module with the same interface:

  • key_expansion_128, key_expansion_192, key_expansion_256 expand a key into the encryption round keys.
  • inverse_key_schedule_128, inverse_key_schedule_192, inverse_key_schedule_256 turn 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.