authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-09-07 21:03:19+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-10-06 10:49:20+02:00
logc60dfbcc6e64af722498ceef370fc84f6b2540fc
treeb1771003a22800cb54cb17ef6bf84635866e9b30
parentafb5c196125f8435b03ebdd7addcb300df7335d0

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.

1 files changed, 51 insertions(+), 0 deletions(-)

lib/std/crypto/ff.zig+51
......@@ -621,6 +621,23 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
621621 return fe;
622622 }
623623
624 /// Returns `true` if the element is 1
625 pub fn isOne(self: Self, x: Fe) bool {
626 if (x.montgomery) {
627 var o = self.one();
628 self.toMontgomery(&o) catch unreachable;
629 return x.eql(o);
630 }
631 return x.v.isOne();
632 }
633
634 /// Returns `true` if the element is equivalent to -1
635 pub fn isMinusOne(self: Self, x: Fe) bool {
636 var mo = self.subOne(self.zero);
637 if (x.montgomery) self.toMontgomery(&mo) catch unreachable;
638 return x.eql(mo);
639 }
640
624641 /// Creates a new modulus from a `Uint` value.
625642 /// The modulus must be odd and larger than 2.
626643 pub fn fromUint(v_: FeUint) InvalidModulusError!Self {
......@@ -787,6 +804,11 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
787804 }
788805 }
789806
807 /// Subtracts 1 from a field element
808 pub fn subOne(self: Self, x: Fe) Fe {
809 return self.sub(x, self.one());
810 }
811
790812 /// Converts a field element to the Montgomery form.
791813 pub fn toMontgomery(self: Self, x: *Fe) RepresentationError!void {
792814 if (x.montgomery) {
......@@ -1511,3 +1533,32 @@ test "Uint addition and multiply-add" {
15111533 try testing.expectEqual(1, short.limbs_len);
15121534 try testing.expect(short.isOne());
15131535}
1536
1537test "field element constants and subOne" {
1538 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1539
1540 const M = Modulus(256);
1541 const m = try M.fromPrimitive(u128, (1 << t_bits) + 1);
1542 var one = m.one();
1543 var minus_one = try M.Fe.fromPrimitive(u128, m, 1 << t_bits);
1544 var other = try M.Fe.fromPrimitive(u8, m, 5);
1545 for ([_]bool{ false, true }) |montgomery| {
1546 if (montgomery) {
1547 try m.toMontgomery(&one);
1548 try m.toMontgomery(&minus_one);
1549 try m.toMontgomery(&other);
1550 }
1551 try testing.expect(m.isOne(one));
1552 try testing.expect(!m.isOne(minus_one));
1553 try testing.expect(m.isMinusOne(minus_one));
1554 try testing.expect(!m.isMinusOne(one));
1555 const zero = m.subOne(one);
1556 try testing.expect(zero.isZero());
1557 try testing.expect(!m.isOne(zero));
1558 try testing.expect(!m.isMinusOne(zero));
1559 try testing.expect(m.isMinusOne(m.subOne(zero)));
1560 var four = m.subOne(other);
1561 if (montgomery) try m.fromMontgomery(&four);
1562 try testing.expectEqual(4, try four.toPrimitive(u8));
1563 }
1564}