| ... | @@ -1,11 +1,11 @@ | ... | @@ -1,11 +1,11 @@ |
| 1 | // Based on Go stdlib implementation | | |
| 2 | | | |
| 3 | const std = @import("../../std.zig"); | 1 | const std = @import("../../std.zig"); |
| 4 | const math = std.math; | 2 | const math = std.math; |
| 5 | const mem = std.mem; | 3 | const mem = std.mem; |
| 6 | | 4 | |
| 7 | const BlockVec = [4]u32; | 5 | const BlockVec = [4]u32; |
| 8 | | 6 | |
| | 7 | const side_channels_mitigations = std.crypto.config.side_channels_mitigations; |
| | 8 | |
| 9 | /// A single AES block. | 9 | /// A single AES block. |
| 10 | pub const Block = struct { | 10 | pub const Block = struct { |
| 11 | pub const block_length: usize = 16; | 11 | pub const block_length: usize = 16; |
| ... | @@ -15,20 +15,20 @@ pub const Block = struct { | ... | @@ -15,20 +15,20 @@ pub const Block = struct { |
| 15 | | 15 | |
| 16 | /// Convert a byte sequence into an internal representation. | 16 | /// Convert a byte sequence into an internal representation. |
| 17 | pub inline fn fromBytes(bytes: *const [16]u8) Block { | 17 | pub inline fn fromBytes(bytes: *const [16]u8) Block { |
| 18 | const s0 = mem.readIntBig(u32, bytes[0..4]); | 18 | const s0 = mem.readIntLittle(u32, bytes[0..4]); |
| 19 | const s1 = mem.readIntBig(u32, bytes[4..8]); | 19 | const s1 = mem.readIntLittle(u32, bytes[4..8]); |
| 20 | const s2 = mem.readIntBig(u32, bytes[8..12]); | 20 | const s2 = mem.readIntLittle(u32, bytes[8..12]); |
| 21 | const s3 = mem.readIntBig(u32, bytes[12..16]); | 21 | const s3 = mem.readIntLittle(u32, bytes[12..16]); |
| 22 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; | 22 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; |
| 23 | } | 23 | } |
| 24 | | 24 | |
| 25 | /// Convert the internal representation of a block into a byte sequence. | 25 | /// Convert the internal representation of a block into a byte sequence. |
| 26 | pub inline fn toBytes(block: Block) [16]u8 { | 26 | pub inline fn toBytes(block: Block) [16]u8 { |
| 27 | var bytes: [16]u8 = undefined; | 27 | var bytes: [16]u8 = undefined; |
| 28 | mem.writeIntBig(u32, bytes[0..4], block.repr[0]); | 28 | mem.writeIntLittle(u32, bytes[0..4], block.repr[0]); |
| 29 | mem.writeIntBig(u32, bytes[4..8], block.repr[1]); | 29 | mem.writeIntLittle(u32, bytes[4..8], block.repr[1]); |
| 30 | mem.writeIntBig(u32, bytes[8..12], block.repr[2]); | 30 | mem.writeIntLittle(u32, bytes[8..12], block.repr[2]); |
| 31 | mem.writeIntBig(u32, bytes[12..16], block.repr[3]); | 31 | mem.writeIntLittle(u32, bytes[12..16], block.repr[3]); |
| 32 | return bytes; | 32 | return bytes; |
| 33 | } | 33 | } |
| 34 | | 34 | |
| ... | @@ -50,32 +50,93 @@ pub const Block = struct { | ... | @@ -50,32 +50,93 @@ pub const Block = struct { |
| 50 | const s2 = block.repr[2]; | 50 | const s2 = block.repr[2]; |
| 51 | const s3 = block.repr[3]; | 51 | const s3 = block.repr[3]; |
| 52 | | 52 | |
| 53 | const t0 = round_key.repr[0] ^ table_encrypt[0][@truncate(u8, s0 >> 24)] ^ table_encrypt[1][@truncate(u8, s1 >> 16)] ^ table_encrypt[2][@truncate(u8, s2 >> 8)] ^ table_encrypt[3][@truncate(u8, s3)]; | 53 | var x: [4]u32 = undefined; |
| 54 | const t1 = round_key.repr[1] ^ table_encrypt[0][@truncate(u8, s1 >> 24)] ^ table_encrypt[1][@truncate(u8, s2 >> 16)] ^ table_encrypt[2][@truncate(u8, s3 >> 8)] ^ table_encrypt[3][@truncate(u8, s0)]; | 54 | x = table_lookup(&table_encrypt, @truncate(u8, s0), @truncate(u8, s1 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 24)); |
| 55 | const t2 = round_key.repr[2] ^ table_encrypt[0][@truncate(u8, s2 >> 24)] ^ table_encrypt[1][@truncate(u8, s3 >> 16)] ^ table_encrypt[2][@truncate(u8, s0 >> 8)] ^ table_encrypt[3][@truncate(u8, s1)]; | 55 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| 56 | const t3 = round_key.repr[3] ^ table_encrypt[0][@truncate(u8, s3 >> 24)] ^ table_encrypt[1][@truncate(u8, s0 >> 16)] ^ table_encrypt[2][@truncate(u8, s1 >> 8)] ^ table_encrypt[3][@truncate(u8, s2)]; | 56 | x = table_lookup(&table_encrypt, @truncate(u8, s1), @truncate(u8, s2 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 24)); |
| | 57 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 58 | x = table_lookup(&table_encrypt, @truncate(u8, s2), @truncate(u8, s3 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 24)); |
| | 59 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 60 | x = table_lookup(&table_encrypt, @truncate(u8, s3), @truncate(u8, s0 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 24)); |
| | 61 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 62 | |
| | 63 | t0 ^= round_key.repr[0]; |
| | 64 | t1 ^= round_key.repr[1]; |
| | 65 | t2 ^= round_key.repr[2]; |
| | 66 | t3 ^= round_key.repr[3]; |
| | 67 | |
| | 68 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| | 69 | } |
| | 70 | |
| | 71 | /// Encrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS* |
| | 72 | pub inline fn encryptUnprotected(block: Block, round_key: Block) Block { |
| | 73 | const s0 = block.repr[0]; |
| | 74 | const s1 = block.repr[1]; |
| | 75 | const s2 = block.repr[2]; |
| | 76 | const s3 = block.repr[3]; |
| | 77 | |
| | 78 | var x: [4]u32 = undefined; |
| | 79 | x = .{ |
| | 80 | table_encrypt[0][@truncate(u8, s0)], |
| | 81 | table_encrypt[1][@truncate(u8, s1 >> 8)], |
| | 82 | table_encrypt[2][@truncate(u8, s2 >> 16)], |
| | 83 | table_encrypt[3][@truncate(u8, s3 >> 24)], |
| | 84 | }; |
| | 85 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 86 | x = .{ |
| | 87 | table_encrypt[0][@truncate(u8, s1)], |
| | 88 | table_encrypt[1][@truncate(u8, s2 >> 8)], |
| | 89 | table_encrypt[2][@truncate(u8, s3 >> 16)], |
| | 90 | table_encrypt[3][@truncate(u8, s0 >> 24)], |
| | 91 | }; |
| | 92 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 93 | x = .{ |
| | 94 | table_encrypt[0][@truncate(u8, s2)], |
| | 95 | table_encrypt[1][@truncate(u8, s3 >> 8)], |
| | 96 | table_encrypt[2][@truncate(u8, s0 >> 16)], |
| | 97 | table_encrypt[3][@truncate(u8, s1 >> 24)], |
| | 98 | }; |
| | 99 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 100 | x = .{ |
| | 101 | table_encrypt[0][@truncate(u8, s3)], |
| | 102 | table_encrypt[1][@truncate(u8, s0 >> 8)], |
| | 103 | table_encrypt[2][@truncate(u8, s1 >> 16)], |
| | 104 | table_encrypt[3][@truncate(u8, s2 >> 24)], |
| | 105 | }; |
| | 106 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 107 | |
| | 108 | t0 ^= round_key.repr[0]; |
| | 109 | t1 ^= round_key.repr[1]; |
| | 110 | t2 ^= round_key.repr[2]; |
| | 111 | t3 ^= round_key.repr[3]; |
| 57 | | 112 | |
| 58 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; | 113 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 59 | } | 114 | } |
| 60 | | 115 | |
| 61 | /// Encrypt a block with the last round key. | 116 | /// Encrypt a block with the last round key. |
| 62 | pub inline fn encryptLast(block: Block, round_key: Block) Block { | 117 | pub inline fn encryptLast(block: Block, round_key: Block) Block { |
| 63 | const t0 = block.repr[0]; | 118 | const s0 = block.repr[0]; |
| 64 | const t1 = block.repr[1]; | 119 | const s1 = block.repr[1]; |
| 65 | const t2 = block.repr[2]; | 120 | const s2 = block.repr[2]; |
| 66 | const t3 = block.repr[3]; | 121 | const s3 = block.repr[3]; |
| 67 | | 122 | |
| 68 | // Last round uses s-box directly and XORs to produce output. | 123 | // Last round uses s-box directly and XORs to produce output. |
| 69 | var s0 = @as(u32, sbox_encrypt[t0 >> 24]) << 24 | @as(u32, sbox_encrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t3 & 0xff]); | 124 | var x: [4]u8 = undefined; |
| 70 | var s1 = @as(u32, sbox_encrypt[t1 >> 24]) << 24 | @as(u32, sbox_encrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t0 & 0xff]); | 125 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s3 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s0)); |
| 71 | var s2 = @as(u32, sbox_encrypt[t2 >> 24]) << 24 | @as(u32, sbox_encrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t1 & 0xff]); | 126 | var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 72 | var s3 = @as(u32, sbox_encrypt[t3 >> 24]) << 24 | @as(u32, sbox_encrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t2 & 0xff]); | 127 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s0 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s1)); |
| 73 | s0 ^= round_key.repr[0]; | 128 | var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 74 | s1 ^= round_key.repr[1]; | 129 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s1 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s2)); |
| 75 | s2 ^= round_key.repr[2]; | 130 | var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 76 | s3 ^= round_key.repr[3]; | 131 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s2 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s3)); |
| | 132 | var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| | 133 | |
| | 134 | t0 ^= round_key.repr[0]; |
| | 135 | t1 ^= round_key.repr[1]; |
| | 136 | t2 ^= round_key.repr[2]; |
| | 137 | t3 ^= round_key.repr[3]; |
| 77 | | 138 | |
| 78 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; | 139 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 79 | } | 140 | } |
| 80 | | 141 | |
| 81 | /// Decrypt a block with a round key. | 142 | /// Decrypt a block with a round key. |
| ... | @@ -85,32 +146,93 @@ pub const Block = struct { | ... | @@ -85,32 +146,93 @@ pub const Block = struct { |
| 85 | const s2 = block.repr[2]; | 146 | const s2 = block.repr[2]; |
| 86 | const s3 = block.repr[3]; | 147 | const s3 = block.repr[3]; |
| 87 | | 148 | |
| 88 | const t0 = round_key.repr[0] ^ table_decrypt[0][@truncate(u8, s0 >> 24)] ^ table_decrypt[1][@truncate(u8, s3 >> 16)] ^ table_decrypt[2][@truncate(u8, s2 >> 8)] ^ table_decrypt[3][@truncate(u8, s1)]; | 149 | var x: [4]u32 = undefined; |
| 89 | const t1 = round_key.repr[1] ^ table_decrypt[0][@truncate(u8, s1 >> 24)] ^ table_decrypt[1][@truncate(u8, s0 >> 16)] ^ table_decrypt[2][@truncate(u8, s3 >> 8)] ^ table_decrypt[3][@truncate(u8, s2)]; | 150 | x = table_lookup(&table_decrypt, @truncate(u8, s0), @truncate(u8, s3 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 24)); |
| 90 | const t2 = round_key.repr[2] ^ table_decrypt[0][@truncate(u8, s2 >> 24)] ^ table_decrypt[1][@truncate(u8, s1 >> 16)] ^ table_decrypt[2][@truncate(u8, s0 >> 8)] ^ table_decrypt[3][@truncate(u8, s3)]; | 151 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| 91 | const t3 = round_key.repr[3] ^ table_decrypt[0][@truncate(u8, s3 >> 24)] ^ table_decrypt[1][@truncate(u8, s2 >> 16)] ^ table_decrypt[2][@truncate(u8, s1 >> 8)] ^ table_decrypt[3][@truncate(u8, s0)]; | 152 | x = table_lookup(&table_decrypt, @truncate(u8, s1), @truncate(u8, s0 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 24)); |
| | 153 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 154 | x = table_lookup(&table_decrypt, @truncate(u8, s2), @truncate(u8, s1 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 24)); |
| | 155 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 156 | x = table_lookup(&table_decrypt, @truncate(u8, s3), @truncate(u8, s2 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 24)); |
| | 157 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 158 | |
| | 159 | t0 ^= round_key.repr[0]; |
| | 160 | t1 ^= round_key.repr[1]; |
| | 161 | t2 ^= round_key.repr[2]; |
| | 162 | t3 ^= round_key.repr[3]; |
| | 163 | |
| | 164 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| | 165 | } |
| | 166 | |
| | 167 | /// Decrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS* |
| | 168 | pub inline fn decryptUnprotected(block: Block, round_key: Block) Block { |
| | 169 | const s0 = block.repr[0]; |
| | 170 | const s1 = block.repr[1]; |
| | 171 | const s2 = block.repr[2]; |
| | 172 | const s3 = block.repr[3]; |
| | 173 | |
| | 174 | var x: [4]u32 = undefined; |
| | 175 | x = .{ |
| | 176 | table_decrypt[0][@truncate(u8, s0)], |
| | 177 | table_decrypt[1][@truncate(u8, s3 >> 8)], |
| | 178 | table_decrypt[2][@truncate(u8, s2 >> 16)], |
| | 179 | table_decrypt[3][@truncate(u8, s1 >> 24)], |
| | 180 | }; |
| | 181 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 182 | x = .{ |
| | 183 | table_decrypt[0][@truncate(u8, s1)], |
| | 184 | table_decrypt[1][@truncate(u8, s0 >> 8)], |
| | 185 | table_decrypt[2][@truncate(u8, s3 >> 16)], |
| | 186 | table_decrypt[3][@truncate(u8, s2 >> 24)], |
| | 187 | }; |
| | 188 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 189 | x = .{ |
| | 190 | table_decrypt[0][@truncate(u8, s2)], |
| | 191 | table_decrypt[1][@truncate(u8, s1 >> 8)], |
| | 192 | table_decrypt[2][@truncate(u8, s0 >> 16)], |
| | 193 | table_decrypt[3][@truncate(u8, s3 >> 24)], |
| | 194 | }; |
| | 195 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 196 | x = .{ |
| | 197 | table_decrypt[0][@truncate(u8, s3)], |
| | 198 | table_decrypt[1][@truncate(u8, s2 >> 8)], |
| | 199 | table_decrypt[2][@truncate(u8, s1 >> 16)], |
| | 200 | table_decrypt[3][@truncate(u8, s0 >> 24)], |
| | 201 | }; |
| | 202 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| | 203 | |
| | 204 | t0 ^= round_key.repr[0]; |
| | 205 | t1 ^= round_key.repr[1]; |
| | 206 | t2 ^= round_key.repr[2]; |
| | 207 | t3 ^= round_key.repr[3]; |
| 92 | | 208 | |
| 93 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; | 209 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 94 | } | 210 | } |
| 95 | | 211 | |
| 96 | /// Decrypt a block with the last round key. | 212 | /// Decrypt a block with the last round key. |
| 97 | pub inline fn decryptLast(block: Block, round_key: Block) Block { | 213 | pub inline fn decryptLast(block: Block, round_key: Block) Block { |
| 98 | const t0 = block.repr[0]; | 214 | const s0 = block.repr[0]; |
| 99 | const t1 = block.repr[1]; | 215 | const s1 = block.repr[1]; |
| 100 | const t2 = block.repr[2]; | 216 | const s2 = block.repr[2]; |
| 101 | const t3 = block.repr[3]; | 217 | const s3 = block.repr[3]; |
| 102 | | 218 | |
| 103 | // Last round uses s-box directly and XORs to produce output. | 219 | // Last round uses s-box directly and XORs to produce output. |
| 104 | var s0 = @as(u32, sbox_decrypt[t0 >> 24]) << 24 | @as(u32, sbox_decrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t1 & 0xff]); | 220 | var x: [4]u8 = undefined; |
| 105 | var s1 = @as(u32, sbox_decrypt[t1 >> 24]) << 24 | @as(u32, sbox_decrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t2 & 0xff]); | 221 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s1 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s0)); |
| 106 | var s2 = @as(u32, sbox_decrypt[t2 >> 24]) << 24 | @as(u32, sbox_decrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t3 & 0xff]); | 222 | var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 107 | var s3 = @as(u32, sbox_decrypt[t3 >> 24]) << 24 | @as(u32, sbox_decrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t0 & 0xff]); | 223 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s2 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s1)); |
| 108 | s0 ^= round_key.repr[0]; | 224 | var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 109 | s1 ^= round_key.repr[1]; | 225 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s3 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s2)); |
| 110 | s2 ^= round_key.repr[2]; | 226 | var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 111 | s3 ^= round_key.repr[3]; | 227 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s0 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s3)); |
| | 228 | var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| | 229 | |
| | 230 | t0 ^= round_key.repr[0]; |
| | 231 | t1 ^= round_key.repr[1]; |
| | 232 | t2 ^= round_key.repr[2]; |
| | 233 | t3 ^= round_key.repr[3]; |
| 112 | | 234 | |
| 113 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; | 235 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 114 | } | 236 | } |
| 115 | | 237 | |
| 116 | /// Apply the bitwise XOR operation to the content of two blocks. | 238 | /// Apply the bitwise XOR operation to the content of two blocks. |
| ... | @@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type { | ... | @@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type { |
| 226 | const subw = struct { | 348 | const subw = struct { |
| 227 | // Apply sbox_encrypt to each byte in w. | 349 | // Apply sbox_encrypt to each byte in w. |
| 228 | fn func(w: u32) u32 { | 350 | fn func(w: u32) u32 { |
| 229 | return @as(u32, sbox_encrypt[w >> 24]) << 24 | @as(u32, sbox_encrypt[w >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[w >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[w & 0xff]); | 351 | const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, w), @truncate(u8, w >> 8), @truncate(u8, w >> 16), @truncate(u8, w >> 24)); |
| | 352 | return @as(u32, x[3]) << 24 | @as(u32, x[2]) << 16 | @as(u32, x[1]) << 8 | @as(u32, x[0]); |
| 230 | } | 353 | } |
| 231 | }.func; | 354 | }.func; |
| 232 | | 355 | |
| ... | @@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type { | ... | @@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type { |
| 244 | } | 367 | } |
| 245 | round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t; | 368 | round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t; |
| 246 | } | 369 | } |
| | 370 | i = 0; |
| | 371 | inline while (i < round_keys.len * 4) : (i += 1) { |
| | 372 | round_keys[i / 4].repr[i % 4] = @byteSwap(round_keys[i / 4].repr[i % 4]); |
| | 373 | } |
| 247 | return Self{ .round_keys = round_keys }; | 374 | return Self{ .round_keys = round_keys }; |
| 248 | } | 375 | } |
| 249 | | 376 | |
| ... | @@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type { | ... | @@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type { |
| 257 | const ei = total_words - i - 4; | 384 | const ei = total_words - i - 4; |
| 258 | comptime var j: usize = 0; | 385 | comptime var j: usize = 0; |
| 259 | inline while (j < 4) : (j += 1) { | 386 | inline while (j < 4) : (j += 1) { |
| 260 | var x = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; | 387 | var rk = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; |
| 261 | if (i > 0 and i + 4 < total_words) { | 388 | if (i > 0 and i + 4 < total_words) { |
| 262 | x = table_decrypt[0][sbox_encrypt[x >> 24]] ^ table_decrypt[1][sbox_encrypt[x >> 16 & 0xff]] ^ table_decrypt[2][sbox_encrypt[x >> 8 & 0xff]] ^ table_decrypt[3][sbox_encrypt[x & 0xff]]; | 389 | const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, rk >> 24), @truncate(u8, rk >> 16), @truncate(u8, rk >> 8), @truncate(u8, rk)); |
| | 390 | const y = table_lookup(&table_decrypt, x[3], x[2], x[1], x[0]); |
| | 391 | rk = y[0] ^ y[1] ^ y[2] ^ y[3]; |
| 263 | } | 392 | } |
| 264 | inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x; | 393 | inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = rk; |
| 265 | } | 394 | } |
| 266 | } | 395 | } |
| 267 | return Self{ .round_keys = inv_round_keys }; | 396 | return Self{ .round_keys = inv_round_keys }; |
| ... | @@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { | ... | @@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { |
| 293 | const round_keys = ctx.key_schedule.round_keys; | 422 | const round_keys = ctx.key_schedule.round_keys; |
| 294 | var t = Block.fromBytes(src).xorBlocks(round_keys[0]); | 423 | var t = Block.fromBytes(src).xorBlocks(round_keys[0]); |
| 295 | comptime var i = 1; | 424 | comptime var i = 1; |
| 296 | inline while (i < rounds) : (i += 1) { | 425 | if (side_channels_mitigations == .full) { |
| | 426 | inline while (i < rounds) : (i += 1) { |
| | 427 | t = t.encrypt(round_keys[i]); |
| | 428 | } |
| | 429 | } else { |
| | 430 | inline while (i < 5) : (i += 1) { |
| | 431 | t = t.encrypt(round_keys[i]); |
| | 432 | } |
| | 433 | inline while (i < rounds - 1) : (i += 1) { |
| | 434 | t = t.encryptUnprotected(round_keys[i]); |
| | 435 | } |
| 297 | t = t.encrypt(round_keys[i]); | 436 | t = t.encrypt(round_keys[i]); |
| 298 | } | 437 | } |
| 299 | t = t.encryptLast(round_keys[rounds]); | 438 | t = t.encryptLast(round_keys[rounds]); |
| ... | @@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { | ... | @@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { |
| 305 | const round_keys = ctx.key_schedule.round_keys; | 444 | const round_keys = ctx.key_schedule.round_keys; |
| 306 | var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]); | 445 | var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]); |
| 307 | comptime var i = 1; | 446 | comptime var i = 1; |
| 308 | inline while (i < rounds) : (i += 1) { | 447 | if (side_channels_mitigations == .full) { |
| | 448 | inline while (i < rounds) : (i += 1) { |
| | 449 | t = t.encrypt(round_keys[i]); |
| | 450 | } |
| | 451 | } else { |
| | 452 | inline while (i < 5) : (i += 1) { |
| | 453 | t = t.encrypt(round_keys[i]); |
| | 454 | } |
| | 455 | inline while (i < rounds - 1) : (i += 1) { |
| | 456 | t = t.encryptUnprotected(round_keys[i]); |
| | 457 | } |
| 309 | t = t.encrypt(round_keys[i]); | 458 | t = t.encrypt(round_keys[i]); |
| 310 | } | 459 | } |
| 311 | t = t.encryptLast(round_keys[rounds]); | 460 | t = t.encryptLast(round_keys[rounds]); |
| ... | @@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type { | ... | @@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type { |
| 359 | const inv_round_keys = ctx.key_schedule.round_keys; | 508 | const inv_round_keys = ctx.key_schedule.round_keys; |
| 360 | var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]); | 509 | var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]); |
| 361 | comptime var i = 1; | 510 | comptime var i = 1; |
| 362 | inline while (i < rounds) : (i += 1) { | 511 | if (side_channels_mitigations == .full) { |
| | 512 | inline while (i < rounds) : (i += 1) { |
| | 513 | t = t.decrypt(inv_round_keys[i]); |
| | 514 | } |
| | 515 | } else { |
| | 516 | inline while (i < 5) : (i += 1) { |
| | 517 | t = t.decrypt(inv_round_keys[i]); |
| | 518 | } |
| | 519 | inline while (i < rounds - 1) : (i += 1) { |
| | 520 | t = t.decryptUnprotected(inv_round_keys[i]); |
| | 521 | } |
| 363 | t = t.decrypt(inv_round_keys[i]); | 522 | t = t.decrypt(inv_round_keys[i]); |
| 364 | } | 523 | } |
| 365 | t = t.decryptLast(inv_round_keys[rounds]); | 524 | t = t.decryptLast(inv_round_keys[rounds]); |
| ... | @@ -428,10 +587,11 @@ const powx = init: { | ... | @@ -428,10 +587,11 @@ const powx = init: { |
| 428 | break :init array; | 587 | break :init array; |
| 429 | }; | 588 | }; |
| 430 | | 589 | |
| 431 | const sbox_encrypt align(64) = generateSbox(false); | 590 | const sbox_encrypt align(64) = generateSbox(false); // S-box for encryption |
| 432 | const sbox_decrypt align(64) = generateSbox(true); | 591 | const sbox_key_schedule align(64) = generateSbox(false); // S-box only for key schedule, so that it uses distinct L1 cache entries than the S-box used for encryption |
| 433 | const table_encrypt align(64) = generateTable(false); | 592 | const sbox_decrypt align(64) = generateSbox(true); // S-box for decryption |
| 434 | const table_decrypt align(64) = generateTable(true); | 593 | const table_encrypt align(64) = generateTable(false); // 4-byte LUTs for encryption |
| | 594 | const table_decrypt align(64) = generateTable(true); // 4-byte LUTs for decryption |
| 435 | | 595 | |
| 436 | // Generate S-box substitution values. | 596 | // Generate S-box substitution values. |
| 437 | fn generateSbox(invert: bool) [256]u8 { | 597 | fn generateSbox(invert: bool) [256]u8 { |
| ... | @@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 { | ... | @@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 { |
| 472 | var table: [4][256]u32 = undefined; | 632 | var table: [4][256]u32 = undefined; |
| 473 | | 633 | |
| 474 | for (generateSbox(invert), 0..) |value, index| { | 634 | for (generateSbox(invert), 0..) |value, index| { |
| 475 | table[0][index] = mul(value, if (invert) 0xb else 0x3); | 635 | table[0][index] = math.shl(u32, mul(value, if (invert) 0xb else 0x3), 24); |
| 476 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 8); | 636 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 16); |
| 477 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 16); | 637 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 8); |
| 478 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0xe else 0x2), 24); | 638 | table[0][index] |= mul(value, if (invert) 0xe else 0x2); |
| 479 | | 639 | |
| 480 | table[1][index] = math.rotr(u32, table[0][index], 8); | 640 | table[1][index] = math.rotl(u32, table[0][index], 8); |
| 481 | table[2][index] = math.rotr(u32, table[0][index], 16); | 641 | table[2][index] = math.rotl(u32, table[0][index], 16); |
| 482 | table[3][index] = math.rotr(u32, table[0][index], 24); | 642 | table[3][index] = math.rotl(u32, table[0][index], 24); |
| 483 | } | 643 | } |
| 484 | | 644 | |
| 485 | return table; | 645 | return table; |
| ... | @@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 { | ... | @@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 { |
| 506 | | 666 | |
| 507 | return @truncate(u8, s); | 667 | return @truncate(u8, s); |
| 508 | } | 668 | } |
| | 669 | |
| | 670 | const cache_line_bytes = 64; |
| | 671 | |
| | 672 | inline fn sbox_lookup(sbox: *align(64) const [256]u8, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u8 { |
| | 673 | if (side_channels_mitigations == .none) { |
| | 674 | return [4]u8{ |
| | 675 | sbox[idx0], |
| | 676 | sbox[idx1], |
| | 677 | sbox[idx2], |
| | 678 | sbox[idx3], |
| | 679 | }; |
| | 680 | } else { |
| | 681 | const stride = switch (side_channels_mitigations) { |
| | 682 | .none => unreachable, |
| | 683 | .basic => sbox.len / 4, |
| | 684 | .medium => sbox.len / (sbox.len / cache_line_bytes) * 2, |
| | 685 | .full => sbox.len / (sbox.len / cache_line_bytes), |
| | 686 | }; |
| | 687 | const of0 = idx0 % stride; |
| | 688 | const of1 = idx1 % stride; |
| | 689 | const of2 = idx2 % stride; |
| | 690 | const of3 = idx3 % stride; |
| | 691 | var t: [4][sbox.len / stride]u8 align(64) = undefined; |
| | 692 | var i: usize = 0; |
| | 693 | while (i < t[0].len) : (i += 1) { |
| | 694 | const tx = sbox[i * stride ..]; |
| | 695 | t[0][i] = tx[of0]; |
| | 696 | t[1][i] = tx[of1]; |
| | 697 | t[2][i] = tx[of2]; |
| | 698 | t[3][i] = tx[of3]; |
| | 699 | } |
| | 700 | std.mem.doNotOptimizeAway(t); |
| | 701 | return [4]u8{ |
| | 702 | t[0][idx0 / stride], |
| | 703 | t[1][idx1 / stride], |
| | 704 | t[2][idx2 / stride], |
| | 705 | t[3][idx3 / stride], |
| | 706 | }; |
| | 707 | } |
| | 708 | } |
| | 709 | |
| | 710 | inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u32 { |
| | 711 | if (side_channels_mitigations == .none) { |
| | 712 | return [4]u32{ |
| | 713 | table[0][idx0], |
| | 714 | table[1][idx1], |
| | 715 | table[2][idx2], |
| | 716 | table[3][idx3], |
| | 717 | }; |
| | 718 | } else { |
| | 719 | const table_bytes = @sizeOf(@TypeOf(table[0])); |
| | 720 | const stride = switch (side_channels_mitigations) { |
| | 721 | .none => unreachable, |
| | 722 | .basic => table[0].len / 4, |
| | 723 | .medium => table[0].len / (table_bytes / cache_line_bytes) * 2, |
| | 724 | .full => table[0].len / (table_bytes / cache_line_bytes), |
| | 725 | }; |
| | 726 | const of0 = idx0 % stride; |
| | 727 | const of1 = idx1 % stride; |
| | 728 | const of2 = idx2 % stride; |
| | 729 | const of3 = idx3 % stride; |
| | 730 | var t: [4][table[0].len / stride]u32 align(64) = undefined; |
| | 731 | var i: usize = 0; |
| | 732 | while (i < t[0].len) : (i += 1) { |
| | 733 | const tx = table[0][i * stride ..]; |
| | 734 | t[0][i] = tx[of0]; |
| | 735 | t[1][i] = tx[of1]; |
| | 736 | t[2][i] = tx[of2]; |
| | 737 | t[3][i] = tx[of3]; |
| | 738 | } |
| | 739 | std.mem.doNotOptimizeAway(t); |
| | 740 | return [4]u32{ |
| | 741 | t[0][idx0 / stride], |
| | 742 | math.rotl(u32, t[1][idx1 / stride], 8), |
| | 743 | math.rotl(u32, t[2][idx2 / stride], 16), |
| | 744 | math.rotl(u32, t[3][idx3 / stride], 24), |
| | 745 | }; |
| | 746 | } |
| | 747 | } |