authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2025-11-29 19:25:07+01:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2025-11-29 19:25:22+01:00
log5e00a0c9b5627195ae9b6e09a9be3e186d4f76d5
tree001395f831746ad2db8e9e5ae8a5a6bd6ad3c24d
parent7d9ad992ab636d51f24125e3ebb046d4333b1f80

std.crypto.aes: expose the inverse MixColumns operation

The inverse MixColumns operation is already used internally for AES decryption, but it wasn’t exposed in the public API because it didn’t seem necessary at the time. Since then, several new AES-based block ciphers and permutations (such as Vistrutah and Areion) have been developed, and they require this operation to be implementable in Zig. Since then, new interesting AES-based block ciphers and permutations (Vistrutah, Areion, etc). have been invented, and require that operation to be implementable in Zig.

4 files changed, 101 insertions(+), 0 deletions(-)

lib/std/crypto/aes.zig+30
......@@ -108,6 +108,36 @@ test "expand 128-bit key" {
108108 }
109109}
110110
111test "invMixColumns" {
112 const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
113 const enc_ctx = Aes128.initEnc(key);
114 const dec_ctx = Aes128.initDec(key);
115
116 for (1..10) |i| {
117 const enc_rk = enc_ctx.key_schedule.round_keys[10 - i];
118 const dec_rk = dec_ctx.key_schedule.round_keys[i];
119 const computed = enc_rk.invMixColumns();
120 try testing.expectEqualSlices(u8, &dec_rk.toBytes(), &computed.toBytes());
121 }
122}
123
124test "BlockVec invMixColumns" {
125 const input = [_]u8{
126 0x5f, 0x57, 0xf7, 0x1d, 0x72, 0xf5, 0xbe, 0xb9, 0x64, 0xbc, 0x3b, 0xf9, 0x15, 0x92, 0x29, 0x1a,
127 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
128 };
129
130 const vec2 = BlockVec(2).fromBytes(&input);
131 const result_vec = vec2.invMixColumns();
132 const result_bytes = result_vec.toBytes();
133
134 for (0..2) |i| {
135 const block = Block.fromBytes(input[i * 16 ..][0..16]);
136 const expected = block.invMixColumns().toBytes();
137 try testing.expectEqualSlices(u8, &expected, result_bytes[i * 16 ..][0..16]);
138 }
139}
140
111141test "expand 256-bit key" {
112142 const key = [_]u8{
113143 0x60, 0x3d, 0xeb, 0x10,
lib/std/crypto/aes/aesni.zig+22
......@@ -96,6 +96,17 @@ pub const Block = struct {
9696 return Block{ .repr = block1.repr | block2.repr };
9797 }
9898
99 /// Apply the inverse MixColumns operation to a block.
100 pub fn invMixColumns(block: Block) Block {
101 return Block{
102 .repr = asm (
103 \\ vaesimc %[in], %[out]
104 : [out] "=x" (-> Repr),
105 : [in] "x" (block.repr),
106 ),
107 };
108 }
109
99110 /// Perform operations on multiple blocks in parallel.
100111 pub const parallel = struct {
101112 const cpu = std.Target.x86.cpu;
......@@ -308,6 +319,17 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
308319 }
309320 return out;
310321 }
322
323 /// Apply the inverse MixColumns operation to each block in the vector.
324 pub fn invMixColumns(block_vec: Self) Self {
325 var out_bytes: [blocks_count * 16]u8 = undefined;
326 const in_bytes = block_vec.toBytes();
327 inline for (0..blocks_count) |i| {
328 const block = Block.fromBytes(in_bytes[i * 16 ..][0..16]);
329 out_bytes[i * 16 ..][0..16].* = block.invMixColumns().toBytes();
330 }
331 return fromBytes(&out_bytes);
332 }
311333 };
312334}
313335
lib/std/crypto/aes/armcrypto.zig+20
......@@ -99,6 +99,17 @@ pub const Block = struct {
9999 return Block{ .repr = block1.repr | block2.repr };
100100 }
101101
102 /// Apply the inverse MixColumns operation to a block.
103 pub fn invMixColumns(block: Block) Block {
104 return Block{
105 .repr = asm (
106 \\ aesimc %[out].16b, %[in].16b
107 : [out] "=x" (-> Repr),
108 : [in] "x" (block.repr),
109 ),
110 };
111 }
112
102113 /// Perform operations on multiple blocks in parallel.
103114 pub const parallel = struct {
104115 /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
......@@ -275,6 +286,15 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
275286 }
276287 return out;
277288 }
289
290 /// Apply the inverse MixColumns operation to each block in the vector.
291 pub fn invMixColumns(block_vec: Self) Self {
292 var out: Self = undefined;
293 inline for (0..native_words) |i| {
294 out.repr[i] = block_vec.repr[i].invMixColumns();
295 }
296 return out;
297 }
278298 };
279299}
280300
lib/std/crypto/aes/soft.zig+29
......@@ -265,6 +265,26 @@ pub const Block = struct {
265265 return Block{ .repr = x };
266266 }
267267
268 /// Apply the inverse MixColumns operation to a block.
269 pub fn invMixColumns(block: Block) Block {
270 var out: Repr = undefined;
271 inline for (0..4) |i| {
272 const col = block.repr[i];
273 const b0: u8 = @truncate(col);
274 const b1: u8 = @truncate(col >> 8);
275 const b2: u8 = @truncate(col >> 16);
276 const b3: u8 = @truncate(col >> 24);
277
278 const r0 = mul(0x0e, b0) ^ mul(0x0b, b1) ^ mul(0x0d, b2) ^ mul(0x09, b3);
279 const r1 = mul(0x09, b0) ^ mul(0x0e, b1) ^ mul(0x0b, b2) ^ mul(0x0d, b3);
280 const r2 = mul(0x0d, b0) ^ mul(0x09, b1) ^ mul(0x0e, b2) ^ mul(0x0b, b3);
281 const r3 = mul(0x0b, b0) ^ mul(0x0d, b1) ^ mul(0x09, b2) ^ mul(0x0e, b3);
282
283 out[i] = @as(u32, r0) | (@as(u32, r1) << 8) | (@as(u32, r2) << 16) | (@as(u32, r3) << 24);
284 }
285 return Block{ .repr = out };
286 }
287
268288 /// Perform operations on multiple blocks in parallel.
269289 pub const parallel = struct {
270290 /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
......@@ -441,6 +461,15 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
441461 }
442462 return out;
443463 }
464
465 /// Apply the inverse MixColumns operation to each block in the vector.
466 pub fn invMixColumns(block_vec: Self) Self {
467 var out: Self = undefined;
468 for (0..native_words) |i| {
469 out.repr[i] = block_vec.repr[i].invMixColumns();
470 }
471 return out;
472 }
444473 };
445474}
446475