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