Refactor ChaCha into a saner interface.
This commit is contained in:
@ -6,6 +6,12 @@ const testing = std.testing;
|
||||
const CHACHA20_BLOCK_SIZE = 512 / 8;
|
||||
const CHACHA20_BLOCK_WORDS = CHACHA20_BLOCK_SIZE / 4;
|
||||
|
||||
const CHACHA20_KEY_SIZE = 256 / 8;
|
||||
const CHACHA20_KEY_WORDS = CHACHA20_KEY_SIZE / 4;
|
||||
|
||||
const CHACHA20_NONCE_SIZE = 128 / 8;
|
||||
const CHACHA20_NONCE_WORDS = CHACHA20_NONCE_SIZE / 4;
|
||||
|
||||
const CHACHA20_CONSTANTS = [128 / 8 / 4]u32{
|
||||
bytes_to_word_le("expa"),
|
||||
bytes_to_word_le("nd 3"),
|
||||
@ -14,65 +20,64 @@ const CHACHA20_CONSTANTS = [128 / 8 / 4]u32{
|
||||
};
|
||||
|
||||
pub const ChaCha20_Bernstein_Parameters = struct {
|
||||
pub const KEY_SIZE = 256 / 8;
|
||||
pub const NONCE_SIZE = 64 / 8;
|
||||
pub const COUNTER_SIZE = 64 / 8;
|
||||
};
|
||||
|
||||
pub const ChaCha20_RFC7539_Parameters = struct {
|
||||
pub const KEY_SIZE = 256 / 8;
|
||||
pub const NONCE_SIZE = 96 / 8;
|
||||
pub const COUNTER_SIZE = 32 / 8;
|
||||
};
|
||||
|
||||
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
||||
|
||||
pub const ChaCha20_Bernstein_Ctx = struct {
|
||||
pub const ChaCha20Ctx = struct {
|
||||
key: [CHACHA20_KEY_WORDS]u32,
|
||||
nonce: [CHACHA20_NONCE_WORDS]u32,
|
||||
state: [CHACHA20_BLOCK_WORDS]u32,
|
||||
key: [ChaCha20_Bernstein_Parameters.KEY_SIZE / 4]u32,
|
||||
nonce: [ChaCha20_Bernstein_Parameters.NONCE_SIZE / 4]u32,
|
||||
counter: [ChaCha20_Bernstein_Parameters.COUNTER_SIZE / 4]u32,
|
||||
working_state: [CHACHA20_BLOCK_WORDS]u32,
|
||||
keystream_idx: u6,
|
||||
};
|
||||
|
||||
pub const ChaCha20_RFC7539_Ctx = struct {
|
||||
state: [CHACHA20_BLOCK_WORDS]u32,
|
||||
key: [ChaCha20_RFC7539_Parameters.KEY_SIZE / 4]u32,
|
||||
nonce: [ChaCha20_RFC7539_Parameters.NONCE_SIZE / 4]u32,
|
||||
counter: [ChaCha20_RFC7539_Parameters.COUNTER_SIZE / 4]u32,
|
||||
};
|
||||
|
||||
pub fn chacha20_bernstein_new(
|
||||
key: *const [ChaCha20_Bernstein_Parameters.KEY_SIZE]u8,
|
||||
nonce: *const [ChaCha20_Bernstein_Parameters.NONCE_SIZE]u8,
|
||||
counter: *const [ChaCha20_Bernstein_Parameters.COUNTER_SIZE]u8,
|
||||
) ChaCha20_Bernstein_Ctx {
|
||||
var ctx = ChaCha20_Bernstein_Ctx{
|
||||
.state = undefined,
|
||||
pub fn chacha20_new(key: *const [CHACHA20_KEY_SIZE]u8, nonce: *const [CHACHA20_NONCE_SIZE]u8) ChaCha20Ctx {
|
||||
var ctx = ChaCha20Ctx{
|
||||
.key = undefined,
|
||||
.nonce = undefined,
|
||||
.counter = undefined,
|
||||
.state = undefined,
|
||||
.working_state = undefined,
|
||||
.keystream_idx = undefined,
|
||||
};
|
||||
chacha20_deserialize(ChaCha20_Bernstein_Parameters.KEY_SIZE / 4, key, &ctx.key);
|
||||
chacha20_deserialize(ChaCha20_Bernstein_Parameters.NONCE_SIZE / 4, nonce, &ctx.nonce);
|
||||
chacha20_deserialize(ChaCha20_Bernstein_Parameters.COUNTER_SIZE / 4, counter, &ctx.counter);
|
||||
|
||||
chacha20_deserialize(CHACHA20_KEY_WORDS, key, &ctx.key);
|
||||
chacha20_deserialize(CHACHA20_NONCE_WORDS, nonce, &ctx.nonce);
|
||||
|
||||
chacha20_block_function(&ctx);
|
||||
ctx.keystream_idx = 0;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
pub fn chacha20_destroy(ctx: *ChaCha20Ctx) void {
|
||||
@memset(ctx.state, 0);
|
||||
@memset(ctx.key, 0);
|
||||
@memset(ctx.nonce, 0);
|
||||
ctx.keystream_idx = 0;
|
||||
}
|
||||
|
||||
pub fn chacha20_bernstein_new(
|
||||
key: *const [CHACHA20_KEY_SIZE]u8,
|
||||
nonce: *const [ChaCha20_Bernstein_Parameters.NONCE_SIZE]u8,
|
||||
counter: *const [ChaCha20_Bernstein_Parameters.COUNTER_SIZE]u8,
|
||||
) ChaCha20Ctx {
|
||||
return chacha20_new(key, counter ++ nonce);
|
||||
}
|
||||
|
||||
pub fn chacha20_rfc7539_new(
|
||||
key: *const [ChaCha20_RFC7539_Parameters.KEY_SIZE]u8,
|
||||
key: *const [CHACHA20_KEY_SIZE]u8,
|
||||
nonce: *const [ChaCha20_RFC7539_Parameters.NONCE_SIZE]u8,
|
||||
counter: *const [ChaCha20_RFC7539_Parameters.COUNTER_SIZE]u8,
|
||||
) ChaCha20_RFC7539_Ctx {
|
||||
var ctx = ChaCha20_RFC7539_Ctx{
|
||||
.state = undefined,
|
||||
.key = undefined,
|
||||
.nonce = undefined,
|
||||
.counter = undefined,
|
||||
};
|
||||
chacha20_deserialize(ChaCha20_RFC7539_Parameters.KEY_SIZE / 4, key, &ctx.key);
|
||||
chacha20_deserialize(ChaCha20_RFC7539_Parameters.NONCE_SIZE / 4, nonce, &ctx.nonce);
|
||||
chacha20_deserialize(ChaCha20_RFC7539_Parameters.COUNTER_SIZE / 4, counter, &ctx.counter);
|
||||
return ctx;
|
||||
) ChaCha20Ctx {
|
||||
return chacha20_new(key, counter ++ nonce);
|
||||
}
|
||||
|
||||
pub fn chacha20_quarter_round(
|
||||
@ -117,69 +122,38 @@ pub fn chacha20_inner_block(state: *[CHACHA20_BLOCK_WORDS]u32) void {
|
||||
chacha20_quarter_round(state, 3, 4, 9, 14);
|
||||
}
|
||||
|
||||
pub fn chacha20_block_function(
|
||||
key_size: comptime_int,
|
||||
nonce_size: comptime_int,
|
||||
counter_size: comptime_int,
|
||||
state: *[CHACHA20_BLOCK_WORDS]u32,
|
||||
key: *const [key_size / 4]u32,
|
||||
nonce: *const [nonce_size / 4]u32,
|
||||
counter: *const [counter_size / 4]u32,
|
||||
) void {
|
||||
pub fn chacha20_block_function(ctx: *ChaCha20Ctx) void {
|
||||
// Reset state.
|
||||
{
|
||||
comptime var i = 0;
|
||||
@memcpy(state[i .. i + CHACHA20_CONSTANTS.len], &CHACHA20_CONSTANTS);
|
||||
|
||||
@memcpy(ctx.state[i .. i + CHACHA20_CONSTANTS.len], &CHACHA20_CONSTANTS);
|
||||
i += CHACHA20_CONSTANTS.len;
|
||||
@memcpy(state[i .. i + key_size / 4], key[0..]);
|
||||
i += key_size / 4;
|
||||
@memcpy(state[i .. i + counter_size / 4], counter[0..]);
|
||||
i += counter_size / 4;
|
||||
@memcpy(state[i .. i + nonce_size / 4], nonce[0..]);
|
||||
i += nonce_size / 4;
|
||||
|
||||
@memcpy(ctx.state[i .. i + CHACHA20_KEY_WORDS], ctx.key[0..]);
|
||||
i += CHACHA20_KEY_WORDS;
|
||||
|
||||
@memcpy(ctx.state[i .. i + CHACHA20_NONCE_WORDS], ctx.nonce[0..]);
|
||||
i += CHACHA20_NONCE_WORDS;
|
||||
|
||||
if (comptime i != CHACHA20_BLOCK_WORDS)
|
||||
@panic("Invalid ChaCha20 parameters: |constants + key + nonce + counter| != block_size!");
|
||||
}
|
||||
|
||||
// Copy state to working_state.
|
||||
var working_state: [CHACHA20_BLOCK_WORDS]u32 = undefined;
|
||||
@memcpy(&working_state, state);
|
||||
@memcpy(&ctx.working_state, &ctx.state);
|
||||
|
||||
// Perform all 20 rounds (10 column rounds and 10 diagonal rounds).
|
||||
for (0..10) |_| {
|
||||
chacha20_inner_block(state);
|
||||
chacha20_inner_block(&ctx.working_state);
|
||||
}
|
||||
|
||||
// Add the working_state to the state.
|
||||
for (0..CHACHA20_BLOCK_WORDS) |i| {
|
||||
state[i] +%= working_state[i];
|
||||
ctx.state[i] +%= ctx.working_state[i];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chacha20_bernstein_block_function(ctx: *ChaCha20_Bernstein_Ctx) void {
|
||||
chacha20_block_function(
|
||||
ChaCha20_Bernstein_Parameters.KEY_SIZE,
|
||||
ChaCha20_Bernstein_Parameters.NONCE_SIZE,
|
||||
ChaCha20_Bernstein_Parameters.COUNTER_SIZE,
|
||||
&ctx.state,
|
||||
&ctx.key,
|
||||
&ctx.nonce,
|
||||
&ctx.counter,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn chacha20_rfc7539_block_function(ctx: *ChaCha20_RFC7539_Ctx) void {
|
||||
chacha20_block_function(
|
||||
ChaCha20_RFC7539_Parameters.KEY_SIZE,
|
||||
ChaCha20_RFC7539_Parameters.NONCE_SIZE,
|
||||
ChaCha20_RFC7539_Parameters.COUNTER_SIZE,
|
||||
&ctx.state,
|
||||
&ctx.key,
|
||||
&ctx.nonce,
|
||||
&ctx.counter,
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------- LITTLE ENDIAN HELPERS ----------------------------------- //
|
||||
|
||||
fn chacha20_serialize(L: comptime_int, words: *const [L]u32, bytes: *[L * 4]u8) void {
|
||||
@ -273,7 +247,7 @@ test "ChaCha20 Block Function" {
|
||||
0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e,
|
||||
};
|
||||
|
||||
chacha20_rfc7539_block_function(&chacha);
|
||||
chacha20_block_function(&chacha);
|
||||
|
||||
var buffer: [CHACHA20_BLOCK_SIZE]u8 = undefined;
|
||||
chacha20_serialize(CHACHA20_BLOCK_WORDS, &chacha.state, &buffer);
|
||||
|
||||
Reference in New Issue
Block a user