| ... | @@ -656,6 +656,101 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -656,6 +656,101 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 656 | return d; | 656 | return d; |
| 657 | } | 657 | } |
| 658 | | 658 | |
| | 659 | // Returns x^e (mod m), with the exponent provided as a byte string. |
| | 660 | // `public` must be set to `false` if the exponent it secret. |
| | 661 | fn powWithEncodedExponentInternal(self: Self, x: Fe, e: []const u8, endian: builtin.Endian, comptime public: bool) NullExponentError!Fe { |
| | 662 | var acc: u8 = 0; |
| | 663 | for (e) |b| acc |= b; |
| | 664 | if (acc == 0) return error.NullExponent; |
| | 665 | |
| | 666 | var out = self.one(); |
| | 667 | self.toMontgomery(&out) catch unreachable; |
| | 668 | |
| | 669 | if (public and e.len < 3 or (e.len == 3 and e[if (endian == .Big) 0 else 2] <= 0b1111)) { |
| | 670 | // Do not use a precomputation table for short, public exponents |
| | 671 | var x_m = x; |
| | 672 | if (x.montgomery == false) { |
| | 673 | self.toMontgomery(&x_m) catch unreachable; |
| | 674 | } |
| | 675 | var s = switch (endian) { |
| | 676 | .Big => 0, |
| | 677 | .Little => e.len - 1, |
| | 678 | }; |
| | 679 | while (true) { |
| | 680 | const b = e[s]; |
| | 681 | var j: u3 = 7; |
| | 682 | while (true) : (j -= 1) { |
| | 683 | out = self.montgomerySq(out); |
| | 684 | const k: u1 = @truncate(b >> j); |
| | 685 | if (k != 0) { |
| | 686 | const t = self.montgomeryMul(out, x_m); |
| | 687 | @memcpy(out.v.limbs.slice(), t.v.limbs.constSlice()); |
| | 688 | } |
| | 689 | if (j == 0) break; |
| | 690 | } |
| | 691 | switch (endian) { |
| | 692 | .Big => { |
| | 693 | s += 1; |
| | 694 | if (s == e.len) break; |
| | 695 | }, |
| | 696 | .Little => { |
| | 697 | if (s == 0) break; |
| | 698 | s -= 1; |
| | 699 | }, |
| | 700 | } |
| | 701 | } |
| | 702 | } else { |
| | 703 | // Use a precomputation table for large exponents |
| | 704 | var pc = [1]Fe{x} ++ [_]Fe{self.zero} ** 14; |
| | 705 | if (x.montgomery == false) { |
| | 706 | self.toMontgomery(&pc[0]) catch unreachable; |
| | 707 | } |
| | 708 | for (1..pc.len) |i| { |
| | 709 | pc[i] = self.montgomeryMul(pc[i - 1], pc[0]); |
| | 710 | } |
| | 711 | var t0 = self.zero; |
| | 712 | var s = switch (endian) { |
| | 713 | .Big => 0, |
| | 714 | .Little => e.len - 1, |
| | 715 | }; |
| | 716 | while (true) { |
| | 717 | const b = e[s]; |
| | 718 | for ([_]u3{ 4, 0 }) |j| { |
| | 719 | for (0..4) |_| { |
| | 720 | out = self.montgomerySq(out); |
| | 721 | } |
| | 722 | const k = (b >> j) & 0b1111; |
| | 723 | if (public or std.options.side_channels_mitigations == .none) { |
| | 724 | if (k == 0) continue; |
| | 725 | t0 = pc[k - 1]; |
| | 726 | } else { |
| | 727 | for (pc, 0..) |t, i| { |
| | 728 | t0.v.cmov(ct.eql(k, @as(u8, @truncate(i + 1))), t.v); |
| | 729 | } |
| | 730 | } |
| | 731 | const t1 = self.montgomeryMul(out, t0); |
| | 732 | if (public) { |
| | 733 | @memcpy(out.v.limbs.slice(), t1.v.limbs.constSlice()); |
| | 734 | } else { |
| | 735 | out.v.cmov(!ct.eql(k, 0), t1.v); |
| | 736 | } |
| | 737 | } |
| | 738 | switch (endian) { |
| | 739 | .Big => { |
| | 740 | s += 1; |
| | 741 | if (s == e.len) break; |
| | 742 | }, |
| | 743 | .Little => { |
| | 744 | if (s == 0) break; |
| | 745 | s -= 1; |
| | 746 | }, |
| | 747 | } |
| | 748 | } |
| | 749 | } |
| | 750 | self.fromMontgomery(&out) catch unreachable; |
| | 751 | return out; |
| | 752 | } |
| | 753 | |
| 659 | /// Multiplies two field elements. | 754 | /// Multiplies two field elements. |
| 660 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { | 755 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { |
| 661 | if (x.montgomery != y.montgomery) { | 756 | if (x.montgomery != y.montgomery) { |
| ... | @@ -698,62 +793,25 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -698,62 +793,25 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 698 | e_normalized.toBytes(buf, .Little) catch unreachable; | 793 | e_normalized.toBytes(buf, .Little) catch unreachable; |
| 699 | const leading = @clz(e_normalized.v.limbs.get(e_normalized.v.limbs_count() - carry_bits)); | 794 | const leading = @clz(e_normalized.v.limbs.get(e_normalized.v.limbs_count() - carry_bits)); |
| 700 | buf = buf[0 .. buf.len - leading / 8]; | 795 | buf = buf[0 .. buf.len - leading / 8]; |
| 701 | return self.powWithEncodedExponent(x, buf, .Little); | 796 | return self.powWithEncodedPublicExponent(x, buf, .Little); |
| 702 | } | 797 | } |
| 703 | | 798 | |
| 704 | /// Returns x^e (mod m), assuming that the exponent is public, and provided as a byte string. | 799 | /// Returns x^e (mod m), with the exponent provided as a byte string. |
| 705 | /// Exponents are usually small, so this function is faster than `powPublic` as a field element | 800 | /// Exponents are usually small, so this function is faster than `powPublic` as a field element |
| 706 | /// doesn't have to be created if a serialized representation is already available. | 801 | /// doesn't have to be created if a serialized representation is already available. |
| | 802 | /// |
| | 803 | /// If the exponent is public, `powWithEncodedPublicExponent()` can be used instead for a slight speedup. |
| 707 | pub fn powWithEncodedExponent(self: Self, x: Fe, e: []const u8, endian: builtin.Endian) NullExponentError!Fe { | 804 | pub fn powWithEncodedExponent(self: Self, x: Fe, e: []const u8, endian: builtin.Endian) NullExponentError!Fe { |
| 708 | var acc: u8 = 0; | 805 | return self.powWithEncodedExponentInternal(x, e, endian, false); |
| 709 | for (e) |b| acc |= b; | 806 | } |
| 710 | if (acc == 0) return error.NullExponent; | | |
| 711 | | 807 | |
| 712 | var pc = [1]Fe{x} ++ [_]Fe{self.zero} ** 14; | 808 | /// Returns x^e (mod m), the exponent being public and provided as a byte string. |
| 713 | if (x.montgomery == false) { | 809 | /// Exponents are usually small, so this function is faster than `powPublic` as a field element |
| 714 | self.toMontgomery(&pc[0]) catch unreachable; | 810 | /// doesn't have to be created if a serialized representation is already available. |
| 715 | } | 811 | /// |
| 716 | for (1..pc.len) |i| { | 812 | /// If the exponent is secret, `powWithEncodedExponent` must be used instead. |
| 717 | pc[i] = self.montgomeryMul(pc[i - 1], pc[0]); | 813 | pub fn powWithEncodedPublicExponent(self: Self, x: Fe, e: []const u8, endian: builtin.Endian) NullExponentError!Fe { |
| 718 | } | 814 | return self.powWithEncodedExponentInternal(x, e, endian, true); |
| 719 | var out = self.one(); | | |
| 720 | self.toMontgomery(&out) catch unreachable; | | |
| 721 | var t0 = self.zero; | | |
| 722 | var s = switch (endian) { | | |
| 723 | .Big => 0, | | |
| 724 | .Little => e.len - 1, | | |
| 725 | }; | | |
| 726 | while (true) { | | |
| 727 | const b = e[s]; | | |
| 728 | for ([_]u3{ 4, 0 }) |j| { | | |
| 729 | for (0..4) |_| { | | |
| 730 | out = self.montgomerySq(out); | | |
| 731 | } | | |
| 732 | const k = (b >> j) & 0b1111; | | |
| 733 | if (std.options.side_channels_mitigations == .none) { | | |
| 734 | if (k == 0) continue; | | |
| 735 | t0 = pc[k - 1]; | | |
| 736 | } else { | | |
| 737 | for (pc, 0..) |t, i| { | | |
| 738 | t0.v.cmov(ct.eql(k, @as(u8, @truncate(i + 1))), t.v); | | |
| 739 | } | | |
| 740 | } | | |
| 741 | const t1 = self.montgomeryMul(out, t0); | | |
| 742 | out.v.cmov(!ct.eql(k, 0), t1.v); | | |
| 743 | } | | |
| 744 | switch (endian) { | | |
| 745 | .Big => { | | |
| 746 | s += 1; | | |
| 747 | if (s == e.len) break; | | |
| 748 | }, | | |
| 749 | .Little => { | | |
| 750 | if (s == 0) break; | | |
| 751 | s -= 1; | | |
| 752 | }, | | |
| 753 | } | | |
| 754 | } | | |
| 755 | self.fromMontgomery(&out) catch unreachable; | | |
| 756 | return out; | | |
| 757 | } | 815 | } |
| 758 | }; | 816 | }; |
| 759 | } | 817 | } |