A Go library exposing the fundamental building blocks of AES encryption for developers building custom cryptographic primitives. Unlike traditional AES libraries that provide complete encryption/decryption, this package gives you direct access to individual round functions, transformations, and wide-block permutations.
- go-aes
- Low-level AES operations: SubBytes, ShiftRows, MixColumns, and complete round functions
- Hardware acceleration: Intel AES-NI, ARM Crypto Extensions, and VAES for parallel processing
- Parallel block processing: Process 2 or 4 blocks simultaneously with VAES/AVX2/AVX512
- Multi-round functions: Optimized 4/6/7/10/12/14 round operations
- Wide-block permutations: Areion256 (32-byte) and Areion512 (64-byte)
- Keyed block ciphers from permutations: Areion256-EM and Areion512-EM (Even-Mansour construction)
- Beyond-birthday-bound PRF: Areion-SoEM-256 and Areion-SoEM-512 (Sum of Even-Mansour)
- Short fixed-input hashing: Areion256-DM and Areion512-DM (Davies-Meyer construction)
- Keyed short-input hashing: Vistrutah256-MP and Vistrutah512-MP (Miyaguchi-Preneel construction)
- AES-based hashing: Haraka v2 (256-bit and 512-bit input variants)
- Tweakable block ciphers: KIASU-BC, Deoxys-BC-256, Pholkos (256-bit and 512-bit)
- Large-block ciphers: Vistrutah-256 and Vistrutah-512
- Expanding PRF: ButterKnife (128-bit to 1024-bit expansion)
- Cross-platform: Identical results on Intel and ARM with automatic fallback to pure Go
go get github.com/jedisct1/go-aespackage main
import (
"fmt"
aes "github.com/jedisct1/go-aes"
)
func main() {
var block, key aes.Block
copy(block[:], "Hello, World!...")
copy(key[:], "SecretKey1234567")
// Perform one AES forward round (modifies block in place)
aes.Round(&block, &key)
fmt.Printf("After one round: %x\n", block)
// Hardware acceleration is automatic
if aes.CPU.HasAESNI || aes.CPU.HasARMCrypto {
copy(block[:], "Hello, World!...")
aes.RoundHW(&block, &key)
fmt.Printf("Hardware result: %x\n", block)
}
}All operations work on 128-bit blocks represented as [16]byte arrays. Three round function variants accommodate different use cases:
| Variant | Key XOR Position | Use Case |
|---|---|---|
Standard (Round, InvRound) |
End | FIPS-197/Intel semantics |
KeyFirst (RoundKeyFirst) |
Beginning | ARM-native semantics |
NoKey (RoundNoKey) |
None | Custom constructions |
The package detects CPU capabilities at runtime:
aes.CPU.HasAESNI // Intel AES-NI (single-block)
aes.CPU.HasARMCrypto // ARM Crypto Extensions
aes.CPU.HasVAES // Intel VAES (parallel)
aes.CPU.HasAVX2 // 2-block parallel with VAES
aes.CPU.HasAVX512 // 4-block parallel with VAESIntel and ARM AES instructions have different operation orders. The library handles this transparently, ensuring identical results across platforms.
For high-throughput applications, process multiple blocks simultaneously:
// Process two blocks (AVX2)
var blocks aes.Block2 // 32 bytes = 2Ă—16-byte blocks
var keys aes.Key2 // 32 bytes = 2Ă—16-byte keys
result := aes.Round2(blocks, keys)
// Process four blocks (AVX512)
var blocks4 aes.Block4 // 64 bytes
var keys4 aes.Key4
result4 := aes.Round4(blocks4, keys4)Each block is processed with its corresponding key. Falls back to sequential processing without VAES.
Optimized functions for multiple rounds in a single call (2-3x faster than separate calls):
var keys aes.RoundKeys10
result := aes.Rounds10(block, keys)
// For complete AES encryption (N-1 full rounds + 1 final round without MixColumns)
result := aes.Rounds10WithFinal(block, keys)Available: Rounds4, Rounds6, Rounds7, Rounds10, Rounds12, Rounds14, plus inverse and NoKey variants.
Standard key expansion for AES-128, AES-192, and AES-256:
var masterKey [16]byte
roundKeys := aes.KeyExpansion128(masterKey)
// Or use the KeySchedule type
ks, _ := aes.NewKeySchedule(masterKey[:])
roundKey := ks.GetRoundKey(0)Wide-block cryptographic permutations built using AES rounds, useful for hash functions and authenticated encryption.
- Areion256: 32-byte state, 10 rounds
- Areion512: 64-byte state, 15 rounds
var state aes.Areion256
copy(state[:], yourData)
state.Permute()
// Inverse permutation
state.InversePermute()
// 512-bit variant
var largeState aes.Areion512
largeState.Permute()Areion-DM: Short fixed-input hash functions using the Davies-Meyer construction (h = P(m) XOR m).
- Areion256-DM: 32-byte input, 32-byte output
- Areion512-DM: 64-byte input, 32-byte output (truncated)
var input32 [32]byte
hash256 := aes.Areion256DM(&input32) // [32]byte
var input64 [64]byte
hash512 := aes.Areion512DM(&input64) // [32]byteKeyed block ciphers using the single-key Even-Mansour construction: E_k(m) = P(m XOR k) XOR k, where P is an Areion permutation. Provides a simple way to turn the Areion permutation into a keyed primitive.
- Areion256-EM: 32-byte key, 32-byte block
- Areion512-EM: 64-byte key, 64-byte block
var key [32]byte
var block [32]byte
ciphertext := aes.Areion256EM(&key, &block) // Encrypt
plaintext := aes.Areion256EMDecrypt(&key, &ciphertext) // Decrypt
// 512-bit variant
var key64 [64]byte
var block64 [64]byte
ct512 := aes.Areion512EM(&key64, &block64)
pt512 := aes.Areion512EMDecrypt(&key64, &ct512)Beyond-birthday-bound PRF using Sum of Even-Mansour with Areion permutations: F(k1, k2, m) = P(m XOR k1) XOR P(m XOR k2 XOR d), where d is a domain separation constant. Two independent subkeys push PRF security to ~2n/3 bits, well beyond the n/2-bit birthday bound of single-call constructions.
- Areion-SoEM-256: 64-byte key (2x32), 32-byte input/output, ~170-bit PRF security
- Areion-SoEM-512: 128-byte key (2x64), 64-byte input/output, ~341-bit PRF security
var key256 [64]byte // two independent 32-byte subkeys
var input256 [32]byte
output256 := aes.AreionSoEM256(&key256, &input256)
var key512 [128]byte // two independent 64-byte subkeys
var input512 [64]byte
output512 := aes.AreionSoEM512(&key512, &input512)Pseudorandom function using AES rounds with feed-forward structure: 4 rounds, XOR with input, then 6 more rounds (5 full + 1 final).
prf, _ := aes.NewAESPRF(key[:]) // 16, 24, or 32 bytes
var block aes.Block
copy(block[:], "Input data here!")
prf.PRF(&block) // Modifies in placePerformance: ~152 ns/op with zero allocations on Apple M4 ARM64.
AES-based cryptographic hash function designed for short inputs. Uses AES rounds with round constants derived from the digits of pi.
- Haraka-256: 32-byte input, 32-byte output, 5 rounds
- Haraka-512: 64-byte input, 32-byte output (truncated), 5 rounds
// Hash a 32-byte input
var input [32]byte
copy(input[:], yourData)
hash := aes.Haraka256(&input)
// Hash a 64-byte input
var largeInput [64]byte
hash512 := aes.Haraka512(&largeInput)
// Convenience: get single 16-byte block output
block := aes.Haraka256ToBlock(&input)AES-128 with 8-byte tweak XORed into each round. Used in ipcrypt-nd for non-deterministic IP address encryption.
ctx, _ := aes.NewKiasuContext(key)
var plaintext [16]byte
var tweak [8]byte
ciphertext := ctx.KiasuEncrypt(plaintext, tweak)
decrypted := ctx.KiasuDecrypt(ciphertext, tweak)Tweak format: Padded to 16 bytes as [T0 T1 00 00 T2 T3 00 00 T4 T5 00 00 T6 T7 00 00]
From the TWEAKEY framework: 256-bit tweakey (128-bit key + 128-bit tweak), 14 rounds.
var tweakey aes.Tweakey256
copy(tweakey[0:16], key[:])
copy(tweakey[16:32], tweak[:])
rk := aes.NewDeoxysBC256(&tweakey)
ciphertext := aes.DeoxysBC256Encrypt(rk, &plaintext)Low-level round functions with domain separation are also available for custom constructions.
Tweakable PRF expanding 128-bit input to 1024-bit output (8 branches). Based on the Iterate-Fork-Iterate paradigm.
var tweakey aes.Tweakey256
var input aes.Block
output := aes.ButterKnife(&tweakey, &input)
// For multiple evaluations, use context
ctx := aes.NewButterKnifeContext(&tweakey)
output1 := ctx.Eval(&input1)
output2 := ctx.Eval(&input2)Structure: 7 rounds before fork (domain 0), then 8 rounds in 8 parallel branches (domains 1-8), with feed-forward XOR.
Reference: ePrint 2021/1534
Large-state tweakable block cipher family based on AES rounds, designed for high security and high performance. Follows the design strategy of Haraka and AESQ with two-round steps.
Variants:
- Pholkos-256-256: 256-bit block, 256-bit key, 128-bit tweak, 8 steps
- Pholkos-512-256: 512-bit block, 256-bit key, 128-bit tweak, 10 steps
- Pholkos-512-512: 512-bit block, 512-bit key, 128-bit tweak, 10 steps
// Pholkos-256 (32-byte block, 32-byte key)
var block aes.Pholkos256Block
var key aes.Pholkos256Key
var tweak aes.PholkosTweak
copy(block[:], plaintext)
copy(key[:], keyBytes)
copy(tweak[:], tweakBytes)
ctx := aes.NewPholkos256Context(&key, &tweak)
ctx.Encrypt(&block)
ctx.Decrypt(&block)
// Pholkos-512 with 256-bit key
var block512 aes.Pholkos512Block
ctx512 := aes.NewPholkos512Context(&key, &tweak)
ctx512.Encrypt(&block512)
// Pholkos-512 with 512-bit key
var key512 aes.Pholkos512Key
ctx512_512 := aes.NewPholkos512Context512(&key512, &tweak)
ctx512_512.Encrypt(&block512)For single-block operations, convenience functions are available: Pholkos256Encrypt, Pholkos256Decrypt, Pholkos512Encrypt, Pholkos512Decrypt, Pholkos512Encrypt512, Pholkos512Decrypt512.
Large-block cipher family using Generalized Even-Mansour construction.
Vistrutah-256 (32-byte blocks):
plaintext := make([]byte, 32)
ciphertext := make([]byte, 32)
key := make([]byte, 32) // 16 or 32 bytes
aes.Vistrutah256Encrypt(plaintext, ciphertext, key, aes.Vistrutah256RoundsLong)
aes.Vistrutah256Decrypt(ciphertext, plaintext, key, aes.Vistrutah256RoundsLong)Vistrutah-512 (64-byte blocks):
plaintext := make([]byte, 64)
ciphertext := make([]byte, 64)
key := make([]byte, 64) // 32 or 64 bytes
aes.Vistrutah512Encrypt(plaintext, ciphertext, key, aes.Vistrutah512RoundsLong512Key)Round options:
| Variant | Short | Long |
|---|---|---|
| Vistrutah-256 | 10 | 14 |
| Vistrutah-512 (256-bit key) | 10 | 14 |
| Vistrutah-512 (512-bit key) | 12 | 18 |
Reference: ePrint 2024/1534
Vistrutah-MP: Keyed fixed-input hash functions using the Miyaguchi-Preneel construction (h = E(k, X) XOR k XOR X), one of the 12 provably secure PGV compression functions (Black-Rogaway-Shrimpton, CRYPTO 2002).
var input [32]byte
var key [32]byte
hash256 := aes.Vistrutah256MP(&input, key[:], aes.Vistrutah256RoundsLong) // [32]byte
var input64 [64]byte
var key64 [64]byte
hash512 := aes.Vistrutah512MP(&input64, key64[:], aes.Vistrutah512RoundsLong512Key) // [64]byteLightweight authenticated encryption (AEAD) using two AES-128 keys. Located in examples/cymric/.
Two variants:
- Cymric1: |msg| + |nonce| <= 16, |nonce| + |ad| <= 15
- Cymric2: |msg| <= 16, |nonce| + |ad| <= 15
import "github.com/jedisct1/go-aes/examples/cymric"
var key [32]byte
copy(key[:], keyBytes)
ctx := cymric.NewContext(&key)
// Encrypt
nonce := make([]byte, 12)
msg := []byte("Hi!")
ad := []byte("v1")
ctext := make([]byte, len(msg))
var tag [16]byte
ctx.Cymric1Encrypt(ctext, &tag, msg, ad, nonce)
// Decrypt
ptext := make([]byte, len(ctext))
err := ctx.Cymric1Decrypt(ptext, ctext, &tag, ad, nonce)Features: 256-bit key, 128-bit tag, constant-time verification, zero allocations.
High-speed MAC using parallel AES rounds. Located in examples/lemac/.
import "github.com/jedisct1/go-aes/examples/lemac"
var key [16]byte
var nonce [16]byte
copy(key[:], "SecretMACKey1234")
ctx := lemac.NewLeMacContext(key)
tag := lemac.LeMac(ctx, []byte("Authenticate this"), nonce)Provides 128-bit security with unique nonces, 64-bit with static nonces.
Key derivation function for DH shared secrets (e.g., X3DH handshakes). Located in examples/skye/.
import "github.com/jedisct1/go-aes/examples/skye"
samples := [][]byte{dh1, dh2, dh3} // 3-4 DH secrets (32 bytes each)
var info skye.SkyeInfo
copy(info[:], "Signal Protocol v1.0")
key, _ := skye.Skye(samples, &info, 64)
// For multiple derivations
ctx, _ := skye.NewSkyeContext(samples)
key1 := ctx.Expand(&info1, 32)
key2 := ctx.Expand(&info2, 32)Reference: ePrint 2024/781
Full-state keyed sponge PRF/MAC using Areion512. Located in examples/macakey/.
import "github.com/jedisct1/go-aes/examples/macakey"
var key [32]byte
copy(key[:], keyBytes)
// One-shot MAC
mac, _ := macakey.Macakey(key[:], message, 32)
// Streaming
ctx, _ := macakey.NewMacakeyContext(key[:])
ctx.Write(chunk1)
ctx.Write(chunk2)
mac := ctx.Sum(32)
// With explicit IV (32 bytes)
mac, _ := macakey.MacakeyWithIV(key[:], iv, message, 32)Based on MacaKey V2 with NCP-based domain separation. Provides 128-bit security.
Sponge construction with feed-forward using Areion512 as the underlying permutation. Located in examples/spongef/.
Unlike a standard sponge, Sponge-F XORs the pre-permutation capacity back into the post-permutation state during both absorption and squeezing. This feed-forward makes the construction secure even when the permutation is not ideal, requiring only unpredictability rather than indifferentiability from a random permutation.
import "github.com/jedisct1/go-aes/examples/spongef"
// One-shot hash (256-bit output)
hash := spongef.Hash(message)
// Variable-length output (XOF-style)
out, _ := spongef.SpongeF(message, 64) // 64 bytes
// With custom IV
out, _ := spongef.SpongeFWithIV(iv, message, 64)
// Streaming
ctx := spongef.NewSpongeFContext()
ctx.Write(chunk1)
ctx.Write(chunk2)
hash := ctx.Sum(32)Parameters: rate r = 256 bits, capacity c = 256 bits, Areion512 permutation (b = 512 bits). Minimum output size is 32 bytes.
Reference: ePrint 2025/1006
Hardware acceleration provides ~10x speedup over pure Go. VAES with AVX512 processes four blocks in roughly the time of one. Multi-round functions add another 2-3x improvement.
go test -bench=.| Function | Description |
|---|---|
Round(block, key *Block) |
Forward round (key XOR at end) |
InvRound(block, key *Block) |
Inverse round |
FinalRound(block, key *Block) |
Final round (no MixColumns) |
RoundKeyFirst(block, key *Block) |
Key XOR at beginning |
RoundNoKey(block *Block) |
No key XOR |
RoundHW(block, key *Block) |
Hardware-accelerated |
SubBytes, ShiftRows, MixColumns, AddRoundKey and their inverse variants.
| Function | Description |
|---|---|
Round2(Block2, Key2) Block2 |
Process 2 blocks |
Round4(Block4, Key4) Block4 |
Process 4 blocks |
Available in standard, KeyFirst, NoKey, and HW variants.
Rounds4, Rounds6, Rounds7, Rounds10, Rounds12, Rounds14 with inverse, NoKey, HW, and WithFinal variants.
| Function | Description |
|---|---|
KeyExpansion128([16]byte) [176]byte |
AES-128 key expansion |
KeyExpansion192([24]byte) [208]byte |
AES-192 key expansion |
KeyExpansion256([32]byte) [240]byte |
AES-256 key expansion |
NewKeySchedule([]byte) (*KeySchedule, error) |
Create key schedule |
EncryptBlockAES128, EncryptBlockAES192, EncryptBlockAES256, EncryptBlockAES for full block encryption.
| Construction | Key Functions |
|---|---|
| Areion | Areion256.Permute/InversePermute, Areion512.Permute/InversePermute, Areion256DM, Areion512DM, Areion256EM/EMDecrypt, Areion512EM/EMDecrypt, AreionSoEM256, AreionSoEM512 |
| AES-PRF | NewAESPRF, (*AESPRF).PRF |
| Haraka | Haraka256, Haraka512, Haraka256ToBlock, Haraka512ToBlock |
| KIASU-BC | NewKiasuContext, KiasuEncrypt, KiasuDecrypt |
| Deoxys-BC-256 | NewDeoxysBC256, DeoxysBC256Encrypt, DeoxysBC256Decrypt |
| ButterKnife | ButterKnife, NewButterKnifeContext, (*ButterKnifeContext).Eval |
| Pholkos | NewPholkos256Context, NewPholkos512Context, Pholkos256Encrypt/Decrypt |
| Vistrutah | Vistrutah256Encrypt/Decrypt, Vistrutah512Encrypt/Decrypt, Vistrutah256MP, Vistrutah512MP |
Skye, NewSkyeContext, DExtLsb, FExp
go test -v # Run all tests
go test -v -run TestName # Run specific test
go test -bench=. # Run benchmarksThe test suite includes FIPS-197 test vectors and cross-platform tests ensuring hardware and software implementations produce identical results.