1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const testing = std.testing;
4
5const has_aesni = builtin.cpu.has(.x86, .aes);
6const has_avx = builtin.cpu.has(.x86, .avx);
7const has_armaes = builtin.cpu.has(.aarch64, .aes);
8// C backend doesn't currently support passing vectors to inline asm.
9const impl = if (builtin.cpu.arch == .x86_64 and has_aesni and has_avx) impl: {
10 break :impl @import("aes/aesni.zig");
11} else if (builtin.cpu.arch == .aarch64 and (builtin.zig_backend != .stage2_c or !builtin.os.tag.isDarwin()) and has_armaes) impl: {
12 break :impl @import("aes/armcrypto.zig");
13} else impl: {
14 break :impl @import("aes/soft.zig");
15};
16
17/// `true` if AES is backed by hardware (AES-NI on x86_64, ARM Crypto Extensions on AArch64).
18/// Software implementations are much slower, and should be avoided if possible.
19pub const has_hardware_support =
20 (builtin.cpu.arch == .x86_64 and has_aesni and has_avx) or
21 (builtin.cpu.arch == .aarch64 and has_armaes);
22
23pub const Block = impl.Block;
24pub const BlockVec = impl.BlockVec;
25pub const AesEncryptCtx = impl.AesEncryptCtx;
26pub const AesDecryptCtx = impl.AesDecryptCtx;
27pub const Aes128 = impl.Aes128;
28pub const Aes256 = impl.Aes256;
29
30test "encrypt" {
31 // Appendix B
32 {
33 const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
34 const in = [_]u8{ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 };
35 const exp_out = [_]u8{ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 };
36
37 var out: [exp_out.len]u8 = undefined;
38 var ctx = Aes128.initEnc(key);
39 ctx.encrypt(out[0..], in[0..]);
40 try testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
41 }
42
43 // Appendix C.3
44 {
45 const key = [_]u8{
46 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
47 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
48 };
49 const in = [_]u8{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
50 const exp_out = [_]u8{ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 };
51
52 var out: [exp_out.len]u8 = undefined;
53 var ctx = Aes256.initEnc(key);
54 ctx.encrypt(out[0..], in[0..]);
55 try testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
56 }
57}
58
59test "decrypt" {
60 // Appendix B
61 {
62 const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
63 const in = [_]u8{ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 };
64 const exp_out = [_]u8{ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 };
65
66 var out: [exp_out.len]u8 = undefined;
67 var ctx = Aes128.initDec(key);
68 ctx.decrypt(out[0..], in[0..]);
69 try testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
70 }
71
72 // Appendix C.3
73 {
74 const key = [_]u8{
75 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
76 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
77 };
78 const in = [_]u8{ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 };
79 const exp_out = [_]u8{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
80
81 var out: [exp_out.len]u8 = undefined;
82 var ctx = Aes256.initDec(key);
83 ctx.decrypt(out[0..], in[0..]);
84 try testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
85 }
86}
87
88test "expand 128-bit key" {
89 const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
90 const exp_enc = [_]*const [32:0]u8{
91 "2b7e151628aed2a6abf7158809cf4f3c", "a0fafe1788542cb123a339392a6c7605", "f2c295f27a96b9435935807a7359f67f", "3d80477d4716fe3e1e237e446d7a883b", "ef44a541a8525b7fb671253bdb0bad00", "d4d1c6f87c839d87caf2b8bc11f915bc", "6d88a37a110b3efddbf98641ca0093fd", "4e54f70e5f5fc9f384a64fb24ea6dc4f", "ead27321b58dbad2312bf5607f8d292f", "ac7766f319fadc2128d12941575c006e", "d014f9a8c9ee2589e13f0cc8b6630ca6",
92 };
93 const exp_dec = [_]*const [32:0]u8{
94 "d014f9a8c9ee2589e13f0cc8b6630ca6", "0c7b5a631319eafeb0398890664cfbb4", "df7d925a1f62b09da320626ed6757324", "12c07647c01f22c7bc42d2f37555114a", "6efcd876d2df54807c5df034c917c3b9", "6ea30afcbc238cf6ae82a4b4b54a338d", "90884413d280860a12a128421bc89739", "7c1f13f74208c219c021ae480969bf7b", "cc7505eb3e17d1ee82296c51c9481133", "2b3708a7f262d405bc3ebdbf4b617d62", "2b7e151628aed2a6abf7158809cf4f3c",
95 };
96 const enc = Aes128.initEnc(key);
97 const dec = Aes128.initDec(key);
98 var exp: [16]u8 = undefined;
99
100 for (enc.key_schedule.round_keys, 0..) |round_key, i| {
101 _ = try std.fmt.hexToBytes(&exp, exp_enc[i]);
102 try testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
103 }
104 for (dec.key_schedule.round_keys, 0..) |round_key, i| {
105 _ = try std.fmt.hexToBytes(&exp, exp_dec[i]);
106 try testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
107 }
108}
109
110test "invMixColumns" {
111 const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
112 const enc_ctx = Aes128.initEnc(key);
113 const dec_ctx = Aes128.initDec(key);
114
115 for (1..10) |i| {
116 const enc_rk = enc_ctx.key_schedule.round_keys[10 - i];
117 const dec_rk = dec_ctx.key_schedule.round_keys[i];
118 const computed = enc_rk.invMixColumns();
119 try testing.expectEqualSlices(u8, &dec_rk.toBytes(), &computed.toBytes());
120 }
121}
122
123test "BlockVec invMixColumns" {
124 const input = [_]u8{
125 0x5f, 0x57, 0xf7, 0x1d, 0x72, 0xf5, 0xbe, 0xb9, 0x64, 0xbc, 0x3b, 0xf9, 0x15, 0x92, 0x29, 0x1a,
126 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
127 };
128
129 const vec2 = BlockVec(2).fromBytes(&input);
130 const result_vec = vec2.invMixColumns();
131 const result_bytes = result_vec.toBytes();
132
133 for (0..2) |i| {
134 const block = Block.fromBytes(input[i * 16 ..][0..16]);
135 const expected = block.invMixColumns().toBytes();
136 try testing.expectEqualSlices(u8, &expected, result_bytes[i * 16 ..][0..16]);
137 }
138}
139
140test "BlockVec bitwise operations" {
141 const a_bytes: [32]u8 = @splat(0xaa);
142 const b_bytes: [32]u8 = @splat(0xbb);
143 const a = BlockVec(2).fromBytes(&a_bytes);
144 const b = BlockVec(2).fromBytes(&b_bytes);
145
146 try testing.expectEqual(@as([32]u8, @splat(0x11)), a.xorBytes(&b_bytes));
147 try testing.expectEqual(@as([32]u8, @splat(0x11)), a.xorBlocks(b).toBytes());
148 try testing.expectEqual(@as([32]u8, @splat(0xbb)), a.orBlocks(b).toBytes());
149 try testing.expectEqual(@as([32]u8, @splat(0xaa)), a.andBlocks(b).toBytes());
150}
151
152test "expand 256-bit key" {
153 const key = [_]u8{
154 0x60, 0x3d, 0xeb, 0x10,
155 0x15, 0xca, 0x71, 0xbe,
156 0x2b, 0x73, 0xae, 0xf0,
157 0x85, 0x7d, 0x77, 0x81,
158 0x1f, 0x35, 0x2c, 0x07,
159 0x3b, 0x61, 0x08, 0xd7,
160 0x2d, 0x98, 0x10, 0xa3,
161 0x09, 0x14, 0xdf, 0xf4,
162 };
163 const exp_enc = [_]*const [32:0]u8{
164 "603deb1015ca71be2b73aef0857d7781", "1f352c073b6108d72d9810a30914dff4", "9ba354118e6925afa51a8b5f2067fcde",
165 "a8b09c1a93d194cdbe49846eb75d5b9a", "d59aecb85bf3c917fee94248de8ebe96", "b5a9328a2678a647983122292f6c79b3",
166 "812c81addadf48ba24360af2fab8b464", "98c5bfc9bebd198e268c3ba709e04214", "68007bacb2df331696e939e46c518d80",
167 "c814e20476a9fb8a5025c02d59c58239", "de1369676ccc5a71fa2563959674ee15", "5886ca5d2e2f31d77e0af1fa27cf73c3",
168 "749c47ab18501ddae2757e4f7401905a", "cafaaae3e4d59b349adf6acebd10190d", "fe4890d1e6188d0b046df344706c631e",
169 };
170 const exp_dec = [_]*const [32:0]u8{
171 "fe4890d1e6188d0b046df344706c631e", "ada23f4963e23b2455427c8a5c709104", "57c96cf6074f07c0706abb07137f9241",
172 "b668b621ce40046d36a047ae0932ed8e", "34ad1e4450866b367725bcc763152946", "32526c367828b24cf8e043c33f92aa20",
173 "c440b289642b757227a3d7f114309581", "d669a7334a7ade7a80c8f18fc772e9e3", "25ba3c22a06bc7fb4388a28333934270",
174 "54fb808b9c137949cab22ff547ba186c", "6c3d632985d1fbd9e3e36578701be0f3", "4a7459f9c8e8f9c256a156bc8d083799",
175 "42107758e9ec98f066329ea193f8858b", "8ec6bff6829ca03b9e49af7edba96125", "603deb1015ca71be2b73aef0857d7781",
176 };
177 const enc = Aes256.initEnc(key);
178 const dec = Aes256.initDec(key);
179 var exp: [16]u8 = undefined;
180
181 for (enc.key_schedule.round_keys, 0..) |round_key, i| {
182 _ = try std.fmt.hexToBytes(&exp, exp_enc[i]);
183 try testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
184 }
185 for (dec.key_schedule.round_keys, 0..) |round_key, i| {
186 _ = try std.fmt.hexToBytes(&exp, exp_dec[i]);
187 try testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
188 }
189}