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; }