| ... | ... | @@ -9,6 +9,7 @@ const IdentityElementError = crypto.errors.IdentityElementError; |
| 9 | 9 | const NonCanonicalError = crypto.errors.NonCanonicalError; |
| 10 | 10 | const NotSquareError = crypto.errors.NotSquareError; |
| 11 | 11 | const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; |
| 12 | const UnexpectedSubgroupError = crypto.errors.UnexpectedSubgroupError; |
| 12 | 13 | |
| 13 | 14 | /// Group operations over Edwards25519. |
| 14 | 15 | 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 | 122 | /// Multiply a point by the cofactor |
| 82 | 123 | pub fn clearCofactor(p: Edwards25519) Edwards25519 { |
| 83 | 124 | return p.dbl().dbl().dbl(); |
| ... | ... | @@ -142,6 +183,13 @@ pub const Edwards25519 = struct { |
| 142 | 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 | 193 | inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void { |
| 146 | 194 | p.x.cMov(a.x, c); |
| 147 | 195 | p.y.cMov(a.y, c); |
| ... | ... | @@ -575,3 +623,16 @@ test "implicit reduction of invalid scalars" { |
| 575 | 623 | try htest.assertEqual("339f189ecc5fbebe9895345c72dc07bda6e615f8a40e768441b6f529cd6c671a", p1.toBytes()[0..]); |
| 576 | 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 | } |