From c60dfbcc6e64af722498ceef370fc84f6b2540fc Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Sep 2026 21:03:19 +0200 Subject: [PATCH] crypto.ff: add unit element helpers Add isOne(), isMinusOne(), and subOne() helpers for quick constant-time operations against the unit element, regardless of the representation. --- lib/std/crypto/ff.zig | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/lib/std/crypto/ff.zig b/lib/std/crypto/ff.zig index 81f55539af0c276f20dbef4cfe96809ddf317ab3..9da447e6bc3ae7c582b2e2d7325a18e328a7052a 100644 --- a/lib/std/crypto/ff.zig +++ b/lib/std/crypto/ff.zig @@ -621,6 +621,23 @@ pub fn Modulus(comptime max_bits: comptime_int) type { return fe; } + /// Returns `true` if the element is 1 + pub fn isOne(self: Self, x: Fe) bool { + if (x.montgomery) { + var o = self.one(); + self.toMontgomery(&o) catch unreachable; + return x.eql(o); + } + return x.v.isOne(); + } + + /// Returns `true` if the element is equivalent to -1 + pub fn isMinusOne(self: Self, x: Fe) bool { + var mo = self.subOne(self.zero); + if (x.montgomery) self.toMontgomery(&mo) catch unreachable; + 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 { @@ -787,6 +804,11 @@ pub fn Modulus(comptime max_bits: comptime_int) type { } } + /// Subtracts 1 from a field element + pub fn subOne(self: Self, x: Fe) Fe { + return self.sub(x, self.one()); + } + /// Converts a field element to the Montgomery form. pub fn toMontgomery(self: Self, x: *Fe) RepresentationError!void { if (x.montgomery) { @@ -1511,3 +1533,32 @@ test "Uint addition and multiply-add" { try testing.expectEqual(1, short.limbs_len); try testing.expect(short.isOne()); } + +test "field element constants and subOne" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + + const M = Modulus(256); + const m = try M.fromPrimitive(u128, (1 << t_bits) + 1); + var one = m.one(); + var minus_one = try M.Fe.fromPrimitive(u128, m, 1 << t_bits); + var other = try M.Fe.fromPrimitive(u8, m, 5); + for ([_]bool{ false, true }) |montgomery| { + if (montgomery) { + try m.toMontgomery(&one); + try m.toMontgomery(&minus_one); + try m.toMontgomery(&other); + } + try testing.expect(m.isOne(one)); + try testing.expect(!m.isOne(minus_one)); + try testing.expect(m.isMinusOne(minus_one)); + try testing.expect(!m.isMinusOne(one)); + const zero = m.subOne(one); + try testing.expect(zero.isZero()); + try testing.expect(!m.isOne(zero)); + try testing.expect(!m.isMinusOne(zero)); + try testing.expect(m.isMinusOne(m.subOne(zero))); + var four = m.subOne(other); + if (montgomery) try m.fromMontgomery(&four); + try testing.expectEqual(4, try four.toPrimitive(u8)); + } +} -- 2.54.0