| ... | ... | @@ -329,7 +329,11 @@ fn Fe_(comptime bits: comptime_int) type { |
| 329 | 329 | |
| 330 | 330 | /// Converts the field element to a primitive. |
| 331 | 331 | /// This function may not run in constant time. |
| 332 | | pub fn toPrimitive(self: Self, comptime T: type) OverflowError!T { |
| 332 | /// Returns an error if the element is in Montgomery form. |
| 333 | pub fn toPrimitive(self: Self, comptime T: type) (OverflowError || RepresentationError)!T { |
| 334 | if (self.montgomery) { |
| 335 | return error.UnexpectedRepresentation; |
| 336 | } |
| 333 | 337 | return self.v.toPrimitive(T); |
| 334 | 338 | } |
| 335 | 339 | |
| ... | ... | @@ -343,7 +347,11 @@ fn Fe_(comptime bits: comptime_int) type { |
| 343 | 347 | } |
| 344 | 348 | |
| 345 | 349 | /// Converts the field element to a byte string. |
| 346 | | pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) OverflowError!void { |
| 350 | /// Returns an error if the element is in Montgomery form. |
| 351 | pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) (OverflowError || RepresentationError)!void { |
| 352 | if (self.montgomery) { |
| 353 | return error.UnexpectedRepresentation; |
| 354 | } |
| 347 | 355 | return self.v.toBytes(bytes, endian); |
| 348 | 356 | } |
| 349 | 357 | |
| ... | ... | @@ -530,19 +538,46 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 530 | 538 | /// Adds two field elements (mod m). |
| 531 | 539 | pub fn add(self: Self, x: Fe, y: Fe) Fe { |
| 532 | 540 | var out = x; |
| 533 | | const overflow = out.v.addWithOverflow(y.v); |
| 534 | | const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); |
| 535 | | const need_sub = ct.eql(overflow, underflow); |
| 536 | | _ = out.v.conditionalSubWithOverflow(need_sub, self.v); |
| 537 | | return out; |
| 541 | if (x.montgomery == y.montgomery) { |
| 542 | @branchHint(.likely); |
| 543 | const overflow = out.v.addWithOverflow(y.v); |
| 544 | const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); |
| 545 | const need_sub = ct.eql(overflow, underflow); |
| 546 | _ = out.v.conditionalSubWithOverflow(need_sub, self.v); |
| 547 | return out; |
| 548 | } else { |
| 549 | var y_ = y; |
| 550 | if (y.montgomery) { |
| 551 | self.fromMontgomery(&y_) catch unreachable; |
| 552 | } else { |
| 553 | self.toMontgomery(&y_) catch unreachable; |
| 554 | } |
| 555 | const overflow = out.v.addWithOverflow(y_.v); |
| 556 | const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); |
| 557 | const need_sub = ct.eql(overflow, underflow); |
| 558 | _ = out.v.conditionalSubWithOverflow(need_sub, self.v); |
| 559 | return out; |
| 560 | } |
| 538 | 561 | } |
| 539 | 562 | |
| 540 | 563 | /// Subtracts two field elements (mod m). |
| 541 | 564 | pub fn sub(self: Self, x: Fe, y: Fe) Fe { |
| 542 | 565 | var out = x; |
| 543 | | const underflow: bool = @bitCast(out.v.subWithOverflow(y.v)); |
| 544 | | _ = out.v.conditionalAddWithOverflow(underflow, self.v); |
| 545 | | return out; |
| 566 | if (x.montgomery == y.montgomery) { |
| 567 | const underflow: bool = @bitCast(out.v.subWithOverflow(y.v)); |
| 568 | _ = out.v.conditionalAddWithOverflow(underflow, self.v); |
| 569 | return out; |
| 570 | } else { |
| 571 | var y_ = y; |
| 572 | if (y.montgomery) { |
| 573 | self.fromMontgomery(&y_) catch unreachable; |
| 574 | } else { |
| 575 | self.toMontgomery(&y_) catch unreachable; |
| 576 | } |
| 577 | const underflow: bool = @bitCast(out.v.subWithOverflow(y_.v)); |
| 578 | _ = out.v.conditionalAddWithOverflow(underflow, self.v); |
| 579 | return out; |
| 580 | } |
| 546 | 581 | } |
| 547 | 582 | |
| 548 | 583 | /// Converts a field element to the Montgomery form. |
| ... | ... | @@ -663,13 +698,15 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 663 | 698 | for (e) |b| acc |= b; |
| 664 | 699 | if (acc == 0) return error.NullExponent; |
| 665 | 700 | |
| 701 | const was_montgomery = x.montgomery; |
| 702 | |
| 666 | 703 | var out = self.one(); |
| 667 | 704 | self.toMontgomery(&out) catch unreachable; |
| 668 | 705 | |
| 669 | 706 | if (public and e.len < 3 or (e.len == 3 and e[if (endian == .big) 0 else 2] <= 0b1111)) { |
| 670 | 707 | // Do not use a precomputation table for short, public exponents |
| 671 | 708 | var x_m = x; |
| 672 | | if (x.montgomery == false) { |
| 709 | if (!x.montgomery) { |
| 673 | 710 | self.toMontgomery(&x_m) catch unreachable; |
| 674 | 711 | } |
| 675 | 712 | var s = switch (endian) { |
| ... | ... | @@ -702,7 +739,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 702 | 739 | } else { |
| 703 | 740 | // Use a precomputation table for large exponents |
| 704 | 741 | var pc = [1]Fe{x} ++ [_]Fe{self.zero} ** 14; |
| 705 | | if (x.montgomery == false) { |
| 742 | if (!x.montgomery) { |
| 706 | 743 | self.toMontgomery(&pc[0]) catch unreachable; |
| 707 | 744 | } |
| 708 | 745 | for (1..pc.len) |i| { |
| ... | ... | @@ -747,38 +784,55 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 747 | 784 | } |
| 748 | 785 | } |
| 749 | 786 | } |
| 750 | | self.fromMontgomery(&out) catch unreachable; |
| 787 | if (!was_montgomery) { |
| 788 | self.fromMontgomery(&out) catch unreachable; |
| 789 | } |
| 751 | 790 | return out; |
| 752 | 791 | } |
| 753 | 792 | |
| 754 | 793 | /// Multiplies two field elements. |
| 794 | /// Result preserves the first operand's form. |
| 755 | 795 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { |
| 756 | | if (x.montgomery != y.montgomery) { |
| 757 | | return self.montgomeryMul(x, y); |
| 758 | | } |
| 759 | | var a_ = x; |
| 760 | | if (x.montgomery == false) { |
| 761 | | self.toMontgomery(&a_) catch unreachable; |
| 796 | if (x.montgomery) { |
| 797 | const y_ = if (!y.montgomery) blk: { |
| 798 | var yy = y; |
| 799 | self.toMontgomery(&yy) catch unreachable; |
| 800 | break :blk yy; |
| 801 | } else y; |
| 802 | return self.montgomeryMul(x, y_); |
| 762 | 803 | } else { |
| 763 | | self.fromMontgomery(&a_) catch unreachable; |
| 804 | var x_m = x; |
| 805 | var y_m = if (y.montgomery) blk: { |
| 806 | var yy = y; |
| 807 | self.fromMontgomery(&yy) catch unreachable; |
| 808 | break :blk yy; |
| 809 | } else y; |
| 810 | self.toMontgomery(&x_m) catch unreachable; |
| 811 | self.toMontgomery(&y_m) catch unreachable; |
| 812 | var out = self.montgomeryMul(x_m, y_m); |
| 813 | self.fromMontgomery(&out) catch unreachable; |
| 814 | return out; |
| 764 | 815 | } |
| 765 | | return self.montgomeryMul(a_, y); |
| 766 | 816 | } |
| 767 | 817 | |
| 768 | 818 | /// Squares a field element. |
| 769 | 819 | pub fn sq(self: Self, x: Fe) Fe { |
| 770 | | var out = x; |
| 771 | | if (x.montgomery == true) { |
| 820 | if (x.montgomery) { |
| 821 | return self.montgomerySq(x); |
| 822 | } else { |
| 823 | var out = x; |
| 824 | self.toMontgomery(&out) catch unreachable; |
| 825 | out = self.montgomerySq(out); |
| 772 | 826 | self.fromMontgomery(&out) catch unreachable; |
| 827 | return out; |
| 773 | 828 | } |
| 774 | | out = self.montgomerySq(out); |
| 775 | | out.montgomery = false; |
| 776 | | self.toMontgomery(&out) catch unreachable; |
| 777 | | return out; |
| 778 | 829 | } |
| 779 | 830 | |
| 780 | 831 | /// Returns x^e (mod m) in constant time. |
| 781 | | pub fn pow(self: Self, x: Fe, e: Fe) NullExponentError!Fe { |
| 832 | pub fn pow(self: Self, x: Fe, e: Fe) (NullExponentError || RepresentationError)!Fe { |
| 833 | if (e.montgomery) { |
| 834 | return error.UnexpectedRepresentation; |
| 835 | } |
| 782 | 836 | var buf: [Fe.encoded_bytes]u8 = undefined; |
| 783 | 837 | e.toBytes(&buf, native_endian) catch unreachable; |
| 784 | 838 | return self.powWithEncodedExponent(x, &buf, native_endian); |
| ... | ... | @@ -786,7 +840,10 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 786 | 840 | |
| 787 | 841 | /// Returns x^e (mod m), assuming that the exponent is public. |
| 788 | 842 | /// The function remains constant time with respect to `x`. |
| 789 | | pub fn powPublic(self: Self, x: Fe, e: Fe) NullExponentError!Fe { |
| 843 | pub fn powPublic(self: Self, x: Fe, e: Fe) (NullExponentError || RepresentationError)!Fe { |
| 844 | if (e.montgomery) { |
| 845 | return error.UnexpectedRepresentation; |
| 846 | } |
| 790 | 847 | var e_normalized = Fe{ .v = e.v.normalize() }; |
| 791 | 848 | var buf_: [Fe.encoded_bytes]u8 = undefined; |
| 792 | 849 | var buf = buf_[0 .. math.divCeil(usize, e_normalized.v.limbs_len * t_bits, 8) catch unreachable]; |
| ... | ... | @@ -927,6 +984,8 @@ test "finite field arithmetic" { |
| 927 | 984 | |
| 928 | 985 | try m.toMontgomery(&x); |
| 929 | 986 | x_y = m.mul(x, y); |
| 987 | try testing.expect(x_y.montgomery); // result preserves first operand's form |
| 988 | try m.fromMontgomery(&x_y); |
| 930 | 989 | try testing.expectEqual(x_y.toPrimitive(u256), 1666576607955767413750776202132407807424848069716933450241); |
| 931 | 990 | try m.fromMontgomery(&x); |
| 932 | 991 | |
| ... | ... | @@ -941,8 +1000,11 @@ test "finite field arithmetic" { |
| 941 | 1000 | |
| 942 | 1001 | const x_pow_y = try m.powPublic(x, y); |
| 943 | 1002 | try testing.expectEqual(x_pow_y.toPrimitive(u256), 1631933139300737762906024873185789093007782131928298618473); |
| 1003 | try testing.expect(!x_pow_y.montgomery); |
| 944 | 1004 | try m.toMontgomery(&x); |
| 945 | | const x_pow_y2 = try m.powPublic(x, y); |
| 1005 | var x_pow_y2 = try m.powPublic(x, y); |
| 1006 | try testing.expect(x_pow_y2.montgomery); |
| 1007 | try m.fromMontgomery(&x_pow_y2); |
| 946 | 1008 | try m.fromMontgomery(&x); |
| 947 | 1009 | try testing.expect(x_pow_y2.eql(x_pow_y)); |
| 948 | 1010 | try testing.expectError(error.NullExponent, m.powPublic(x, m.zero)); |
| ... | ... | @@ -953,13 +1015,53 @@ test "finite field arithmetic" { |
| 953 | 1015 | |
| 954 | 1016 | const x_sq = m.sq(x); |
| 955 | 1017 | const x_sq2 = m.mul(x, x); |
| 1018 | try testing.expect(!x_sq.montgomery); |
| 1019 | try testing.expect(!x_sq2.montgomery); |
| 956 | 1020 | try testing.expect(x_sq.eql(x_sq2)); |
| 957 | 1021 | try m.toMontgomery(&x); |
| 958 | | const x_sq3 = m.sq(x); |
| 959 | | const x_sq4 = m.mul(x, x); |
| 1022 | var x_sq3 = m.sq(x); |
| 1023 | var x_sq4 = m.mul(x, x); |
| 1024 | try testing.expect(x_sq3.montgomery); |
| 1025 | try testing.expect(x_sq4.montgomery); |
| 1026 | try m.fromMontgomery(&x_sq3); |
| 1027 | try m.fromMontgomery(&x_sq4); |
| 960 | 1028 | try testing.expect(x_sq.eql(x_sq3)); |
| 961 | 1029 | try testing.expect(x_sq3.eql(x_sq4)); |
| 962 | 1030 | try m.fromMontgomery(&x); |
| 1031 | |
| 1032 | var x_mont = x; |
| 1033 | try m.toMontgomery(&x_mont); |
| 1034 | |
| 1035 | // Non-montgomery + montgomery |
| 1036 | const add_nm_m = m.add(x, x_mont); |
| 1037 | try testing.expect(!add_nm_m.montgomery); |
| 1038 | var add_m_nm = m.add(x_mont, x); |
| 1039 | try testing.expect(add_m_nm.montgomery); |
| 1040 | try m.fromMontgomery(&add_m_nm); |
| 1041 | try testing.expect(add_nm_m.eql(add_m_nm)); |
| 1042 | |
| 1043 | // Non-montgomery - montgomery |
| 1044 | const sub_nm_m = m.sub(x, y); |
| 1045 | try testing.expect(!sub_nm_m.montgomery); |
| 1046 | var y_mont = y; |
| 1047 | try m.toMontgomery(&y_mont); |
| 1048 | var sub_m_nm = m.sub(x_mont, y); |
| 1049 | try testing.expect(sub_m_nm.montgomery); |
| 1050 | try m.fromMontgomery(&sub_m_nm); |
| 1051 | try testing.expect(sub_nm_m.eql(sub_m_nm)); |
| 1052 | |
| 1053 | // mul: preserves first operand's form |
| 1054 | const mul_nm_m = m.mul(x, x_mont); |
| 1055 | try testing.expect(!mul_nm_m.montgomery); |
| 1056 | const mul_nm_nm = m.mul(x, x); |
| 1057 | try testing.expect(mul_nm_m.eql(mul_nm_nm)); |
| 1058 | var mul_m_nm = m.mul(x_mont, x); |
| 1059 | try testing.expect(mul_m_nm.montgomery); |
| 1060 | try m.fromMontgomery(&mul_m_nm); |
| 1061 | try testing.expect(mul_m_nm.eql(mul_nm_nm)); |
| 1062 | |
| 1063 | try testing.expectEqual(x.toPrimitive(u256), 80169837251094269539116136208111827396136208141182357733); |
| 1064 | try testing.expectError(error.UnexpectedRepresentation, x_mont.toPrimitive(u256)); |
| 963 | 1065 | } |
| 964 | 1066 | |
| 965 | 1067 | fn testCt(ct_: anytype) !void { |