authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-09-07 21:07:26+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-10-25 08:38:01+01:00
log4707a01ad3ab437e4d8165e2b41b17ad346e6e99
tree01882e8d24b68d013d11fed45b252f5e94a42c0f
parentbb3fd332249e312b68f8839e98e386aa70980742

crypto.ff: add functions to create a moduli from two integers

An RSA modulus is the product of two integers, so add functions to directly contruct such moduli.

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

lib/std/crypto/ff.zig+46
...@@ -751,6 +751,26 @@ pub fn Modulus(comptime max_bits: comptime_int) type {...@@ -751,6 +751,26 @@ pub fn Modulus(comptime max_bits: comptime_int) type {
751 return out;751 return out;
752 }752 }
753753
754 /// Creates an odd modulus from the product of two integers.
755 pub fn fromProduct(a: FeUint, b: FeUint) (InvalidModulusError || OverflowError)!Self {
756 return fromProductWithOptions(a, b, .{});
757 }
758
759 /// Creates a modulus from the product of two integers, with options (set `allow_even` to `true` to allow the product to be even)
760 pub fn fromProductWithOptions(a: FeUint, b: FeUint, options: Options) (InvalidModulusError || OverflowError)!Self {
761 const product = fullProduct(a.limbsConst(), b.limbsConst());
762
763 if (orLimbs(product.limbs_buffer[FeUint.max_limbs_count..]) != 0) {
764 return error.Overflow;
765 }
766
767 const v = FeUint{
768 .limbs_buffer = product.limbs_buffer[0..FeUint.max_limbs_count].*,
769 .limbs_len = FeUint.max_limbs_count,
770 };
771 return try Self.fromUintWithOptions(v, options);
772 }
773
754 /// Serializes the modulus to a byte string.774 /// Serializes the modulus to a byte string.
755 pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) OverflowError!void {775 pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) OverflowError!void {
756 return self.v.toBytes(bytes, endian);776 return self.v.toBytes(bytes, endian);
...@@ -1664,6 +1684,32 @@ test "field element constants and subOne" {...@@ -1664,6 +1684,32 @@ test "field element constants and subOne" {
1664 }1684 }
1665}1685}
16661686
1687test "modulus from a product" {
1688 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1689
1690 const M = Modulus(256);
1691 const U = Uint(256);
1692 const one = try U.fromPrimitive(u8, 1);
1693 const a = (try U.fromPrimitive(u32, 65537)).normalize();
1694 const b = try U.fromPrimitive(u256, (1 << 200) + 12345);
1695 const m = try M.fromProduct(a, b);
1696 try testing.expectEqual(65537 * ((1 << 200) + 12345), try m.toUint().toPrimitive(u256));
1697 try testing.expect((try M.fromProduct(b, a)).v.eql(m.v));
1698 try testing.expectError(error.EvenModulus, M.fromProduct(U.zero, a));
1699 try testing.expectError(error.ModulusTooSmall, M.fromProduct(one, one));
1700 const two = try U.fromPrimitive(u8, 2);
1701 try testing.expectError(error.EvenModulus, M.fromProduct(a, two));
1702 try testing.expectEqual(2 * 65537, try (try M.fromProductWithOptions(a, two, .{ .allow_even = true })).toUint().toPrimitive(u32));
1703 try testing.expectError(error.ModulusTooSmall, M.fromProductWithOptions(U.zero, a, .{ .allow_even = true }));
1704
1705 const full = try U.fromBytes(&maxUintBytes(U), .big);
1706 try testing.expect((try M.fromProduct(full, one)).toUint().eql(full));
1707 try testing.expectError(error.Overflow, M.fromProduct(full, a));
1708 try testing.expectError(error.Overflow, M.fromProduct(a, full));
1709 const power = try U.fromPrimitive(u256, 1 << 160);
1710 try testing.expectError(error.Overflow, M.fromProductWithOptions(power, power, .{ .allow_even = true }));
1711}
1712
1667test "even modulus arithmetic" {1713test "even modulus arithmetic" {
1668 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;1714 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
16691715