| ... | @@ -601,6 +601,12 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -601,6 +601,12 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 601 | | 601 | |
| 602 | const FeUint = Fe.FeUint; | 602 | const FeUint = Fe.FeUint; |
| 603 | | 603 | |
| | 604 | pub const Options = struct { |
| | 605 | /// Accept even moduli and treat their parity as public. |
| | 606 | /// Disabled by default: with even moduli, we can't use Montgomery form, so multiplication is slower. |
| | 607 | allow_even: bool = false, |
| | 608 | }; |
| | 609 | |
| 604 | /// The neutral element. | 610 | /// The neutral element. |
| 605 | zero: Fe, | 611 | zero: Fe, |
| 606 | | 612 | |
| ... | @@ -698,15 +704,51 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -698,15 +704,51 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 698 | /// Creates a new modulus from a primitive value. | 704 | /// Creates a new modulus from a primitive value. |
| 699 | /// The modulus must be odd and larger than 2. | 705 | /// The modulus must be odd and larger than 2. |
| 700 | pub fn fromPrimitive(comptime T: type, x: T) (InvalidModulusError || OverflowError)!Self { | 706 | pub fn fromPrimitive(comptime T: type, x: T) (InvalidModulusError || OverflowError)!Self { |
| 701 | comptime assert(@bitSizeOf(T) <= max_bits); // Primitive type is larger than the modulus type. | 707 | return fromPrimitiveWithOptions(T, x, .{}); |
| 702 | const v = try FeUint.fromPrimitive(T, x); | | |
| 703 | return try Self.fromUint(v); | | |
| 704 | } | 708 | } |
| 705 | | 709 | |
| 706 | /// Creates a new modulus from a byte string. | 710 | /// Creates a new modulus from a byte string. |
| 707 | pub fn fromBytes(bytes: []const u8, comptime endian: Endian) (InvalidModulusError || OverflowError)!Self { | 711 | pub fn fromBytes(bytes: []const u8, comptime endian: Endian) (InvalidModulusError || OverflowError)!Self { |
| | 712 | return fromBytesWithOptions(bytes, endian, .{}); |
| | 713 | } |
| | 714 | |
| | 715 | /// Creates a new modulus from a Uint value, with options. |
| | 716 | pub fn fromUintWithOptions(v_: FeUint, options: Options) InvalidModulusError!Self { |
| | 717 | if (v_.isOdd()) return fromUint(v_); |
| | 718 | if (!options.allow_even) return error.EvenModulus; |
| | 719 | |
| | 720 | const v = v_.normalize(); |
| | 721 | if (v.limbs_len < 2 and v.limbsConst()[0] < 2) { |
| | 722 | return error.ModulusTooSmall; |
| | 723 | } |
| | 724 | const hi = v.limbsConst()[v.limbs_len - 1]; |
| | 725 | |
| | 726 | var m = initWithoutRR(v, @clz(hi) - carry_bits, 0); |
| | 727 | m.rr = m.zero; |
| | 728 | |
| | 729 | return m; |
| | 730 | } |
| | 731 | |
| | 732 | /// Creates a new big integer from a primitive type, with options. |
| | 733 | pub fn fromPrimitiveWithOptions(comptime T: type, x: T, options: Options) (InvalidModulusError || OverflowError)!Self { |
| | 734 | comptime assert(@bitSizeOf(T) <= max_bits); |
| | 735 | const v = try FeUint.fromPrimitive(T, x); |
| | 736 | return try Self.fromUintWithOptions(v, options); |
| | 737 | } |
| | 738 | |
| | 739 | /// Creates a new big integer from a byte array, with options. |
| | 740 | pub fn fromBytesWithOptions(bytes: []const u8, comptime endian: Endian, options: Options) (InvalidModulusError || OverflowError)!Self { |
| 708 | const v = try FeUint.fromBytes(bytes, endian); | 741 | const v = try FeUint.fromBytes(bytes, endian); |
| 709 | return try Self.fromUint(v); | 742 | return try Self.fromUintWithOptions(v, options); |
| | 743 | } |
| | 744 | |
| | 745 | const ProductUint = Uint(2 * FeUint.capacity_bits); |
| | 746 | |
| | 747 | fn fullProduct(a: []const Limb, b: []const Limb) ProductUint { |
| | 748 | var out = ProductUint.zero; |
| | 749 | out.limbs_len = a.len + b.len; |
| | 750 | mulAddWide(out.limbs(), a, b); |
| | 751 | return out; |
| 710 | } | 752 | } |
| 711 | | 753 | |
| 712 | /// Serializes the modulus to a byte string. | 754 | /// Serializes the modulus to a byte string. |
| ... | @@ -867,8 +909,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -867,8 +909,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 867 | } | 909 | } |
| 868 | | 910 | |
| 869 | /// Converts a field element to the Montgomery form. | 911 | /// Converts a field element to the Montgomery form. |
| | 912 | /// Rejects even moduli, which have no Montgomery form. |
| 870 | pub fn toMontgomery(self: Self, x: *Fe) RepresentationError!void { | 913 | pub fn toMontgomery(self: Self, x: *Fe) RepresentationError!void { |
| 871 | if (x.montgomery) { | 914 | if (x.montgomery or !self.v.isOdd()) { |
| 872 | return error.UnexpectedRepresentation; | 915 | return error.UnexpectedRepresentation; |
| 873 | } | 916 | } |
| 874 | self.shrink(x) catch unreachable; | 917 | self.shrink(x) catch unreachable; |
| ... | @@ -877,8 +920,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -877,8 +920,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 877 | } | 920 | } |
| 878 | | 921 | |
| 879 | /// Takes a field element out of the Montgomery form. | 922 | /// Takes a field element out of the Montgomery form. |
| | 923 | /// Rejects even moduli, which have no Montgomery form. |
| 880 | pub fn fromMontgomery(self: Self, x: *Fe) RepresentationError!void { | 924 | pub fn fromMontgomery(self: Self, x: *Fe) RepresentationError!void { |
| 881 | if (!x.montgomery) { | 925 | if (!x.montgomery or !self.v.isOdd()) { |
| 882 | return error.UnexpectedRepresentation; | 926 | return error.UnexpectedRepresentation; |
| 883 | } | 927 | } |
| 884 | self.shrink(x) catch unreachable; | 928 | self.shrink(x) catch unreachable; |
| ... | @@ -985,25 +1029,36 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -985,25 +1029,36 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 985 | return d; | 1029 | return d; |
| 986 | } | 1030 | } |
| 987 | | 1031 | |
| | 1032 | fn plainMul(self: Self, x: Fe, y: Fe) Fe { |
| | 1033 | assert(x.limbs_count() == self.limbs_count()); |
| | 1034 | assert(y.limbs_count() == self.limbs_count()); |
| | 1035 | return self.reduce(fullProduct(x.v.limbsConst(), y.v.limbsConst())); |
| | 1036 | } |
| | 1037 | |
| 988 | // Set `public` to `false` for secret exponents. | 1038 | // Set `public` to `false` for secret exponents. |
| 989 | fn powWithEncodedExponentInternal(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool) NullExponentError!Fe { | 1039 | fn powWithEncodedExponentInternal(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool) NullExponentError!Fe { |
| 990 | var acc: u8 = 0; | 1040 | var acc: u8 = 0; |
| 991 | for (e) |b| acc |= b; | 1041 | for (e) |b| acc |= b; |
| 992 | if (acc == 0) return error.NullExponent; | 1042 | if (acc == 0) return error.NullExponent; |
| 993 | | 1043 | |
| | 1044 | // Modulus parity is public; even moduli need plain arithmetic. |
| | 1045 | if (!self.v.isOdd()) { |
| | 1046 | return self.powLadder(x, e, endian, public, false); |
| | 1047 | } |
| | 1048 | |
| 994 | var x_m = x; | 1049 | var x_m = x; |
| 995 | if (!x.montgomery) { | 1050 | if (!x.montgomery) { |
| 996 | self.toMontgomery(&x_m) catch unreachable; | 1051 | self.toMontgomery(&x_m) catch unreachable; |
| 997 | } | 1052 | } |
| 998 | var out = self.powLadder(x_m, e, endian, public); | 1053 | var out = self.powLadder(x_m, e, endian, public, true); |
| 999 | if (!x.montgomery) { | 1054 | if (!x.montgomery) { |
| 1000 | self.fromMontgomery(&out) catch unreachable; | 1055 | self.fromMontgomery(&out) catch unreachable; |
| 1001 | } | 1056 | } |
| 1002 | return out; | 1057 | return out; |
| 1003 | } | 1058 | } |
| 1004 | | 1059 | |
| 1005 | // `x` and the result use Montgomery form; the exponent must be nonzero. | 1060 | // `x` and the result use the representation selected by `montgomery`; the exponent must be nonzero. |
| 1006 | fn powLadder(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool) Fe { | 1061 | fn powLadder(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool, comptime montgomery: bool) Fe { |
| 1007 | if (public and | 1062 | if (public and |
| 1008 | (e.len < 3 or (e.len == 3 and e[if (endian == .big) 0 else 2] <= 0b1111))) | 1063 | (e.len < 3 or (e.len == 3 and e[if (endian == .big) 0 else 2] <= 0b1111))) |
| 1009 | { | 1064 | { |
| ... | @@ -1016,9 +1071,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -1016,9 +1071,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 1016 | var j = math.log2_int(u32, e_int); | 1071 | var j = math.log2_int(u32, e_int); |
| 1017 | while (j > 0) { | 1072 | while (j > 0) { |
| 1018 | j -= 1; | 1073 | j -= 1; |
| 1019 | out = self.montgomerySq(out); | 1074 | out = if (montgomery) self.montgomerySq(out) else self.plainMul(out, out); |
| 1020 | if ((e_int >> j) & 1 != 0) { | 1075 | if ((e_int >> j) & 1 != 0) { |
| 1021 | out = self.montgomeryMul(out, x); | 1076 | out = if (montgomery) self.montgomeryMul(out, x) else self.plainMul(out, x); |
| 1022 | } | 1077 | } |
| 1023 | } | 1078 | } |
| 1024 | return out; | 1079 | return out; |
| ... | @@ -1026,18 +1081,20 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -1026,18 +1081,20 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 1026 | | 1081 | |
| 1027 | var pc: [15]Fe = [1]Fe{x} ++ @as([14]Fe, @splat(self.zero)); | 1082 | var pc: [15]Fe = [1]Fe{x} ++ @as([14]Fe, @splat(self.zero)); |
| 1028 | for (1..pc.len) |i| { | 1083 | for (1..pc.len) |i| { |
| 1029 | pc[i] = self.montgomeryMul(pc[i - 1], pc[0]); | 1084 | pc[i] = if (montgomery) self.montgomeryMul(pc[i - 1], pc[0]) else self.plainMul(pc[i - 1], pc[0]); |
| 1030 | } | 1085 | } |
| 1031 | var out = self.one(); | 1086 | var out = self.one(); |
| 1032 | self.toMontgomery(&out) catch unreachable; | 1087 | if (montgomery) { |
| | 1088 | self.toMontgomery(&out) catch unreachable; |
| | 1089 | } |
| 1033 | | 1090 | |
| 1034 | var t0 = self.zero; | 1091 | var t0 = self.zero; |
| 1035 | t0.montgomery = true; | 1092 | t0.montgomery = montgomery; |
| 1036 | for (0..e.len) |s| { | 1093 | for (0..e.len) |s| { |
| 1037 | const b = e[if (endian == .big) s else e.len - 1 - s]; | 1094 | const b = e[if (endian == .big) s else e.len - 1 - s]; |
| 1038 | for ([_]u3{ 4, 0 }) |j| { | 1095 | for ([_]u3{ 4, 0 }) |j| { |
| 1039 | for (0..4) |_| { | 1096 | for (0..4) |_| { |
| 1040 | out = self.montgomerySq(out); | 1097 | out = if (montgomery) self.montgomerySq(out) else self.plainMul(out, out); |
| 1041 | } | 1098 | } |
| 1042 | const k = (b >> j) & 0b1111; | 1099 | const k = (b >> j) & 0b1111; |
| 1043 | if (public or std.options.side_channels_mitigations == .none) { | 1100 | if (public or std.options.side_channels_mitigations == .none) { |
| ... | @@ -1048,7 +1105,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -1048,7 +1105,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 1048 | t0.v.cmov(ct.eql(k, @truncate(i + 1)), t.v); | 1105 | t0.v.cmov(ct.eql(k, @truncate(i + 1)), t.v); |
| 1049 | } | 1106 | } |
| 1050 | } | 1107 | } |
| 1051 | const t1 = self.montgomeryMul(out, t0); | 1108 | const t1 = if (montgomery) self.montgomeryMul(out, t0) else self.plainMul(out, t0); |
| 1052 | if (public) { | 1109 | if (public) { |
| 1053 | @memcpy(out.v.limbs(), t1.v.limbsConst()); | 1110 | @memcpy(out.v.limbs(), t1.v.limbsConst()); |
| 1054 | } else { | 1111 | } else { |
| ... | @@ -1062,6 +1119,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -1062,6 +1119,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 1062 | /// Multiplies two field elements. | 1119 | /// Multiplies two field elements. |
| 1063 | /// Result preserves the first operand's form. | 1120 | /// Result preserves the first operand's form. |
| 1064 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { | 1121 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { |
| | 1122 | if (!self.v.isOdd()) { |
| | 1123 | return self.plainMul(x, y); |
| | 1124 | } |
| 1065 | if (x.montgomery) { | 1125 | if (x.montgomery) { |
| 1066 | const y_ = if (!y.montgomery) blk: { | 1126 | const y_ = if (!y.montgomery) blk: { |
| 1067 | var yy = y; | 1127 | var yy = y; |
| ... | @@ -1086,6 +1146,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -1086,6 +1146,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 1086 | | 1146 | |
| 1087 | /// Squares a field element. | 1147 | /// Squares a field element. |
| 1088 | pub fn sq(self: Self, x: Fe) Fe { | 1148 | pub fn sq(self: Self, x: Fe) Fe { |
| | 1149 | if (!self.v.isOdd()) { |
| | 1150 | return self.plainMul(x, x); |
| | 1151 | } |
| 1089 | if (x.montgomery) { | 1152 | if (x.montgomery) { |
| 1090 | return self.montgomerySq(x); | 1153 | return self.montgomerySq(x); |
| 1091 | } else { | 1154 | } else { |