std.crypto.kem.kyber: mitigate KyberSlash (#18316)

On some architectures, including AMD Zen CPUs, dividing a secret
by a constant denominator may not be a constant-time operation.

And most Kyber implementations, including ours, could leak the
hamming weight of the shared secret because of this. See:

https://kyberslash.cr.yp.to

Multiplications aren't guaranteed to be constant-time either, but
at least on the CPUs we currently support, it is.
This commit is contained in:
Frank Denis 2023-12-22 16:57:16 +01:00 committed by GitHub
parent 42ddf592dd
commit 21ae64852a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1020,8 +1020,15 @@ const Poly = struct {
// = (2/q)x+½ mod 2
// = ((x << d) + q/2) / q mod 2
// = DIV((x << d) + q/2, q) & ((1<<d) - 1)
const t = @as(u32, @intCast(p.cs[in_off + i])) << d;
in[i] = @as(u16, @intCast(@divFloor(t + q_over_2, Q) & two_d_min_1));
const t = @as(u24, @intCast(p.cs[in_off + i])) << d;
// Division by invariant multiplication, equivalent to DIV(t + q/2, q).
// A division may not be a constant-time operation, even with a constant denominator.
// Here, side channels would leak information about the shared secret, see https://kyberslash.cr.yp.to
// Multiplication, on the other hand, is a constant-time operation on the CPUs we currently support.
comptime assert(d <= 11);
comptime assert(((20642679 * @as(u64, Q)) >> 36) == 1);
const u: u32 = @intCast((@as(u64, t + q_over_2) * 20642679) >> 36);
in[i] = @intCast(u & two_d_min_1);
}
// Now we pack the d-bit integers from `in' into out as bytes.