| ... | ... | @@ -76,30 +76,98 @@ pub const XChaCha12Poly1305 = XChaChaPoly1305(12); |
| 76 | 76 | pub const XChaCha8Poly1305 = XChaChaPoly1305(8); |
| 77 | 77 | |
| 78 | 78 | // Vectorized implementation of the core function |
| 79 | | fn ChaChaVecImpl(comptime rounds_nb: usize) type { |
| 79 | fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type { |
| 80 | 80 | return struct { |
| 81 | | const Lane = @Vector(4, u32); |
| 81 | const Lane = @Vector(4 * degree, u32); |
| 82 | 82 | const BlockVec = [4]Lane; |
| 83 | 83 | |
| 84 | 84 | fn initContext(key: [8]u32, d: [4]u32) BlockVec { |
| 85 | 85 | const c = "expand 32-byte k"; |
| 86 | | const constant_le = comptime Lane{ |
| 87 | | mem.readIntLittle(u32, c[0..4]), |
| 88 | | mem.readIntLittle(u32, c[4..8]), |
| 89 | | mem.readIntLittle(u32, c[8..12]), |
| 90 | | mem.readIntLittle(u32, c[12..16]), |
| 91 | | }; |
| 92 | | return BlockVec{ |
| 93 | | constant_le, |
| 94 | | Lane{ key[0], key[1], key[2], key[3] }, |
| 95 | | Lane{ key[4], key[5], key[6], key[7] }, |
| 96 | | Lane{ d[0], d[1], d[2], d[3] }, |
| 97 | | }; |
| 86 | switch (degree) { |
| 87 | 1 => { |
| 88 | const constant_le = Lane{ |
| 89 | mem.readIntLittle(u32, c[0..4]), |
| 90 | mem.readIntLittle(u32, c[4..8]), |
| 91 | mem.readIntLittle(u32, c[8..12]), |
| 92 | mem.readIntLittle(u32, c[12..16]), |
| 93 | }; |
| 94 | return BlockVec{ |
| 95 | constant_le, |
| 96 | Lane{ key[0], key[1], key[2], key[3] }, |
| 97 | Lane{ key[4], key[5], key[6], key[7] }, |
| 98 | Lane{ d[0], d[1], d[2], d[3] }, |
| 99 | }; |
| 100 | }, |
| 101 | 2 => { |
| 102 | const constant_le = Lane{ |
| 103 | mem.readIntLittle(u32, c[0..4]), |
| 104 | mem.readIntLittle(u32, c[4..8]), |
| 105 | mem.readIntLittle(u32, c[8..12]), |
| 106 | mem.readIntLittle(u32, c[12..16]), |
| 107 | mem.readIntLittle(u32, c[0..4]), |
| 108 | mem.readIntLittle(u32, c[4..8]), |
| 109 | mem.readIntLittle(u32, c[8..12]), |
| 110 | mem.readIntLittle(u32, c[12..16]), |
| 111 | }; |
| 112 | return BlockVec{ |
| 113 | constant_le, |
| 114 | Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] }, |
| 115 | Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] }, |
| 116 | Lane{ d[0], d[1], d[2], d[3], d[0] +% 1, d[1], d[2], d[3] }, |
| 117 | }; |
| 118 | }, |
| 119 | 4 => { |
| 120 | const constant_le = Lane{ |
| 121 | mem.readIntLittle(u32, c[0..4]), |
| 122 | mem.readIntLittle(u32, c[4..8]), |
| 123 | mem.readIntLittle(u32, c[8..12]), |
| 124 | mem.readIntLittle(u32, c[12..16]), |
| 125 | mem.readIntLittle(u32, c[0..4]), |
| 126 | mem.readIntLittle(u32, c[4..8]), |
| 127 | mem.readIntLittle(u32, c[8..12]), |
| 128 | mem.readIntLittle(u32, c[12..16]), |
| 129 | mem.readIntLittle(u32, c[0..4]), |
| 130 | mem.readIntLittle(u32, c[4..8]), |
| 131 | mem.readIntLittle(u32, c[8..12]), |
| 132 | mem.readIntLittle(u32, c[12..16]), |
| 133 | mem.readIntLittle(u32, c[0..4]), |
| 134 | mem.readIntLittle(u32, c[4..8]), |
| 135 | mem.readIntLittle(u32, c[8..12]), |
| 136 | mem.readIntLittle(u32, c[12..16]), |
| 137 | }; |
| 138 | return BlockVec{ |
| 139 | constant_le, |
| 140 | Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] }, |
| 141 | Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] }, |
| 142 | Lane{ d[0], d[1], d[2], d[3], d[0] +% 1, d[1], d[2], d[3], d[0] +% 2, d[1], d[2], d[3], d[0] +% 3, d[1], d[2], d[3] }, |
| 143 | }; |
| 144 | }, |
| 145 | else => @panic("invalid degree"), |
| 146 | } |
| 98 | 147 | } |
| 99 | 148 | |
| 100 | 149 | inline fn chacha20Core(x: *BlockVec, input: BlockVec) void { |
| 101 | 150 | x.* = input; |
| 102 | 151 | |
| 152 | const m0 = switch (degree) { |
| 153 | 1 => [_]i32{ 3, 0, 1, 2 }, |
| 154 | 2 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 }, |
| 155 | 4 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 } ++ [_]i32{ 11, 8, 9, 10 } ++ [_]i32{ 15, 12, 13, 14 }, |
| 156 | else => @panic("invalid degree"), |
| 157 | }; |
| 158 | const m1 = switch (degree) { |
| 159 | 1 => [_]i32{ 2, 3, 0, 1 }, |
| 160 | 2 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 }, |
| 161 | 4 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 } ++ [_]i32{ 10, 11, 8, 9 } ++ [_]i32{ 14, 15, 12, 13 }, |
| 162 | else => @panic("invalid degree"), |
| 163 | }; |
| 164 | const m2 = switch (degree) { |
| 165 | 1 => [_]i32{ 1, 2, 3, 0 }, |
| 166 | 2 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 }, |
| 167 | 4 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 } ++ [_]i32{ 9, 10, 11, 8 } ++ [_]i32{ 13, 14, 15, 12 }, |
| 168 | else => @panic("invalid degree"), |
| 169 | }; |
| 170 | |
| 103 | 171 | var r: usize = 0; |
| 104 | 172 | while (r < rounds_nb) : (r += 2) { |
| 105 | 173 | x[0] +%= x[1]; |
| ... | ... | @@ -112,13 +180,13 @@ fn ChaChaVecImpl(comptime rounds_nb: usize) type { |
| 112 | 180 | |
| 113 | 181 | x[0] +%= x[1]; |
| 114 | 182 | x[3] ^= x[0]; |
| 115 | | x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 }); |
| 183 | x[0] = @shuffle(u32, x[0], undefined, m0); |
| 116 | 184 | x[3] = math.rotl(Lane, x[3], 8); |
| 117 | 185 | |
| 118 | 186 | x[2] +%= x[3]; |
| 119 | | x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 }); |
| 187 | x[3] = @shuffle(u32, x[3], undefined, m1); |
| 120 | 188 | x[1] ^= x[2]; |
| 121 | | x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 }); |
| 189 | x[2] = @shuffle(u32, x[2], undefined, m2); |
| 122 | 190 | x[1] = math.rotl(Lane, x[1], 7); |
| 123 | 191 | |
| 124 | 192 | x[0] +%= x[1]; |
| ... | ... | @@ -131,24 +199,26 @@ fn ChaChaVecImpl(comptime rounds_nb: usize) type { |
| 131 | 199 | |
| 132 | 200 | x[0] +%= x[1]; |
| 133 | 201 | x[3] ^= x[0]; |
| 134 | | x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 }); |
| 202 | x[0] = @shuffle(u32, x[0], undefined, m2); |
| 135 | 203 | x[3] = math.rotl(Lane, x[3], 8); |
| 136 | 204 | |
| 137 | 205 | x[2] +%= x[3]; |
| 138 | | x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 }); |
| 206 | x[3] = @shuffle(u32, x[3], undefined, m1); |
| 139 | 207 | x[1] ^= x[2]; |
| 140 | | x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 }); |
| 208 | x[2] = @shuffle(u32, x[2], undefined, m0); |
| 141 | 209 | x[1] = math.rotl(Lane, x[1], 7); |
| 142 | 210 | } |
| 143 | 211 | } |
| 144 | 212 | |
| 145 | | inline fn hashToBytes(out: *[64]u8, x: BlockVec) void { |
| 146 | | var i: usize = 0; |
| 147 | | while (i < 4) : (i += 1) { |
| 148 | | mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]); |
| 149 | | mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]); |
| 150 | | mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]); |
| 151 | | mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]); |
| 213 | inline fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void { |
| 214 | for (0..dm) |d| { |
| 215 | var i: usize = 0; |
| 216 | while (i < 4) : (i += 1) { |
| 217 | mem.writeIntLittle(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d]); |
| 218 | mem.writeIntLittle(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d]); |
| 219 | mem.writeIntLittle(u32, out[64 * d + 16 * i + 8 ..][0..4], x[i][2 + 4 * d]); |
| 220 | mem.writeIntLittle(u32, out[64 * d + 16 * i + 12 ..][0..4], x[i][3 + 4 * d]); |
| 221 | } |
| 152 | 222 | } |
| 153 | 223 | } |
| 154 | 224 | |
| ... | ... | @@ -162,29 +232,33 @@ fn ChaChaVecImpl(comptime rounds_nb: usize) type { |
| 162 | 232 | fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void { |
| 163 | 233 | var ctx = initContext(key, counter); |
| 164 | 234 | var x: BlockVec = undefined; |
| 165 | | var buf: [64]u8 = undefined; |
| 235 | var buf: [64 * degree]u8 = undefined; |
| 166 | 236 | var i: usize = 0; |
| 167 | | while (i + 64 <= in.len) : (i += 64) { |
| 168 | | chacha20Core(x[0..], ctx); |
| 169 | | contextFeedback(&x, ctx); |
| 170 | | hashToBytes(buf[0..], x); |
| 171 | | |
| 172 | | var xout = out[i..]; |
| 173 | | const xin = in[i..]; |
| 174 | | var j: usize = 0; |
| 175 | | while (j < 64) : (j += 1) { |
| 176 | | xout[j] = xin[j]; |
| 177 | | } |
| 178 | | j = 0; |
| 179 | | while (j < 64) : (j += 1) { |
| 180 | | xout[j] ^= buf[j]; |
| 237 | inline for ([_]comptime_int{ 4, 2, 1 }) |d| { |
| 238 | while (degree >= d and i + 64 * d <= in.len) : (i += 64 * d) { |
| 239 | chacha20Core(x[0..], ctx); |
| 240 | contextFeedback(&x, ctx); |
| 241 | hashToBytes(d, buf[0 .. 64 * d], x); |
| 242 | |
| 243 | var xout = out[i..]; |
| 244 | const xin = in[i..]; |
| 245 | var j: usize = 0; |
| 246 | while (j < 64 * d) : (j += 1) { |
| 247 | xout[j] = xin[j]; |
| 248 | } |
| 249 | j = 0; |
| 250 | while (j < 64 * d) : (j += 1) { |
| 251 | xout[j] ^= buf[j]; |
| 252 | } |
| 253 | inline for (0..d) |d_| { |
| 254 | ctx[3][4 * d_] += @intCast(u32, d); |
| 255 | } |
| 181 | 256 | } |
| 182 | | ctx[3][0] += 1; |
| 183 | 257 | } |
| 184 | 258 | if (i < in.len) { |
| 185 | 259 | chacha20Core(x[0..], ctx); |
| 186 | 260 | contextFeedback(&x, ctx); |
| 187 | | hashToBytes(buf[0..], x); |
| 261 | hashToBytes(1, buf[0..64], x); |
| 188 | 262 | |
| 189 | 263 | var xout = out[i..]; |
| 190 | 264 | const xin = in[i..]; |
| ... | ... | @@ -199,18 +273,22 @@ fn ChaChaVecImpl(comptime rounds_nb: usize) type { |
| 199 | 273 | var ctx = initContext(key, counter); |
| 200 | 274 | var x: BlockVec = undefined; |
| 201 | 275 | var i: usize = 0; |
| 202 | | while (i + 64 <= out.len) : (i += 64) { |
| 203 | | chacha20Core(x[0..], ctx); |
| 204 | | contextFeedback(&x, ctx); |
| 205 | | hashToBytes(out[i..][0..64], x); |
| 206 | | ctx[3][0] += 1; |
| 276 | inline for ([_]comptime_int{ 4, 2, 1 }) |d| { |
| 277 | while (degree >= d and i + 64 * d <= out.len) : (i += 64 * d) { |
| 278 | chacha20Core(x[0..], ctx); |
| 279 | contextFeedback(&x, ctx); |
| 280 | hashToBytes(d, out[i..][0 .. 64 * d], x); |
| 281 | inline for (0..d) |d_| { |
| 282 | ctx[3][4 * d_] += @intCast(u32, d); |
| 283 | } |
| 284 | } |
| 207 | 285 | } |
| 208 | 286 | if (i < out.len) { |
| 209 | 287 | chacha20Core(x[0..], ctx); |
| 210 | 288 | contextFeedback(&x, ctx); |
| 211 | 289 | |
| 212 | 290 | var buf: [64]u8 = undefined; |
| 213 | | hashToBytes(buf[0..], x); |
| 291 | hashToBytes(1, buf[0..], x); |
| 214 | 292 | @memcpy(out[i..], buf[0 .. out.len - i]); |
| 215 | 293 | } |
| 216 | 294 | } |
| ... | ... | @@ -399,7 +477,21 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { |
| 399 | 477 | } |
| 400 | 478 | |
| 401 | 479 | fn ChaChaImpl(comptime rounds_nb: usize) type { |
| 402 | | return if (builtin.cpu.arch == .x86_64) ChaChaVecImpl(rounds_nb) else ChaChaNonVecImpl(rounds_nb); |
| 480 | switch (builtin.cpu.arch) { |
| 481 | .x86_64 => { |
| 482 | const has_avx2 = std.Target.x86.featureSetHas(builtin.cpu.features, .avx2); |
| 483 | const has_avx512f = std.Target.x86.featureSetHas(builtin.cpu.features, .avx512f); |
| 484 | if (has_avx512f) return ChaChaVecImpl(rounds_nb, 4); |
| 485 | if (has_avx2) return ChaChaVecImpl(rounds_nb, 2); |
| 486 | return ChaChaVecImpl(rounds_nb, 1); |
| 487 | }, |
| 488 | .aarch64 => { |
| 489 | const has_neon = std.Target.aarch64.featureSetHas(builtin.cpu.features, .neon); |
| 490 | if (has_neon) return ChaChaVecImpl(rounds_nb, 4); |
| 491 | return ChaChaNonVecImpl(rounds_nb); |
| 492 | }, |
| 493 | else => return ChaChaNonVecImpl(rounds_nb), |
| 494 | } |
| 403 | 495 | } |
| 404 | 496 | |
| 405 | 497 | fn keyToWords(key: [32]u8) [8]u32 { |