* Reorganize crypto/aes in order to separate parameters, implementations and modes. * Add a zero-cost abstraction over the internal representation of a block, so that blocks can be kept in vector registers in optimized implementations. * Add architecture-independent aesenc/aesdec/aesenclast/aesdeclast operations, so that any AES-based primitive can be implemented, including these that don't use the original key schedule (AES-PRF, AEGIS, MeowHash...) * Add support for parallelization/wide blocks to take advantage of hardware implementations. * Align T-tables to cache lines in the software implementations to slightly reduce side channels. * Add an optimized implementation for modern Intel CPUs with AES-NI. * Add new tests (AES256 key expansion). * Reimplement the counter mode to work with any block cipher, any endianness and to take advantage of wide blocks. * Add benchmarks for AES.
52 lines
2.4 KiB
Zig
52 lines
2.4 KiB
Zig
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2015-2020 Zig Contributors
|
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
|
// The MIT license requires this copyright notice to be included in all copies
|
|
// and substantial portions of the software.
|
|
// Based on Go stdlib implementation
|
|
|
|
const std = @import("../std.zig");
|
|
const builtin = std.builtin;
|
|
const mem = std.mem;
|
|
const debug = std.debug;
|
|
|
|
/// Counter mode.
|
|
///
|
|
/// This mode creates a key stream by encrypting an incrementing counter using a block cipher, and adding it to the source material.
|
|
///
|
|
/// Important: the counter mode doesn't provide authenticated encryption: the ciphertext can be trivially modified without this being detected.
|
|
/// As a result, applications should generally never use it directly, but only in a construction that includes a MAC.
|
|
pub fn ctr(comptime BlockCipher: anytype, block_cipher: BlockCipher, dst: []u8, src: []const u8, iv: [BlockCipher.block_size]u8, endian: comptime builtin.Endian) void {
|
|
debug.assert(dst.len >= src.len);
|
|
const block_size = BlockCipher.block_size;
|
|
var counter: [BlockCipher.block_size]u8 = undefined;
|
|
var counterInt = mem.readInt(u128, &iv, endian);
|
|
var i: usize = 0;
|
|
|
|
const parallel_count = BlockCipher.block.parallel.optimal_parallel_blocks;
|
|
const wide_block_size = parallel_count * 16;
|
|
if (src.len >= wide_block_size) {
|
|
var counters: [parallel_count * 16]u8 = undefined;
|
|
while (i + wide_block_size <= src.len) : (i += wide_block_size) {
|
|
comptime var j = 0;
|
|
inline while (j < parallel_count) : (j += 1) {
|
|
mem.writeInt(u128, counters[j * 16 .. j * 16 + 16], counterInt, endian);
|
|
counterInt +%= 1;
|
|
}
|
|
block_cipher.xorWide(parallel_count, dst[i .. i + wide_block_size][0..wide_block_size], src[i .. i + wide_block_size][0..wide_block_size], counters);
|
|
}
|
|
}
|
|
while (i + block_size <= src.len) : (i += block_size) {
|
|
mem.writeInt(u128, &counter, counterInt, endian);
|
|
counterInt +%= 1;
|
|
block_cipher.xor(dst[i .. i + block_size][0..block_size], src[i .. i + block_size][0..block_size], counter);
|
|
}
|
|
if (i < src.len) {
|
|
mem.writeInt(u128, &counter, counterInt, endian);
|
|
var pad = [_]u8{0} ** block_size;
|
|
mem.copy(u8, &pad, src[i..]);
|
|
block_cipher.xor(&pad, &pad, counter);
|
|
mem.copy(u8, dst[i..], pad[0 .. src.len - i]);
|
|
}
|
|
}
|