chiark / gitweb /
t/: Add a test suite.
[catacomb-python] / t / t-field.py
1 ### -*-python-*-
2 ###
3 ### Testing finite-field 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 itertools as I
31
32 import catacomb as C
33 import unittest as U
34 import testutils as T
35
36 ###--------------------------------------------------------------------------
37 class TestFields (T.GenericTestMixin):
38   """Test finite fields."""
39
40   def _test_field(me, k):
41
42     ## Some initial values.
43     zero = k.zero
44     one = k.one
45     x = k(2760820866)
46     y = k(3757175895)
47     z = k(920571320)
48
49     ## Check that they're all different.
50     v = [zero, one, x, y, z]
51     for i in T.range(len(v)):
52       for j in T.range(len(v)):
53         if i == j: me.assertEqual(v[i], v[j])
54         else: me.assertNotEqual(v[i], v[j])
55
56     ## Basic arithmetic.  Knowing the answers is too hard.  For now, just
57     ## check that the field laws hold.
58     t = x + y; me.assertEqual(t - y, x); me.assertEqual(t - x, y)
59     t = x - y; me.assertEqual(t + y, x)
60     t = x*y; me.assertEqual(t/x, y); me.assertEqual(t/y, x)
61     me.assertEqual(+x, x)
62     me.assertEqual(x + -x, zero)
63     me.assertEqual(x - x, zero)
64     me.assertEqual(x*x.inv(), one)
65     me.assertEqual(x/x, one)
66     me.assertRaises(ZeroDivisionError, k.inv, zero)
67
68     ## Exponentiation.  At least we know the group exponent.
69     me.assertEqual(x**(k.q - 1), one)
70
71     ## Comparisons.  We've already done equality and inequality, and nothing
72     ## else should work.
73     me.assertRaises(TypeError, T.lt, x, y)
74     me.assertRaises(TypeError, T.le, x, y)
75     me.assertRaises(TypeError, T.ge, x, y)
76     me.assertRaises(TypeError, T.gt, x, y)
77
78     ## Conversion back to integer.
79     me.assertEqual(int(x), 2760820866)
80
81     ## Square and square-root.  In a prime field, we may need to search
82     ## around to find a quadratic residue.  In binary fields, squaring is
83     ## linear, and every element has a unique square root.
84     me.assertEqual(x*x, x.sqr())
85     for i in T.range(128):
86       t = k(int(x) + i)
87       q = t.sqrt()
88       if q is not None: break
89     else:
90       me.fail("no quadratic residue found")
91     me.assertEqual(q.sqr(), t)
92
93     ## Hex and octal conversions.
94     me.assertEqual(hex(x), hex(T.long(x.value)).rstrip("L"))
95     me.assertEqual(oct(x), oct(T.long(x.value)).rstrip("L"))
96
97     if isinstance(k, C.PrimeField):
98
99       ## Representation.
100       me.assertEqual(type(x.value), C.MP)
101       me.assertEqual(k.type, C.FTY_PRIME)
102
103       ## Properties.
104       me.assertEqual(k.p, k.q)
105
106       ## Operations.
107       me.assertEqual(x.dbl(), 2*x)
108       me.assertEqual(x.tpl(), 3*x)
109       me.assertEqual(x.qdl(), 4*x)
110       me.assertEqual(x.hlv(), x/2)
111
112     else:
113
114       ## Representation.
115       me.assertEqual(type(x.value), C.GF)
116       me.assertEqual(k.type, C.FTY_BINARY)
117
118       ## Properties.
119       me.assertEqual(k.m, k.nbits)
120       me.assertEqual(k.p.degree, k.m)
121       if isinstance(k, C.BinNormField):
122         l = C.BinPolyField(k.p)
123         a, b = l.zero, l(k.beta)
124         for i in T.range(k.m):
125           a += b
126           b = b.sqr()
127         me.assertEqual(a, l.one)
128
129       ## Operations.
130       for i in T.range(128):
131         t = k(int(x) + i)
132         u = t.quadsolve()
133         if u is not None: break
134       else:
135         me.fail("no quadratic solution found")
136       me.assertEqual(u*u + u, t)
137
138     ## Encoding.
139     me.assertEqual(k.nbits, (k.q - 1).nbits)
140     me.assertEqual(k.noctets, (k.q - 1).noctets)
141
142 TestFields.generate_testcases \
143   (("%s-%s" % (name, suffix), getfn(C.eccurves[name]))
144    for suffix, getfn in [("coords", lambda einfo: einfo.curve.field),
145                          ("scalars", lambda einfo: C.PrimeField(einfo.r))]
146    for name in ["nist-p256", "nist-k233", "nist-b163", "nist-b283n"])
147
148 ###----- That's all, folks --------------------------------------------------
149
150 if __name__ == "__main__": U.main()