Files
2025-03-22 21:54:00 +01:00

171 lines
6.7 KiB
Zig

const std = @import("std");
const testing = std.testing;
const serpent = @import("primitive").blockcipher.serpent;
const bytes_to_word = @import("utility").byte_operations.bytes_to_word_le;
test "Serpent linear transformation (bitslice mode)" {
var state = [_]u32{
bytes_to_word(&.{ 0x5C, 0x80, 0xE4, 0xA9 }),
bytes_to_word(&.{ 0x63, 0xC1, 0x2A, 0x74 }),
bytes_to_word(&.{ 0x9C, 0x62, 0x65, 0x4A }),
bytes_to_word(&.{ 0xF1, 0xCC, 0xED, 0xBA }),
};
serpent.linear_transformation(&state);
try testing.expectEqualSlices(u32, &.{
0x64fdbe66, 0x6e14817b, 0x35a514d4, 0xcd38f9b4,
}, &state);
}
test "Serpent-128 key expansion (bitslice mode)" {
const key = std.mem.zeroes([16]u8);
const subkeys = [_][4]u32{
.{ 0x6f579dd2, 0xa7a3a3ce, 0xf29990ed, 0x8ed77392 },
.{ 0x80125b0e, 0xec264d48, 0x6023c3d7, 0x41d3a19f },
.{ 0xf8c9ea64, 0x85e629c5, 0xedc06a3c, 0x5208c226 },
.{ 0x996edca3, 0x87c0e675, 0x28d9d59c, 0x0a69f259 },
.{ 0x3b9094e6, 0x0b918e16, 0xeb3f3d7a, 0x81a43b80 },
.{ 0xde2068fe, 0x3c0a592a, 0x6987f660, 0xb85c1049 },
.{ 0x57fe226c, 0x7e01d297, 0xfe13d038, 0x145fb65a },
.{ 0x4f5df0c5, 0x6648582b, 0xc385f12f, 0x984381b0 },
.{ 0x447bcf29, 0x68941f24, 0x523b25d2, 0x086144f0 },
.{ 0xf45443f8, 0x9fe43d24, 0x9cb5b319, 0x3eda4321 },
.{ 0xb4146dd5, 0x2cd8083e, 0x9ed2f57f, 0x6e55b8b8 },
.{ 0x94d3397a, 0xc6e90a6f, 0x9f35d452, 0x39146b55 },
.{ 0xfcf11184, 0x357ba27f, 0xd1e4e6d0, 0x2ebf4f5e },
.{ 0x2eaa8274, 0xde5a7089, 0xe2ff0356, 0x17482ffc },
.{ 0xeeb5bb44, 0xee77bde8, 0x7ecc739d, 0x357ba7ec },
.{ 0x755c9a06, 0x0dbd73d7, 0x5a16a42e, 0xbd4253f7 },
.{ 0xcae3885c, 0xa594cc7a, 0x6487de0e, 0x83cb2331 },
.{ 0x213c9fbb, 0x106c6af8, 0x0c9c9f4c, 0xd554b5cc },
.{ 0x656f9008, 0x85db98ce, 0x04ec632b, 0x83f0fd5e },
.{ 0x9c72345e, 0x0c547d37, 0x3c580e7b, 0x68c52253 },
.{ 0x5a8033f3, 0xf0f5c3ff, 0xa17aac1d, 0x48d929c1 },
.{ 0x7275b839, 0xbcd32557, 0x6242379f, 0x9f18aef2 },
.{ 0x3e641fbf, 0x2bf25532, 0xd80d8936, 0x7a38e7a8 },
.{ 0x75878446, 0x3b59afdd, 0x6f9a2417, 0xd374c9e8 },
.{ 0x92372bc2, 0x0fe107a2, 0x7ea908d8, 0xd2f3ec0f },
.{ 0x7afe62ef, 0x3d876138, 0x8e783eb4, 0x3313e04d },
.{ 0x7b554140, 0x3b1c350d, 0xa75c40ed, 0xdcab53af },
.{ 0xad845ca5, 0x24bbd3f9, 0x53bd7cb2, 0x5149d6c4 },
.{ 0x4e10ee30, 0xcf3be057, 0xfabc555c, 0x8b4276e9 },
.{ 0x6f4408ec, 0x1cd43a6d, 0xa021e8fd, 0x6169bcc0 },
.{ 0x4d0a99c5, 0xc9e96286, 0xfa234c4e, 0x92541cdb },
.{ 0x1f08f310, 0x7aaf29b2, 0xebf4e180, 0xc23175a7 },
.{ 0x5dba09c8, 0x0735e815, 0x86801046, 0x4af628f4 },
};
const expanded_key = try serpent.expand_key(&key);
try testing.expectEqualDeep(subkeys, expanded_key);
}
// https://biham.cs.technion.ac.il/Reports/Serpent/Serpent-128-128.verified.test-vectors
test "NESSIE Serpent-128 test vector set 1" {
try test_encryption(
&fromhex(16, "80000000000000000000000000000000"),
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "264e5481eff42a4606abda06c0bfda3d"),
);
try test_encryption(
&fromhex(16, "40000000000000000000000000000000"),
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "4A231B3BC727993407AC6EC8350E8524"),
);
try test_encryption(
&fromhex(16, "20000000000000000000000000000000"),
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "E03269F9E9FD853C7D8156DF14B98D56"),
);
try test_encryption(
&fromhex(16, "10000000000000000000000000000000"),
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "A798181C3081AC59D5BA89754DACC48F"),
);
// TODO: ...
}
test "NESSIE Serpent-128 test vector set 2" {
try test_encryption(
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "80000000000000000000000000000000"),
&fromhex(16, "A3B35DE7C358DDD82644678C64B8BCBB"),
);
try test_encryption(
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "40000000000000000000000000000000"),
&fromhex(16, "04ABCFE4E0AF27FF92A2BB10949D7DD2"),
);
try test_encryption(
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "20000000000000000000000000000000"),
&fromhex(16, "8F773194B78EF2B2740237EF12D08608"),
);
try test_encryption(
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "10000000000000000000000000000000"),
&fromhex(16, "8B1EA69EE8D7C8D95B1DE4A670EC6997"),
);
// TODO: ...
}
test "NESSIE Serpent-128 test vector set 3" {
try test_encryption(
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "00000000000000000000000000000000"),
&fromhex(16, "3620B17AE6A993D09618B8768266BAE9"),
);
try test_encryption(
&fromhex(16, "01010101010101010101010101010101"),
&fromhex(16, "01010101010101010101010101010101"),
&fromhex(16, "5107E36DBE81D9996D1EF7F3656FFC63"),
);
try test_encryption(
&fromhex(16, "02020202020202020202020202020202"),
&fromhex(16, "02020202020202020202020202020202"),
&fromhex(16, "1AE5355487F88F824B6462B45C4C6AA5"),
);
try test_encryption(
&fromhex(16, "03030303030303030303030303030303"),
&fromhex(16, "03030303030303030303030303030303"),
&fromhex(16, "1F830AF7D2A1B18F7A011C6FD0EEE8FB"),
);
// TODO: ...
}
// Helpers
fn test_encryption(key: *const [16]u8, plain: *const [16]u8, cipher: *const [16]u8) !void {
var output: [16]u8 = undefined;
const key_schedule = try serpent.expand_key(key);
serpent.encrypt_block(plain, &output, &key_schedule);
try testing.expectEqualSlices(u8, cipher, &output);
}
// This monstrosity is only temporary...
fn fromhex(L: comptime_int, comptime s: *const [2 * L]u8) [L]u8 {
var result: [L]u8 = undefined;
inline for (0..L) |i| {
result[i] = (if (s[2 * i] >= '0' and s[2 * i] <= '9')
s[2 * i] - '0'
else if (s[2 * i] >= 'a' and s[2 * i] <= 'f')
s[2 * i] - 'a' + 10
else if (s[2 * i] >= 'A' and s[2 * i] <= 'F')
s[2 * i] - 'A' + 10
else
@compileError("Invalid hex string.")) * 16 + (if (s[2 * i + 1] >= '0' and s[2 * i + 1] <= '9')
s[2 * i + 1] - '0'
else if (s[2 * i + 1] >= 'a' and s[2 * i + 1] <= 'f')
s[2 * i + 1] - 'a' + 10
else if (s[2 * i + 1] >= 'A' and s[2 * i + 1] <= 'F')
s[2 * i + 1] - 'A' + 10
else
@compileError("Invalid hex string."));
}
return result;
}