From 12caf4e3de3b8fb54ce5dd705d50609dd05264b4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Sep 2026 21:03:12 +0200 Subject: [PATCH] crypto.ff: fix encoded exponent lengths Use a buffer large enough for the field's full capacity. For exponentiation with secret exponents, always encode the exponents at the modulus public length, so that their length doesn't act as a side channel. And the public counterpart should use the exponent's public bit length to include every byte needed for its highest set bit. --- lib/std/crypto/ff.zig | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/std/crypto/ff.zig b/lib/std/crypto/ff.zig index b09cfa5033a45d24939f6459322a71438c1229f5..6292bdf68756ec854dd438ae83fe3cf0e5b661d7 100644 --- a/lib/std/crypto/ff.zig +++ b/lib/std/crypto/ff.zig @@ -1031,14 +1031,18 @@ pub fn Modulus(comptime max_bits: comptime_int) type { } } + const max_encoded_bytes = @divCeil(FeUint.capacity_bits, 8); + /// Returns x^e (mod m) in constant time. pub fn pow(self: Self, x: Fe, e: Fe) (NullExponentError || RepresentationError)!Fe { if (e.montgomery) { return error.UnexpectedRepresentation; } - var buf: [Fe.encoded_bytes]u8 = undefined; - e.toBytes(&buf, native_endian) catch unreachable; - return self.powWithEncodedExponent(x, &buf, native_endian); + + var buf: [max_encoded_bytes]u8 = undefined; + const len = self.encodedLen(); + e.toBytes(buf[0..len], native_endian) catch unreachable; + return self.powWithEncodedExponent(x, buf[0..len], native_endian); } /// Returns x^e (mod m), assuming that the exponent is public. @@ -1047,12 +1051,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { if (e.montgomery) { return error.UnexpectedRepresentation; } - var e_normalized = Fe{ .v = e.v.normalize() }; - var buf_: [Fe.encoded_bytes]u8 = undefined; - var buf = buf_[0..@divCeil(e_normalized.v.limbs_len * t_bits, 8)]; - e_normalized.toBytes(buf, .little) catch unreachable; - const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]); - buf = buf[0 .. buf.len - leading / 8]; + var buf_: [max_encoded_bytes]u8 = undefined; + const buf = buf_[0..@divCeil(e.v.bitLenPublic(), 8)]; + e.toBytes(buf, .little) catch unreachable; return self.powWithEncodedPublicExponent(x, buf, .little); } -- 2.54.0