misc: Code housekeeping, file restructuring.

This commit is contained in:
Milan Špinka
2025-01-30 00:07:32 +01:00
parent 2852ce4685
commit 4b96179ac9
12 changed files with 147 additions and 111 deletions

View File

@ -22,8 +22,8 @@ Most (**theoretical!**) users should directly use one of the cryptographic *prot
- Advanced Encryption Standard (FIPS 197): AES-128, AES-192, AES-256
- ChaCha20 (RFC 7539): ChaCha20 with 64-bit nonce and 64-bit counter, ChaCha20 with 96-bit nonce and 32-bit counter
- Salsa20: Salsa20/20 with 256-key, Salsa20/20 with 128-bit key
- Secure Hashing Algorithm: SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256
- Salsa20 (Bernstein, not standardized): Salsa20/20 with 256-key, Salsa20/20 with 128-bit key
- Secure Hash Standard (FIPS 180-4): SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256
### Protocols
@ -40,7 +40,7 @@ Most (**theoretical!**) users should directly use one of the cryptographic *prot
- DES, 3DES
- Block cipher modes: CBC-PKCS7, CFB, OFB, CTR, GCM
- Poly1305
- SHA-3, HMAC
- SHA-512/t, SHA-3, HMAC
- BigIntegers & modular arithmetic
- Cryptographically secure random BigInteger generation & primality testing
- Elliptic Curve groups (over Fp fields)

View File

@ -67,7 +67,7 @@ pub fn build(b: *std.Build) void {
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/root.zig"),
.root_source_file = b.path("src/test.zig"),
.target = target,
.optimize = optimize,
});

View File

@ -63,7 +63,12 @@ const AES_RCON = [_]u32{
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
pub fn aes_encrypt_block(n_rounds: comptime_int, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8, expanded_key: *const [4 * (n_rounds + 1)]u32) void {
pub fn aes_encrypt_block(
n_rounds: comptime_int,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
expanded_key: *const [4 * (n_rounds + 1)]u32,
) void {
// Copy input buffer into state (we're treating the buffer as a column-first matrix).
var state: [AES_BLOCK_SIZE]u8 = undefined;
@memcpy(state[0..], block_in);
@ -120,63 +125,121 @@ pub fn aes_decrypt_block(n_rounds: comptime_int, block_in: *const [AES_BLOCK_SIZ
@memset(state[0..], 0);
}
pub fn aes_128_encrypt_block(key: *const [Aes128Parameters.KEY_SIZE]u8, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8) void {
pub fn aes_128_encrypt_block(
key: *const [Aes128Parameters.KEY_SIZE]u8,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
) void {
// Prepare the subkeys for AddRoundKey.
var expanded_key = aes_128_expand_key(key);
defer @memset(&expanded_key, 0);
// Call the generic encryption procedure.
aes_encrypt_block(Aes128Parameters.N_ROUNDS, block_in, block_out, &expanded_key);
aes_encrypt_block(
Aes128Parameters.N_ROUNDS,
block_in,
block_out,
&expanded_key,
);
}
pub fn aes_128_decrypt_block(key: *const [Aes128Parameters.KEY_SIZE]u8, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8) void {
pub fn aes_128_decrypt_block(
key: *const [Aes128Parameters.KEY_SIZE]u8,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
) void {
// Prepare the subkeys for AddRoundKey.
var expanded_key = aes_128_expand_key(key);
defer @memset(&expanded_key, 0);
// Call the generic decryption procedure.
aes_decrypt_block(Aes128Parameters.N_ROUNDS, block_in, block_out, &expanded_key);
aes_decrypt_block(
Aes128Parameters.N_ROUNDS,
block_in,
block_out,
&expanded_key,
);
}
pub fn aes_192_encrypt_block(key: *const [Aes192Parameters.KEY_SIZE]u8, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8) void {
pub fn aes_192_encrypt_block(
key: *const [Aes192Parameters.KEY_SIZE]u8,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
) void {
// Prepare the subkeys for AddRoundKey.
var expanded_key = aes_192_expand_key(key);
defer @memset(&expanded_key, 0);
// Call the generic encryption procedure.
aes_encrypt_block(Aes192Parameters.N_ROUNDS, block_in, block_out, &expanded_key);
aes_encrypt_block(
Aes192Parameters.N_ROUNDS,
block_in,
block_out,
&expanded_key,
);
}
pub fn aes_192_decrypt_block(key: *const [Aes192Parameters.KEY_SIZE]u8, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8) void {
pub fn aes_192_decrypt_block(
key: *const [Aes192Parameters.KEY_SIZE]u8,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
) void {
// Prepare the subkeys for AddRoundKey.
var expanded_key = aes_192_expand_key(key);
defer @memset(&expanded_key, 0);
// Call the generic decryption procedure.
aes_decrypt_block(Aes192Parameters.N_ROUNDS, block_in, block_out, &expanded_key);
aes_decrypt_block(
Aes192Parameters.N_ROUNDS,
block_in,
block_out,
&expanded_key,
);
}
pub fn aes_256_encrypt_block(key: *const [Aes256Parameters.KEY_SIZE]u8, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8) void {
pub fn aes_256_encrypt_block(
key: *const [Aes256Parameters.KEY_SIZE]u8,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
) void {
// Prepare the subkeys for AddRoundKey.
var expanded_key = aes_256_expand_key(key);
defer @memset(&expanded_key, 0);
// Call the generic encryption procedure.
aes_encrypt_block(Aes256Parameters.N_ROUNDS, block_in, block_out, &expanded_key);
aes_encrypt_block(
Aes256Parameters.N_ROUNDS,
block_in,
block_out,
&expanded_key,
);
}
pub fn aes_256_decrypt_block(key: *const [Aes256Parameters.KEY_SIZE]u8, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8) void {
pub fn aes_256_decrypt_block(
key: *const [Aes256Parameters.KEY_SIZE]u8,
block_in: *const [AES_BLOCK_SIZE]u8,
block_out: *[AES_BLOCK_SIZE]u8,
) void {
// Prepare the subkeys for AddRoundKey.
var expanded_key = aes_256_expand_key(key);
defer @memset(&expanded_key, 0);
// Call the generic decryption procedure.
aes_decrypt_block(Aes256Parameters.N_ROUNDS, block_in, block_out, &expanded_key);
aes_decrypt_block(
Aes256Parameters.N_ROUNDS,
block_in,
block_out,
&expanded_key,
);
}
// ----------------------------------- KEY EXPANSION ----------------------------------- //
pub fn aes_expand_key(n_rounds: comptime_int, n_key_words: comptime_int, key: *const [n_key_words * 4]u8) [4 * (n_rounds + 1)]u32 {
pub fn aes_expand_key(
n_rounds: comptime_int,
n_key_words: comptime_int,
key: *const [n_key_words * 4]u8,
) [4 * (n_rounds + 1)]u32 {
var expanded_key: [4 * (n_rounds + 1)]u32 = undefined;
var i: u32 = 0;
@ -197,15 +260,27 @@ pub fn aes_expand_key(n_rounds: comptime_int, n_key_words: comptime_int, key: *c
}
pub fn aes_128_expand_key(key: *const [Aes128Parameters.KEY_SIZE]u8) [4 * (Aes128Parameters.N_ROUNDS + 1)]u32 {
return aes_expand_key(Aes128Parameters.N_ROUNDS, Aes128Parameters.KEY_SIZE / 4, key);
return aes_expand_key(
Aes128Parameters.N_ROUNDS,
Aes128Parameters.KEY_SIZE / 4,
key,
);
}
pub fn aes_192_expand_key(key: *const [Aes192Parameters.KEY_SIZE]u8) [4 * (Aes192Parameters.N_ROUNDS + 1)]u32 {
return aes_expand_key(Aes192Parameters.N_ROUNDS, Aes192Parameters.KEY_SIZE / 4, key);
return aes_expand_key(
Aes192Parameters.N_ROUNDS,
Aes192Parameters.KEY_SIZE / 4,
key,
);
}
pub fn aes_256_expand_key(key: *const [Aes256Parameters.KEY_SIZE]u8) [4 * (Aes256Parameters.N_ROUNDS + 1)]u32 {
return aes_expand_key(Aes256Parameters.N_ROUNDS, Aes256Parameters.KEY_SIZE / 4, key);
return aes_expand_key(
Aes256Parameters.N_ROUNDS,
Aes256Parameters.KEY_SIZE / 4,
key,
);
}
// ----------------------------------- AES OPERATIONS ----------------------------------- //
@ -373,15 +448,12 @@ fn gfmult(factor: comptime_int, element: u8) u8 {
fn word_to_bytes(word: u32) [4]u8 {
var bytes: [4]u8 = undefined;
bytes[0] = @truncate(word >> 24);
bytes[1] = @truncate(word >> 16);
bytes[2] = @truncate(word >> 8);
bytes[3] = @truncate(word);
std.mem.writeInt(u32, &bytes, word, .big);
return bytes;
}
fn bytes_to_word(bytes: *const [4]u8) u32 {
return (@as(u32, bytes[0]) << 24) | (@as(u32, bytes[1]) << 16) | (@as(u32, bytes[2]) << 8) | @as(u32, bytes[3]);
return std.mem.readInt(u32, bytes, .big);
}
// ----------------------------------- TEST VECTORS ----------------------------------- //
@ -631,7 +703,7 @@ test "AES SubBytes" {
};
aes_sub_bytes(&state);
try testing.expect(std.mem.eql(u8, &state, &reference));
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES ShiftRows" {
@ -645,7 +717,7 @@ test "AES ShiftRows" {
};
aes_shift_rows(&state);
try testing.expect(std.mem.eql(u8, &state, &reference));
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES MixColumns" {
@ -659,7 +731,7 @@ test "AES MixColumns" {
};
aes_mix_columns(&state);
try testing.expect(std.mem.eql(u8, &state, &reference));
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES InvSubBytes" {
@ -673,7 +745,7 @@ test "AES InvSubBytes" {
};
aes_inv_sub_bytes(&state);
try testing.expect(std.mem.eql(u8, &state, &reference));
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES InvShiftRows" {
@ -687,7 +759,7 @@ test "AES InvShiftRows" {
};
aes_inv_shift_rows(&state);
try testing.expect(std.mem.eql(u8, &state, &reference));
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES InvMixColumns" {
@ -701,5 +773,5 @@ test "AES InvMixColumns" {
};
aes_inv_mix_columns(&state);
try testing.expect(std.mem.eql(u8, &state, &reference));
try testing.expectEqualSlices(u8, &reference, &state);
}

View File

@ -4,15 +4,12 @@ const testing = std.testing;
// ----------------------------------- DES CONSTANTS ----------------------------------- //
pub const DES_BLOCK_SIZE = 64 / 8;
pub const DES_TRUE_KEY_SIZE = 56 / 8;
pub const DES_ENCODED_KEY_SIZE = 64 / 8;
pub const DES_N_ROUNDS = 16;
pub const DES_SUBKEY_SIZE = 48 / 8;
pub const DES_N_ROUNDS = 16;
pub const DES_INITIAL_PERMUTATION = [_]u6{
const DES_INITIAL_PERMUTATION = [_]u6{
58, 50, 42, 35, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
@ -23,7 +20,7 @@ pub const DES_INITIAL_PERMUTATION = [_]u6{
63, 55, 47, 39, 31, 23, 15, 7,
};
pub const DES_INV_INITIAL_PERMUTATION = [_]u6{
const DES_INV_INITIAL_PERMUTATION = [_]u6{
40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
@ -34,7 +31,7 @@ pub const DES_INV_INITIAL_PERMUTATION = [_]u6{
33, 1, 41, 9, 49, 17, 57, 25,
};
pub const DES_BIT_SELECTION_TABLE_E = [_]u5{
const DES_BIT_SELECTION_TABLE_E = [_]u5{
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
@ -45,14 +42,14 @@ pub const DES_BIT_SELECTION_TABLE_E = [_]u5{
28, 29, 30, 31, 32, 1,
};
pub const DES_PERMUTATION_FUNCTION_P = [_]u5{
const DES_PERMUTATION_FUNCTION_P = [_]u5{
16, 7, 20, 21, 29, 12, 28, 17,
1, 15, 23, 26, 5, 18, 31, 10,
2, 8, 24, 14, 32, 27, 3, 9,
19, 13, 30, 6, 22, 11, 4, 25,
};
pub const DES_KS_SHIFT_SCHEDULE = .{ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
const DES_KS_SHIFT_SCHEDULE = .{ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
@ -192,4 +189,4 @@ fn rotate_halves_left(cd: *[DES_TRUE_KEY_SIZE]u8, positions: comptime_int) void
// ----------------------------------- TEST VECTORS ----------------------------------- //
//
// TODO

View File

@ -0,0 +1,2 @@
pub const aes = @import("aes.zig");
pub const des = @import("des.zig");

View File

@ -0,0 +1,2 @@
pub const sha1 = @import("sha_1.zig");
pub const sha2 = @import("sha_2.zig");

3
src/primitive/index.zig Normal file
View File

@ -0,0 +1,3 @@
pub const blockcipher = @import("blockcipher/index.zig");
pub const digest = @import("digest/index.zig");
pub const streamcipher = @import("streamcipher/index.zig");

View File

@ -222,40 +222,22 @@ pub fn chacha20_block_function(ctx: *ChaCha20Ctx) void {
// ----------------------------------- LITTLE ENDIAN HELPERS ----------------------------------- //
fn chacha20_serialize(L: comptime_int, words: *const [L]u32, bytes: *[L * 4]u8) void {
if (comptime @import("builtin").target.cpu.arch.endian() == .little) {
@memcpy(bytes, @as(*const [L * 4]u8, @ptrCast(words)));
} else {
var tmp: [4]u8 = undefined;
for (0..L) |i| {
tmp = word_to_bytes_le(words[i]);
bytes[i * 4] = tmp[0];
bytes[i * 4 + 1] = tmp[1];
bytes[i * 4 + 2] = tmp[2];
bytes[i * 4 + 3] = tmp[3];
}
}
for (0..L) |i|
std.mem.writeInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), words[i], .little);
}
fn chacha20_deserialize(L: comptime_int, bytes: *const [L * 4]u8, words: *[L]u32) void {
if (comptime @import("builtin").target.cpu.arch.endian() == .little) {
@memcpy(@as(*[L * 4]u8, @ptrCast(words)), bytes);
} else {
for (0..L) |i| {
words[i] = bytes_to_word_le(@ptrCast(bytes[(i * 4)..(i * 4 + 4)]));
}
}
for (0..L) |i|
words[i] = std.mem.readInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), .little);
}
fn bytes_to_word_le(bytes: *const [4]u8) u32 {
return (@as(u32, bytes[3]) << 24) | (@as(u32, bytes[2]) << 16) | (@as(u32, bytes[1]) << 8) | @as(u32, bytes[0]);
return std.mem.readInt(u32, bytes, .little);
}
fn word_to_bytes_le(word: u32) [4]u8 {
var bytes: [4]u8 = undefined;
bytes[3] = @truncate(word >> 24);
bytes[2] = @truncate(word >> 16);
bytes[1] = @truncate(word >> 8);
bytes[0] = @truncate(word);
std.mem.writeInt(u32, &bytes, word, .little);
return bytes;
}

View File

@ -0,0 +1,2 @@
pub const chacha20 = @import("chacha20.zig");
pub const salsa20 = @import("salsa20.zig");

View File

@ -201,40 +201,22 @@ pub fn salsa20_block_function(ctx: *Salsa20Ctx) void {
// ----------------------------------- LITTLE ENDIAN HELPERS ----------------------------------- //
fn salsa20_serialize(L: comptime_int, words: *const [L]u32, bytes: *[L * 4]u8) void {
if (comptime @import("builtin").target.cpu.arch.endian() == .little) {
@memcpy(bytes, @as(*const [L * 4]u8, @ptrCast(words)));
} else {
var tmp: [4]u8 = undefined;
for (0..L) |i| {
tmp = word_to_bytes_le(words[i]);
bytes[i * 4] = tmp[0];
bytes[i * 4 + 1] = tmp[1];
bytes[i * 4 + 2] = tmp[2];
bytes[i * 4 + 3] = tmp[3];
}
}
for (0..L) |i|
std.mem.writeInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), words[i], .little);
}
fn salsa20_deserialize(L: comptime_int, bytes: *const [L * 4]u8, words: *[L]u32) void {
if (comptime @import("builtin").target.cpu.arch.endian() == .little) {
@memcpy(@as(*[L * 4]u8, @ptrCast(words)), bytes);
} else {
for (0..L) |i| {
words[i] = bytes_to_word_le(@ptrCast(bytes[(i * 4)..(i * 4 + 4)]));
}
}
for (0..L) |i|
words[i] = std.mem.readInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), .little);
}
fn bytes_to_word_le(bytes: *const [4]u8) u32 {
return (@as(u32, bytes[3]) << 24) | (@as(u32, bytes[2]) << 16) | (@as(u32, bytes[1]) << 8) | @as(u32, bytes[0]);
return std.mem.readInt(u32, bytes, .little);
}
fn word_to_bytes_le(word: u32) [4]u8 {
var bytes: [4]u8 = undefined;
bytes[3] = @truncate(word >> 24);
bytes[2] = @truncate(word >> 16);
bytes[1] = @truncate(word >> 8);
bytes[0] = @truncate(word);
std.mem.writeInt(u32, &bytes, word, .little);
return bytes;
}

View File

@ -1,21 +1 @@
const std = @import("std");
const testing = std.testing;
const CryptoError = error{
InvalidBufferSize,
};
// rn just for build, later will be used by high-level API
pub const aes = @import("primitive/blockcipher/aes.zig");
pub const chacha20 = @import("primitive/streamcipher/chacha20.zig");
pub const salsa20 = @import("primitive/streamcipher/salsa20.zig");
pub const sha = @import("primitive/digest/sha.zig");
// Leave this for later, maybe make a separate ffi module
//export fn aes_128_encrypt_block_ffi(block_in: [*c]const u8, len_in: usize, block_out: [*c]u8, len_out: usize) !void {
// if (len_in != AES_128_BLOCK_SIZE or len_out != AES_128_BLOCK_SIZE)
// return CryptoError.InvalidBufferSize;
//
// aes_128_encrypt_block(block_in[0..AES_128_BLOCK_SIZE], block_out[0..AES_128_BLOCK_SIZE]);
//}
pub const primitive = @import("./primitive/index.zig");

14
src/test.zig Normal file
View File

@ -0,0 +1,14 @@
comptime {
_ = .{
@import("./primitive/blockcipher/aes.zig"),
@import("./primitive/blockcipher/des.zig"),
@import("./primitive/digest/sha_1.zig"),
@import("./primitive/digest/sha_2.zig"),
@import("./primitive/streamcipher/chacha20.zig"),
@import("./primitive/streamcipher/salsa20.zig"),
};
}
test {
@import("std").testing.refAllDecls(@This());
}