Counter mode with configurable counter position and size.
This extended version allows specifying where the counter is located within the IV block and how many bytes it occupies. This is useful for modes like AES-GCM-SIV which use a 32-bit counter at the beginning of the block.
@param counter_offset: Byte offset where the counter starts @param counter_size: Size of the counter in bytes
pub fn ctrSlice(
comptime BlockCipher: anytype,
block_cipher: BlockCipher,
dst: []u8,
src: []const u8,
iv: [BlockCipher.block_length]u8,
endian: std.builtin.Endian,
comptime counter_offset: usize,
comptime counter_size: usize,
) void
pub fn ctrSlice(
comptime BlockCipher: anytype,
block_cipher: BlockCipher,
dst: []u8,
src: []const u8,
iv: [BlockCipher.block_length]u8,
endian: std.builtin.Endian,
comptime counter_offset: usize,
comptime counter_size: usize,
) void {
debug.assert(dst.len >= src.len);
const block_length = BlockCipher.block_length;
debug.assert(counter_offset + counter_size <= block_length);
debug.assert(counter_size > 0 and counter_size <= block_length);
var counterBlock = iv;
var i: usize = 0;
const CounterInt = @Int(.unsigned, counter_size * 8);
const parallel_count = BlockCipher.block.parallel.optimal_parallel_blocks;
const wide_block_length = parallel_count * block_length;
var cnt_val = mem.readInt(CounterInt, counterBlock[counter_offset..][0..counter_size], endian);
if (src.len >= wide_block_length) {
var counters: [parallel_count * block_length]u8 = undefined;
inline for (0..parallel_count) |j| {
counters[j * block_length ..][0..block_length].* = iv;
}
while (i + wide_block_length <= src.len) : (i += wide_block_length) {
comptime var j = 0;
inline while (j < parallel_count) : (j += 1) {
mem.writeInt(CounterInt, counters[j * block_length + counter_offset ..][0..counter_size], cnt_val +% j, endian);
}
cnt_val +%= parallel_count;
block_cipher.xorWide(parallel_count, dst[i .. i + wide_block_length][0..wide_block_length], src[i .. i + wide_block_length][0..wide_block_length], counters);
}
mem.writeInt(CounterInt, counterBlock[counter_offset..][0..counter_size], cnt_val, endian);
}
while (i + block_length <= src.len) : (i += block_length) {
block_cipher.xor(dst[i .. i + block_length][0..block_length], src[i .. i + block_length][0..block_length], counterBlock);
cnt_val +%= 1;
mem.writeInt(CounterInt, counterBlock[counter_offset..][0..counter_size], cnt_val, endian);
}
if (i < src.len) {
var pad: [block_length]u8 = @splat(0);
const src_slice = src[i..];
@memcpy(pad[0..src_slice.len], src_slice);
block_cipher.xor(&pad, &pad, counterBlock);
const pad_slice = pad[0 .. src.len - i];
@memcpy(dst[i..][0..pad_slice.len], pad_slice);
}
}