--- /dev/null
+### -*-python-*-
+###
+### Testing cyclic-group functionality
+###
+### (c) 2019 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to Catacomb.
+###
+### Catacomb/Python is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### Catacomb/Python is distributed in the hope that it will be useful, but
+### WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with Catacomb/Python. If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+###--------------------------------------------------------------------------
+### Imported modules.
+
+import catacomb as C
+import unittest as U
+import testutils as T
+
+###--------------------------------------------------------------------------
+class TestGroups (T.GenericTestMixin):
+ """Test cyclic groups."""
+
+ def _test_group(me, G):
+
+ ## Some initial values.
+ id = G.i
+ g = G.g
+ x = g**2014319630
+ y = g**721326623
+
+ ## Check that they're all different.
+ v = [id, g, x, y]
+ for i in T.range(len(v)):
+ for j in T.range(len(v)):
+ if i == j: me.assertEqual(v[i], v[j])
+ else: me.assertNotEqual(v[i], v[j])
+
+ ## Basic arithmetic. Knowing the answers is too hard. For now, just
+ ## check that the field laws hold.
+ t = x*y; me.assertEqual(t/x, y); me.assertEqual(t/y, x)
+ me.assertEqual(x*x.inv(), id)
+ me.assertEqual(x.sqr(), x*x)
+ me.assertEqual(x/x, id)
+
+ ## Exponentiation.
+ me.assertEqual(x**G.r, id)
+ me.assertEqual(G.mexp((x, 5), (y, 7)), g**15120884511)
+
+ ## Comparisons. We've already done equality and inequality, and nothing
+ ## else should work.
+ me.assertRaises(TypeError, T.lt, x, y)
+ me.assertRaises(TypeError, T.le, x, y)
+ me.assertRaises(TypeError, T.ge, x, y)
+ me.assertRaises(TypeError, T.gt, x, y)
+
+ if isinstance(G, C.ECGroup):
+
+ ## Properties.
+ me.assertEqual(G.noctets, 2*G.info.curve.field.noctets + 1)
+ me.assertEqual(G.nbits, 2*G.info.curve.field.nbits)
+
+ ## Conversion to integer.
+ i = y.toint(); me.assertEqual(type(i), C.MP)
+ t = G(i); me.assertTrue(t == y or t == y.inv())
+
+ ## Conversion to elliptic curve.
+ Q = y.toec()
+ me.assertTrue(isinstance(Q, C.ECPtCurve))
+ me.assertEqual(G(Q), y)
+
+ ## Encoding.
+ t = y.tobuf()
+ me.assertEqual(t, Q.tobuf())
+ me.assertEqual(G.frombuf(t)[0], y)
+ me.assertEqual(id.tobuf(), C.ByteString.zero(2))
+ t = y.toraw()
+ me.assertEqual(t, Q.ec2osp())
+ me.assertEqual(G.fromraw(t)[0], y)
+ me.assertEqual(id.toraw(), C.ByteString.zero(1))
+
+ else:
+
+ ## Properties.
+ me.assertEqual(G.noctets, G.info.p.noctets)
+ if isinstance(G.info, C.BinDHInfo):
+ me.assertEqual(G.nbits, G.info.p.degree)
+ else:
+ me.assertEqual(G.nbits, G.info.p.nbits)
+
+ ## Conversion to integer.
+ i = y.toint(); me.assertEqual(type(i), C.MP)
+ me.assertTrue(G(i) == y)
+
+ ## Conversion to elliptic curve.
+ me.assertRaises(TypeError, G.toec, x)
+
+ ## Encoding.
+ t = y.tobuf()
+ me.assertEqual(t, C.ByteString(C.WriteBuffer().putmp(i)))
+ me.assertEqual(G.frombuf(t)[0], y)
+ me.assertEqual(id.tobuf(), C.bytes("000101"))
+ t = y.toraw()
+ me.assertEqual(t, i.storeb(G.noctets))
+ me.assertEqual(G.fromraw(t)[0], y)
+ me.assertEqual(id.toraw(),
+ C.ByteString(C.WriteBuffer().zero(G.noctets - 1).putu8(1)))
+
+ ## String conversion.
+ ystr = str(y)
+ me.assertEqual(G.fromstring(ystr), (y, ""))
+
+ ## Checking operations.
+ me.assertRaises(ValueError, id.check)
+ x.check()
+ y.check()
+ G.checkgroup()
+
+ def test_dhinfo(me):
+ dhinfo = C.DHInfo.parse("127, 7, 2")
+ me.assertEqual(dhinfo.p, 127)
+ me.assertEqual(dhinfo.r, 7)
+ me.assertEqual(dhinfo.g, 2)
+ dhinfo.group().checkgroup()
+
+ def test_bindhinfo(me):
+ bindhinfo = C.BinDHInfo.parse("0x805, 89, 0x22")
+ me.assertEqual(bindhinfo.p, C.GF(0x805))
+ me.assertEqual(bindhinfo.r, 89)
+ me.assertEqual(bindhinfo.g, C.GF(0x22))
+ bindhinfo.group().checkgroup()
+
+ def test_parse(me):
+
+ G = C.Group.parse("prime 127, 7, 2")
+ me.assertEqual(G.info.p, 127)
+ me.assertEqual(G.r, 7)
+ me.assertEqual(G.info.g, 2)
+ G.checkgroup()
+
+ G = C.Group.parse("bin 0x805, 89, 0x22")
+ me.assertEqual(G.info.p, C.GF(0x805))
+ me.assertEqual(G.r, 89)
+ me.assertEqual(G.info.g, C.GF(0x22))
+ G.checkgroup()
+
+ def test_gen_schnorr(me):
+ ev = T.EventRecorder()
+ dhinfo = C.DHInfo.generate(512, 64, event = ev,
+ rng = T.detrand("schnorr"))
+ me.assertEqual(dhinfo.p.nbits, 512)
+ me.assertEqual(dhinfo.r.nbits, 64)
+ me.assertTrue(dhinfo.p.primep())
+ me.assertTrue(dhinfo.r.primep())
+ me.assertEqual(dhinfo.p%dhinfo.r, 1)
+ me._test_group(dhinfo.group())
+ me.assertEqual(ev.events, "[q:F4/P26/D][p:F5/P5/D][g:D]")
+
+ def test_gen_limlee(me):
+ ev = T.EventRecorder()
+ dhinfo, ff = C.DHInfo.genlimlee(512, 64, event = ev, ievent = ev,
+ rng = T.detrand("limlee"))
+ me.assertEqual(dhinfo.p.nbits, 512)
+ me.assertEqual(dhinfo.r.nbits, 64)
+ me.assertTrue(dhinfo.p.primep())
+ me.assertTrue(dhinfo.r.primep())
+ for f in ff:
+ me.assertTrue(f.primep())
+ me.assertTrue(f.nbits >= 64)
+ me.assertEqual(C.MPMul().factor(2).factor(ff).done() + 1, dhinfo.p)
+ me._test_group(dhinfo.group())
+ me.assertEqual(ev.events,
+ "[p:"
+ "[p_0:F8/P26/D]"
+ "[p_1:P26/D]"
+ "[p_2:F4/P26/D]"
+ "[p_3:P26/D]"
+ "[p_4:F1/P26/D]"
+ "[p_5:F1/P26/D]"
+ "[p_6:P26/D]"
+ "[p*_7:P26/D]"
+ "[p_8:F1/P26/D]"
+ "[p_9:F1/P26/D]"
+ "[p*_10:P26/D]"
+ "[p_11:F6/P26/D]"
+ "[p_12:P26/D]"
+ "[p_13:P26/D]"
+ "[p_14:F4/P26/D]"
+ "[p_15:F1/P26/D]"
+ "[p_16:F1/P26/D]"
+ "[p_17:F1/P26/D]"
+ "[p_18:F6/P26/D]"
+ "[p_19:F1/P26/D]"
+ "[p_20:F3/P26/D]"
+ "[p_21:P26/D]"
+ "[p_22:F2/P26/D]"
+ "[p_23:F4/P26/D]"
+ "[p_24:F7/P26/D]"
+ "[p_25:F2/P26/D]"
+ "[p_26:F9/P26/D]"
+ "[p_27:F4/P26/D]"
+ "[p*_28:F11/P26/D]"
+ "[p*_29:F4/P26/D]"
+ "[p*_30:F1/P26/D]"
+ "[p*_31:F6/P26/D]"
+ "[p*_32:F4/P26/D]"
+ "[p*_33:F3/P26/D]"
+ "[p*_34:P26/D]"
+ "[p*_35:F3/P26/D]"
+ "[p*_36:F1/P26/D]"
+ "[p*_37:F1/P26/D]"
+ "[p*_38:F4/P26/D]"
+ "[p*_39:P26/D]"
+ "[p*_40:P26/D]"
+ "[p*_41:F2/P26/D]"
+ "[p*_42:F1/P26/D]"
+ "F22/P5/D]"
+ "[g:D]")
+
+ def test_gen_kcdsa(me):
+ ev = T.EventRecorder()
+ dhinfo, h = C.DHInfo.genkcdsa(512, 64, event = ev,
+ rng = T.detrand("kcdsa"))
+ me.assertEqual(dhinfo.p.nbits, 512)
+ me.assertEqual(dhinfo.r.nbits, 64)
+ me.assertTrue(dhinfo.p.primep())
+ me.assertTrue(dhinfo.r.primep())
+ me.assertTrue(h.primep())
+ me.assertEqual(2*h*dhinfo.r + 1, dhinfo.p)
+ me._test_group(dhinfo.group())
+ me.assertEqual(ev.events, "[v:F23/P6/D][p:F86/P26/D][g:D]")
+
+TestGroups.generate_testcases((name, map[name].group()) for name, map in
+ [("nist-p256", C.eccurves),
+ ("nist-b283", C.eccurves),
+ ("catacomb-ll-128-512", C.primegroups),
+ ("p1363-64", C.bingroups)])
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()