| ... | @@ -985,104 +985,77 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -985,104 +985,77 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 985 | return d; | 985 | return d; |
| 986 | } | 986 | } |
| 987 | | 987 | |
| 988 | // Returns x^e (mod m), with the exponent provided as a byte string. | 988 | // Set `public` to `false` for secret exponents. |
| 989 | // `public` must be set to `false` if the exponent it secret. | | |
| 990 | fn powWithEncodedExponentInternal(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool) NullExponentError!Fe { | 989 | fn powWithEncodedExponentInternal(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool) NullExponentError!Fe { |
| 991 | var acc: u8 = 0; | 990 | var acc: u8 = 0; |
| 992 | for (e) |b| acc |= b; | 991 | for (e) |b| acc |= b; |
| 993 | if (acc == 0) return error.NullExponent; | 992 | if (acc == 0) return error.NullExponent; |
| 994 | | 993 | |
| 995 | const was_montgomery = x.montgomery; | 994 | var x_m = x; |
| 996 | | 995 | if (!x.montgomery) { |
| 997 | var out = self.one(); | 996 | self.toMontgomery(&x_m) catch unreachable; |
| 998 | self.toMontgomery(&out) catch unreachable; | 997 | } |
| | 998 | var out = self.powLadder(x_m, e, endian, public); |
| | 999 | if (!x.montgomery) { |
| | 1000 | self.fromMontgomery(&out) catch unreachable; |
| | 1001 | } |
| | 1002 | return out; |
| | 1003 | } |
| 999 | | 1004 | |
| | 1005 | // `x` and the result use Montgomery form; the exponent must be nonzero. |
| | 1006 | fn powLadder(self: Self, x: Fe, e: []const u8, endian: Endian, comptime public: bool) Fe { |
| 1000 | if (public and | 1007 | if (public and |
| 1001 | (e.len < 3 or (e.len == 3 and e[if (endian == .big) 0 else 2] <= 0b1111))) | 1008 | (e.len < 3 or (e.len == 3 and e[if (endian == .big) 0 else 2] <= 0b1111))) |
| 1002 | { | 1009 | { |
| 1003 | // Do not use a precomputation table for short, public exponents | 1010 | // Do not use a precomputation table for short, public exponents |
| 1004 | var x_m = x; | 1011 | var e_int: u32 = 0; |
| 1005 | if (!x.montgomery) { | 1012 | for (0..e.len) |i| { |
| 1006 | self.toMontgomery(&x_m) catch unreachable; | 1013 | e_int = (e_int << 8) | e[if (endian == .big) i else e.len - 1 - i]; |
| 1007 | } | 1014 | } |
| 1008 | var s = switch (endian) { | 1015 | var out = x; |
| 1009 | .big => 0, | 1016 | var j = math.log2_int(u32, e_int); |
| 1010 | .little => e.len - 1, | 1017 | while (j > 0) { |
| 1011 | }; | 1018 | j -= 1; |
| 1012 | while (true) { | 1019 | out = self.montgomerySq(out); |
| 1013 | const b = e[s]; | 1020 | if ((e_int >> j) & 1 != 0) { |
| 1014 | var j: u3 = 7; | 1021 | out = self.montgomeryMul(out, x); |
| 1015 | while (true) : (j -= 1) { | | |
| 1016 | out = self.montgomerySq(out); | | |
| 1017 | const k: u1 = @truncate(b >> j); | | |
| 1018 | if (k != 0) { | | |
| 1019 | const t = self.montgomeryMul(out, x_m); | | |
| 1020 | @memcpy(out.v.limbs(), t.v.limbsConst()); | | |
| 1021 | } | | |
| 1022 | if (j == 0) break; | | |
| 1023 | } | | |
| 1024 | switch (endian) { | | |
| 1025 | .big => { | | |
| 1026 | s += 1; | | |
| 1027 | if (s == e.len) break; | | |
| 1028 | }, | | |
| 1029 | .little => { | | |
| 1030 | if (s == 0) break; | | |
| 1031 | s -= 1; | | |
| 1032 | }, | | |
| 1033 | } | 1022 | } |
| 1034 | } | 1023 | } |
| 1035 | } else { | 1024 | return out; |
| 1036 | // Use a precomputation table for large exponents | 1025 | } |
| 1037 | var pc: [15]Fe = [1]Fe{x} ++ @as([14]Fe, @splat(self.zero)); | 1026 | |
| 1038 | if (!x.montgomery) { | 1027 | var pc: [15]Fe = [1]Fe{x} ++ @as([14]Fe, @splat(self.zero)); |
| 1039 | self.toMontgomery(&pc[0]) catch unreachable; | 1028 | for (1..pc.len) |i| { |
| 1040 | } | 1029 | pc[i] = self.montgomeryMul(pc[i - 1], pc[0]); |
| 1041 | for (1..pc.len) |i| { | 1030 | } |
| 1042 | pc[i] = self.montgomeryMul(pc[i - 1], pc[0]); | 1031 | var out = self.one(); |
| 1043 | } | 1032 | self.toMontgomery(&out) catch unreachable; |
| 1044 | var t0 = self.zero; | 1033 | |
| 1045 | var s = switch (endian) { | 1034 | var t0 = self.zero; |
| 1046 | .big => 0, | 1035 | t0.montgomery = true; |
| 1047 | .little => e.len - 1, | 1036 | for (0..e.len) |s| { |
| 1048 | }; | 1037 | const b = e[if (endian == .big) s else e.len - 1 - s]; |
| 1049 | while (true) { | 1038 | for ([_]u3{ 4, 0 }) |j| { |
| 1050 | const b = e[s]; | 1039 | for (0..4) |_| { |
| 1051 | for ([_]u3{ 4, 0 }) |j| { | 1040 | out = self.montgomerySq(out); |
| 1052 | for (0..4) |_| { | 1041 | } |
| 1053 | out = self.montgomerySq(out); | 1042 | const k = (b >> j) & 0b1111; |
| 1054 | } | 1043 | if (public or std.options.side_channels_mitigations == .none) { |
| 1055 | const k = (b >> j) & 0b1111; | 1044 | if (k == 0) continue; |
| 1056 | if (public or std.options.side_channels_mitigations == .none) { | 1045 | t0 = pc[k - 1]; |
| 1057 | if (k == 0) continue; | 1046 | } else { |
| 1058 | t0 = pc[k - 1]; | 1047 | for (pc, 0..) |t, i| { |
| 1059 | } else { | 1048 | t0.v.cmov(ct.eql(k, @truncate(i + 1)), t.v); |
| 1060 | for (pc, 0..) |t, i| { | | |
| 1061 | t0.v.cmov(ct.eql(k, @as(u8, @truncate(i + 1))), t.v); | | |
| 1062 | } | | |
| 1063 | } | | |
| 1064 | const t1 = self.montgomeryMul(out, t0); | | |
| 1065 | if (public) { | | |
| 1066 | @memcpy(out.v.limbs(), t1.v.limbsConst()); | | |
| 1067 | } else { | | |
| 1068 | out.v.cmov(!ct.eql(k, 0), t1.v); | | |
| 1069 | } | 1049 | } |
| 1070 | } | 1050 | } |
| 1071 | switch (endian) { | 1051 | const t1 = self.montgomeryMul(out, t0); |
| 1072 | .big => { | 1052 | if (public) { |
| 1073 | s += 1; | 1053 | @memcpy(out.v.limbs(), t1.v.limbsConst()); |
| 1074 | if (s == e.len) break; | 1054 | } else { |
| 1075 | }, | 1055 | out.v.cmov(!ct.eql(k, 0), t1.v); |
| 1076 | .little => { | | |
| 1077 | if (s == 0) break; | | |
| 1078 | s -= 1; | | |
| 1079 | }, | | |
| 1080 | } | 1056 | } |
| 1081 | } | 1057 | } |
| 1082 | } | 1058 | } |
| 1083 | if (!was_montgomery) { | | |
| 1084 | self.fromMontgomery(&out) catch unreachable; | | |
| 1085 | } | | |
| 1086 | return out; | 1059 | return out; |
| 1087 | } | 1060 | } |
| 1088 | | 1061 | |