| ... | @@ -329,7 +329,11 @@ fn Fe_(comptime bits: comptime_int) type { | ... | @@ -329,7 +329,11 @@ fn Fe_(comptime bits: comptime_int) type { |
| 329 | | 329 | |
| 330 | /// Converts the field element to a primitive. | 330 | /// Converts the field element to a primitive. |
| 331 | /// This function may not run in constant time. | 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 | return self.v.toPrimitive(T); | 337 | return self.v.toPrimitive(T); |
| 334 | } | 338 | } |
| 335 | | 339 | |
| ... | @@ -343,7 +347,11 @@ fn Fe_(comptime bits: comptime_int) type { | ... | @@ -343,7 +347,11 @@ fn Fe_(comptime bits: comptime_int) type { |
| 343 | } | 347 | } |
| 344 | | 348 | |
| 345 | /// Converts the field element to a byte string. | 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 | return self.v.toBytes(bytes, endian); | 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,19 +538,46 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 530 | /// Adds two field elements (mod m). | 538 | /// Adds two field elements (mod m). |
| 531 | pub fn add(self: Self, x: Fe, y: Fe) Fe { | 539 | pub fn add(self: Self, x: Fe, y: Fe) Fe { |
| 532 | var out = x; | 540 | var out = x; |
| 533 | const overflow = out.v.addWithOverflow(y.v); | 541 | if (x.montgomery == y.montgomery) { |
| 534 | const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); | 542 | @branchHint(.likely); |
| 535 | const need_sub = ct.eql(overflow, underflow); | 543 | const overflow = out.v.addWithOverflow(y.v); |
| 536 | _ = out.v.conditionalSubWithOverflow(need_sub, self.v); | 544 | const underflow: u1 = @bitCast(ct.limbsCmpLt(out.v, self.v)); |
| 537 | return out; | 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 | /// Subtracts two field elements (mod m). | 563 | /// Subtracts two field elements (mod m). |
| 541 | pub fn sub(self: Self, x: Fe, y: Fe) Fe { | 564 | pub fn sub(self: Self, x: Fe, y: Fe) Fe { |
| 542 | var out = x; | 565 | var out = x; |
| 543 | const underflow: bool = @bitCast(out.v.subWithOverflow(y.v)); | 566 | if (x.montgomery == y.montgomery) { |
| 544 | _ = out.v.conditionalAddWithOverflow(underflow, self.v); | 567 | const underflow: bool = @bitCast(out.v.subWithOverflow(y.v)); |
| 545 | return out; | 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 | /// Converts a field element to the Montgomery form. | 583 | /// Converts a field element to the Montgomery form. |
| ... | @@ -663,13 +698,15 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -663,13 +698,15 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 663 | for (e) |b| acc |= b; | 698 | for (e) |b| acc |= b; |
| 664 | if (acc == 0) return error.NullExponent; | 699 | if (acc == 0) return error.NullExponent; |
| 665 | | 700 | |
| | 701 | const was_montgomery = x.montgomery; |
| | 702 | |
| 666 | var out = self.one(); | 703 | var out = self.one(); |
| 667 | self.toMontgomery(&out) catch unreachable; | 704 | self.toMontgomery(&out) catch unreachable; |
| 668 | | 705 | |
| 669 | if (public and e.len < 3 or (e.len == 3 and e[if (endian == .big) 0 else 2] <= 0b1111)) { | 706 | 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 | 707 | // Do not use a precomputation table for short, public exponents |
| 671 | var x_m = x; | 708 | var x_m = x; |
| 672 | if (x.montgomery == false) { | 709 | if (!x.montgomery) { |
| 673 | self.toMontgomery(&x_m) catch unreachable; | 710 | self.toMontgomery(&x_m) catch unreachable; |
| 674 | } | 711 | } |
| 675 | var s = switch (endian) { | 712 | var s = switch (endian) { |
| ... | @@ -702,7 +739,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -702,7 +739,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 702 | } else { | 739 | } else { |
| 703 | // Use a precomputation table for large exponents | 740 | // Use a precomputation table for large exponents |
| 704 | var pc = [1]Fe{x} ++ [_]Fe{self.zero} ** 14; | 741 | var pc = [1]Fe{x} ++ [_]Fe{self.zero} ** 14; |
| 705 | if (x.montgomery == false) { | 742 | if (!x.montgomery) { |
| 706 | self.toMontgomery(&pc[0]) catch unreachable; | 743 | self.toMontgomery(&pc[0]) catch unreachable; |
| 707 | } | 744 | } |
| 708 | for (1..pc.len) |i| { | 745 | for (1..pc.len) |i| { |
| ... | @@ -747,38 +784,55 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -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 | return out; | 790 | return out; |
| 752 | } | 791 | } |
| 753 | | 792 | |
| 754 | /// Multiplies two field elements. | 793 | /// Multiplies two field elements. |
| | 794 | /// Result preserves the first operand's form. |
| 755 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { | 795 | pub fn mul(self: Self, x: Fe, y: Fe) Fe { |
| 756 | if (x.montgomery != y.montgomery) { | 796 | if (x.montgomery) { |
| 757 | return self.montgomeryMul(x, y); | 797 | const y_ = if (!y.montgomery) blk: { |
| 758 | } | 798 | var yy = y; |
| 759 | var a_ = x; | 799 | self.toMontgomery(&yy) catch unreachable; |
| 760 | if (x.montgomery == false) { | 800 | break :blk yy; |
| 761 | self.toMontgomery(&a_) catch unreachable; | 801 | } else y; |
| | 802 | return self.montgomeryMul(x, y_); |
| 762 | } else { | 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 | /// Squares a field element. | 818 | /// Squares a field element. |
| 769 | pub fn sq(self: Self, x: Fe) Fe { | 819 | pub fn sq(self: Self, x: Fe) Fe { |
| 770 | var out = x; | 820 | if (x.montgomery) { |
| 771 | if (x.montgomery == true) { | 821 | return self.montgomerySq(x); |
| | 822 | } else { |
| | 823 | var out = x; |
| | 824 | self.toMontgomery(&out) catch unreachable; |
| | 825 | out = self.montgomerySq(out); |
| 772 | self.fromMontgomery(&out) catch unreachable; | 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 | /// Returns x^e (mod m) in constant time. | 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 | var buf: [Fe.encoded_bytes]u8 = undefined; | 836 | var buf: [Fe.encoded_bytes]u8 = undefined; |
| 783 | e.toBytes(&buf, native_endian) catch unreachable; | 837 | e.toBytes(&buf, native_endian) catch unreachable; |
| 784 | return self.powWithEncodedExponent(x, &buf, native_endian); | 838 | return self.powWithEncodedExponent(x, &buf, native_endian); |
| ... | @@ -786,7 +840,10 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -786,7 +840,10 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 786 | | 840 | |
| 787 | /// Returns x^e (mod m), assuming that the exponent is public. | 841 | /// Returns x^e (mod m), assuming that the exponent is public. |
| 788 | /// The function remains constant time with respect to `x`. | 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 | var e_normalized = Fe{ .v = e.v.normalize() }; | 847 | var e_normalized = Fe{ .v = e.v.normalize() }; |
| 791 | var buf_: [Fe.encoded_bytes]u8 = undefined; | 848 | var buf_: [Fe.encoded_bytes]u8 = undefined; |
| 792 | var buf = buf_[0 .. math.divCeil(usize, e_normalized.v.limbs_len * t_bits, 8) catch unreachable]; | 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,6 +984,8 @@ test "finite field arithmetic" { |
| 927 | | 984 | |
| 928 | try m.toMontgomery(&x); | 985 | try m.toMontgomery(&x); |
| 929 | x_y = m.mul(x, y); | 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 | try testing.expectEqual(x_y.toPrimitive(u256), 1666576607955767413750776202132407807424848069716933450241); | 989 | try testing.expectEqual(x_y.toPrimitive(u256), 1666576607955767413750776202132407807424848069716933450241); |
| 931 | try m.fromMontgomery(&x); | 990 | try m.fromMontgomery(&x); |
| 932 | | 991 | |
| ... | @@ -941,8 +1000,11 @@ test "finite field arithmetic" { | ... | @@ -941,8 +1000,11 @@ test "finite field arithmetic" { |
| 941 | | 1000 | |
| 942 | const x_pow_y = try m.powPublic(x, y); | 1001 | const x_pow_y = try m.powPublic(x, y); |
| 943 | try testing.expectEqual(x_pow_y.toPrimitive(u256), 1631933139300737762906024873185789093007782131928298618473); | 1002 | try testing.expectEqual(x_pow_y.toPrimitive(u256), 1631933139300737762906024873185789093007782131928298618473); |
| | 1003 | try testing.expect(!x_pow_y.montgomery); |
| 944 | try m.toMontgomery(&x); | 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 | try m.fromMontgomery(&x); | 1008 | try m.fromMontgomery(&x); |
| 947 | try testing.expect(x_pow_y2.eql(x_pow_y)); | 1009 | try testing.expect(x_pow_y2.eql(x_pow_y)); |
| 948 | try testing.expectError(error.NullExponent, m.powPublic(x, m.zero)); | 1010 | try testing.expectError(error.NullExponent, m.powPublic(x, m.zero)); |
| ... | @@ -953,13 +1015,53 @@ test "finite field arithmetic" { | ... | @@ -953,13 +1015,53 @@ test "finite field arithmetic" { |
| 953 | | 1015 | |
| 954 | const x_sq = m.sq(x); | 1016 | const x_sq = m.sq(x); |
| 955 | const x_sq2 = m.mul(x, x); | 1017 | const x_sq2 = m.mul(x, x); |
| | 1018 | try testing.expect(!x_sq.montgomery); |
| | 1019 | try testing.expect(!x_sq2.montgomery); |
| 956 | try testing.expect(x_sq.eql(x_sq2)); | 1020 | try testing.expect(x_sq.eql(x_sq2)); |
| 957 | try m.toMontgomery(&x); | 1021 | try m.toMontgomery(&x); |
| 958 | const x_sq3 = m.sq(x); | 1022 | var x_sq3 = m.sq(x); |
| 959 | const x_sq4 = m.mul(x, 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 | try testing.expect(x_sq.eql(x_sq3)); | 1028 | try testing.expect(x_sq.eql(x_sq3)); |
| 961 | try testing.expect(x_sq3.eql(x_sq4)); | 1029 | try testing.expect(x_sq3.eql(x_sq4)); |
| 962 | try m.fromMontgomery(&x); | 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 | fn testCt(ct_: anytype) !void { | 1067 | fn testCt(ct_: anytype) !void { |