A single AES block.
pub const Block = struct
pub const Block = struct {
const Repr = @Vector(2, u64);
/// The length of an AES block in bytes.
pub const block_length: usize = 16;
/// Internal representation of a block.
repr: Repr,
/// Convert a byte sequence into an internal representation.
pub fn fromBytes(bytes: *const [16]u8) Block {
const repr = mem.bytesToValue(Repr, bytes);
return Block{ .repr = repr };
}
/// Convert the internal representation of a block into a byte sequence.
pub fn toBytes(block: Block) [16]u8 {
return mem.toBytes(block.repr);
}
/// XOR the block with a byte sequence.
pub fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 {
const x = block.repr ^ fromBytes(bytes).repr;
return mem.toBytes(x);
}
/// Encrypt a block with a round key.
pub fn encrypt(block: Block, round_key: Block) Block {
return Block{
.repr = asm (
\\ vaesenc %[rk], %[in], %[out]
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
[rk] "x" (round_key.repr),
),
};
}
/// Encrypt a block with the last round key.
pub fn encryptLast(block: Block, round_key: Block) Block {
return Block{
.repr = asm (
\\ vaesenclast %[rk], %[in], %[out]
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
[rk] "x" (round_key.repr),
),
};
}
/// Decrypt a block with a round key.
pub fn decrypt(block: Block, inv_round_key: Block) Block {
return Block{
.repr = asm (
\\ vaesdec %[rk], %[in], %[out]
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
[rk] "x" (inv_round_key.repr),
),
};
}
/// Decrypt a block with the last round key.
pub fn decryptLast(block: Block, inv_round_key: Block) Block {
return Block{
.repr = asm (
\\ vaesdeclast %[rk], %[in], %[out]
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
[rk] "x" (inv_round_key.repr),
),
};
}
/// Apply the bitwise XOR operation to the content of two blocks.
pub fn xorBlocks(block1: Block, block2: Block) Block {
return Block{ .repr = block1.repr ^ block2.repr };
}
/// Apply the bitwise AND operation to the content of two blocks.
pub fn andBlocks(block1: Block, block2: Block) Block {
return Block{ .repr = block1.repr & block2.repr };
}
/// Apply the bitwise OR operation to the content of two blocks.
pub fn orBlocks(block1: Block, block2: Block) Block {
return Block{ .repr = block1.repr | block2.repr };
}
/// Apply the inverse MixColumns operation to a block.
pub fn invMixColumns(block: Block) Block {
return Block{
.repr = asm (
\\ vaesimc %[in], %[out]
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
),
};
}
/// Perform operations on multiple blocks in parallel.
pub const parallel = struct {
const cpu = std.Target.x86.cpu;
/// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
pub const optimal_parallel_blocks = switch (builtin.cpu.model) {
&cpu.westmere, &cpu.goldmont => 3,
&cpu.cannonlake, &cpu.skylake, &cpu.skylake_avx512, &cpu.tremont, &cpu.goldmont_plus, &cpu.cascadelake => 4,
&cpu.icelake_client, &cpu.icelake_server, &cpu.tigerlake, &cpu.rocketlake, &cpu.alderlake => 6,
&cpu.haswell, &cpu.broadwell => 7,
&cpu.sandybridge, &cpu.ivybridge => 8,
&cpu.znver1, &cpu.znver2, &cpu.znver3, &cpu.znver4 => 8,
else => 8,
};
/// Encrypt multiple blocks in parallel, each their own round key.
pub fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
out[i] = blocks[i].encrypt(round_keys[i]);
}
return out;
}
/// Decrypt multiple blocks in parallel, each their own round key.
pub fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
out[i] = blocks[i].decrypt(round_keys[i]);
}
return out;
}
/// Encrypt multiple blocks in parallel with the same round key.
pub fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
out[i] = blocks[i].encrypt(round_key);
}
return out;
}
/// Decrypt multiple blocks in parallel with the same round key.
pub fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
out[i] = blocks[i].decrypt(round_key);
}
return out;
}
/// Encrypt multiple blocks in parallel with the same last round key.
pub fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
out[i] = blocks[i].encryptLast(round_key);
}
return out;
}
/// Decrypt multiple blocks in parallel with the same last round key.
pub fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
out[i] = blocks[i].decryptLast(round_key);
}
return out;
}
};
}