authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-09-07 21:03:12+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-10-03 14:35:42+02:00
log21ce6e8451be6603b7a21a106c0acbb9e22e1a8f
tree826b61d7c48bb6a768a2004bb5c1fbd07573ab34
parent1926e8c679dcf2264cd8af9f5bb1a62b8938c41f

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.

1 files changed, 10 insertions(+), 9 deletions(-)

lib/std/crypto/ff.zig+10-9
...@@ -1031,14 +1031,18 @@ pub fn Modulus(comptime max_bits: comptime_int) type {...@@ -1031,14 +1031,18 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
1031 }1031 }
1032 }1032 }
10331033
1034 const max_encoded_bytes = @divCeil(FeUint.capacity_bits, 8);
1035
1034 /// Returns x^e (mod m) in constant time.1036 /// Returns x^e (mod m) in constant time.
1035 pub fn pow(self: Self, x: Fe, e: Fe) (NullExponentError || RepresentationError)!Fe {1037 pub fn pow(self: Self, x: Fe, e: Fe) (NullExponentError || RepresentationError)!Fe {
1036 if (e.montgomery) {1038 if (e.montgomery) {
1037 return error.UnexpectedRepresentation;1039 return error.UnexpectedRepresentation;
1038 }1040 }
1039 var buf: [Fe.encoded_bytes]u8 = undefined;1041
1040 e.toBytes(&buf, native_endian) catch unreachable;1042 var buf: [max_encoded_bytes]u8 = undefined;
1041 return self.powWithEncodedExponent(x, &buf, native_endian);1043 const len = self.encodedLen();
1044 e.toBytes(buf[0..len], native_endian) catch unreachable;
1045 return self.powWithEncodedExponent(x, buf[0..len], native_endian);
1042 }1046 }
10431047
1044 /// Returns x^e (mod m), assuming that the exponent is public.1048 /// Returns x^e (mod m), assuming that the exponent is public.
...@@ -1047,12 +1051,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type {...@@ -1047,12 +1051,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
1047 if (e.montgomery) {1051 if (e.montgomery) {
1048 return error.UnexpectedRepresentation;1052 return error.UnexpectedRepresentation;
1049 }1053 }
1050 var e_normalized = Fe{ .v = e.v.normalize() };1054 var buf_: [max_encoded_bytes]u8 = undefined;
1051 var buf_: [Fe.encoded_bytes]u8 = undefined;1055 const buf = buf_[0..@divCeil(e.v.bitLenPublic(), 8)];
1052 var buf = buf_[0..@divCeil(e_normalized.v.limbs_len * t_bits, 8)];1056 e.toBytes(buf, .little) catch unreachable;
1053 e_normalized.toBytes(buf, .little) catch unreachable;
1054 const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]);
1055 buf = buf[0 .. buf.len - leading / 8];
1056 return self.powWithEncodedPublicExponent(x, buf, .little);1057 return self.powWithEncodedPublicExponent(x, buf, .little);
1057 }1058 }
10581059