From a1363b3b5a2bfdd9609cf1baed176a613289d499 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Sep 2026 21:03:29 +0200 Subject: [PATCH] crypto.ff: avoid code duplication Modular addition uses the same code in plain and Montgomery representations, so factor it. Computation of m0inv can also be in a dedicated function. This improves clarity, but this is also something that we'll need for working with a secret modulus, so that will avoid code duplication as well. --- lib/std/crypto/ff.zig | 66 ++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/lib/std/crypto/ff.zig b/lib/std/crypto/ff.zig index 6988967cd8ef0930f9bb4010bfe3c877cb7256a1..6df19bfae39297f430fdc7f81534b671a48353c5 100644 --- a/lib/std/crypto/ff.zig +++ b/lib/std/crypto/ff.zig @@ -638,38 +638,43 @@ pub fn Modulus(comptime max_bits: comptime_int) type { return x.eql(mo); } - /// Creates a new modulus from a `Uint` value. - /// The modulus must be odd and larger than 2. - pub fn fromUint(v_: FeUint) InvalidModulusError!Self { - if (!v_.isOdd()) return error.EvenModulus; - - var v = v_.normalize(); - const hi = v.limbsConst()[v.limbs_len - 1]; - const lo = v.limbsConst()[0]; - - if (v.limbs_len < 2 and lo < 3) { - return error.ModulusTooSmall; - } - - const leading = @clz(hi) - carry_bits; - + // Computes -lo^-1 mod 2^t_bits for an odd `lo`. + fn computeM0Inv(lo: Limb) Limb { var y = lo; - inline for (0..comptime math.log2_int(usize, t_bits)) |_| { y = y *% (2 -% lo *% y); } - const m0inv = (@as(Limb, 1) << t_bits) - (@as(TLimb, @truncate(y))); - - const zero = Fe{ .v = FeUint.zero }; + return (@as(Limb, 1) << t_bits) - (@as(TLimb, @truncate(y))); + } + // Initialize without R2 + fn initWithoutRR(v: FeUint, leading: usize, m0inv: Limb) Self { var m = Self{ - .zero = zero, + .zero = Fe{ .v = FeUint.zero }, .v = v, .leading = leading, .m0inv = m0inv, - .rr = undefined, // will be computed right after + .rr = undefined, }; m.shrink(&m.zero) catch unreachable; + return m; + } + + /// Creates a new modulus from a `Uint` value. + /// The modulus must be odd and larger than 2. + pub fn fromUint(v_: FeUint) InvalidModulusError!Self { + if (!v_.isOdd()) return error.EvenModulus; + + var v = v_.normalize(); + const hi = v.limbsConst()[v.limbs_len - 1]; + const lo = v.limbsConst()[0]; + + if (v.limbs_len < 2 and lo < 3) { + return error.ModulusTooSmall; + } + + var m = initWithoutRR(v, @clz(hi) - carry_bits, computeM0Inv(lo)); + computeRR(&m); return m; @@ -759,15 +764,21 @@ pub fn Modulus(comptime max_bits: comptime_int) type { x.v.cmov(need_sub, d.v); } + // Adds y to x. Returns whether reduction was needed. + fn addAndReduce(x: *FeUint, y: FeUint, bound: FeUint) bool { + const overflow = x.addWithOverflow(y); + const underflow: u1 = @bitCast(ct.limbsCmpLt(x.*, bound)); + const need_sub = ct.eql(overflow, underflow); + _ = x.conditionalSubWithOverflow(need_sub, bound); + return need_sub; + } + /// Adds two field elements (mod m). pub fn add(self: Self, x: Fe, y: Fe) Fe { var out = x; if (x.montgomery == y.montgomery) { @branchHint(.likely); - const overflow = out.v.addWithOverflow(y.v); - const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); - const need_sub = ct.eql(overflow, underflow); - _ = out.v.conditionalSubWithOverflow(need_sub, self.v); + _ = addAndReduce(&out.v, y.v, self.v); return out; } else { var y_ = y; @@ -776,10 +787,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { } else { self.toMontgomery(&y_) catch unreachable; } - const overflow = out.v.addWithOverflow(y_.v); - const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); - const need_sub = ct.eql(overflow, underflow); - _ = out.v.conditionalSubWithOverflow(need_sub, self.v); + _ = addAndReduce(&out.v, y_.v, self.v); return out; } } -- 2.54.0