| 1 | ### -*- mode: python, coding: utf-8 -*- |
| 2 | ### |
| 3 | ### Testing elliptic curve functionality |
| 4 | ### |
| 5 | ### (c) 2019 Straylight/Edgeware |
| 6 | ### |
| 7 | |
| 8 | ###----- Licensing notice --------------------------------------------------- |
| 9 | ### |
| 10 | ### This file is part of the Python interface to Catacomb. |
| 11 | ### |
| 12 | ### Catacomb/Python is free software: you can redistribute it and/or |
| 13 | ### modify it under the terms of the GNU General Public License as |
| 14 | ### published by the Free Software Foundation; either version 2 of the |
| 15 | ### License, or (at your option) any later version. |
| 16 | ### |
| 17 | ### Catacomb/Python is distributed in the hope that it will be useful, but |
| 18 | ### WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 20 | ### General Public License for more details. |
| 21 | ### |
| 22 | ### You should have received a copy of the GNU General Public License |
| 23 | ### along with Catacomb/Python. If not, write to the Free Software |
| 24 | ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| 25 | ### USA. |
| 26 | |
| 27 | ###-------------------------------------------------------------------------- |
| 28 | ### Imported modules. |
| 29 | |
| 30 | import catacomb as C |
| 31 | import unittest as U |
| 32 | import testutils as T |
| 33 | |
| 34 | k = C.PrimeField(19) |
| 35 | E = k.ec(-3, 6) |
| 36 | P = E(0) # order 26 |
| 37 | |
| 38 | kk = C.BinPolyField(0x13) |
| 39 | EE = kk.ec(1, 8) |
| 40 | PP = EE(8) # order 20 |
| 41 | |
| 42 | ###-------------------------------------------------------------------------- |
| 43 | class TestCurvelessPoints (U.TestCase): |
| 44 | """Test handling of points without an explicit curve.""" |
| 45 | |
| 46 | def test(me): |
| 47 | |
| 48 | ## Construction. |
| 49 | O = C.ECPt(); me.assertFalse(O) |
| 50 | P = C.ECPt(12345, 67890); me.assertTrue(P) |
| 51 | Q = C.ECPt(23456, 78901); me.assertTrue(Q); me.assertNotEqual(P, Q) |
| 52 | R = C.ECPt(O); me.assertFalse(R); me.assertEqual(O, R); me.assertNotEqual(P, R) |
| 53 | R = C.ECPt(None); me.assertFalse(R); me.assertEqual(O, R) |
| 54 | me.assertEqual(C.ECPt("12345, 67890"), P) |
| 55 | me.assertEqual(C.ECPt((12345, 67890)), P) |
| 56 | me.assertRaises(TypeError, C.ECPt, ()) |
| 57 | me.assertRaises(TypeError, C.ECPt, (1234,)) |
| 58 | me.assertRaises(TypeError, C.ECPt, (1, 2, 3, 4)) |
| 59 | me.assertRaises(ValueError, C.ECPt, "12345") |
| 60 | me.assertRaises(ValueError, C.ECPt, "12345,") |
| 61 | me.assertRaises(ValueError, C.ECPt, "12345, xyzzy") |
| 62 | me.assertRaises(ValueError, C.ECPt, "12345, 67890!??") |
| 63 | me.assertRaises(ValueError, C.ECPt, (1, 2, 3)) |
| 64 | me.assertRaises(TypeError, C.ECPt, 1, 2, 3) |
| 65 | me.assertRaises(TypeError, C.ECPt, 1234) |
| 66 | me.assertRaises(TypeError, C.ECPt, object()) |
| 67 | me.assertRaises(TypeError, C.ECPt, 1, None) |
| 68 | #me.assertRaises(TypeError, C.ECPt, (1, None)) |
| 69 | |
| 70 | ## Arithmetic shouldn't work. |
| 71 | me.assertRaises(TypeError, T.neg, P) |
| 72 | me.assertRaises(TypeError, T.add, P, Q) |
| 73 | |
| 74 | ## Attributes. We only have raw integer access. |
| 75 | me.assertTrue(P.point is P) |
| 76 | me.assertEqual(P.ix, 12345) |
| 77 | me.assertEqual(P.iy, 67890) |
| 78 | me.assertEqual(P.tobuf(), C.bytes("000230390003010932")) |
| 79 | me.assertRaises(AttributeError, lambda: P.curve) |
| 80 | me.assertRaises(AttributeError, lambda: P.x) |
| 81 | me.assertRaises(AttributeError, lambda: P.y) |
| 82 | me.assertRaises(AttributeError, lambda: P._x) |
| 83 | me.assertRaises(AttributeError, lambda: P._y) |
| 84 | me.assertRaises(AttributeError, lambda: P._z) |
| 85 | |
| 86 | ## Encoding and decoding. |
| 87 | P = C.ECPt(254, 291) |
| 88 | me.assertEqual(O.tobuf(), C.bytes("0000")) |
| 89 | me.assertEqual(C.ECPt(0, 0).tobuf(), C.bytes("000100000100")) |
| 90 | me.assertEqual(P.tobuf(), C.bytes("0001fe00020123")) |
| 91 | me.assertEqual(C.ECPt.frombuf(C.bytes("0001fe000201233f")), |
| 92 | (P, C.bytes("3f"))) |
| 93 | me.assertRaises(ValueError, C.ECPt.frombuf, C.bytes("0001fe000201")) |
| 94 | |
| 95 | ## String conversion and parsing. |
| 96 | me.assertEqual(C.ECPt.parse("254, 291)"), (P, ")")) |
| 97 | me.assertRaises(ValueError, C.ECPt.parse, "(254, 291") |
| 98 | |
| 99 | ###-------------------------------------------------------------------------- |
| 100 | class TestCurves (T.GenericTestMixin): |
| 101 | """Test elliptic curves.""" |
| 102 | |
| 103 | def test_compare(me): |
| 104 | me.assertEqual(E, E) |
| 105 | E1 = k.ec(-3, 6) |
| 106 | me.assertFalse(E is E1) |
| 107 | me.assertEqual(E, E1) |
| 108 | me.assertNotEqual(E, EE) |
| 109 | me.assertNotEqual(E, []) |
| 110 | |
| 111 | def _test_curve(me, einfo, checkfail = False): |
| 112 | |
| 113 | ## Some useful values. |
| 114 | E = einfo.curve |
| 115 | P = einfo.G |
| 116 | O = E() |
| 117 | n = einfo.r |
| 118 | h = einfo.h |
| 119 | k = E.field |
| 120 | me.assertTrue(n.primep()); l = C.NicePrimeField(n) |
| 121 | |
| 122 | ## Check that things are basically sane. |
| 123 | me.assertFalse(O) |
| 124 | me.assertTrue(P) |
| 125 | me.assertTrue(n) |
| 126 | nP = n*P; me.assertFalse(nP); me.assertEqual(nP, O) |
| 127 | |
| 128 | ## Check point construction. |
| 129 | me.assertEqual(type(P.ix), C.MP) |
| 130 | me.assertEqual(type(P.iy), C.MP) |
| 131 | me.assertTrue(isinstance(P.x, C.FE)) |
| 132 | me.assertTrue(isinstance(P.y, C.FE)) |
| 133 | me.assertTrue(isinstance(P._x, C.FE)) |
| 134 | me.assertTrue(isinstance(P._y, C.FE)) |
| 135 | if isinstance(E, C.ECPrimeProjCurve) or isinstance(E, C.ECBinProjCurve): |
| 136 | me.assertTrue(isinstance(P._z, C.FE)) |
| 137 | else: |
| 138 | me.assertEqual(P._z, None) |
| 139 | me.assertEqual(E(None), O) |
| 140 | me.assertEqual(E(P.x, P.y), P) |
| 141 | me.assertEqual(E((P.x, P.y)), P) |
| 142 | me.assertEqual(E(P._x, P._y, P._z), P) |
| 143 | me.assertEqual(E((P._x, P._y, P._z)), P) |
| 144 | Q = E(P.point); me.assertEqual(type(Q), E); me.assertEqual(Q, P) |
| 145 | me.assertEqual(E("%s, %s" % (P.ix, P.iy)), P) |
| 146 | me.assertRaises(ValueError, E, "1234") |
| 147 | me.assertRaises(ValueError, E, "1234,") |
| 148 | me.assertRaises(ValueError, E, "1234, 5678?") |
| 149 | me.assertRaises(TypeError, E, 1, None) |
| 150 | Q = E(P.ix); me.assertTrue(Q == P or Q == -P) |
| 151 | for i in T.range(128): |
| 152 | x = P.ix + i |
| 153 | try: E(x) |
| 154 | except ValueError: badx = x; break |
| 155 | else: |
| 156 | me.fail("no off-curve point found") |
| 157 | |
| 158 | ## Attributes. |
| 159 | me.assertEqual(P.ix, P.point.ix) |
| 160 | me.assertEqual(P.iy, P.point.iy) |
| 161 | me.assertEqual(P.x, k(P.point.ix)) |
| 162 | me.assertEqual(P.y, k(P.point.iy)) |
| 163 | R = 6*P |
| 164 | if isinstance(E, C.ECPrimeProjCurve) or isinstance(E, C.ECBinProjCurve): |
| 165 | me.assertEqual(P._z, k.one) |
| 166 | me.assertEqual(R._x, R.x*R._z**2) |
| 167 | me.assertEqual(R._y, R.y*R._z**3) |
| 168 | me.assertNotEqual(R._z, k.one) |
| 169 | else: |
| 170 | me.assertEqual(P._z, None) |
| 171 | me.assertEqual(R._x, R.x) |
| 172 | me.assertEqual(R._y, R.y) |
| 173 | me.assertEqual(R._z, None) |
| 174 | me.assertEqual(R.curve, E) |
| 175 | |
| 176 | ## Arithmetic. |
| 177 | Q = 17*P |
| 178 | me.assertEqual(Q, P*17) |
| 179 | me.assertEqual(-Q, (n - 17)*P) |
| 180 | me.assertEqual(Q + R, 23*P) |
| 181 | me.assertEqual(Q + R.point, 23*P) |
| 182 | me.assertRaises(TypeError, T.add, Q.point, R.point) |
| 183 | me.assertRaises(TypeError, T.mul, kk(1), Q) |
| 184 | me.assertEqual(Q - R, 11*P) |
| 185 | me.assertEqual(l(17)*P, Q) |
| 186 | me.assertEqual(P*l(17), Q) |
| 187 | |
| 188 | ## Ordering. |
| 189 | me.assertTrue(P == P) |
| 190 | me.assertTrue(P != Q) |
| 191 | me.assertRaises(TypeError, T.lt, P, Q) |
| 192 | me.assertRaises(TypeError, T.le, P, Q) |
| 193 | me.assertRaises(TypeError, T.ge, P, Q) |
| 194 | me.assertRaises(TypeError, T.gt, P, Q) |
| 195 | |
| 196 | ## Encoding. |
| 197 | Z0 = C.ByteString.zero(0) |
| 198 | Z1 = C.ByteString.zero(1) |
| 199 | me.assertEqual(O.ec2osp(), Z1) |
| 200 | me.assertEqual(E.os2ecp(Z1), (O, Z0)) |
| 201 | t = C.WriteBuffer() \ |
| 202 | .putu8(0x04) \ |
| 203 | .put(P.ix.storeb(k.noctets)) \ |
| 204 | .put(P.iy.storeb(k.noctets)) \ |
| 205 | .contents |
| 206 | me.assertEqual(P.ec2osp(), t) |
| 207 | me.assertEqual(C.WriteBuffer().putecptraw(P).contents, t) |
| 208 | me.assertEqual(C.WriteBuffer().ec2osp(P).contents, t) |
| 209 | me.assertEqual(E.os2ecp(t), (P, Z0)) |
| 210 | me.assertEqual(C.ReadBuffer(t).getecptraw(E), P) |
| 211 | me.assertEqual(C.ReadBuffer(t).os2ecp(E), P) |
| 212 | if isinstance(k, C.PrimeField): ybit = int(P.iy&1) |
| 213 | else: |
| 214 | try: ybit = int((P.y/P.x).value&C.GF(1)) |
| 215 | except ZeroDivisionError: ybit = 0 |
| 216 | t = C.WriteBuffer() \ |
| 217 | .putu8(0x02 | ybit) \ |
| 218 | .put(P.ix.storeb(k.noctets)) \ |
| 219 | .contents |
| 220 | me.assertEqual(P.ec2osp(C.EC_LSB), t) |
| 221 | me.assertEqual(C.WriteBuffer().ec2osp(P, C.EC_LSB).contents, t) |
| 222 | me.assertEqual(E.os2ecp(t, C.EC_LSB), (P, Z0)) |
| 223 | me.assertEqual(C.ReadBuffer(t).os2ecp(E, C.EC_LSB), P) |
| 224 | |
| 225 | ## Curve methods. |
| 226 | Q = E.find(P.x); me.assertTrue(Q == P or Q == -P) |
| 227 | Q = E.find(P.ix); me.assertTrue(Q == P or Q == -P) |
| 228 | me.assertRaises(ValueError, E.find, badx) |
| 229 | for i in T.range(128): |
| 230 | if E.rand() != P: break |
| 231 | else: |
| 232 | me.fail("random point always gives me P") |
| 233 | for i in T.range(128): |
| 234 | R = E.rand(C.LCRand(i)) |
| 235 | if R != P: break |
| 236 | else: |
| 237 | me.fail("random point always gives me P") |
| 238 | me.assertEqual(R, E.rand(C.LCRand(i))) |
| 239 | me.assertEqual(E.parse("%s, %s!xyzzy" % (P.ix, P.iy)), (P, "!xyzzy")) |
| 240 | |
| 241 | ## Simultaneous multiplication. |
| 242 | Q, R, S = 5*P, 7*P, 11*P |
| 243 | me.assertEqual(E.mmul([Q, 9, R, 8, S, 5]), 156*P) |
| 244 | me.assertEqual(E.mmul(Q, 9, R, 8, S, 5), 156*P) |
| 245 | |
| 246 | ## Test other curve info things while we're here. |
| 247 | if not checkfail: einfo.check() |
| 248 | else: me.assertRaises(ValueError, einfo.check) |
| 249 | |
| 250 | def test_tinycurves(me): |
| 251 | me._test_curve(C.ECInfo(E, 2*P, 13, 2), checkfail = True) |
| 252 | ei, _ = C.ECInfo.parse("binpoly: 0x13; bin: 0x01, 0x08; 0x02, 0x0c: 5*4") |
| 253 | me._test_curve(ei, checkfail = True) |
| 254 | |
| 255 | TestCurves.generate_testcases((name, C.eccurves[name]) for name in |
| 256 | ["nist-p256", "nist-k233", "nist-b163", "nist-b283n"]) |
| 257 | |
| 258 | ###----- That's all, folks -------------------------------------------------- |
| 259 | |
| 260 | if __name__ == "__main__": U.main() |