| ... | @@ -9,6 +9,7 @@ const IdentityElementError = crypto.errors.IdentityElementError; | ... | @@ -9,6 +9,7 @@ const IdentityElementError = crypto.errors.IdentityElementError; |
| 9 | const NonCanonicalError = crypto.errors.NonCanonicalError; | 9 | const NonCanonicalError = crypto.errors.NonCanonicalError; |
| 10 | const NotSquareError = crypto.errors.NotSquareError; | 10 | const NotSquareError = crypto.errors.NotSquareError; |
| 11 | const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; | 11 | const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; |
| | 12 | const UnexpectedSubgroupError = crypto.errors.UnexpectedSubgroupError; |
| 12 | | 13 | |
| 13 | /// Group operations over Edwards25519. | 14 | /// Group operations over Edwards25519. |
| 14 | pub const Edwards25519 = struct { | 15 | pub const Edwards25519 = struct { |
| ... | @@ -78,6 +79,46 @@ pub const Edwards25519 = struct { | ... | @@ -78,6 +79,46 @@ pub const Edwards25519 = struct { |
| 78 | } | 79 | } |
| 79 | } | 80 | } |
| 80 | | 81 | |
| | 82 | /// Reject a point if it is not in the prime order subgroup generated by the standard base point. |
| | 83 | /// |
| | 84 | /// If the point is not in the main subgroup: |
| | 85 | /// |
| | 86 | /// - `WeakPublicKeyError` is returned if the point belongs to a low-order subgroup. |
| | 87 | /// - `UnexpectedSubgroupError` is returned otherwise. |
| | 88 | pub fn rejectUnexpectedSubgroup(p: Edwards25519) (WeakPublicKeyError || UnexpectedSubgroupError)!void { |
| | 89 | try p.rejectLowOrder(); |
| | 90 | |
| | 91 | // Multiply p by the order of subgroup - This is a prime order group, so the result should be the neutral element. |
| | 92 | const _10 = p.dbl(); |
| | 93 | const _11 = p.add(_10); |
| | 94 | const _100 = p.add(_11); |
| | 95 | const _110 = _10.add(_100); |
| | 96 | const _1000 = _10.add(_110); |
| | 97 | const _1011 = _11.add(_1000); |
| | 98 | const _10000 = _1000.dbl(); |
| | 99 | const _100000 = _10000.dbl(); |
| | 100 | const _100110 = _110.add(_100000); |
| | 101 | const _1000000 = _100000.dbl(); |
| | 102 | const _1010000 = _10000.add(_1000000); |
| | 103 | const _1010011 = _11.add(_1010000); |
| | 104 | const _1100011 = _10000.add(_1010011); |
| | 105 | const _1100111 = _100.add(_1100011); |
| | 106 | const _1101011 = _100.add(_1100111); |
| | 107 | const _10010011 = _1000000.add(_1010011); |
| | 108 | const _10010111 = _100.add(_10010011); |
| | 109 | const _10111101 = _100110.add(_10010111); |
| | 110 | const _11010011 = _1000000.add(_10010011); |
| | 111 | const _11100111 = _1010000.add(_10010111); |
| | 112 | const _11101101 = _110.add(_11100111); |
| | 113 | const _11110101 = _1000.add(_11101101); |
| | 114 | const q = ((_11110101.add(((((_1101011.add(((((_10.add(((_1011.add(_11110101)).shift(126) |
| | 115 | .add(_1010011)).shift(9).add(_11110101))).shift(7).add(_1100111)).shift(9).add(_11110101).shift(11) |
| | 116 | .add(_10111101)).shift(8).add(_11100111)).shift(9))).shift(6).add(_1011)).shift(14).add(_10010011).shift(10) |
| | 117 | .add(_1100011)).shift(9).add(_10010111)).shift(10))).shift(8).add(_11010011)).shift(8).add(_11101101); |
| | 118 | q.rejectIdentity() catch return; |
| | 119 | return error.UnexpectedSubgroup; |
| | 120 | } |
| | 121 | |
| 81 | /// Multiply a point by the cofactor | 122 | /// Multiply a point by the cofactor |
| 82 | pub fn clearCofactor(p: Edwards25519) Edwards25519 { | 123 | pub fn clearCofactor(p: Edwards25519) Edwards25519 { |
| 83 | return p.dbl().dbl().dbl(); | 124 | return p.dbl().dbl().dbl(); |
| ... | @@ -142,6 +183,13 @@ pub const Edwards25519 = struct { | ... | @@ -142,6 +183,13 @@ pub const Edwards25519 = struct { |
| 142 | return p.add(q.neg()); | 183 | return p.add(q.neg()); |
| 143 | } | 184 | } |
| 144 | | 185 | |
| | 186 | /// Double a point `n` times. |
| | 187 | fn shift(p: Edwards25519, n: comptime_int) Edwards25519 { |
| | 188 | var q = p; |
| | 189 | for (0..n) |_| q = q.dbl(); |
| | 190 | return q; |
| | 191 | } |
| | 192 | |
| 145 | inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void { | 193 | inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void { |
| 146 | p.x.cMov(a.x, c); | 194 | p.x.cMov(a.x, c); |
| 147 | p.y.cMov(a.y, c); | 195 | p.y.cMov(a.y, c); |
| ... | @@ -575,3 +623,16 @@ test "implicit reduction of invalid scalars" { | ... | @@ -575,3 +623,16 @@ test "implicit reduction of invalid scalars" { |
| 575 | try htest.assertEqual("339f189ecc5fbebe9895345c72dc07bda6e615f8a40e768441b6f529cd6c671a", p1.toBytes()[0..]); | 623 | try htest.assertEqual("339f189ecc5fbebe9895345c72dc07bda6e615f8a40e768441b6f529cd6c671a", p1.toBytes()[0..]); |
| 576 | try htest.assertEqual("a501e4c595a3686d8bee7058c7e6af7fd237f945c47546910e37e0e79b1bafb0", p3.toBytes()[0..]); | 624 | try htest.assertEqual("a501e4c595a3686d8bee7058c7e6af7fd237f945c47546910e37e0e79b1bafb0", p3.toBytes()[0..]); |
| 577 | } | 625 | } |
| | 626 | |
| | 627 | test "subgroup check" { |
| | 628 | for (0..100) |_| { |
| | 629 | var p = Edwards25519.basePoint; |
| | 630 | const s = Edwards25519.scalar.random(); |
| | 631 | p = try p.mulPublic(s); |
| | 632 | try p.rejectUnexpectedSubgroup(); |
| | 633 | } |
| | 634 | var bogus: [Edwards25519.encoded_length]u8 = undefined; |
| | 635 | _ = try std.fmt.hexToBytes(&bogus, "4dc95e3c28d78c48a60531525e6327e259b7ba0d2f5c81b694052c766a14b625"); |
| | 636 | const p = try Edwards25519.fromBytes(bogus); |
| | 637 | try std.testing.expectError(error.UnexpectedSubgroup, p.rejectUnexpectedSubgroup()); |
| | 638 | } |