--- /dev/null
+*.pyc
+__pycache__/
+
+/COPYING
+/MANIFEST
+/algorithms.h
+/auto-version
+/build/
+/mdwsetup.py
+/pysetup.mk
+
+/t/keyring.old
--- /dev/null
+COPYING
+auto-version
+mdwsetup.py
+pysetup.mk
--- /dev/null
+Mark Wooding <mdw@distorted.org.uk> <mdw>
(append
'((author . "Straylight/Edgeware")
(licence-text . "[[gpl]]")
- (full-title . "Pyke: the Python Kit for Extensions")
- (program . "Pyke"))
+ (full-title . "the Python interface to Catacomb")
+ (program . "Catacomb/Python"))
skel-alist))
--- /dev/null
+### Manifest template for Catacomb/Python. -*-conf-*-
+
+## Generated build machinery.
+include COPYING auto-version mdwsetup.py pysetup.mk
+
+## Basic build stuff.
+include MANIFEST.in setup.py Makefile
+
+## C extension code.
+include *.c *.h
+include pyke/*.c pyke/*.h
+include t/*.py t/keyring
+include algorithms.py
+exclude algorithms.h
+
+## Scripts.
+include pock
+include pwsafe
+
+## Manual pages.
+include *.[13]
+
+## Python wrapping.
+recursive-include catacomb *.py
+
+## Debian.
+include debian/rules debian/control debian/changelog
+include debian/copyright debian/compat debian/source/format
+include debian/*.install
--- /dev/null
+### -*-makefile-*-
+include pysetup.mk
--- /dev/null
+Nothing much to say yet.
+
+Class hierarchy
+
+ bytestring
+
+ mp
+ mpmont
+ mpbarrett
+ mpreduce
+ mpcrt
+
+ gf
+ gfreduce
+ gfn
+
+ field
+ primefield
+ niceprimefield
+ binfield
+ binpolyfield
+ binnormfield
+ fe
+ [field objects]
+
+ eccurve
+ ecprimecurve
+ ecprimeprojcurve
+ ecbincurve
+ ecbinprojcurve
+ ecinfo
+ ecpt
+ [eccurve objects]
+
+ fginfo
+ dhinfo
+ bindhinfo
+
+ group
+ primegroup
+ bingroup
+ ecgroup
+ ge
+ [group objects]
+
+ grand
+ truerand
+
+ keysz
+ keyszany
+ keyszrange
+ keyszset
+
+ gccipher
+ gcipher
+ [gccipher objects]
+ gcmac
+ gmac
+ [gcmac objects]
+ gchash
+ ghash
+ [gchash objects]
+ gmhash
+ [gmac objects]
+
+ecinfo(curve = ecprimeprojcurve(),
+ G = ecprimeprojcurve()(),
+ r = 115792089210356248762697446949407573529996955224135760342422259061068512044369,
+ h = 1)
--- /dev/null
+/* -*-c-*-
+ *
+ * Symmetric cryptography
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+PUBLIC_SYMBOLS;
+#include "algorithms.h"
+PRIVATE_SYMBOLS;
+
+/*----- Key sizes ---------------------------------------------------------*/
+
+static PyTypeObject *keysz_pytype;
+static PyTypeObject *keyszany_pytype, *keyszrange_pytype, *keyszset_pytype;
+
+typedef struct keysz_pyobj {
+ PyObject_HEAD
+ int dfl;
+} keysz_pyobj;
+
+typedef struct keyszrange_pyobj {
+ PyObject_HEAD
+ int dfl;
+ int min, max, mod;
+} keyszrange_pyobj;
+
+typedef struct keyszset_pyobj {
+ PyObject_HEAD
+ int dfl;
+ PyObject *set;
+} keyszset_pyobj;
+
+#define KEYSZ_PYCHECK(o) PyObject_TypeCheck((o), keysz_pytype)
+
+#ifndef KSZ_OPMASK
+# define KSZ_OPMASK 0x1f
+#endif
+
+#ifndef KSZ_16BIT
+# define KSZ_16BIT 0x20
+#endif
+
+PyObject *keysz_pywrap(const octet *k)
+{
+ unsigned op = *k++;
+#define ARG(i) (op&KSZ_16BIT ? LOAD16(k + 2*(i)) : k[i])
+ switch (op&KSZ_OPMASK) {
+ case KSZ_ANY: {
+ keysz_pyobj *o = PyObject_New(keysz_pyobj, keyszany_pytype);
+ o->dfl = ARG(0);
+ return ((PyObject *)o);
+ } break;
+ case KSZ_RANGE: {
+ keyszrange_pyobj *o =
+ PyObject_New(keyszrange_pyobj, keyszrange_pytype);
+ o->dfl = ARG(0);
+ o->min = ARG(1);
+ o->max = ARG(2);
+ o->mod = ARG(3);
+ if (!o->mod) o->mod = 1;
+ return ((PyObject *)o);
+ } break;
+ case KSZ_SET: {
+ keyszset_pyobj *o =
+ PyObject_New(keyszset_pyobj, keyszset_pytype);
+ PyObject *l;
+ int i, n;
+ o->dfl = ARG(0);
+ for (i = 0; ARG(i); i++) ;
+ n = i; l = PyList_New(n);
+ for (i = 0; i < n; i++) PyList_SET_ITEM(l, i, PyInt_FromLong(ARG(i)));
+ o->set = PyFrozenSet_New(l); Py_DECREF(l);
+ return ((PyObject *)o);
+ } break;
+ default:
+ abort();
+ }
+#undef ARG
+}
+
+static PyObject *keyszany_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "default", 0 };
+ int dfl;
+ keysz_pyobj *o;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "i:new", KWLIST, &dfl))
+ goto end;
+ if (dfl < 0) VALERR("key size cannot be negative");
+ o = (keysz_pyobj *)ty->tp_alloc(ty, 0);
+ o->dfl = dfl;
+ return ((PyObject *)o);
+end:
+ return (0);
+}
+
+static PyObject *keyszrange_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "default", "min", "max", "mod", 0 };
+ int dfl, min = 0, max, mod = 1;
+ PyObject *maxobj = Py_None;
+ keyszrange_pyobj *o;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|iOi:new", KWLIST,
+ &dfl, &min, &maxobj, &mod))
+ goto end;
+ if (maxobj == Py_None)
+ max = 0;
+ else {
+ max = PyInt_AsLong(maxobj);
+ if (max == -1 && PyErr_Occurred()) goto end;
+ }
+ if (dfl < 0 || min < 0 || max < 0) VALERR("key size cannot be negative");
+ if (min > dfl || (max && dfl > max)) VALERR("bad key size bounds");
+ if (mod <= 0 || dfl%mod || min%mod || max%mod)
+ VALERR("bad key size modulus");
+ o = (keyszrange_pyobj *)ty->tp_alloc(ty, 0);
+ o->dfl = dfl;
+ o->min = min;
+ o->max = max;
+ o->mod = mod;
+ return ((PyObject *)o);
+end:
+ return (0);
+}
+
+static PyObject *keyszset_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "default", "set", 0 };
+ int dfl, xx;
+ PyObject *set = 0;
+ PyObject *x = 0, *l = 0, *i = 0;
+ keyszset_pyobj *o = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|O:new", KWLIST, &dfl, &set))
+ goto end;
+ if (set) i = PyObject_GetIter(set);
+ else { set = PyTuple_New(0); i = PyObject_GetIter(set); Py_DECREF(set); }
+ if (!i) goto end;
+ l = PyList_New(0); if (!l) goto end;
+ if (dfl < 0) VALERR("key size cannot be negative");
+ x = PyInt_FromLong(dfl); PyList_Append(l, x); Py_DECREF(x); x = 0;
+ for (;;) {
+ x = PyIter_Next(i); if (!x) break;
+ xx = PyInt_AsLong(x); if (xx == -1 && PyErr_Occurred()) goto end;
+ if (xx < 0) VALERR("key size cannot be negative");
+ PyList_Append(l, x); Py_DECREF(x); x = 0;
+ }
+ if ((set = PyFrozenSet_New(l)) == 0) goto end;
+ o = (keyszset_pyobj *)ty->tp_alloc(ty, 0);
+ o->dfl = dfl;
+ o->set = set;
+end:
+ Py_XDECREF(l);
+ Py_XDECREF(x);
+ return ((PyObject *)o);
+}
+
+static PyObject *kaget_min(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(0)); }
+static PyObject *kaget_max(PyObject *me, void *hunoz)
+ { RETURN_NONE; }
+
+static PyObject *krget_max(PyObject *me, void *hunoz)
+{
+ int max = ((keyszrange_pyobj *)me)->max;
+ if (max) return (PyInt_FromLong(max));
+ else RETURN_NONE;
+}
+
+static PyObject *ksget_min(PyObject *me, void *hunoz)
+{
+ PyObject *i = PyObject_GetIter(((keyszset_pyobj *)me)->set);
+ PyObject *v = 0;
+ int y, x = -1;
+ for (;;) {
+ v = PyIter_Next(i); if (!v) break;
+ y = PyInt_AsLong(v); assert(y >= 0);
+ if (x == -1 || y < x) x = y;
+ }
+ Py_DECREF(i); Py_XDECREF(v);
+ if (PyErr_Occurred()) return (0);
+ return (PyInt_FromLong(x));
+}
+
+static PyObject *ksget_max(PyObject *me, void *hunoz)
+{
+ PyObject *i = PyObject_GetIter(((keyszset_pyobj *)me)->set);
+ PyObject *v = 0;
+ int y, x = -1;
+ for (;;) {
+ v = PyIter_Next(i); if (!v) break;
+ y = PyInt_AsLong(v); assert(y >= 0);
+ if (y > x) x = y;
+ }
+ Py_DECREF(i); Py_XDECREF(v);
+ if (PyErr_Occurred()) return (0);
+ return (PyInt_FromLong(x));
+}
+
+static const PyMemberDef keysz_pymembers[] = {
+#define MEMBERSTRUCT keysz_pyobj
+ MEMRNM(default, T_INT, dfl, READONLY, "KSZ.default -> default key size")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+#define KSZCONVOP(op) \
+ static PyObject *kszmeth_##op(PyObject *me, PyObject *arg) \
+ { \
+ double x, y; \
+ if (!PyArg_ParseTuple(arg, "d:" #op, &x)) return (0); \
+ y = keysz_##op(x); \
+ return (PyFloat_FromDouble(y)); \
+ }
+KSZCONVOP(fromdl)
+KSZCONVOP(fromschnorr)
+KSZCONVOP(fromif)
+KSZCONVOP(fromec)
+KSZCONVOP(todl)
+KSZCONVOP(toschnorr)
+KSZCONVOP(toif)
+KSZCONVOP(toec)
+#undef KSZCONVOP
+
+static const PyMethodDef keysz_pymethods[] = {
+#define METHNAME(name) kszmeth_##name
+ SMTH (fromdl, "fromdl(N) -> M: "
+ "convert integer discrete log field size to work factor")
+ SMTH (fromschnorr, "fromschnorr(N) -> M: "
+ "convert Schnorr group order to work factor")
+ SMTH (fromif, "fromif(N) -> M: "
+ "convert integer factorization problem size to work factor")
+ SMTH (fromec, "fromec(N) -> M: "
+ "convert elliptic curve group order to work factor")
+ SMTH (todl, "todl(N) -> M: "
+ "convert work factor to integer discrete log field size")
+ SMTH (toschnorr, "toschnorr(N) -> M: "
+ "convert work factor to Schnorr group order")
+ SMTH (toif, "toif(N) -> M: "
+ "convert work factor to integer factorization problem size")
+ SMTH (toec, "toec(N) -> M: "
+ "convert work factor to elliptic curve group order")
+ SMTH (toec, "toec(N) -> M: "
+ "convert work factor to elliptic curve group order")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef keyszany_pygetset[] = {
+#define GETSETNAME(op, name) ka##op##_##name
+ GET (min, "KSZ.min -> smallest allowed key size")
+ GET (max, "KSZ.max -> largest allowed key size")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMemberDef keyszrange_pymembers[] = {
+#define MEMBERSTRUCT keyszrange_pyobj
+ MEMBER(min, T_INT, READONLY, "KSZ.min -> smallest allowed key size")
+ MEMBER(mod, T_INT, READONLY,
+ "KSZ.mod -> key size must be a multiple of this")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyGetSetDef keyszrange_pygetset[] = {
+#define GETSETNAME(op, name) kr##op##_##name
+ GET (max, "KSZ.max -> largest allowed key size")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyGetSetDef keyszset_pygetset[] = {
+#define GETSETNAME(op, name) ks##op##_##name
+ GET (min, "KSZ.min -> smallest allowed key size")
+ GET (max, "KSZ.max -> largest allowed key size")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMemberDef keyszset_pymembers[] = {
+#define MEMBERSTRUCT keyszset_pyobj
+ MEMBER(set, T_OBJECT, READONLY, "KSZ.set -> allowed key sizes")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyTypeObject keysz_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeySZ", /* @tp_name@ */
+ sizeof(keysz_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Key size constraints. Abstract.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(keysz), /* @tp_methods@ */
+ PYMEMBERS(keysz), /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject keyszany_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeySZAny", /* @tp_name@ */
+ sizeof(keysz_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeySZAny(DEFAULT)\n"
+ " Key size constraints. This object imposes no constraints on size.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keyszany), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keyszany_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject keyszrange_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeySZRange", /* @tp_name@ */
+ sizeof(keyszrange_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeySZRange(DEFAULT, [min = 0], [max = 0], [mod = 1])\n"
+ " Key size constraints: size must be between MIN and MAX inclusive, and\n"
+ " be a multiple of MOD.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ PYMEMBERS(keyszrange), /* @tp_members@ */
+ PYGETSET(keyszrange), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keyszrange_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject keyszset_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeySZSet", /* @tp_name@ */
+ sizeof(keyszset_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeySZSet(DEFAULT, ITER)\n"
+ " Key size constraints: size must be DEFAULT or an element of ITER.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ PYMEMBERS(keyszset), /* @tp_members@ */
+ PYGETSET(keyszset), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keyszset_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Symmetric encryption ----------------------------------------------*/
+
+static PyTypeObject *gccipher_pytype, *gcipher_pytype;
+
+typedef struct gccipher_pyobj {
+ PyHeapTypeObject ty;
+ gccipher *cc;
+} gccipher_pyobj;
+
+#define GCCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gccipher_pytype)
+#define GCCIPHER_CC(o) (((gccipher_pyobj *)(o))->cc)
+
+typedef struct gcipher_pyobj {
+ PyObject_HEAD
+ gcipher *c;
+} gcipher_pyobj;
+
+#define GCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gcipher_pytype)
+#define GCIPHER_C(o) (((gcipher_pyobj *)(o))->c)
+
+CONVFUNC(gccipher, gccipher *, GCCIPHER_CC)
+
+static PyObject *gcipher_pywrap(PyObject *cobj, gcipher *c)
+{
+ gcipher_pyobj *g;
+ if (!cobj) cobj = gccipher_pywrap((/*unconst*/ gccipher *)GC_CLASS(c));
+ else Py_INCREF(cobj);
+ g = PyObject_NEW(gcipher_pyobj, (PyTypeObject *)cobj);
+ g->c = c;
+ return ((PyObject *)g);
+}
+
+static PyObject *gcipher_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "k", 0 };
+ struct bin k;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &k))
+ goto end;
+ if (keysz(k.sz, GCCIPHER_CC(ty)->keysz) != k.sz) VALERR("bad key length");
+ return (gcipher_pywrap((PyObject *)ty,
+ GC_INIT(GCCIPHER_CC(ty), k.p, k.sz)));
+end:
+ return (0);
+}
+
+PyObject *gccipher_pywrap(gccipher *cc)
+{
+ gccipher_pyobj *g = newtype(gccipher_pytype, 0, cc->name);
+ g->cc = cc;
+ g->ty.ht_type.tp_basicsize = sizeof(gcipher_pyobj);
+ g->ty.ht_type.tp_base = gcipher_pytype;
+ Py_INCREF(gcipher_pytype);
+ g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ g->ty.ht_type.tp_free = 0;
+ g->ty.ht_type.tp_new = gcipher_pynew;
+ typeready(&g->ty.ht_type);
+ return ((PyObject *)g);
+}
+
+static void gcipher_pydealloc(PyObject *me)
+{
+ GC_DESTROY(GCIPHER_C(me));
+ Py_DECREF(Py_TYPE(me));
+ FREEOBJ(me);
+}
+
+static PyObject *gccget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GCCIPHER_CC(me)->name)); }
+
+static PyObject *gccget_keysz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCCIPHER_CC(me)->keysz)); }
+
+static PyObject *gccget_blksz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCCIPHER_CC(me)->blksz)); }
+
+static PyObject *gcmeth_encrypt(PyObject *me, PyObject *arg)
+{
+ struct bin m;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:encrypt", convbin, &m)) return (0);
+ rc = bytestring_pywrap(0, m.sz);
+ GC_ENCRYPT(GCIPHER_C(me), m.p, BIN_PTR(rc), m.sz);
+ return (rc);
+}
+
+static PyObject *gcmeth_enczero(PyObject *me, PyObject *arg)
+{
+ char *p;
+ int sz;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "i:enczero", &sz)) return (0);
+ rc = bytestring_pywrap(0, sz);
+ p = BIN_PTR(rc);
+ memset(p, 0, sz);
+ GC_ENCRYPT(GCIPHER_C(me), p, p, sz);
+ return (rc);
+}
+
+static PyObject *gcmeth_decrypt(PyObject *me, PyObject *arg)
+{
+ struct bin c;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:decrypt", convbin, &c)) return (0);
+ rc = bytestring_pywrap(0, c.sz);
+ GC_DECRYPT(GCIPHER_C(me), c.p, BIN_PTR(rc), c.sz);
+ return (rc);
+}
+
+static PyObject *gcmeth_deczero(PyObject *me, PyObject *arg)
+{
+ char *p;
+ int sz;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "i:deczero", &sz)) return (0);
+ rc = bytestring_pywrap(0, sz);
+ p = BIN_PTR(rc);
+ memset(p, 0, sz);
+ GC_DECRYPT(GCIPHER_C(me), p, p, sz);
+ return (rc);
+}
+
+static PyObject *gcmeth_setiv(PyObject *me, PyObject *arg)
+{
+ struct bin iv;
+
+ if (!PyArg_ParseTuple(arg, "O&:setiv", convbin, &iv)) goto end;
+ if (!GCIPHER_C(me)->ops->setiv) VALERR("`setiv' not supported");
+ if (!GC_CLASS(GCIPHER_C(me))->blksz) VALERR("not a block cipher mode");
+ if (iv.sz != GC_CLASS(GCIPHER_C(me))->blksz) VALERR("bad IV length");
+ GC_SETIV(GCIPHER_C(me), iv.p);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *gcmeth_bdry(PyObject *me)
+{
+ if (!GCIPHER_C(me)->ops->bdry) VALERR("`bdry' not supported");
+ if (!GC_CLASS(GCIPHER_C(me))->blksz) VALERR("not a block cipher mode");
+ GC_BDRY(GCIPHER_C(me));
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static const PyGetSetDef gccipher_pygetset[] = {
+#define GETSETNAME(op, name) gcc##op##_##name
+ GET (keysz, "CC.keysz -> acceptable key sizes")
+ GET (blksz, "CC.blksz -> block size, or zero")
+ GET (name, "CC.name -> name of this kind of cipher")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef gcipher_pymethods[] = {
+#define METHNAME(name) gcmeth_##name
+ METH (encrypt, "C.encrypt(PT) -> CT")
+ METH (enczero, "C.enczero(N) -> CT")
+ METH (decrypt, "C.decrypt(CT) -> PT")
+ METH (deczero, "C.deczero(N) -> PT")
+ METH (setiv, "C.setiv(IV)")
+ NAMETH(bdry, "C.bdry()")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gccipher_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCCipher", /* @tp_name@ */
+ sizeof(gccipher_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Symmetric cipher metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gccipher), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gcipher_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCipher", /* @tp_name@ */
+ sizeof(gcipher_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gcipher_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Symmetric cipher, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gcipher), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Authenticated encryption ------------------------------------------*/
+
+static PyTypeObject *gcaead_pytype, *gaeadkey_pytype;
+static PyTypeObject *gcaeadaad_pytype, *gaeadaad_pytype;
+static PyTypeObject *gcaeadenc_pytype, *gaeadenc_pytype;
+static PyTypeObject *gcaeaddec_pytype, *gaeaddec_pytype;
+
+typedef struct gcaead_pyobj {
+ PyHeapTypeObject ty;
+ gcaead *aec;
+ struct gcaeadaad_pyobj *aad;
+ struct gcaeadenc_pyobj *enc;
+ struct gcaeaddec_pyobj *dec;
+} gcaead_pyobj;
+
+#define GCAEAD_PYCHECK(o) PyObject_TypeCheck((o), gcaead_pytype)
+#define GCAEAD_AEC(o) (((gcaead_pyobj *)(o))->aec)
+#define GCAEAD_AAD(o) (((gcaead_pyobj *)(o))->aad)
+#define GCAEAD_ENC(o) (((gcaead_pyobj *)(o))->enc)
+#define GCAEAD_DEC(o) (((gcaead_pyobj *)(o))->dec)
+static PyObject *gcaead_pywrap(gcaead *);
+
+typedef struct gaeadkey_pyobj {
+ PyObject_HEAD
+ gaead_key *k;
+} gaeadkey_pyobj;
+
+#define GAEADKEY_PYCHECK(o) PyObject_TypeCheck((o), gaeadkey_pytype)
+#define GAEADKEY_K(o) (((gaeadkey_pyobj *)(o))->k)
+
+typedef struct gcaeadaad_pyobj {
+ PyHeapTypeObject ty;
+ gcaead_pyobj *key;
+} gcaeadaad_pyobj;
+
+#define GCAEADAAD_KEY(o) (((gcaeadaad_pyobj *)(o))->key)
+static PyObject *gaeadaad_pywrap(PyObject *, gaead_aad *, unsigned, size_t);
+
+typedef struct gaeadaad_pyobj {
+ PyObject_HEAD
+ gaead_aad *a;
+ unsigned f;
+#define AEADF_DEAD 32768u
+ size_t hsz, hlen;
+} gaeadaad_pyobj;
+
+#define GAEADAAD_PYCHECK(o) PyObject_TypeCheck((o), gaeadaad_pytype)
+#define GAEADAAD_A(o) (((gaeadaad_pyobj *)(o))->a)
+#define GAEADAAD_F(o) (((gaeadaad_pyobj *)(o))->f)
+#define GAEADAAD_HSZ(o) (((gaeadaad_pyobj *)(o))->hsz)
+#define GAEADAAD_HLEN(o) (((gaeadaad_pyobj *)(o))->hlen)
+
+typedef struct gcaeadenc_pyobj {
+ PyHeapTypeObject ty;
+ gcaead_pyobj *key;
+} gcaeadenc_pyobj;
+
+#define GCAEADENC_KEY(o) (((gcaeadenc_pyobj *)(o))->key)
+static PyObject *gaeadenc_pywrap(PyObject *, gaead_enc *, unsigned,
+ size_t, size_t, size_t);
+
+typedef struct gaeadenc_pyobj {
+ PyObject_HEAD
+ gaead_enc *e;
+ gaeadaad_pyobj *aad;
+ unsigned f;
+ size_t hsz, msz, tsz;
+ size_t mlen;
+} gaeadenc_pyobj;
+
+#define GAEADENC_PYCHECK(o) PyObject_TypeCheck((o), gaeadenc_pytype)
+#define GAEADENC_AAD(o) (((gaeadenc_pyobj *)(o))->aad)
+#define GAEADENC_E(o) (((gaeadenc_pyobj *)(o))->e)
+#define GAEADENC_F(o) (((gaeadenc_pyobj *)(o))->f)
+#define GAEADENC_HSZ(o) (((gaeadenc_pyobj *)(o))->hsz)
+#define GAEADENC_MSZ(o) (((gaeadenc_pyobj *)(o))->msz)
+#define GAEADENC_TSZ(o) (((gaeadenc_pyobj *)(o))->tsz)
+#define GAEADENC_MLEN(o) (((gaeadenc_pyobj *)(o))->mlen)
+
+typedef struct gcaeaddec_pyobj {
+ PyHeapTypeObject ty;
+ gcaead_pyobj *key;
+} gcaeaddec_pyobj;
+
+#define GCAEADDEC_KEY(o) (((gcaeaddec_pyobj *)(o))->key)
+static PyObject *gaeaddec_pywrap(PyObject *, gaead_dec *, unsigned,
+ size_t, size_t, size_t);
+
+typedef struct gaeaddec_pyobj {
+ PyObject_HEAD
+ gaead_dec *d;
+ gaeadaad_pyobj *aad;
+ unsigned f;
+ size_t hsz, csz, tsz;
+ size_t clen;
+} gaeaddec_pyobj;
+
+#define GAEADDEC_PYCHECK(o) PyObject_TypeCheck((o), gaeaddec_pytype)
+#define GAEADDEC_AAD(o) (((gaeaddec_pyobj *)(o))->aad)
+#define GAEADDEC_D(o) (((gaeaddec_pyobj *)(o))->d)
+#define GAEADDEC_F(o) (((gaeaddec_pyobj *)(o))->f)
+#define GAEADDEC_HSZ(o) (((gaeaddec_pyobj *)(o))->hsz)
+#define GAEADDEC_CSZ(o) (((gaeaddec_pyobj *)(o))->csz)
+#define GAEADDEC_TSZ(o) (((gaeaddec_pyobj *)(o))->tsz)
+#define GAEADDEC_CLEN(o) (((gaeaddec_pyobj *)(o))->clen)
+
+static PyObject *gaeadkey_pywrap(PyObject *cobj, gaead_key *k)
+{
+ gaeadkey_pyobj *gk;
+
+ if (!cobj) cobj = gcaead_pywrap((/*unconst*/ gcaead *)GAEAD_CLASS(k));
+ else Py_INCREF(cobj);
+ gk = PyObject_NEW(gaeadkey_pyobj, (PyTypeObject *)cobj);
+ gk->k = k;
+ return ((PyObject *)gk);
+}
+
+static PyObject *gaeadkey_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "k", 0 };
+ struct bin k;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &k))
+ goto end;
+ if (keysz(k.sz, GCAEAD_AEC(ty)->keysz) != k.sz) VALERR("bad key length");
+ return (gaeadkey_pywrap((PyObject *)ty,
+ GAEAD_KEY(GCAEAD_AEC(ty), k.p, k.sz)));
+end:
+ return (0);
+}
+
+static PyObject *gcaead_pywrap(gcaead *aec)
+{
+ gcaead_pyobj *gck;
+ gcaeadaad_pyobj *gca;
+ gcaeadenc_pyobj *gce;
+ gcaeaddec_pyobj *gcd;
+
+#define MKTYPE(obj, thing, newfn, namefmt) do { \
+ (obj) = newtype(gcaead_pytype, 0, 0); \
+ (obj)->ty.ht_name = TEXT_FORMAT(namefmt, aec->name); \
+ (obj)->ty.ht_type.tp_name = TEXT_PTR((obj)->ty.ht_name); \
+ (obj)->ty.ht_type.tp_basicsize = sizeof(gaead##thing##_pyobj); \
+ (obj)->ty.ht_type.tp_base = gaead##thing##_pytype; \
+ Py_INCREF(gaead##thing##_pytype); \
+ (obj)->ty.ht_type.tp_flags = \
+ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE); \
+ (obj)->ty.ht_type.tp_alloc = PyType_GenericAlloc; \
+ (obj)->ty.ht_type.tp_free = 0; \
+ (obj)->ty.ht_type.tp_new = newfn; \
+ typeready(&(obj)->ty.ht_type); \
+} while (0)
+
+ MKTYPE(gck, key, gaeadkey_pynew, "%s(key)");
+ MKTYPE(gca, aad, abstract_pynew, "%s(aad-hash)");
+ MKTYPE(gce, enc, abstract_pynew, "%s(encrypt)");
+ MKTYPE(gcd, dec, abstract_pynew, "%s(decrypt)");
+
+#undef MKTYPE
+
+ gck->aec = aec; gck->aad = gca; gck->enc = gce; gck->dec = gcd;
+ gca->key = gce->key = gcd->key = gck;
+ return ((PyObject *)gck);
+}
+
+static void gaeadkey_pydealloc(PyObject *me)
+ { GAEAD_DESTROY(GAEADKEY_K(me)); Py_DECREF(Py_TYPE(me)); FREEOBJ(me); }
+
+static PyObject *gcaeget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GCAEAD_AEC(me)->name)); }
+
+static PyObject *gcaeget_keysz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCAEAD_AEC(me)->keysz)); }
+
+static PyObject *gcaeget_noncesz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCAEAD_AEC(me)->noncesz)); }
+
+static PyObject *gcaeget_tagsz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCAEAD_AEC(me)->tagsz)); }
+
+static PyObject *gcaeget_blksz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCAEAD_AEC(me)->blksz)); }
+
+static PyObject *gcaeget_bufsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCAEAD_AEC(me)->bufsz)); }
+
+static PyObject *gcaeget_ohd(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCAEAD_AEC(me)->ohd)); }
+
+static PyObject *gcaeget_flags(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCAEAD_AEC(me)->f)); }
+
+static const PyGetSetDef gcaead_pygetset[] = {
+#define GETSETNAME(op, name) gcae##op##_##name
+ GET (keysz, "AEC.keysz -> acceptable key sizes")
+ GET (noncesz, "AEC.noncesz -> acceptable nonce sizes")
+ GET (tagsz, "AEC.tagsz -> acceptable tag sizes")
+ GET (blksz, "AEC.blksz -> block size, or zero")
+ GET (bufsz, "AEC.bufsz -> amount of data buffered internally")
+ GET (ohd, "AEC.ohd -> maximum encryption overhead")
+ GET (name, "AEC.name -> name of this kind of AEAD scheme")
+ GET (flags, "AEC.flags -> mask of `AEADF_...' flags")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *gaekmeth_aad(PyObject *me)
+{
+ const gaead_key *k = GAEADKEY_K(me);
+ PyObject *rc = 0;
+
+ if (k->ops->c->f&AEADF_AADNDEP)
+ VALERR("aad must be associated with enc/dec op");
+ rc = gaeadaad_pywrap((PyObject *)GCAEAD_AAD(Py_TYPE(me)),
+ GAEAD_AAD(k), 0, 0);
+end:
+ return (rc);
+}
+
+static int check_aead_encdec(const gcaead *aec, unsigned *f_out, size_t nsz,
+ PyObject *hszobj, size_t *hsz_out,
+ PyObject *mszobj, size_t *msz_out,
+ PyObject *tszobj, size_t *tsz_out)
+{
+ unsigned f = 0, miss;
+ int rc = -1;
+
+ if (hszobj != Py_None)
+ { f |= AEADF_PCHSZ; if (!convszt(hszobj, hsz_out)) goto end; }
+ if (mszobj != Py_None)
+ { f |= AEADF_PCMSZ; if (!convszt(mszobj, msz_out)) goto end; }
+ if (tszobj != Py_None)
+ { f |= AEADF_PCTSZ; if (!convszt(tszobj, tsz_out)) goto end; }
+ miss = aec->f&~f;
+ if (miss&AEADF_PCHSZ) VALERR("header length precommitment required");
+ if (miss&AEADF_PCMSZ) VALERR("message length precommitment required");
+ if (miss&AEADF_PCTSZ) VALERR("tag length precommitment required");
+ if (keysz(nsz, aec->noncesz) != nsz) VALERR("bad nonce length");
+ if (tszobj != Py_None && keysz(*tsz_out, aec->tagsz) != *tsz_out)
+ VALERR("bad tag length");
+ *f_out = f | aec->f; rc = 0;
+end:
+ return (rc);
+}
+
+static PyObject *gaekmeth_enc(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "nonce", "hsz", "msz", "tsz", 0 };
+ const gaead_key *k = GAEADKEY_K(me);
+ gaead_enc *e;
+ PyObject *rc = 0;
+ struct bin n;
+ PyObject *hszobj = Py_None, *mszobj = Py_None, *tszobj = Py_None;
+ size_t hsz = 0, msz = 0, tsz = 0;
+ unsigned f;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|OOO:enc", KWLIST,
+ convbin, &n, &hszobj, &mszobj, &tszobj))
+ goto end;
+ if (check_aead_encdec(k->ops->c, &f, n.sz,
+ hszobj, &hsz, mszobj, &msz, tszobj, &tsz))
+ goto end;
+ e = GAEAD_ENC(GAEADKEY_K(me), n.p, n.sz, hsz, msz, tsz);
+ if (!e) VALERR("bad aead parameter combination");
+ rc = gaeadenc_pywrap((PyObject *)GCAEAD_ENC(Py_TYPE(me)),
+ e, f, hsz, msz, tsz);
+end:
+ return (rc);
+}
+
+static PyObject *gaekmeth_dec(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "nonce", "hsz", "csz", "tsz", 0 };
+ const gaead_key *k = GAEADKEY_K(me);
+ gaead_dec *d;
+ PyObject *rc = 0;
+ struct bin n;
+ PyObject *hszobj = Py_None, *cszobj = Py_None, *tszobj = Py_None;
+ size_t hsz = 0, csz = 0, tsz = 0;
+ unsigned f;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|OOO:dec", KWLIST,
+ convbin, &n, &hszobj, &cszobj, &tszobj))
+ goto end;
+ if (check_aead_encdec(k->ops->c, &f, n.sz,
+ hszobj, &hsz, cszobj, &csz, tszobj, &tsz))
+ goto end;
+ d = GAEAD_DEC(GAEADKEY_K(me), n.p, n.sz, hsz, csz, tsz);
+ if (!d) VALERR("bad aead parameter combination");
+ rc = gaeaddec_pywrap((PyObject *)GCAEAD_DEC(Py_TYPE(me)),
+ d, f, hsz, csz, tsz);
+end:
+ return (rc);
+}
+
+static const PyMethodDef gaeadkey_pymethods[] = {
+#define METHNAME(name) gaekmeth_##name
+ NAMETH(aad, "KEY.aad() -> AAD")
+ KWMETH(enc, "KEY.enc(NONCE, [hsz], [msz], [tsz]) -> ENC")
+ KWMETH(dec, "KEY.dec(NONCE, [hsz], [csz], [tsz]) -> DEC")
+#undef METHNAME
+ { 0 }
+};
+
+static PyObject *gaeadaad_pywrap(PyObject *cobj, gaead_aad *a,
+ unsigned f, size_t hsz)
+{
+ gaeadaad_pyobj *ga;
+
+ assert(cobj); Py_INCREF(cobj);
+ ga = PyObject_NEW(gaeadaad_pyobj, (PyTypeObject *)cobj);
+ ga->a = a; ga->f = f; ga->hsz = hsz; ga->hlen = 0;
+ return ((PyObject *)ga);
+}
+
+static void gaeadaad_pydealloc(PyObject *me)
+{
+ gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
+
+ if (ga->a) GAEAD_DESTROY(ga->a);
+ Py_DECREF(Py_TYPE(me)); FREEOBJ(me);
+}
+
+static int gaea_check(PyObject *me)
+{
+ gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
+ int rc = -1;
+
+ if ((ga->f&AEADF_DEAD) || !ga->a) VALERR("aad object no longer active");
+ rc = 0;
+end:
+ return (rc);
+}
+
+static void gaea_invalidate(gaeadaad_pyobj *ga)
+ { if (ga) ga->f |= AEADF_DEAD; }
+
+static void gaea_sever(gaeadaad_pyobj **ga_inout)
+{
+ gaeadaad_pyobj *ga = *ga_inout;
+ if (ga) { ga->f |= AEADF_DEAD; ga->a = 0; Py_DECREF(ga); *ga_inout = 0; }
+}
+
+static PyObject *gaeaget_hsz(PyObject *me, void *hunoz)
+{
+ if (gaea_check(me)) return (0);
+ else if (GAEADAAD_F(me)&AEADF_PCHSZ) return getulong(GAEADAAD_HSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaeaget_hlen(PyObject *me, void *hunoz)
+ { return (gaea_check(me) ? 0 : getulong(GAEADAAD_HLEN(me))); }
+
+static const PyGetSetDef gaeadaad_pygetset[] = {
+#define GETSETNAME(op, name) gaea##op##_##name
+ GET (hsz, "AAD.hsz -> precommitted header length or `None'")
+ GET (hlen, "AAD.hlen -> header length so far")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *gaeameth_copy(PyObject *me)
+{
+ PyObject *rc = 0;
+
+ if (gaea_check(me)) goto end;
+ if (GAEADAAD_F(me)&AEADF_AADNDEP)
+ VALERR("can't duplicate nonce-dependent aad");
+ rc = gaeadaad_pywrap((PyObject *)Py_TYPE(me),
+ GAEAD_DUP(GAEADAAD_A(me)), 0, 0);
+ GAEADAAD_HLEN(rc) = GAEADAAD_HLEN(me);
+end:
+ return (rc);
+}
+
+static int gaeadaad_hash(PyObject *me, const void *h, size_t hsz)
+{
+ gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
+ int rc = -1;
+
+ if (gaea_check(me)) goto end;
+ if ((ga->f&AEADF_NOAAD) && hsz)
+ VALERR("header data not permitted");
+ if ((ga->f&AEADF_PCHSZ) && hsz > ga->hsz - ga->hlen)
+ VALERR("too large for precommitted header length");
+ GAEAD_HASH(ga->a, h, hsz); ga->hlen += hsz;
+ rc = 0;
+end:
+ return (rc);
+}
+
+static PyObject *gaeameth_hash(PyObject *me, PyObject *arg)
+{
+ struct bin h;
+
+ if (!PyArg_ParseTuple(arg, "O&:hash", convbin, &h)) return (0);
+ if (gaeadaad_hash(me, h.p, h.sz)) return (0);
+ RETURN_ME;
+}
+
+#define GAEAMETH_HASHU_(n, W, w) \
+ static PyObject *gaeameth_hashu##w(PyObject *me, PyObject *arg) \
+ { \
+ uint##n x; octet b[SZ_##W]; \
+ if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
+ STORE##W(b, x); if (gaeadaad_hash(me, b, sizeof(b))) return (0); \
+ RETURN_ME; \
+ }
+DOUINTCONV(GAEAMETH_HASHU_)
+
+#define GAEAMETH_HASHBUF_(n, W, w) \
+ static PyObject *gaeameth_hashbuf##w(PyObject *me, PyObject *arg) \
+ { \
+ struct bin in; octet b[SZ_##W]; \
+ if (!PyArg_ParseTuple(arg, "O&:hashbuf" #w, convbin, &in)) goto end; \
+ if (in.sz > MASK##n) VALERR("too large"); \
+ STORE##W(b, in.sz); if (gaeadaad_hash(me, b, sizeof(b))) goto end; \
+ if (gaeadaad_hash(me, in.p, in.sz)) goto end; \
+ RETURN_ME; \
+ end: \
+ return (0); \
+ }
+DOUINTCONV(GAEAMETH_HASHBUF_)
+
+static PyObject *gaeameth_hashstrz(PyObject *me, PyObject *arg)
+{
+ char *p;
+ if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+ if (gaeadaad_hash(me, p, strlen(p) + 1)) return (0);
+ RETURN_ME;
+}
+
+static const PyMethodDef gaeadaad_pymethods[] = {
+#define METHNAME(name) gaeameth_##name
+ NAMETH(copy, "AAD.copy() -> AAD'")
+ METH (hash, "AAD.hash(H)")
+#define METHU_(n, W, w) METH(hashu##w, "AAD.hashu" #w "(WORD)")
+ DOUINTCONV(METHU_)
+#undef METHU_
+#define METHBUF_(n, W, w) METH(hashbuf##w, "AAD.hashbuf" #w "(BYTES)")
+ DOUINTCONV(METHBUF_)
+#undef METHBUF_
+ METH (hashstrz, "AAD.hashstrz(STRING)")
+#undef METHNAME
+ { 0 }
+};
+
+static PyObject *gaeadenc_pywrap(PyObject *cobj, gaead_enc *e, unsigned f,
+ size_t hsz, size_t msz, size_t tsz)
+{
+ gaeadenc_pyobj *ge;
+
+ assert(cobj); Py_INCREF(cobj);
+ ge = PyObject_NEW(gaeadenc_pyobj, (PyTypeObject *)cobj);
+ ge->e = e; ge->f = f; ge->hsz = hsz; ge->msz = msz; ge->tsz = tsz;
+ ge->aad = 0; ge->mlen = 0;
+ return ((PyObject *)ge);
+}
+
+static void gaeadenc_pydealloc(PyObject *me)
+{
+ gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+
+ gaea_sever(&ge->aad); GAEAD_DESTROY(ge->e);
+ Py_DECREF(Py_TYPE(me)); FREEOBJ(me);
+}
+
+static PyObject *gaeeget_hsz(PyObject *me, void *hunoz)
+{
+ if (GAEADENC_F(me)&AEADF_PCHSZ) return getulong(GAEADENC_HSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaeeget_msz(PyObject *me, void *hunoz)
+{
+ if (GAEADENC_F(me)&AEADF_PCMSZ) return getulong(GAEADENC_MSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaeeget_tsz(PyObject *me, void *hunoz)
+{
+ if (GAEADENC_F(me)&AEADF_PCTSZ) return getulong(GAEADENC_TSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaeeget_mlen(PyObject *me, void *hunoz)
+ { return getulong(GAEADENC_MLEN(me)); }
+
+static const PyGetSetDef gaeadenc_pygetset[] = {
+#define GETSETNAME(op, name) gaee##op##_##name
+ GET (hsz, "ENC.hsz -> precommitted header length or `None'")
+ GET (msz, "ENC.msz -> precommitted message length or `None'")
+ GET (tsz, "ENC.tsz -> precommitted tag length or `None'")
+ GET (mlen, "ENC.mlen -> message length so far")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *gaeemeth_aad(PyObject *me)
+{
+ gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+ PyObject *rc = 0;
+
+ if (!(ge->f&AEADF_AADNDEP))
+ rc = gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(Py_TYPE(ge))->aad,
+ GAEAD_AAD(ge->e), 0, 0);
+ else {
+ if ((ge->f&AEADF_AADFIRST) && ge->mlen)
+ VALERR("too late for aad");
+ if (!ge->aad)
+ ge->aad = (gaeadaad_pyobj *)
+ gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(Py_TYPE(ge))->aad,
+ GAEAD_AAD(ge->e), ge->f&(AEADF_PCHSZ | AEADF_NOAAD),
+ ge->hsz);
+ Py_INCREF(ge->aad);
+ rc = (PyObject *)ge->aad;
+ }
+end:
+ return (rc);
+}
+
+static PyObject *gaeemeth_reinit(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "nonce", "hsz", "msz", "tsz", 0 };
+ gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+ struct bin n;
+ PyObject *hszobj = Py_None, *mszobj = Py_None, *tszobj = Py_None;
+ size_t hsz = 0, msz = 0, tsz = 0;
+ unsigned f;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|OOO:enc", KWLIST,
+ convbin, &n, &hszobj, &mszobj, &tszobj))
+ goto end;
+ if (check_aead_encdec(ge->e->ops->c, &f, n.sz,
+ hszobj, &hsz, mszobj, &msz, tszobj, &tsz))
+ goto end;
+ if (GAEAD_REINIT(ge->e, n.p, n.sz, hsz, msz, tsz))
+ VALERR("bad aead parameter combination");
+ gaea_sever(&ge->aad);
+ ge->f = f; ge->hsz = hsz; ge->msz = msz; ge->tsz = tsz;
+end:
+ return (0);
+}
+
+static PyObject *gaeemeth_encrypt(PyObject *me, PyObject *arg)
+{
+ gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+ struct bin m;
+ char *c = 0; size_t csz; buf b;
+ int err;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:encrypt", convbin, &m)) goto end;
+ if (ge->f&AEADF_AADFIRST) {
+ if ((ge->f&AEADF_PCHSZ) && (ge->aad ? ge->aad->hlen : 0) != ge->hsz)
+ VALERR("header doesn't match precommitted length");
+ gaea_invalidate(ge->aad);
+ }
+ if ((ge->f&AEADF_PCMSZ) && m.sz > ge->msz - ge->mlen)
+ VALERR("too large for precommitted message length");
+ csz = m.sz + ge->e->ops->c->bufsz; c = xmalloc(csz); buf_init(&b, c, csz);
+ err = GAEAD_ENCRYPT(ge->e, m.p, m.sz, &b); assert(!err); (void)err;
+ buf_flip(&b); rc = bytestring_pywrapbuf(&b); ge->mlen += m.sz;
+end:
+ xfree(c);
+ return (rc);
+}
+
+static PyObject *gaeemeth_done(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "tsz", "aad", 0 };
+ gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+ PyObject *aad = Py_None;
+ char *c = 0; size_t csz; buf b;
+ PyObject *tszobj = Py_None; PyObject *tag; size_t tsz;
+ int err;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:done", KWLIST,
+ &tszobj, &aad))
+ goto end;
+ if (tszobj != Py_None && !convszt(tszobj, &tsz)) goto end;
+ if (aad != Py_None &&
+ !PyObject_TypeCheck(aad,
+ (PyTypeObject *)GCAEADENC_KEY(Py_TYPE(me))->aad))
+ TYERR("wanted aad");
+ if ((ge->f&AEADF_AADNDEP) && aad != Py_None && aad != (PyObject *)ge->aad)
+ VALERR("mismatched aad");
+ if ((ge->f&AEADF_PCHSZ) &&
+ (aad == Py_None ? 0 : GAEADAAD_HLEN(aad)) != ge->hsz)
+ VALERR("header doesn't match precommitted length");
+ if ((ge->f&AEADF_PCMSZ) && ge->mlen != ge->msz)
+ VALERR("message doesn't match precommitted length");
+ if (tszobj == Py_None) {
+ if (ge->f&AEADF_PCTSZ) tsz = ge->tsz;
+ else tsz = keysz(0, ge->e->ops->c->tagsz);
+ } else {
+ if ((ge->f&AEADF_PCTSZ) && tsz != ge->tsz)
+ VALERR("tag length doesn't match precommitted value");
+ if (keysz(tsz, ge->e->ops->c->tagsz) != tsz) VALERR("bad tag length");
+ }
+ csz = ge->e->ops->c->bufsz; c = xmalloc(csz); buf_init(&b, c, csz);
+ tag = bytestring_pywrap(0, tsz);
+ err = GAEAD_DONE(ge->e, aad == Py_None ? 0 : GAEADAAD_A(aad), &b,
+ BIN_PTR(tag), tsz);
+ assert(!err); (void)err;
+ buf_flip(&b); rc = Py_BuildValue("NN", bytestring_pywrapbuf(&b), tag);
+end:
+ xfree(c);
+ return (rc);
+}
+
+static const PyMethodDef gaeadenc_pymethods[] = {
+#define METHNAME(name) gaeemeth_##name
+ NAMETH(aad, "ENC.aad() -> AAD")
+ KWMETH(reinit, "ENC.reinit(NONCE, [hsz], [msz], [tsz])")
+ METH (encrypt, "ENC.encrypt(MSG) -> CT")
+ KWMETH(done, "ENC.done([tsz], [aad]) -> CT, TAG")
+#undef METHNAME
+ { 0 }
+};
+
+static PyObject *gaeaddec_pywrap(PyObject *cobj, gaead_dec *d, unsigned f,
+ size_t hsz, size_t csz, size_t tsz)
+{
+ gaeaddec_pyobj *gd;
+ assert(cobj); Py_INCREF(cobj);
+ gd = PyObject_NEW(gaeaddec_pyobj, (PyTypeObject *)cobj);
+ gd->d = d; gd->f = f; gd->hsz = hsz; gd->csz = csz; gd->tsz = tsz;
+ gd->aad = 0; gd->clen = 0;
+ return ((PyObject *)gd);
+}
+
+static void gaeaddec_pydealloc(PyObject *me)
+{
+ gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+
+ gaea_sever(&gd->aad); GAEAD_DESTROY(GAEADDEC_D(me));
+ Py_DECREF(Py_TYPE(me)); FREEOBJ(me);
+}
+
+static PyObject *gaedget_hsz(PyObject *me, void *hunoz)
+{
+ if (GAEADDEC_F(me)&AEADF_PCHSZ) return getulong(GAEADDEC_HSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaedget_csz(PyObject *me, void *hunoz)
+{
+ if (GAEADDEC_F(me)&AEADF_PCMSZ) return getulong(GAEADDEC_CSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaedget_tsz(PyObject *me, void *hunoz)
+{
+ if (GAEADDEC_F(me)&AEADF_PCTSZ) return getulong(GAEADDEC_TSZ(me));
+ else RETURN_NONE;
+}
+
+static PyObject *gaedget_clen(PyObject *me, void *hunoz)
+ { return getulong(GAEADDEC_CLEN(me)); }
+
+static const PyGetSetDef gaeaddec_pygetset[] = {
+#define GETSETNAME(op, name) gaed##op##_##name
+ GET (hsz, "DEC.hsz -> precommitted header length or `None'")
+ GET (csz, "DEC.csz -> precommitted ciphertext length or `None'")
+ GET (tsz, "DEC.tsz -> precommitted tag length or `None'")
+ GET (clen, "DEC.clen -> ciphertext length so far")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *gaedmeth_aad(PyObject *me)
+{
+ gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+
+ if (!(gd->f&AEADF_AADNDEP))
+ return (gaeadaad_pywrap((PyObject *)GCAEADDEC_KEY(Py_TYPE(gd))->aad,
+ GAEAD_AAD(gd->d), 0, 0));
+ else {
+ if (!gd->aad)
+ gd->aad = (gaeadaad_pyobj *)
+ gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(Py_TYPE(gd))->aad,
+ GAEAD_AAD(gd->d), gd->f&(AEADF_PCHSZ | AEADF_NOAAD),
+ gd->hsz);
+ Py_INCREF(gd->aad);
+ return ((PyObject *)gd->aad);
+ }
+}
+
+static PyObject *gaedmeth_reinit(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "nonce", "hsz", "csz", "tsz", 0 };
+ gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+ struct bin n;
+ PyObject *hszobj = Py_None, *cszobj = Py_None, *tszobj = Py_None;
+ size_t hsz = 0, csz = 0, tsz = 0;
+ unsigned f;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|OOO:enc", KWLIST,
+ convbin, &n, &hszobj, &cszobj, &tszobj))
+ goto end;
+ if (check_aead_encdec(gd->d->ops->c, &f, n.sz,
+ hszobj, &hsz, cszobj, &csz, tszobj, &tsz))
+ goto end;
+ if (GAEAD_REINIT(gd->d, n.p, n.sz, hsz, csz, tsz))
+ VALERR("bad aead parameter combination");
+ gaea_sever(&gd->aad);
+ gd->f = f; gd->hsz = hsz; gd->csz = csz; gd->tsz = tsz;
+end:
+ return (0);
+}
+
+static PyObject *gaedmeth_decrypt(PyObject *me, PyObject *arg)
+{
+ gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+ struct bin c;
+ char *m = 0; size_t msz; buf b;
+ int err;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:decrypt", convbin, &c)) goto end;
+ if (gd->f&AEADF_AADFIRST) {
+ if ((gd->f&AEADF_PCHSZ) && (gd->aad ? gd->aad->hlen : 0) != gd->hsz)
+ VALERR("header doesn't match precommitted length");
+ gaea_invalidate(gd->aad);
+ }
+ if ((gd->f&AEADF_PCMSZ) && c.sz > gd->csz - gd->clen)
+ VALERR("too large for precommitted message length");
+ msz = c.sz + gd->d->ops->c->bufsz; m = xmalloc(msz); buf_init(&b, m, msz);
+ err = GAEAD_DECRYPT(gd->d, c.p, c.sz, &b); assert(!err); (void)err;
+ buf_flip(&b); rc = bytestring_pywrapbuf(&b); gd->clen += c.sz;
+end:
+ xfree(m);
+ return (rc);
+}
+
+static PyObject *gaedmeth_done(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "tag", "aad", 0 };
+ gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+ PyObject *aad = Py_None;
+ struct bin t;
+ char *m = 0; size_t msz; buf b;
+ int err;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:done", KWLIST,
+ convbin, &t, &aad))
+ goto end;
+ if (aad != Py_None &&
+ !PyObject_TypeCheck(aad,
+ (PyTypeObject *)GCAEADENC_KEY(Py_TYPE(me))->aad))
+ TYERR("wanted aad");
+ if ((gd->f&AEADF_AADNDEP) && aad != Py_None && aad != (PyObject *)gd->aad)
+ VALERR("mismatched aad");
+ if ((gd->f&AEADF_PCHSZ) &&
+ (aad == Py_None ? 0 : GAEADAAD_HLEN(aad)) != gd->hsz)
+ VALERR("header doesn't match precommitted length");
+ if ((gd->f&AEADF_PCMSZ) && gd->clen != gd->csz)
+ VALERR("message doesn't match precommitted length");
+ if ((gd->f&AEADF_PCTSZ) && t.sz != gd->tsz)
+ VALERR("tag length doesn't match precommitted value");
+ if (keysz(t.sz, gd->d->ops->c->tagsz) != t.sz) VALERR("bad tag length");
+ msz = gd->d->ops->c->bufsz; m = xmalloc(msz); buf_init(&b, m, msz);
+ err = GAEAD_DONE(gd->d, aad == Py_None ? 0 : GAEADAAD_A(aad),
+ &b, t.p, t.sz);
+ assert(err >= 0);
+ if (!err) VALERR("decryption failed");
+ buf_flip(&b); rc = bytestring_pywrapbuf(&b);
+end:
+ xfree(m);
+ return (rc);
+}
+
+static const PyMethodDef gaeaddec_pymethods[] = {
+#define METHNAME(name) gaedmeth_##name
+ NAMETH(aad, "DEC.aad() -> AAD")
+ KWMETH(reinit, "DEC.reinit(NONCE, [hsz], [csz], [tsz])")
+ METH (decrypt, "DEC.decrypt(CT) -> MSG")
+ KWMETH(done, "DEC.done(TAG, [aad]) -> MSG | None")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gcaead_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCAEAD", /* @tp_name@ */
+ sizeof(gcaead_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated encryption (key) metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gcaead), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gaeadkey_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEKey", /* @tp_name@ */
+ sizeof(gaeadkey_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gaeadkey_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated encryption key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gaeadkey), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gcaeadaad_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEAADClass", /* @tp_name@ */
+ sizeof(gcaeadaad_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated encryption additional-data hash metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gaeadaad_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEAAD", /* @tp_name@ */
+ sizeof(gaeadaad_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gaeadaad_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated encryption AAD hash.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gaeadaad), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gaeadaad), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gcaeadenc_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEEncClass", /* @tp_name@ */
+ sizeof(gcaeadenc_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated encryption operation metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gaeadenc_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEEnc", /* @tp_name@ */
+ sizeof(gaeadenc_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gaeadenc_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated encryption operation.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gaeadenc), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gaeadenc), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gcaeaddec_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEDecClass", /* @tp_name@ */
+ sizeof(gcaeaddec_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated decryption operation metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gaeaddec_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GAEDec", /* @tp_name@ */
+ sizeof(gaeaddec_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gaeaddec_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Authenticated decryption operation.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gaeaddec), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gaeaddec), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Hash functions ----------------------------------------------------*/
+
+PyTypeObject *gchash_pytype;
+static PyTypeObject *ghash_pytype;
+PyObject *sha_pyobj, *has160_pyobj;
+
+typedef struct ghash_pyobj {
+ PyObject_HEAD
+ ghash *h;
+} ghash_pyobj;
+
+#define GHASH_PYCHECK(o) PyObject_TypeCheck((o), ghash_pytype)
+#define GHASH_H(o) (((ghash_pyobj *)(o))->h)
+
+CONVFUNC(gchash, gchash *, GCHASH_CH)
+CONVFUNC(ghash, ghash *, GHASH_H)
+
+static PyObject *ghash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST))
+ goto end;
+ return (ghash_pywrap((PyObject *)ty, GH_INIT(GCHASH_CH(ty))));
+end:
+ return (0);
+}
+
+static PyObject *gchash_pywrap(gchash *ch)
+{
+ gchash_pyobj *g = newtype(gchash_pytype, 0, ch->name);
+ g->ch = ch;
+ g->ty.ht_type.tp_basicsize = sizeof(ghash_pyobj);
+ g->ty.ht_type.tp_base = ghash_pytype;
+ Py_INCREF(ghash_pytype);
+ g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ g->ty.ht_type.tp_free = 0;
+ g->ty.ht_type.tp_new = ghash_pynew;
+ typeready(&g->ty.ht_type);
+ return ((PyObject *)g);
+}
+
+PyObject *ghash_pywrap(PyObject *cobj, ghash *h)
+{
+ ghash_pyobj *g;
+ if (!cobj) cobj = gchash_pywrap((/*unconst*/ gchash *)GH_CLASS(h));
+ else Py_INCREF(cobj);
+ g = PyObject_NEW(ghash_pyobj, (PyTypeObject *)cobj);
+ g->h = h;
+ return ((PyObject *)g);
+}
+
+static void ghash_pydealloc(PyObject *me)
+{
+ GH_DESTROY(GHASH_H(me));
+ Py_DECREF(Py_TYPE(me));
+ FREEOBJ(me);
+}
+
+static PyObject *gchget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GCHASH_CH(me)->name)); }
+
+static PyObject *gchget_hashsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCHASH_CH(me)->hashsz)); }
+
+static PyObject *gchget_bufsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCHASH_CH(me)->bufsz)); }
+
+static PyObject *ghmeth_copy(PyObject *me)
+ { return (ghash_pywrap((PyObject *)Py_TYPE(me), GH_COPY(GHASH_H(me)))); }
+
+static PyObject *ghmeth_hash(PyObject *me, PyObject *arg)
+{
+ struct bin m;
+ if (!PyArg_ParseTuple(arg, "O&:hash", convbin, &m)) return (0);
+ GH_HASH(GHASH_H(me), m.p, m.sz);
+ RETURN_ME;
+}
+
+#define GHMETH_HASHU_(n, W, w) \
+ static PyObject *ghmeth_hashu##w(PyObject *me, PyObject *arg) \
+ { \
+ uint##n x; \
+ if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
+ GH_HASHU##W(GHASH_H(me), x); \
+ RETURN_ME; \
+ }
+DOUINTCONV(GHMETH_HASHU_)
+
+#define GHMETH_HASHBUF_(n, W, w) \
+ static PyObject *ghmeth_hashbuf##w(PyObject *me, PyObject *arg) \
+ { \
+ struct bin in; \
+ if (!PyArg_ParseTuple(arg, "O&:hashbuf" #w, convbin, &in)) goto end; \
+ if (in.sz > MASK##n) VALERR("too large"); \
+ GH_HASHBUF##W(GHASH_H(me), in.p, in.sz); \
+ RETURN_ME; \
+ end: \
+ return (0); \
+ }
+DOUINTCONV(GHMETH_HASHBUF_)
+
+static PyObject *ghmeth_hashstrz(PyObject *me, PyObject *arg)
+{
+ char *p;
+ if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+ GH_HASHSTRZ(GHASH_H(me), p);
+ RETURN_ME;
+}
+
+static PyObject *ghmeth_done(PyObject *me)
+{
+ ghash *g;
+ PyObject *rc;
+ g = GH_COPY(GHASH_H(me));
+ rc = bytestring_pywrap(0, g->ops->c->hashsz);
+ GH_DONE(g, BIN_PTR(rc));
+ GH_DESTROY(g);
+ return (rc);
+}
+
+static const PyGetSetDef gchash_pygetset[] = {
+#define GETSETNAME(op, name) gch##op##_##name
+ GET (bufsz, "CH.bufsz -> hash buffer size, or zero")
+ GET (hashsz, "CH.hashsz -> hash output size")
+ GET (name, "CH.name -> name of this kind of hash")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef ghash_pymethods[] = {
+#define METHNAME(name) ghmeth_##name
+ NAMETH(copy, "H.copy() -> HH")
+ METH (hash, "H.hash(M)")
+#define METHU_(n, W, w) METH(hashu##w, "H.hashu" #w "(WORD)")
+ DOUINTCONV(METHU_)
+#undef METHU_
+#define METHBUF_(n, W, w) METH(hashbuf##w, "H.hashbuf" #w "(BYTES)")
+ DOUINTCONV(METHBUF_)
+#undef METHBUF_
+ METH (hashstrz, "H.hashstrz(STRING)")
+ NAMETH(done, "H.done() -> HASH")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gchash_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCHash", /* @tp_name@ */
+ sizeof(gchash_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Hash function metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gchash), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject ghash_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GHash", /* @tp_name@ */
+ sizeof(ghash_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ ghash_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Hash function, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(ghash), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Message authentication --------------------------------------------*/
+
+static PyTypeObject *gcmac_pytype, *gmac_pytype, *gmhash_pytype;
+
+typedef struct gcmac_pyobj {
+ PyHeapTypeObject ty;
+ gcmac *cm;
+} gcmac_pyobj;
+
+#define GCMAC_PYCHECK(o) PyObject_TypeCheck((o), gcmac_pytype)
+#define GCMAC_CM(o) (((gcmac_pyobj *)(o))->cm)
+#define GCMAC_F(o) (((gcmac_pyobj *)(o))->f)
+CONVFUNC(gcmac, gcmac *, GCMAC_CM)
+static PyObject *gmac_pywrap(PyObject *, gmac *);
+
+typedef struct gmac_pyobj {
+ PyHeapTypeObject ty;
+ gmac *m;
+} gmac_pyobj;
+
+extern PyTypeObject *gmac_pytype;
+#define GMAC_PYCHECK(o) PyObject_TypeCheck((o), gmac_pytype)
+#define GMAC_M(o) (((gmac_pyobj *)(o))->m)
+#define GMAC_F(o) (((gmac_pyobj *)(o))->f)
+extern PyObject *gmac_pywrap(PyObject *, gmac *);
+extern int convgmac(PyObject *, void *);
+
+static PyObject *gmac_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "k", 0 };
+ struct bin k;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &k))
+ goto end;
+ if (keysz(k.sz, GCMAC_CM(ty)->keysz) != k.sz) VALERR("bad key length");
+ return (gmac_pywrap((PyObject *)ty,
+ GM_KEY(GCMAC_CM(ty), k.p, k.sz)));
+end:
+ return (0);
+}
+
+static PyObject *gmhash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { 0 };
+ ghash_pyobj *g;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) return (0);
+ g = PyObject_NEW(ghash_pyobj, ty);
+ g->h = GM_INIT(GMAC_M(ty));
+ Py_INCREF(ty);
+ return ((PyObject *)g);
+}
+
+static PyObject *gcmac_pywrap(gcmac *cm)
+{
+ gcmac_pyobj *g = newtype(gcmac_pytype, 0, cm->name);
+ g->cm = cm;
+ g->ty.ht_type.tp_basicsize = sizeof(gmac_pyobj);
+ g->ty.ht_type.tp_base = gmac_pytype;
+ Py_INCREF(gmac_pytype);
+ g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ g->ty.ht_type.tp_free = 0;
+ g->ty.ht_type.tp_new = gmac_pynew;
+ typeready(&g->ty.ht_type);
+ return ((PyObject *)g);
+}
+
+static PyObject *gmac_pywrap(PyObject *cobj, gmac *m)
+{
+ gmac_pyobj *g;
+ if (!cobj) cobj = gcmac_pywrap((/*unconst*/ gcmac *)GM_CLASS(m));
+ else Py_INCREF(cobj);
+ g = newtype((PyTypeObject *)cobj, 0, 0);
+ g->ty.ht_type.tp_basicsize = sizeof(ghash_pyobj);
+ g->ty.ht_name = TEXT_FORMAT("%s(keyed)", m->ops->c->name);
+ g->ty.ht_type.tp_name = TEXT_PTR(g->ty.ht_name);
+ g->ty.ht_type.tp_base = gmhash_pytype;
+ Py_INCREF(gmac_pytype);
+ g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ g->ty.ht_type.tp_free = 0;
+ g->ty.ht_type.tp_new = gmhash_pynew;
+ typeready(&g->ty.ht_type);
+ g->m = m;
+ return ((PyObject *)g);
+}
+
+static void gmac_pydealloc(PyObject *me)
+{
+ GM_DESTROY(GMAC_M(me));
+ Py_DECREF(Py_TYPE(me));
+ PyType_Type.tp_dealloc(me);
+}
+
+static PyObject *gcmget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GCMAC_CM(me)->name)); }
+
+static PyObject *gcmget_keysz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCMAC_CM(me)->keysz)); }
+
+static PyObject *gcmget_tagsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCMAC_CM(me)->hashsz)); }
+
+static const PyGetSetDef gcmac_pygetset[] = {
+#define GETSETNAME(op, name) gcm##op##_##name
+ GET (keysz, "CM.keysz -> acceptable key sizes")
+ GET (tagsz, "CM.tagsz -> MAC output size")
+ GET (name, "CM.name -> name of this kind of MAC")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *gmget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GMAC_M(me)->ops->c->name)); }
+
+static PyObject *gmget_hashsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GMAC_M(me)->ops->c->hashsz)); }
+#define gmget_tagsz gmget_hashsz
+
+static const PyGetSetDef gmac_pygetset[] = {
+#define GETSETNAME(op, name) gm##op##_##name
+ GET (hashsz, "M.hashsz -> MAC output size")
+ GET (tagsz, "M.tagsz -> MAC output size")
+ GET (name, "M.name -> name of this kind of MAC")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject gcmac_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCMAC", /* @tp_name@ */
+ sizeof(gchash_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Message authentication code metametaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gcmac), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gmac_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GMAC", /* @tp_name@ */
+ sizeof(gmac_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gmac_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Message authentication code metaclass, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gmac), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gmhash_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GMACHash", /* @tp_name@ */
+ sizeof(ghash_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ ghash_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Message authentication code, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Special snowflake for Poly1305 ------------------------------------*/
+
+PyTypeObject *poly1305cls_pytype, *poly1305key_pytype, *poly1305hash_pytype;
+
+typedef struct poly1305key_pyobj {
+ PyHeapTypeObject ty;
+ poly1305_key k;
+} poly1305key_pyobj;
+
+typedef struct poly1305hash_pyobj {
+ PyObject_HEAD
+ unsigned f;
+#define f_mask 1u
+ poly1305_ctx ctx;
+} poly1305hash_pyobj;
+
+#define P1305_F(o) (((poly1305hash_pyobj *)(o))->f)
+#define P1305_CTX(o) (&((poly1305hash_pyobj *)(o))->ctx)
+CONVFUNC(poly1305hash, poly1305_ctx *, P1305_CTX)
+
+static PyObject *poly1305hash_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "mask", 0 };
+ poly1305key_pyobj *pk = (poly1305key_pyobj *)ty;
+ poly1305hash_pyobj *ph;
+ struct bin m = { 0, 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convbin, &m))
+ return (0);
+ if (m.p && m.sz != POLY1305_MASKSZ) VALERR("bad mask length");
+ ph = PyObject_NEW(poly1305hash_pyobj, ty);
+ ph->f = 0;
+ if (m.p) ph->f |= f_mask;
+ poly1305_macinit(&ph->ctx, &pk->k, m.p);
+ Py_INCREF(ty);
+ return ((PyObject *)ph);
+end:
+ return (0);
+}
+
+static PyObject *poly1305key_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "k", 0 };
+ poly1305key_pyobj *pk;
+ struct bin k;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &k))
+ goto end;
+ if (keysz(k.sz, poly1305_keysz) != k.sz) VALERR("bad key length");
+
+ pk = newtype(ty, 0, 0);
+ pk->ty.ht_name = TEXT_FROMSTR("poly1305(keyed)");
+ pk->ty.ht_type.tp_basicsize = sizeof(poly1305hash_pyobj);
+ pk->ty.ht_type.tp_name = TEXT_PTR(pk->ty.ht_name);
+ pk->ty.ht_type.tp_base = poly1305hash_pytype;
+ Py_INCREF(poly1305key_pytype);
+ pk->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ pk->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ pk->ty.ht_type.tp_free = 0;
+ pk->ty.ht_type.tp_new = poly1305hash_pynew;
+ typeready(&pk->ty.ht_type);
+
+ poly1305_keyinit(&pk->k, k.p, k.sz);
+ return ((PyObject *)pk);
+
+end:
+ return (0);
+}
+
+static PyObject *poly1305clsget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR("poly1305")); }
+
+static PyObject *poly1305clsget_keysz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(poly1305_keysz)); }
+
+static PyObject *poly1305clsget_masksz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(POLY1305_MASKSZ)); }
+
+static PyObject *poly1305clsget_tagsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(POLY1305_TAGSZ)); }
+
+static PyObject *polymeth_copy(PyObject *me)
+{
+ poly1305hash_pyobj *ph;
+ ph = PyObject_NEW(poly1305hash_pyobj, Py_TYPE(me));
+ poly1305_copy(&ph->ctx, P1305_CTX(me));
+ Py_INCREF(Py_TYPE(me));
+ return ((PyObject *)ph);
+}
+
+static PyObject *polymeth_hash(PyObject *me, PyObject *arg)
+{
+ struct bin m;
+ if (!PyArg_ParseTuple(arg, "O&:hash", convbin, &m)) return (0);
+ poly1305_hash(P1305_CTX(me), m.p, m.sz);
+ RETURN_ME;
+}
+
+#define POLYMETH_HASHU_(n, W, w) \
+ static PyObject *polymeth_hashu##w(PyObject *me, PyObject *arg) \
+ { \
+ uint##n x; \
+ octet b[SZ_##W]; \
+ if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
+ STORE##W(b, x); poly1305_hash(P1305_CTX(me), b, sizeof(b)); \
+ RETURN_ME; \
+ }
+DOUINTCONV(POLYMETH_HASHU_)
+
+#define POLYMETH_HASHBUF_(n, W, w) \
+ static PyObject *polymeth_hashbuf##w(PyObject *me, PyObject *arg) \
+ { \
+ struct bin in; \
+ octet b[SZ_##W]; \
+ if (!PyArg_ParseTuple(arg, "O&:hashbuf" #w, convbin, &in)) goto end; \
+ if (in.sz > MASK##n) VALERR("too large"); \
+ STORE##W(b, in.sz); poly1305_hash(P1305_CTX(me), b, sizeof(b)); \
+ poly1305_hash(P1305_CTX(me), in.p, in.sz); \
+ RETURN_ME; \
+ end: \
+ return (0); \
+ }
+DOUINTCONV(POLYMETH_HASHBUF_)
+
+static PyObject *polymeth_hashstrz(PyObject *me, PyObject *arg)
+{
+ char *p;
+ if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+ poly1305_hash(P1305_CTX(me), p, strlen(p) + 1);
+ RETURN_ME;
+}
+
+static PyObject *polymeth_flush(PyObject *me)
+ { poly1305_flush(P1305_CTX(me)); RETURN_ME; }
+
+static PyObject *polymeth_flushzero(PyObject *me)
+ { poly1305_flushzero(P1305_CTX(me)); RETURN_ME; }
+
+static PyObject *polymeth_concat(PyObject *me, PyObject *arg)
+{
+ PyObject *pre, *suff;
+ if (!PyArg_ParseTuple(arg, "OO:concat", &pre, &suff)) return (0);
+ if (!PyObject_TypeCheck(pre, poly1305hash_pytype) ||
+ !PyObject_TypeCheck(suff, poly1305hash_pytype))
+ TYERR("wanted a poly1305hash");
+ if (Py_TYPE(me) != Py_TYPE(pre) || Py_TYPE(me) != Py_TYPE(suff))
+ TYERR("key mismatch");
+ if (P1305_CTX(pre)->nbuf) VALERR("prefix is not block-aligned");
+ poly1305_concat(P1305_CTX(me), P1305_CTX(pre), P1305_CTX(suff));
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *polymeth_done(PyObject *me)
+{
+ PyObject *rc;
+ if (!(P1305_F(me) & f_mask)) VALERR("no mask");
+ rc = bytestring_pywrap(0, POLY1305_TAGSZ);
+ poly1305_done(P1305_CTX(me), BIN_PTR(rc));
+ return (rc);
+end:
+ return (0);
+}
+
+static const PyGetSetDef poly1305cls_pygetset[] = {
+#define GETSETNAME(op, name) poly1305cls##op##_##name
+ GET (keysz, "PC.keysz -> acceptable key sizes")
+ GET (masksz, "PC.masksz -> mask size")
+ GET (tagsz, "PC.tagsz -> MAC output size")
+ GET (name, "PC.name -> name of this kind of MAC")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *poly1305get_name(PyObject *me, void *hunoz)
+ { RETURN_OBJ(((PyHeapTypeObject *)poly1305key_pytype)->ht_name); }
+
+static PyObject *poly1305get_tagsz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(16)); }
+
+static const PyGetSetDef poly1305_pygetset[] = {
+#define GETSETNAME(op, name) poly1305##op##_##name
+ GET (tagsz, "PK.tagsz -> MAC output size")
+ GET (name, "PK.name -> name of this kind of MAC")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef poly1305hash_pymethods[] = {
+#define METHNAME(name) polymeth_##name
+ NAMETH(copy, "P.copy() -> PP")
+ METH (hash, "P.hash(M)")
+#define METHU_(n, W, w) METH(hashu##w, "P.hashu" #w "(WORD)")
+ DOUINTCONV(METHU_)
+#undef METHU_
+#define METHBUF_(n, W, w) METH(hashbuf##w, "P.hashbuf" #w "(BYTES)")
+ DOUINTCONV(METHBUF_)
+#undef METHBUF_
+ METH (hashstrz, "P.hashstrz(STRING)")
+ NAMETH(flush, "P.flush()")
+ NAMETH(flushzero, "P.flushzero()")
+ METH (concat, "P.concat(PREFIX, SUFFIX)")
+ NAMETH(done, "P.done() -> TAG")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject poly1305cls_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "_Poly1305Class", /* @tp_name@ */
+ sizeof(PyHeapTypeObject), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Poly1305 metametaclass. Best not to ask.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(poly1305cls), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject poly1305key_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "poly1305", /* @tp_name@ */
+ sizeof(poly1305key_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "poly1305(K): Poly1305 key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(poly1305), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ poly1305key_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject poly1305hash_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Poly1305Hash", /* @tp_name@ */
+ sizeof(poly1305hash_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Poly1305 MAC context base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(poly1305hash), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Special snowflake for HSalsa and HChaCha --------------------------*/
+
+#define DEF_HDANCE(DANCE, HDANCE, dance, hdance) \
+ static PyObject *meth_##hdance##_prf(PyObject *me, PyObject *arg) \
+ { \
+ dance##_ctx dance; \
+ struct bin k, n; \
+ PyObject *rc; \
+ if (!PyArg_ParseTuple(arg, "O&O&:" #hdance "_prf", \
+ convbin, &k, convbin, &n)) \
+ goto end; \
+ if (k.sz != keysz(k.sz, dance##_keysz)) VALERR("bad key length"); \
+ if (n.sz != HDANCE##_INSZ) VALERR("bad input length"); \
+ rc = bytestring_pywrap(0, HSALSA20_OUTSZ); \
+ dance##_init(&dance, k.p, k.sz, 0); \
+ hdance##_prf(&dance, n.p, BIN_PTR(rc)); \
+ return (rc); \
+ end: \
+ return (0); \
+ }
+
+DEF_HDANCE(SALSA20, HSALSA20, salsa20, hsalsa20)
+DEF_HDANCE(SALSA20, HSALSA20, salsa20, hsalsa2012)
+DEF_HDANCE(SALSA20, HSALSA20, salsa20, hsalsa208)
+
+DEF_HDANCE(CHACHA, HCHACHA, chacha, hchacha20)
+DEF_HDANCE(CHACHA, HCHACHA, chacha, hchacha12)
+DEF_HDANCE(CHACHA, HCHACHA, chacha, hchacha8)
+
+/*----- Keccak-p[1600, n] -------------------------------------------------*/
+
+static PyTypeObject *kxvik_pytype;
+
+typedef struct kxvik_pyobj {
+ PyObject_HEAD
+ keccak1600_state s;
+ unsigned n;
+} kxvik_pyobj;
+
+static PyObject *kxvik_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ unsigned n = 24;
+ kxvik_pyobj *rc = 0;
+ static const char *const kwlist[] = { "nround", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
+ convuint, &n))
+ goto end;
+ rc = (kxvik_pyobj *)ty->tp_alloc(ty, 0);
+ rc->n = n;
+ keccak1600_init(&rc->s);
+end:
+ return ((PyObject *)rc);
+}
+
+static PyObject *kxvikmeth_copy(PyObject *me)
+{
+ kxvik_pyobj *k = (kxvik_pyobj *)me, *rc = 0;
+ rc = (kxvik_pyobj *)Py_TYPE(k)->tp_alloc(Py_TYPE(k), 0);
+ rc->s = k->s; rc->n = k->n;
+ return ((PyObject *)rc);
+}
+
+static PyObject *kxvikmeth_mix(PyObject *me, PyObject *arg)
+{
+ kxvik_pyobj *k = (kxvik_pyobj *)me;
+ kludge64 t[25];
+ const octet *q;
+ octet buf[8];
+ unsigned i;
+ struct bin in;
+ size_t n;
+
+ if (!PyArg_ParseTuple(arg, "O&:mix", convbin, &in)) goto end;
+ if (in.sz > 200) VALERR("out of range");
+ q = in.p; n = in.sz;
+ i = 0;
+ while (n > 8) { LOAD64_L_(t[i], q); i++; q += 8; n -= 8; }
+ if (n) {
+ memcpy(buf, q, n); memset(buf + n, 0, 8 - n);
+ LOAD64_L_(t[i], buf); i++;
+ }
+ keccak1600_mix(&k->s, t, i);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *kxvikmeth_set(PyObject *me, PyObject *arg)
+{
+ kxvik_pyobj *k = (kxvik_pyobj *)me;
+ kludge64 t[25];
+ const octet *q;
+ unsigned i;
+ struct bin in;
+ size_t n;
+
+ if (!PyArg_ParseTuple(arg, "O&:set", convbin, &in)) goto end;
+ if (in.sz > 200) VALERR("out of range");
+ q = in.p; n = in.sz;
+ i = 0;
+ while (n >= 8) { LOAD64_L_(t[i], q); i++; q += 8; n -= 8; }
+ if (n) VALERR("not 64-bit aligned");
+ keccak1600_set(&k->s, t, i);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *kxvikmeth_extract(PyObject *me, PyObject *arg)
+{
+ kxvik_pyobj *k = (kxvik_pyobj *)me;
+ PyObject *rc = 0;
+ kludge64 t[25];
+ octet *q, buf[8];
+ unsigned i;
+ unsigned n;
+
+ if (!PyArg_ParseTuple(arg, "O&:extract", convuint, &n)) goto end;
+ if (n > 200) VALERR("out of range");
+ rc = bytestring_pywrap(0, n);
+ q = (octet *)BIN_PTR(rc);
+ keccak1600_extract(&k->s, t, (n + 7)/8);
+ i = 0;
+ while (n > 8) { STORE64_L_(q, t[i]); i++; q += 8; n -= 8; }
+ if (n) { STORE64_L_(buf, t[i]); memcpy(q, buf, n); }
+end:
+ return (rc);
+}
+
+static PyObject *kxvikmeth_step(PyObject *me)
+{
+ kxvik_pyobj *k = (kxvik_pyobj *)me;
+ keccak1600_p(&k->s, &k->s, k->n);
+ RETURN_ME;
+}
+
+static const PyMemberDef kxvik_pymembers[] = {
+#define MEMBERSTRUCT kxvik_pyobj
+ MEMRNM(nround, T_UINT, n, 0, "KECCAC.nround -> number of rounds")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyMethodDef kxvik_pymethods[] = {
+#define METHNAME(func) kxvikmeth_##func
+ NAMETH(copy, "KECCAK.copy() -> KECCAK'")
+ METH (mix, "KECCAK.mix(DATA)")
+ METH (set, "KECCAK.set(DATA)")
+ METH (extract, "KECCAK.extract(NOCTETS)")
+ NAMETH(step, "KECCAK.step()")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject kxvik_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Keccak1600", /* @tp_name@ */
+ sizeof(kxvik_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Keccak1600([nround = 24]): Keccak-p[1600, n] state.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(kxvik), /* @tp_methods@ */
+ PYMEMBERS(kxvik), /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ kxvik_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyTypeObject *shake_pytype, *shake128_pytype, *shake256_pytype;
+
+typedef struct shake_pyobj {
+ PyObject_HEAD
+ int st;
+ shake_ctx h;
+} shake_pyobj;
+
+#define SHAKE_H(o) (&((shake_pyobj *)(o))->h)
+#define SHAKE_ST(o) (((shake_pyobj *)(o))->st)
+
+static PyObject *shake_dopynew(void (*initfn)(shake_ctx *,
+ const void *, size_t,
+ const void *, size_t),
+ PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ shake_pyobj *rc = 0;
+ PyObject *pobj = Py_None, *fobj = Py_None;
+ struct bin p = { 0, 0 }, f = { 0, 0 };
+ static const char *const kwlist[] = { "perso", "func", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:new", KWLIST, &pobj, &fobj))
+ goto end;
+ if (pobj != Py_None && !convbin(pobj, &p)) goto end;
+ if (fobj != Py_None && !convbin(fobj, &f)) goto end;
+ rc = (shake_pyobj *)ty->tp_alloc(ty, 0);
+ initfn(&rc->h, f.p, f.sz, p.p, p.sz);
+ rc->st = 0;
+end:
+ return ((PyObject *)rc);
+}
+
+static PyObject *shake128_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+ { return (shake_dopynew(cshake128_init, ty, arg, kw)); }
+
+static PyObject *shake256_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+ { return (shake_dopynew(cshake256_init, ty, arg, kw)); }
+
+static int shake_check(PyObject *me, int st)
+{
+ if (SHAKE_ST(me) != st) VALERR("wrong state");
+ return (0);
+end:
+ return (-1);
+}
+
+static PyObject *shakemeth_hash(PyObject *me, PyObject *arg)
+{
+ struct bin m;
+ if (!PyArg_ParseTuple(arg, "O&:hash", convbin, &m)) return (0);
+ if (shake_check(me, 0)) return (0);
+ shake_hash(SHAKE_H(me), m.p, m.sz);
+ RETURN_ME;
+}
+
+#define SHAKEMETH_HASHU_(n, W, w) \
+ static PyObject *shakemeth_hashu##w(PyObject *me, PyObject *arg) \
+ { \
+ uint##n x; \
+ octet b[SZ_##W]; \
+ if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
+ if (shake_check(me, 0)) return (0); \
+ STORE##W(b, x); shake_hash(SHAKE_H(me), b, sizeof(b)); \
+ RETURN_ME; \
+ }
+DOUINTCONV(SHAKEMETH_HASHU_)
+
+#define SHAKEMETH_HASHBUF_(n, W, w) \
+ static PyObject *shakemeth_hashbuf##w(PyObject *me, PyObject *arg) \
+ { \
+ struct bin in; \
+ octet b[SZ_##W]; \
+ if (!PyArg_ParseTuple(arg, "O&:hashbuf" #w, convbin, &in)) goto end; \
+ if (in.sz > MASK##n) VALERR("too large"); \
+ if (shake_check(me, 0)) goto end; \
+ STORE##W(b, in.sz); shake_hash(SHAKE_H(me), b, sizeof(b)); \
+ shake_hash(SHAKE_H(me), in.p, in.sz); \
+ RETURN_ME; \
+ end: \
+ return (0); \
+ }
+DOUINTCONV(SHAKEMETH_HASHBUF_)
+
+static PyObject *shakemeth_hashstrz(PyObject *me, PyObject *arg)
+{
+ char *p;
+ if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+ if (shake_check(me, 0)) return (0);
+ shake_hash(SHAKE_H(me), p, strlen(p) + 1);
+ RETURN_ME;
+}
+
+static PyObject *shakemeth_xof(PyObject *me)
+{
+ if (shake_check(me, 0)) goto end;
+ shake_xof(SHAKE_H(me));
+ SHAKE_ST(me) = 1;
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *shakemeth_done(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *rc = 0;
+ size_t n = 100 - SHAKE_H(me)->h.r/2;
+ static const char *const kwlist[] = { "hsz", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:done", KWLIST, convszt, &n))
+ goto end;
+ if (shake_check(me, 0)) goto end;
+ rc = bytestring_pywrap(0, n);
+ shake_done(SHAKE_H(me), BIN_PTR(rc), n);
+ SHAKE_ST(me) = -1;
+end:
+ return (rc);
+}
+
+static PyObject *shakemeth_copy(PyObject *me)
+{
+ shake_pyobj *rc = 0;
+
+ rc = PyObject_NEW(shake_pyobj, Py_TYPE(me));
+ rc->h = *SHAKE_H(me);
+ rc->st = SHAKE_ST(me);
+ return ((PyObject *)rc);
+}
+
+static PyObject *shakemeth_get(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ size_t sz;
+
+ if (!PyArg_ParseTuple(arg, "O&:get", convszt, &sz)) goto end;
+ if (shake_check(me, 1)) goto end;
+ rc = bytestring_pywrap(0, sz);
+ shake_get(SHAKE_H(me), BIN_PTR(rc), sz);
+end:
+ return (rc);
+}
+
+static PyObject *shakemeth_mask(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ struct bin in;
+
+ if (!PyArg_ParseTuple(arg, "O&:mask", convbin, &in)) goto end;
+ if (shake_check(me, 1)) goto end;
+ rc = bytestring_pywrap(0, in.sz);
+ shake_mask(SHAKE_H(me), in.p, BIN_PTR(rc), in.sz);
+end:
+ return (rc);
+}
+
+static PyObject *shakeget_state(PyObject *me, void *hunoz)
+{
+ int st = SHAKE_ST(me);
+ return (TEXT_FROMSTR(st == 0 ? "absorb" :
+ st == 1 ? "squeeze" : "dead"));
+}
+
+static const PyMemberDef shake_pymembers[] = {
+#define MEMBERSTRUCT shake_pyobj
+ MEMRNM(rate, T_UINT, h.h.r, READONLY, "S.rate -> rate, in bytes")
+ MEMRNM(buffered, T_UINT, h.h.n, READONLY,
+ "S.buffered -> amount currently buffered")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyGetSetDef shake_pygetset[] = {
+#define GETSETNAME(op, name) shake##op##_##name
+ GET (state, "S.state -> `absorb', `squeeze', `dead'")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef shake_pymethods[] = {
+#define METHNAME(func) shakemeth_##func
+ NAMETH(copy, "S.copy() -> SS")
+ METH (hash, "S.hash(M)")
+#define METHU_(n, W, w) METH(hashu##w, "S.hashu" #w "(WORD)")
+ DOUINTCONV(METHU_)
+#undef METHU_
+#define METHBUF_(n, W, w) METH(hashbuf##w, "S.hashbuf" #w "(BYTES)")
+ DOUINTCONV(METHBUF_)
+#undef METHBUF_
+ METH (hashstrz, "S.hashstrz(STRING)")
+ NAMETH(xof, "S.xof()")
+ KWMETH(done, "S.done([hsz = CAP]) -> H")
+ METH (get, "S.get(LEN) -> H")
+ METH (mask, "S.mask(M) -> C")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject shake_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Shake", /* @tp_name@ */
+ sizeof(shake_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "SHAKE/cSHAKE/KMAC base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(shake), /* @tp_methods@ */
+ PYMEMBERS(shake), /* @tp_members@ */
+ PYGETSET(shake), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject shake128_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Shake128", /* @tp_name@ */
+ 0, /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Shake128([perso = STR], [func = STR]): SHAKE128/cSHAKE128 XOF.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ shake128_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject shake256_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Shake256", /* @tp_name@ */
+ 0, /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Shake256([perso = STR], [func = STR]): SHAKE256/cSHAKE256 XOF.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ shake256_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyTypeObject *kmac_pytype, *kmac128_pytype, *kmac256_pytype;
+
+static PyObject *kmac_dopynew(void (*initfn)(shake_ctx *,
+ const void *, size_t,
+ const void *, size_t),
+ PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ shake_pyobj *rc = 0;
+ PyObject *pobj = Py_None;
+ struct bin k = { 0, 0 }, p = { 0, 0 };
+ static const char *const kwlist[] = { "key", "perso", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:new", KWLIST,
+ convbin, &k, &pobj))
+ goto end;
+ if (pobj != Py_None && !convbin(pobj, &p)) goto end;
+ rc = (shake_pyobj *)ty->tp_alloc(ty, 0);
+ initfn(&rc->h, p.p, p.sz, k.p, k.sz);
+ rc->st = 0;
+end:
+ return ((PyObject *)rc);
+}
+
+static PyObject *kmac128_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+ { return (kmac_dopynew(kmac128_init, ty, arg, kw)); }
+
+static PyObject *kmac256_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+ { return (kmac_dopynew(kmac256_init, ty, arg, kw)); }
+
+static PyObject *kmacmeth_xof(PyObject *me)
+{
+ if (shake_check(me, 0)) goto end;
+ kmac_xof(SHAKE_H(me));
+ SHAKE_ST(me) = 1;
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *kmacmeth_done(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *rc = 0;
+ size_t n = 100 - SHAKE_H(me)->h.r/2;
+ static const char *const kwlist[] = { "hsz", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:done", KWLIST, convszt, &n))
+ goto end;
+ if (shake_check(me, 0)) goto end;
+ rc = bytestring_pywrap(0, n);
+ kmac_done(SHAKE_H(me), BIN_PTR(rc), n);
+ SHAKE_ST(me) = -1;
+end:
+ return (rc);
+}
+
+static const PyMethodDef kmac_pymethods[] = {
+#define METHNAME(func) kmacmeth_##func
+ NAMETH(xof, "K.xof()")
+ KWMETH(done, "K.done([hsz = CAP/2]) -> T")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject kmac_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KMAC", /* @tp_name@ */
+ sizeof(shake_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KMAC base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(kmac), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject kmac128_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KMAC128", /* @tp_name@ */
+ 0, /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KMAC128(KEY, [perso = STR]): KMAC XOMAC.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ kmac128_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject kmac256_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KMAC256", /* @tp_name@ */
+ 0, /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KMAC256(KEY, [perso = STR]): KMAC XOMAC.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ kmac256_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Pseudorandom permutations -----------------------------------------*/
+
+static PyTypeObject *gcprp_pytype, *gprp_pytype;
+
+typedef struct prpinfo {
+ const char *name;
+ const octet *keysz;
+ size_t ctxsz;
+ size_t blksz;
+ void (*init)(void *, const void *, size_t);
+ void (*eblk)(void *, const void *, void *);
+ void (*dblk)(void *, const void *, void *);
+} prpinfo;
+
+#define PRP_DEF(PRE, pre) \
+ static void pre##_prpinit(void *ctx, const void *k, size_t ksz) \
+ { pre##_init(ctx, k, ksz); } \
+ static void pre##_prpeblk(void *ctx, const void *in, void *out) \
+ { \
+ uint32 w[PRE##_BLKSZ/4]; BLKC_LOAD(PRE, w, in); \
+ pre##_eblk(ctx, w, w); BLKC_STORE(PRE, out, w); \
+ } \
+ static void pre##_prpdblk(void *ctx, const void *in, void *out) \
+ { \
+ uint32 w[PRE##_BLKSZ/4]; BLKC_LOAD(PRE, w, in); \
+ pre##_dblk(ctx, w, w); BLKC_STORE(PRE, out, w); \
+ } \
+ static const prpinfo pre##_prpinfo = { \
+ #pre, pre##_keysz, sizeof(pre##_ctx), PRE##_BLKSZ, \
+ pre##_prpinit, pre##_prpeblk, pre##_prpdblk \
+ };
+PRPS(PRP_DEF)
+
+static const struct prpinfo *const gprptab[] = {
+#define PRP_ENTRY(PRE, pre) &pre##_prpinfo,
+ PRPS(PRP_ENTRY)
+ 0
+};
+
+typedef struct gcprp_pyobj {
+ PyHeapTypeObject ty;
+ const prpinfo *prp;
+} gcprp_pyobj;
+#define GCPRP_PRP(o) (((gcprp_pyobj *)(o))->prp)
+
+typedef struct gprp_pyobj {
+ PyObject_HEAD
+ const prpinfo *prp;
+} gprp_pyobj;
+#define GPRP_PRP(o) (((gprp_pyobj *)(o))->prp)
+#define GPRP_CTX(o) (((gprp_pyobj *)(o)) + 1)
+
+typedef struct prp {
+ const prpinfo *prp;
+ void *ctx;
+} prp;
+
+static PyObject *gprp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "key", 0 };
+ struct bin k;
+ const prpinfo *prp = GCPRP_PRP(ty);
+ PyObject *me;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &k))
+ goto end;
+ if (keysz(k.sz, prp->keysz) != k.sz) VALERR("bad key length");
+ me = (PyObject *)ty->tp_alloc(ty, 0);
+ GPRP_PRP(me) = prp;
+ prp->init(GPRP_CTX(me), k.p, k.sz);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static void gprp_pydealloc(PyObject *me)
+ { Py_DECREF(Py_TYPE(me)); FREEOBJ(me); }
+
+static PyObject *gcprp_pywrap(const prpinfo *prp)
+{
+ gcprp_pyobj *g = newtype(gcprp_pytype, 0, prp->name);
+ g->prp = prp;
+ g->ty.ht_type.tp_basicsize = sizeof(gprp_pyobj) + prp->ctxsz;
+ g->ty.ht_type.tp_base = gprp_pytype;
+ Py_INCREF(gprp_pytype);
+ g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ g->ty.ht_type.tp_free = 0;
+ g->ty.ht_type.tp_new = gprp_pynew;
+ typeready(&g->ty.ht_type);
+ return ((PyObject *)g);
+}
+
+static PyObject *gcpget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GCPRP_PRP(me)->name)); }
+static PyObject *gcpget_keysz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCPRP_PRP(me)->keysz)); }
+static PyObject *gcpget_blksz(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GCPRP_PRP(me)->blksz)); }
+
+static PyObject *gpmeth_encrypt(PyObject *me, PyObject *arg)
+{
+ struct bin m;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:encrypt", convbin, &m)) goto end;
+ if (m.sz != GPRP_PRP(me)->blksz) VALERR("incorrect block length");
+ rc = bytestring_pywrap(0, m.sz);
+ GPRP_PRP(me)->eblk(GPRP_CTX(me), m.p, BIN_PTR(rc));
+end:
+ return (rc);
+}
+
+static PyObject *gpmeth_decrypt(PyObject *me, PyObject *arg)
+{
+ struct bin c;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:decrypt", convbin, &c)) goto end;
+ if (c.sz != GPRP_PRP(me)->blksz) VALERR("incorrect block length");
+ rc = bytestring_pywrap(0, c.sz);
+ GPRP_PRP(me)->dblk(GPRP_CTX(me), c.p, BIN_PTR(rc));
+end:
+ return (rc);
+}
+
+static const PyGetSetDef gcprp_pygetset[] = {
+#define GETSETNAME(op, name) gcp##op##_##name
+ GET (keysz, "CP.keysz -> acceptable key sizes")
+ GET (blksz, "CP.blksz -> block size")
+ GET (name, "CP.name -> name of this kind of PRP")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef gprp_pymethods[] = {
+#define METHNAME(name) gpmeth_##name
+ METH (encrypt, "P.encrypt(PT) -> CT")
+ METH (decrypt, "P.decrypt(CT) -> PT")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gcprp_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCPRP", /* @tp_name@ */
+ sizeof(gcprp_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Pseudorandom permutation metaclass.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gcprp), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gprp_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GPRP", /* @tp_name@ */
+ sizeof(gprp_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gprp_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Pseudorandom permutation, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gprp), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(AEADF_PCHSZ), CONST(AEADF_PCMSZ), CONST(AEADF_PCTSZ),
+ CONST(AEADF_AADNDEP), CONST(AEADF_AADFIRST), CONST(AEADF_NOAAD),
+ { 0 }
+};
+
+static const PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+#define METH_HDANCE(hdance, HDance) METH(hdance##_prf, \
+ "" #hdance "_prf(K, N) -> H: calculate " HDance " hash of N with K")
+ METH_HDANCE(hsalsa20, "HSalsa20")
+ METH_HDANCE(hsalsa2012, "HSalsa20/12")
+ METH_HDANCE(hsalsa208, "HSalsa20/8")
+ METH_HDANCE(hchacha20, "HChaCha20")
+ METH_HDANCE(hchacha12, "HChaCha12")
+ METH_HDANCE(hchacha8, "HChaCha8")
+#undef METH_DANCE
+#undef METHNAME
+ { 0 }
+};
+
+void algorithms_pyinit(void)
+{
+ INITTYPE(keysz, root);
+ INITTYPE(keyszany, keysz);
+ INITTYPE(keyszrange, keysz);
+ INITTYPE(keyszset, keysz);
+ INITTYPE(gccipher, type);
+ INITTYPE(gcipher, root);
+ INITTYPE(gcaead, type);
+ INITTYPE(gaeadkey, root);
+ INITTYPE(gcaeadaad, type);
+ INITTYPE(gaeadaad, root);
+ INITTYPE(gcaeadenc, type);
+ INITTYPE(gaeadenc, root);
+ INITTYPE(gcaeaddec, type);
+ INITTYPE(gaeaddec, root);
+ INITTYPE(gchash, type);
+ INITTYPE(ghash, root);
+ INITTYPE(gcmac, type);
+ INITTYPE(gmac, type);
+ INITTYPE(gmhash, ghash);
+ INITTYPE(poly1305cls, type);
+ INITTYPE_META(poly1305key, type, poly1305cls);
+ INITTYPE(poly1305hash, root);
+ INITTYPE(kxvik, root);
+ INITTYPE(shake, root);
+ INITTYPE(shake128, shake);
+ INITTYPE(shake256, shake);
+ INITTYPE(kmac, shake);
+ INITTYPE(kmac128, kmac);
+ INITTYPE(kmac256, kmac);
+ INITTYPE(gcprp, type);
+ INITTYPE(gprp, root);
+ addmethods(methods);
+}
+
+#define gcprp prpinfo
+#define CLASS_TABLES(_) _(cipher) _(aead) _(hash) _(mac) _(prp)
+#define TABLE_FNS(pre) \
+ static const char *pre##_namefn(const void *p) \
+ { const gc##pre *const *cls = p; return (*cls ? (*cls)->name : 0); } \
+ static PyObject *pre##_valfn(const void *p) \
+ { gc##pre *const*cls = p; return (gc##pre##_pywrap(*cls)); }
+CLASS_TABLES(TABLE_FNS)
+
+void algorithms_pyinsert(PyObject *mod)
+{
+ PyObject *d;
+ INSERT("KeySZ", keysz_pytype);
+ INSERT("KeySZAny", keyszany_pytype);
+ INSERT("KeySZRange", keyszrange_pytype);
+ INSERT("KeySZSet", keyszset_pytype);
+ INSERT("GCCipher", gccipher_pytype);
+ INSERT("GCipher", gcipher_pytype);
+ INSERT("gcciphers", make_algtab(gciphertab, sizeof(gccipher *),
+ cipher_namefn, cipher_valfn));
+ INSERT("GCAEAD", gcaead_pytype);
+ INSERT("GAEKey", gaeadkey_pytype);
+ INSERT("GAEAADClass", gcaeadaad_pytype);
+ INSERT("GAEAAD", gaeadaad_pytype);
+ INSERT("GAEEncClass", gcaeadenc_pytype);
+ INSERT("GAEEnc", gaeadenc_pytype);
+ INSERT("GAEDecClass", gcaeaddec_pytype);
+ INSERT("GAEDec", gaeaddec_pytype);
+ INSERT("gcaeads", make_algtab(gaeadtab, sizeof(gcaead *),
+ aead_namefn, aead_valfn));
+ INSERT("GCHash", gchash_pytype);
+ INSERT("GHash", ghash_pytype);
+ d = make_algtab(ghashtab, sizeof(gchash *), hash_namefn, hash_valfn);
+ INSERT("gchashes", d);
+ sha_pyobj = PyMapping_GetItemString(d, "sha"); Py_INCREF(sha_pyobj);
+ has160_pyobj = PyMapping_GetItemString(d, "has160"); Py_INCREF(has160_pyobj);
+ INSERT("GCMAC", gcmac_pytype);
+ INSERT("GMAC", gmac_pytype);
+ INSERT("GMACHash", gmhash_pytype);
+ INSERT("gcmacs", make_algtab(gmactab, sizeof(gcmac *),
+ mac_namefn, mac_valfn));
+ INSERT("_Poly1305Class", poly1305cls_pytype);
+ INSERT("poly1305", poly1305key_pytype);
+ INSERT("Poly1305Hash", poly1305hash_pytype);
+ INSERT("Keccak1600", kxvik_pytype);
+ INSERT("Shake", shake_pytype);
+ INSERT("Shake128", shake128_pytype);
+ INSERT("Shake256", shake256_pytype);
+ INSERT("KMAC", kmac_pytype);
+ INSERT("KMAC128", kmac128_pytype);
+ INSERT("KMAC256", kmac256_pytype);
+ INSERT("GCPRP", gcprp_pytype);
+ INSERT("GPRP", gprp_pytype);
+ INSERT("gcprps", make_algtab(gprptab, sizeof(gcprp *),
+ prp_namefn, prp_valfn));
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+## -*-python-*-
+
+def cross(*seq):
+ if not len(seq):
+ return [(),]
+ x = seq[0]
+ if type(x) is not tuple and type(x) is not list:
+ x = x,
+ r = []
+ for i in x:
+ for j in cross(*seq[1:]):
+ r.append((i,) + j)
+ return r
+
+prps = '''
+des desx des3 mars
+idea safer safersk
+blowfish twofish
+tea xtea
+rc2 rc5
+skipjack
+cast128 cast256
+square rijndael rijndael192 rijndael256
+serpent noekeon
+'''.split()
+pmodes = '''
+ecb cbc cfb ofb counter
+cmac pmac1
+ccm eax gcm ocb1 ocb3
+'''.split()
+streamciphers = '''
+rc4 seal
+'''.split()
+latindances = '''
+salsa20 salsa20/12 salsa20/8
+salsa20-ietf salsa20/12-ietf salsa20/8-ietf
+xsalsa20 xsalsa20/12 xsalsa20/8
+chacha20 chacha12 chacha8
+chacha20-ietf chacha12-ietf chacha8-ietf
+xchacha20 xchacha12 xchacha8
+'''.split()
+streamciphers += map(lambda s: s.replace('/', ''), latindances)
+hashes = '''
+md2 md4 md5 tiger has160
+sha sha224 sha256 sha512/224 sha512/256 sha384 sha512
+rmd128 rmd160 rmd256 rmd320
+whirlpool whirlpool256
+sha3-224 sha3-256 sha3-384 sha3-512
+'''.split()
+hmodes = '''
+mgf hmac
+'''.split()
+
+print('/* algorithms.h [generated] */')
+print('')
+
+for i in prps:
+ print('#include <catacomb/%s.h>' % i.replace('/', '-'))
+ for j in pmodes:
+ print('#include <catacomb/%s-%s.h>' % (i.replace('/', '-'), j))
+for i in streamciphers:
+ print('#include <catacomb/%s.h>' % i.replace('/', '-'))
+print('')
+for i in hashes:
+ print('#include <catacomb/%s.h>' % i.replace('/', '-'))
+ for j in hmodes:
+ print('#include <catacomb/%s-%s.h>' % (i.replace('/', '-'), j))
+print('')
+
+print('#define PRPS(_) \\')
+for i in prps:
+ print('\t_(%s, %s) \\' % (i.upper(), i))
+print('\t/* end */')
+print('')
+
+print('#define RNGS(_) \\')
+for i in (cross(prps, ['ofb', 'counter'])):
+ print(('\t_("%(prim)s-%(mode)s", %(primid)s_keysz, ' +
+ '%(primid)s_%(mode)srand, RNG_PLAIN, 0) \\') %
+ {'prim': i[0], 'mode': i[1],
+ 'primid': i[0].replace('-', '_').replace('/', '_')})
+for i in (cross(hashes, 'mgf')):
+ print(('\t_("%(prim)s-%(mode)s", %(primid)s_%(mode)skeysz, ' +
+ '%(primid)s_%(mode)srand, RNG_PLAIN, 0) \\') %
+ {'prim': i[0], 'mode': i[1],
+ 'primid': i[0].replace('-', '_').replace('/', '_')})
+print('\t_("rc4", rc4_keysz, rc4_rand, 0, 0) \\')
+print('\t_("seal", seal_keysz, seal_rand, RNG_SEAL, 0) \\')
+for i in latindances:
+ for r in ['salsa20', 'xsalsa20', 'chacha', 'xchacha']:
+ if i.startswith(r):
+ root = r
+ break
+ else:
+ raise ValueError('failed to find root name for %s' % i)
+ if i.endswith('-ietf'): root += '_ietf'
+ print(('\t_("%(name)s", %(root)s_keysz, %(id)s_rand, ' +
+ 'RNG_LATIN, %(ROOT)s_NONCESZ) \\') % \
+ {'name': i, 'id': i.replace('/', '').replace('-', '_'),
+ 'root': root, 'ROOT': root.upper()})
+for i in [128, 256]:
+ print(('\t_("shake%(w)d", shake%(w)d_keysz, cshake%(w)d_rand, ' +
+ 'RNG_SHAKE, 0) \\') % \
+ {'w': i})
+ print(('\t_("kmac%(w)d", kmac%(w)d_keysz, kmac%(w)d_rand, ' +
+ 'RNG_KMAC, 0) \\') % \
+ {'w': i})
+print('\t/* end */')
+print('')
--- /dev/null
+/* -*-c-*-
+ *
+ * Reading and writing buffers of stuff
+ *
+ * (c) 2005 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Read buffers ------------------------------------------------------*/
+
+PyTypeObject *rbuf_pytype;
+
+static PyObject *rbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ void *q;
+ buf_pyobj *me = 0;
+ static const char *const kwlist[] = { "data", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &in))
+ goto end;
+ q = xmalloc(in.sz);
+ memcpy(q, in.p, in.sz);
+ me = (buf_pyobj *)ty->tp_alloc(ty, 0);
+ me->sub = 0; me->lk = 0;
+ buf_init(&me->b, q, in.sz);
+end:
+ return ((PyObject *)me);
+}
+
+static void buf_pydealloc(PyObject *me)
+{
+ assert(!BUF_LK(me));
+ if (BUF_SUB(me)) Py_DECREF(BUF_SUB(me));
+ else xfree(BBASE(BUF_B(me)));
+ FREEOBJ(me);
+}
+
+#ifdef PY3
+ static int rbuf_pygetbuf(PyObject *me, Py_buffer *vw, int f)
+ {
+ buf *b = BUF_B(me);
+ return (PyBuffer_FillInfo(vw, me, BCUR(b), BLEFT(b), 1, f));
+ }
+#else
+ static Py_ssize_t rbuf_pysegcount(PyObject *me, Py_ssize_t *nn)
+ { if (nn) *nn = BSZ(BUF_B(me)); return (1); }
+ static Py_ssize_t rbuf_pyreadbuf(PyObject *me, Py_ssize_t seg, void **q)
+ { assert(seg == 0); *q = BCUR(BUF_B(me)); return (BLEFT(BUF_B(me))); }
+#endif
+
+static PyObject *rbmeth_skip(PyObject *me, PyObject *arg)
+{
+ size_t n;
+
+ if (!PyArg_ParseTuple(arg, "O&:skip", convszt, &n)) goto end;
+ if (!buf_get(BUF_B(me), n)) BUFERR("buffer exhausted");
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *rbmeth_get(PyObject *me, PyObject *arg)
+{
+ void *p;
+ size_t n;
+
+ if (!PyArg_ParseTuple(arg, "O&:get", convszt, &n)) goto end;
+ if ((p = buf_get(BUF_B(me), n)) == 0) BUFERR("buffer exhausted");
+ return (bytestring_pywrap(p, n));
+end:
+ return (0);
+}
+
+#define RBMETH_GETU_(n, W, w) \
+ static PyObject *rbmeth_getu##w(PyObject *me) \
+ { \
+ uint##n x; \
+ if (buf_getu##w(BUF_B(me), &x)) BUFERR("buffer exhausted"); \
+ if (MASK##W <= ULONG_MAX) return (getulong(x)); \
+ else { kludge64 y; ASSIGN64(y, x); return (getk64(y)); } \
+ end: \
+ return (0); \
+ }
+DOUINTCONV(RBMETH_GETU_)
+
+#define RBMETH_GETBLK_(n, W, w) \
+ static PyObject *rbmeth_getblk##w(PyObject *me) \
+ { \
+ size_t sz; \
+ char *q; \
+ if ((q = buf_getmem##w(BUF_B(me), &sz)) == 0) \
+ BUFERR("buffer exhausted"); \
+ return (bytestring_pywrap(q, sz)); \
+ end: \
+ return (0); \
+ }
+BUF_DOSUFFIXES(RBMETH_GETBLK_)
+
+#define RBMETH_GETBUF_(n, W, w) \
+ static PyObject *rbmeth_getbuf##w(PyObject *me) \
+ { \
+ buf_pyobj *b; \
+ buf bb; \
+ if (buf_getbuf##w(BUF_B(me), &bb)) BUFERR("buffer exhausted"); \
+ b = PyObject_NEW(buf_pyobj, rbuf_pytype); \
+ b->b = bb; \
+ b->sub = me; \
+ b->lk = 0; \
+ Py_INCREF(me); \
+ return ((PyObject *)b); \
+ end: \
+ return (0); \
+ }
+BUF_DOSUFFIXES(RBMETH_GETBUF_)
+
+static PyObject *rbmeth_getmp(PyObject *me)
+{
+ mp *x;
+ if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR("buffer exhausted");
+ return (mp_pywrap(x));
+end:
+ return (0);
+}
+
+static PyObject *rbmeth_getgf(PyObject *me)
+{
+ mp *x;
+ if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR("buffer exhausted");
+ return (gf_pywrap(x));
+end:
+ return (0);
+}
+
+static PyObject *rbmeth_getecpt(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *cobj = Py_None;
+ static const char *const kwlist[] = { "curve", 0 };
+ ec pt = EC_INIT;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", KWLIST, &cobj))
+ goto end;
+ if (cobj == Py_None) cobj = (PyObject *)ecpt_pytype;
+ if (!PyType_Check(cobj) ||
+ !PyType_IsSubtype((PyTypeObject *)cobj, ecpt_pytype))
+ TYERR("expected elliptic curve type");
+ if (buf_getec(BUF_B(me), &pt)) BUFERR("buffer exhausted");
+ return (ecpt_pywrapout(cobj, &pt));
+end:
+ return (0);
+}
+
+static PyObject *rbmeth_getecptraw(PyObject *me, PyObject *arg)
+{
+ PyObject *cobj;
+ ec pt = EC_INIT;
+ PyObject *rc = 0;
+ if (!PyArg_ParseTuple(arg, "O!:getecptraw", eccurve_pytype, &cobj))
+ goto end;
+ if (ec_getraw(ECCURVE_C(cobj), BUF_B(me), &pt)) BUFERR("buffer exhausted");
+ rc = ecpt_pywrapout(cobj, &pt);
+end:
+ return (rc);
+}
+
+static PyObject *rbmeth_os2ecp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *cobj;
+ ec pt = EC_INIT;
+ unsigned f = EC_XONLY | EC_LSB | EC_SORT | EC_EXPLY;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "curve", "flags", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:os2ecp", KWLIST,
+ eccurve_pytype, &cobj, convuint, &f))
+ goto end;
+ if (ec_os2ecp(ECCURVE_C(cobj), f, BUF_B(me), &pt)) VALERR("bad point");
+ rc = ecpt_pywrapout(cobj, &pt);
+end:
+ return (rc);
+}
+
+static PyObject *rbmeth_getge(PyObject *me, PyObject *arg)
+{
+ PyObject *gobj;
+ ge *x = 0;
+ if (!PyArg_ParseTuple(arg, "O!:getge", group_pytype, &gobj)) goto end;
+ x = G_CREATE(GROUP_G(gobj));
+ if (G_FROMBUF(GROUP_G(gobj), BUF_B(me), x)) BUFERR("buffer exhausted");
+ return (ge_pywrap(gobj, x));
+end:
+ if (x) G_DESTROY(GROUP_G(gobj), x);
+ return (0);
+}
+
+static PyObject *rbmeth_getgeraw(PyObject *me, PyObject *arg)
+{
+ PyObject *gobj;
+ ge *x = 0;
+ if (!PyArg_ParseTuple(arg, "O!:getgeraw", group_pytype, &gobj)) goto end;
+ x = G_CREATE(GROUP_G(gobj));
+ if (G_FROMRAW(GROUP_G(gobj), BUF_B(me), x)) BUFERR("buffer exhausted");
+ return (ge_pywrap(gobj, x));
+end:
+ if (x) G_DESTROY(GROUP_G(gobj), x);
+ return (0);
+}
+
+static PyObject *rbget_size(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(BSZ(BUF_B(me)))); }
+static PyObject *rbget_left(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(BLEFT(BUF_B(me)))); }
+static PyObject *rbget_endp(PyObject *me, void *hunoz)
+ { return (getbool(!BLEFT(BUF_B(me)))); }
+static PyObject *rbget_offset(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(BLEN(BUF_B(me)))); }
+static int rbset_offset(PyObject *me, PyObject *x, void *hunoz)
+{
+ size_t n;
+ if (!x) NIERR("__del__");
+ if (!convszt(x, &n)) goto end;
+ if (n > BSZ(BUF_B(me))) VALERR("out of range");
+ BCUR(BUF_B(me)) = BBASE(BUF_B(me)) + n;
+ return (0);
+end:
+ return (-1);
+}
+
+static const PyGetSetDef rbuf_pygetset[] = {
+#define GETSETNAME(op, name) rb##op##_##name
+ GET (size, "RBUF.size -> SIZE")
+ GET (left, "RBUF.left -> REMAINDER")
+ GET (endp, "RBUF.endp -> BOOL")
+ GETSET(offset, "RBUF.offset -> OFFSET")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef rbuf_pymethods[] = {
+#define METHNAME(func) rbmeth_##func
+ METH (skip, "RBUF.skip(N)")
+ METH (get, "RBUF.get(N) -> BYTES")
+#define RBMETH_DECL_GETU_(n, W, w) \
+ NAMETH(getu##w, "RBUF.getu" #w "() -> INT")
+ DOUINTCONV(RBMETH_DECL_GETU_)
+#define RBMETH_DECL_GETBLK_(n, W, w) \
+ NAMETH(getblk##w, "RBUF.getblk" #w "() -> BYTES")
+ BUF_DOSUFFIXES(RBMETH_DECL_GETBLK_)
+#define RBMETH_DECL_GETBUF_(n, W, w) \
+ NAMETH(getbuf##w, "RBUF.getbuf" #w "() -> RBUF'")
+ BUF_DOSUFFIXES(RBMETH_DECL_GETBUF_)
+ NAMETH(getmp, "RBUF.getmp() -> X")
+ NAMETH(getgf, "RBUF.getgf() -> X")
+ KWMETH(getecpt, "RBUF.getecpt([curve = None]) -> P")
+ METH (getecptraw, "RBUF.getecptraw(CURVE) -> P")
+ KWMETH(os2ecp, "RBUF.os2ecp(CURVE, [flags = ...]) -> P")
+ METH (getge, "RBUF.getge(GROUP) -> X")
+ METH (getgeraw, "RBUF.getgeraw(GROUP) -> X")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyBufferProcs rbuf_pybuffer = {
+#ifdef PY3
+ rbuf_pygetbuf, /* @bf_getbuffer@ */
+ 0, /* @bf_releasebuffer@ */
+#else
+ rbuf_pyreadbuf, /* @bf_getreadbuffer@ */
+ 0, /* @bf_getwritebuffer@ */
+ rbuf_pysegcount, /* @bf_getsegcount@ */
+ 0 /* @bf_getcharbuffer@ */
+#endif
+};
+
+static const PyTypeObject rbuf_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ReadBuffer", /* @tp_name@ */
+ sizeof(buf_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ buf_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ PYBUFFER(rbuf), /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ReadBuffer(STR): a read buffer.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(rbuf), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(rbuf), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ rbuf_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Write buffers -----------------------------------------------------*/
+
+PyTypeObject *wbuf_pytype;
+
+int ensurebuf(PyObject *me, size_t n)
+{
+ buf *b = BUF_B(me);
+ size_t nn = BSZ(b);
+ octet *p;
+ size_t want = BLEN(b) + n;
+
+ if (BLEFT(b) >= n)
+ return (0);
+ else if (BUF_LK(me))
+ BUFERR("buffer locked");
+ else {
+ while (nn < want) nn <<= 1;
+ p = xrealloc(BBASE(b), nn, BSZ(b));
+ BCUR(b) = p + BLEN(b);
+ BLIM(b) = p + nn;
+ BBASE(b) = p;
+ return (0);
+ }
+end:
+ return (-1);
+}
+
+static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ char *p;
+ size_t n = 64;
+ buf_pyobj *me = 0;
+ static const char *const kwlist[] = { "size", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
+ convszt, &n))
+ goto end;
+ me = (buf_pyobj *)ty->tp_alloc(ty, 0);
+ p = xmalloc(n);
+ me->sub = 0; me->lk = 0;
+ buf_init(&me->b, p, n);
+end:
+ return ((PyObject *)me);
+}
+
+#ifdef PY3
+ static int wbuf_pygetbuf(PyObject *me, Py_buffer *vw, int f)
+ {
+ buf *b = BUF_B(me);
+ if (PyBuffer_FillInfo(vw, me, BBASE(b), BLEN(b), 0, f)) return (-1);
+ BUF_LK(me)++; return (0);
+ }
+ static void wbuf_pyrlsbuf(PyObject *me, Py_buffer *vw)
+ { BUF_LK(me)--; }
+#else
+ static Py_ssize_t wbuf_pysegcount(PyObject *me, Py_ssize_t *nn)
+ { if (nn) *nn = BLEN(BUF_B(me)); return (1); }
+ static Py_ssize_t wbuf_pyreadbuf(PyObject *me, Py_ssize_t seg, void **q)
+ { assert(seg == 0); *q = BBASE(BUF_B(me)); return (BLEN(BUF_B(me))); }
+#endif
+
+static PyObject *wbmeth_zero(PyObject *me, PyObject *arg)
+{
+ void *p;
+ size_t n;
+ if (!PyArg_ParseTuple(arg, "O&:zero", convszt, &n)) return (0);
+ if (ensurebuf(me, n)) return (0);
+ p = buf_get(BUF_B(me), n); assert(p && BOK(BUF_B(me)));
+ memset(p, 0, n);
+ RETURN_ME;
+}
+
+static PyObject *wbmeth_put(PyObject *me, PyObject *arg)
+{
+ struct bin in;
+ if (!PyArg_ParseTuple(arg, "O&:put", convbin, &in)) return (0);
+ if (ensurebuf(me, in.sz)) return (0);
+ buf_put(BUF_B(me), in.p, in.sz); assert(BOK(BUF_B(me)));
+ RETURN_ME;
+}
+
+#define WBMETH_PUTU_(n, W, w) \
+ static PyObject *wbmeth_putu##w(PyObject *me, PyObject *arg) \
+ { \
+ uint##n i; \
+ if (!PyArg_ParseTuple(arg, "O&:putu" #w, convu##n, &i)) return (0); \
+ if (ensurebuf(me, SZ_##n)) return (0); \
+ buf_putu##w(BUF_B(me), i); assert(BOK(BUF_B(me))); \
+ RETURN_ME; \
+ }
+DOUINTCONV(WBMETH_PUTU_)
+
+#define MASKz 0
+#define SZ_z 1
+#define WBMETH_PUTBLK_(n, W, w) \
+ static PyObject *wbmeth_putblk##w(PyObject *me, PyObject *arg) \
+ { \
+ struct bin in; \
+ if (!PyArg_ParseTuple(arg, "O&:putblk" #w, convbin, &in)) goto end; \
+ if (MASK##W && in.sz > MASK##W) VALERR("too large"); \
+ if (ensurebuf(me, in.sz + SZ_##n)) return (0); \
+ buf_putmem##w(BUF_B(me), in.p, in.sz); assert(BOK(BUF_B(me))); \
+ RETURN_ME; \
+ end: \
+ return (0); \
+ }
+BUF_DOSUFFIXES(WBMETH_PUTBLK_)
+
+static PyObject *wbmeth_putmp(PyObject *me, PyObject *arg)
+{
+ mp *x = 0;
+ if (!PyArg_ParseTuple(arg, "O&:putmp", convmp, &x)) return (0);
+ if (ensurebuf(me, mp_octets(x) + 2)) return (0);
+ buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
+ RETURN_ME;
+}
+
+static PyObject *wbmeth_putgf(PyObject *me, PyObject *arg)
+{
+ mp *x = 0;
+ if (!PyArg_ParseTuple(arg, "O&:putgf", convgf, &x)) return (0);
+ if (ensurebuf(me, mp_octets(x) + 2)) return (0);
+ buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
+ MP_DROP(x);
+ RETURN_ME;
+}
+
+static PyObject *wbmeth_putecpt(PyObject *me, PyObject *arg)
+{
+ ec pt = EC_INIT;
+ if (!PyArg_ParseTuple(arg, "O&:putecpt", convecpt, &pt)) return (0);
+ if (ensurebuf(me, EC_ATINF(&pt) ? 2 :
+ 6 + mp_octets(pt.x) + mp_octets(pt.y)))
+ return (0);
+ buf_putec(BUF_B(me), &pt); assert(BOK(BUF_B(me)));
+ EC_DESTROY(&pt);
+ RETURN_ME;
+}
+
+static PyObject *wbmeth_putecptraw(PyObject *me, PyObject *arg)
+{
+ PyObject *ptobj;
+ ec_curve *cc;
+ ec pt = EC_INIT;
+ if (!PyArg_ParseTuple(arg, "O!:putecptraw", ecptcurve_pytype, &ptobj))
+ return (0);
+ cc = ECPT_C(ptobj);
+ EC_OUT(cc, &pt, ECPT_P(ptobj));
+ if (ensurebuf(me, 2*cc->f->noctets + 1)) return (0);
+ ec_putraw(cc, BUF_B(me), &pt); assert(BOK(BUF_B(me)));
+ EC_DESTROY(&pt);
+ RETURN_ME;
+}
+
+static PyObject *wbmeth_ec2osp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyTypeObject *ptobj;
+ ec_curve *cc;
+ ec pt = EC_INIT;
+ unsigned f = EC_EXPLY;
+ static const char *const kwlist[] = { "point", "flags", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:os2ecp", KWLIST,
+ ecptcurve_pytype, &ptobj, convuint, &f))
+ goto end;
+ cc = ECPT_C(ptobj);
+ EC_OUT(cc, &pt, ECPT_P(ptobj));
+ if (ensurebuf(me, 2*cc->f->noctets + 1)) return (0);
+ if (ec_ec2osp(cc, f, BUF_B(me), &pt)) VALERR("invalid flags");
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *wbmeth_putge(PyObject *me, PyObject *arg)
+{
+ PyObject *geobj;
+ if (!PyArg_ParseTuple(arg, "O!:putge", ge_pytype, &geobj)) return (0);
+ if (ensurebuf(me, GE_G(geobj)->noctets)) return (0);
+ G_TOBUF(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
+ RETURN_ME;
+}
+
+static PyObject *wbmeth_putgeraw(PyObject *me, PyObject *arg)
+{
+ PyObject *geobj;
+ if (!PyArg_ParseTuple(arg, "O!:putgeraw", ge_pytype, &geobj)) return (0);
+ if (ensurebuf(me, GE_G(geobj)->noctets)) return (0);
+ G_TORAW(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
+ RETURN_ME;
+}
+
+static PyObject *wbget_size(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(BLEN(BUF_B(me)))); }
+
+static PyObject *wbget_contents(PyObject *me, void *hunoz)
+ { return (bytestring_pywrap(BBASE(BUF_B(me)), BLEN(BUF_B(me)))); }
+
+static const PyGetSetDef wbuf_pygetset[] = {
+#define GETSETNAME(op, name) wb##op##_##name
+ GET (size, "WBUF.size -> SIZE")
+ GET (contents, "WBUF.contents -> STR")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef wbuf_pymethods[] = {
+#define METHNAME(func) wbmeth_##func
+ METH (zero, "WBUF.zero(N)")
+ METH (put, "WBUF.put(BYTES)")
+#define WBMETH_DECL_PUTU_(n, W, w) \
+ METH(putu##w, "WBUF.putu" #w "(INT)")
+ DOUINTCONV(WBMETH_DECL_PUTU_)
+#define WBMETH_DECL_PUTBLK_(n, W, w) \
+ METH(putblk##w, "WBUF.putblk" #w "(BYTES)")
+ BUF_DOSUFFIXES(WBMETH_DECL_PUTBLK_)
+ METH (putmp, "WBUF.putmp(X)")
+ METH (putgf, "WBUF.putgf(X)")
+ METH (putecpt, "WBUF.putecpt(P)")
+ METH (putecptraw, "WBUF.putecptraw(P)")
+ KWMETH(ec2osp, "WBUF.ec2osp(P, [flags = EC_EXPLY])")
+ METH (putge, "WBUF.putge(X)")
+ METH (putgeraw, "WBUF.putgeraw(X)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyBufferProcs wbuf_pybuffer = {
+#ifdef PY3
+ wbuf_pygetbuf, /* @bf_getbuffer@ */
+ wbuf_pyrlsbuf /* @bf_releasebuffer@ */
+#else
+ wbuf_pyreadbuf, /* @bf_getreadbuffer@ */
+ 0, /* @bf_getwritebuffer@ */
+ wbuf_pysegcount, /* @bf_getsegcount@ */
+ 0 /* @bf_getcharbuffer@ */
+#endif
+};
+
+static const PyTypeObject wbuf_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "WriteBuffer", /* @tp_name@ */
+ sizeof(buf_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ buf_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ PYBUFFER(wbuf), /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "WriteBuffer([size = ?]): a write buffer.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(wbuf), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(wbuf), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ wbuf_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Initialization ----------------------------------------------------*/
+
+PyObject *buferr;
+
+void buffer_pyinit(void)
+{
+ INITTYPE(rbuf, root);
+ INITTYPE(wbuf, root);
+}
+
+void buffer_pyinsert(PyObject *mod)
+{
+ INSEXC("BufferError", buferr, PyExc_Exception, 0);
+ INSERT("ReadBuffer", rbuf_pytype);
+ INSERT("WriteBuffer", wbuf_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Byte strings
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+static PyTypeObject *bytestring_pytype;
+
+static PyObject *empty, *bytev[256];
+
+static PyObject *allocate(PyTypeObject *ty, size_t n)
+{
+ BINOBJ *x;
+ x = (BINOBJ *)ty->tp_alloc(ty, n);
+ x->ob_sval[n] = 0;
+#if defined(CACHE_HASH) || PY_VERSION_HEX >= 0x02030000
+ x->ob_shash = -1;
+#endif
+#ifdef PY2
+ x->ob_sstate = SSTATE_NOT_INTERNED;
+#endif
+ return ((PyObject *)x);
+}
+
+static PyObject *dowrap(PyTypeObject *ty, const void *p, size_t n)
+{
+ PyObject *x;
+ int ch;
+
+ if (p && ty == bytestring_pytype) {
+ if (!n) {
+ if (!empty) empty = allocate(ty, 0);
+ Py_INCREF(empty); return (empty);
+ } else if (n == 1 && (ch = *(unsigned char *)p) < sizeof(bytev)) {
+ if (!bytev[ch])
+ { bytev[ch] = allocate(ty, 1); *BIN_PTR(bytev[ch]) = ch; }
+ Py_INCREF(bytev[ch]); return (bytev[ch]);
+ }
+ }
+
+ x = allocate(ty, n);
+ if (p) memcpy(BIN_PTR(x), p, n);
+ return (x);
+}
+
+PyObject *bytestring_pywrap(const void *p, size_t n)
+ { return (dowrap(bytestring_pytype, p, n)); }
+
+PyObject *bytestring_pywrapbuf(buf *b)
+ { return (dowrap(bytestring_pytype, BCUR(b), BLEFT(b))); }
+
+static PyObject *bytestring_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ static const char *const kwlist[] = { "data", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &in))
+ return (0);
+ return (dowrap(ty, in.p, in.sz));
+}
+
+static PyObject *meth_ctstreq(PyObject *me, PyObject *arg)
+{
+ struct bin s0, s1;
+ if (!PyArg_ParseTuple(arg, "O&O&:ctstreq", convbin, &s0 , convbin, &s1))
+ goto end;
+ if (s0.sz == s1.sz && ct_memeq(s0.p, s1.p, s0.sz)) RETURN_TRUE;
+ else RETURN_FALSE;
+end:
+ return (0);
+}
+
+static PyObject *bymeth_zero(PyObject *me, PyObject *arg)
+{
+ size_t sz;
+ PyObject *rc = 0;
+ if (!PyArg_ParseTuple(arg, "O&:zero", convszt, &sz)) goto end;
+ rc = bytestring_pywrap(0, sz);
+ memset(BIN_PTR(rc), 0, sz);
+end:
+ return (rc);
+}
+
+static PyObject *bytestring_pyrichcompare(PyObject *me,
+ PyObject *you, int op)
+{
+ struct bin s0, s1;
+ int b;
+ Py_ssize_t minlen;
+
+ s0.p = BIN_PTR(me); s0.sz = BIN_LEN(me);
+ if (!convbin(you, &s1)) { PyErr_Clear(); RETURN_NOTIMPL; }
+
+ switch (op) {
+ case Py_EQ:
+ b = s0.sz == s1.sz && ct_memeq(s0.p, s1.p, s1.sz);
+ break;
+ case Py_NE:
+ b = s0.sz != s1.sz || !ct_memeq(s0.p, s1.p, s1.sz);
+ break;
+ default:
+ minlen = s0.sz < s1.sz ? s0.sz : s1.sz;
+ b = memcmp(s0.p, s1.p, minlen);
+ if (!b) b = s0.sz < s1.sz ? -1 : s0.sz > s1.sz ? +1 : 0;
+ switch (op) {
+ case Py_LT: b = b < 0; break;
+ case Py_LE: b = b <= 0; break;
+ case Py_GE: b = b >= 0; break;
+ case Py_GT: b = b > 0; break;
+ default: abort();
+ }
+ }
+ if (b) RETURN_TRUE;
+ else RETURN_FALSE;
+}
+
+static PyObject *bytestring_pyconcat(PyObject *x, PyObject *y)
+{
+ struct bin xx, yy;
+ PyObject *z = 0; char *zp; size_t zsz;
+
+ if (!convbin(x, &xx) || !convbin(y, &yy)) goto end;
+ zsz = (size_t)xx.sz + (size_t)yy.sz;
+ if (xx.sz < 0 || yy.sz < 0 || zsz < xx.sz) VALERR("too long");
+ z = bytestring_pywrap(0, zsz); zp = BIN_PTR(z);
+ memcpy(zp, xx.p, xx.sz); memcpy(zp + xx.sz, yy.p, yy.sz);
+end:
+ return (z);
+}
+
+static PyObject *bytestring_pyrepeat(PyObject *me, Py_ssize_t n)
+{
+ const unsigned char *xp; size_t xsz;
+ PyObject *z = 0; char *zp; size_t zsz;
+
+ xp = (const unsigned char *)BIN_PTR(me);
+ xsz = BIN_LEN(me);
+ if (n < 0 || (n && xsz >= (size_t)-1/n)) VALERR("too long");
+ zsz = n*xsz; z = bytestring_pywrap(0, zsz); zp = BIN_PTR(z);
+ if (xsz == 1) memset(zp, *xp, zsz);
+ else while (zsz) { memcpy(zp, xp, xsz); zp += xsz; zsz -= xsz; }
+end:
+ return (z);
+}
+
+static PyObject *bytestring_pyitem(PyObject *me, Py_ssize_t i)
+{
+ PyObject *rc = 0;
+
+ if (i < 0 || i >= BIN_LEN(me)) IXERR("out of range");
+#ifdef PY3
+ rc = getulong(BIN_PTR(me)[i]&0xff);
+#else
+ rc = bytestring_pywrap(BIN_PTR(me) + i, 1);
+#endif
+end:
+ return (rc);
+}
+
+static PyObject *bytestring_pyslice(PyObject *me, Py_ssize_t i, Py_ssize_t j)
+{
+ PyObject *rc = 0;
+ size_t n = BIN_LEN(me);
+
+ if (i < 0) i = 0;
+ if (j < 0) j = 0;
+ else if (j > n) j = n;
+ if (j < i) i = j = 0;
+ if (i == 0 && j == n && Py_TYPE(me) == bytestring_pytype)
+ { Py_INCREF(me); rc = me; goto end; }
+ rc = bytestring_pywrap(BIN_PTR(me) + i, j - i);
+end:
+ return (rc);
+}
+
+static PyObject *bytestring_pysubscript(PyObject *me, PyObject *ix)
+{
+ Py_ssize_t i, j, k, n;
+ const unsigned char *p;
+ unsigned char *q;
+ PyObject *rc = 0;
+
+ if (PyIndex_Check(ix)) {
+ i = PyNumber_AsSsize_t(ix, PyExc_IndexError);
+ if (i == -1 && PyErr_Occurred()) return (0);
+ if (i < 0) i += BIN_LEN(me);
+ rc = bytestring_pyitem(me, i);
+ } else if (PySlice_Check(ix)) {
+ if (PySlice_GetIndicesEx(PY23((PySliceObject *), NOTHING)ix,
+ BIN_LEN(me), &i, &j, &k, &n))
+ return (0);
+ if (k == 1) return bytestring_pyslice(me, i, j);
+ rc = bytestring_pywrap(0, n);
+ p = (unsigned char *)BIN_PTR(me) + i;
+ q = (unsigned char *)BIN_PTR(rc);
+ while (n--) { *q++ = *p; p += k; }
+ } else
+ TYERR("wanted integer or slice");
+end:
+ return (rc);
+}
+
+#define BINOP(name, op) \
+ static PyObject *bytestring_py##name(PyObject *x, PyObject *y) { \
+ struct bin xx, yy; \
+ const unsigned char *xp, *yp; \
+ unsigned char *zp; \
+ int i; \
+ PyObject *rc = 0; \
+ if (!convbin(x, &xx) || !convbin(y, &yy)) goto end; \
+ if (xx.sz != yy.sz) VALERR("length mismatch"); \
+ rc = bytestring_pywrap(0, xx.sz); \
+ xp = xx.p; yp = yy.p; zp = (unsigned char *)BIN_PTR(rc); \
+ for (i = xx.sz; i > 0; i--) *zp++ = *xp++ op *yp++; \
+ end: \
+ return (rc); \
+ }
+BINOP(and, &)
+BINOP(or, |)
+BINOP(xor, ^)
+
+#define UNOP(name, op) \
+ static PyObject *bytestring_py##name(PyObject *x) { \
+ struct bin xx; \
+ const unsigned char *xp; \
+ unsigned char *zp; \
+ int i; \
+ PyObject *rc = 0; \
+ if (!convbin(x, &xx)) goto end; \
+ rc = bytestring_pywrap(0, xx.sz); \
+ xp = xx.p; zp = (unsigned char *)BIN_PTR(rc); \
+ for (i = xx.sz; i > 0; i--) *zp++ = op *xp++; \
+ end: \
+ return (rc); \
+ }
+UNOP(not, ~)
+
+static const PyMethodDef bytestring_pymethods[] = {
+#define METHNAME(name) bymeth_##name
+ SMTH (zero, "zero(N) -> 0000...00")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods bytestring_pynumber = {
+ 0, /* @nb_add@ */
+ 0, /* @nb_subtract@ */
+ 0, /* @nb_multiply@ */
+#ifdef PY2
+ 0, /* @nb_divide@ */
+#endif
+ 0, /* @nb_remainder@ */
+ 0, /* @nb_divmod@ */
+ 0, /* @nb_power@ */
+ 0, /* @nb_negative@ */
+ 0, /* @nb_positive@ */
+ 0, /* @nb_absolute@ */
+ 0, /* @nb_nonzero@ */
+ bytestring_pynot, /* @nb_invert@ */
+ 0, /* @nb_lshift@ */
+ 0, /* @nb_rshift@ */
+ bytestring_pyand, /* @nb_and@ */
+ bytestring_pyxor, /* @nb_xor@ */
+ bytestring_pyor, /* @nb_or@ */
+ 0, /* @nb_coerce@ */
+ 0, /* @nb_int@ */
+ 0, /* @nb_long@ */
+ 0, /* @nb_float@ */
+ 0, /* @nb_oct@ */
+ 0, /* @nb_hex@ */
+};
+
+static const PySequenceMethods bytestring_pysequence = {
+ 0, /* @sq_length@ */
+ bytestring_pyconcat, /* @sq_concat@ */
+ bytestring_pyrepeat, /* @sq_repeat@ */
+ bytestring_pyitem, /* @sq_item@ */
+ bytestring_pyslice, /* @sq_slice@ */
+ 0, /* @sq_ass_item@ */
+ 0, /* @sq_ass_slice@ */
+ 0, /* @sq_contains@ */
+ 0, /* @sq_inplace_concat@ */
+ 0, /* @sq_inplace_repeat@ */
+};
+
+static const PyMappingMethods bytestring_pymapping = {
+ 0, /* @mp_length@ */
+ bytestring_pysubscript, /* @mp_subscript@ */
+ 0, /* @mp_ass_subscript@ */
+};
+
+static const PyTypeObject bytestring_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ByteString", /* @tp_name@ */
+ 0, /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ PYNUMBER(bytestring), /* @tp_as_number@ */
+ PYSEQUENCE(bytestring), /* @tp_as_sequence@ */
+ PYMAPPING(bytestring), /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ByteString(STR): byte string class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ bytestring_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(bytestring), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ bytestring_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Initialization ----------------------------------------------------*/
+
+static const PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+ METH (ctstreq, "ctstreq(S, T) -> BOOL")
+#undef METHNAME
+ { 0 }
+};
+
+#define string_pytype &BIN_TYPE
+void bytestring_pyinit(void)
+{
+ INITTYPE(bytestring, string);
+ addmethods(methods);
+}
+
+void bytestring_pyinsert(PyObject *mod)
+{
+ INSERT("ByteString", bytestring_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Definitions for Catacomb bindings
+ *
+ * (c) 2004 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.
+ */
+
+#ifndef CATACOMB_PYTHON_H
+#define CATACOMB_PYTHON_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "pyke/pyke-mLib.h"
+
+PUBLIC_SYMBOLS;
+#include <longintrepr.h>
+
+#include <mLib/dstr.h>
+#include <mLib/macros.h>
+#include <mLib/quis.h>
+#include <mLib/unihash.h>
+
+#include <catacomb/buf.h>
+#include <catacomb/ct.h>
+
+#include <catacomb/grand.h>
+#include <catacomb/rand.h>
+#include <catacomb/noise.h>
+#include <catacomb/bbs.h>
+#include <catacomb/mprand.h>
+#include <catacomb/lcrand.h>
+#include <catacomb/fibrand.h>
+#include <catacomb/dsarand.h>
+#include <catacomb/sslprf.h>
+#include <catacomb/tlsprf.h>
+#include <catacomb/blkc.h>
+
+#include <catacomb/gcipher.h>
+#include <catacomb/gaead.h>
+#include <catacomb/ghash.h>
+#include <catacomb/gmac.h>
+#include <catacomb/md5.h>
+#include <catacomb/md5-hmac.h>
+#include <catacomb/poly1305.h>
+#include <catacomb/sha.h>
+#include <catacomb/sha-mgf.h>
+#include <catacomb/sha-hmac.h>
+#include <catacomb/keccak1600.h>
+#include <catacomb/sha3.h>
+
+#include <catacomb/mp.h>
+#include <catacomb/mpint.h>
+#include <catacomb/mpmul.h>
+#include <catacomb/mpcrt.h>
+#include <catacomb/mpmont.h>
+#include <catacomb/mpbarrett.h>
+#include <catacomb/mpreduce.h>
+#include <catacomb/mp-fibonacci.h>
+
+#include <catacomb/pgen.h>
+#include <catacomb/primeiter.h>
+#include <catacomb/pfilt.h>
+#include <catacomb/strongprime.h>
+#include <catacomb/limlee.h>
+#include <catacomb/dh.h>
+#include <catacomb/ptab.h>
+#include <catacomb/bintab.h>
+#include <catacomb/dsa.h>
+#include <catacomb/x25519.h>
+#include <catacomb/x448.h>
+#include <catacomb/ed25519.h>
+#include <catacomb/ed448.h>
+
+#include <catacomb/gf.h>
+#include <catacomb/gfreduce.h>
+#include <catacomb/gfn.h>
+
+#include <catacomb/field.h>
+#include <catacomb/field-guts.h>
+
+#include <catacomb/ec.h>
+#include <catacomb/ec-raw.h>
+#include <catacomb/ectab.h>
+
+#include <catacomb/group.h>
+#include <catacomb/group-guts.h>
+
+#include <catacomb/gdsa.h>
+#include <catacomb/gkcdsa.h>
+#include <catacomb/rsa.h>
+
+#include <catacomb/key.h>
+#include <catacomb/passphrase.h>
+#include <catacomb/pixie.h>
+
+#include <catacomb/share.h>
+#include <catacomb/gfshare.h>
+PRIVATE_SYMBOLS;
+
+/*----- Miscellaneous preliminaries ---------------------------------------*/
+
+/* Submodules. */
+#define MODULES(_) \
+ _(pyke_core) _(pyke_gmap) \
+ _(bytestring) _(buffer) \
+ _(rand) _(algorithms) _(pubkey) _(pgen) \
+ _(mp) _(field) _(ec) _(group) \
+ _(passphrase) _(share) _(key)
+MODULES(DECLARE_MODINIT)
+
+/* Exceptions. */
+#define PGENERR(err) do { pgenerr(err); goto end; } while (0)
+
+/* Conversions. */
+extern int convmpw(PyObject *, void *);
+
+/* Building tables of things. */
+extern PyObject *make_algtab(const void *tab, size_t esz,
+ const char *(*namefn)(const void *),
+ PyObject *(*valfn)(const void *));
+extern PyObject *make_grouptab(const void *tab, size_t esz,
+ const char *(*namefn)(const void *),
+ int (*ixfn)(const void *),
+ PyObject *(*valfn)(int));
+
+/* Common handling for simultaneous exponentiation. */
+extern PyObject *mexp_common(PyObject *, PyObject *, size_t,
+ PyObject *(*id)(PyObject *),
+ int (*fill)(void *, PyObject *,
+ PyObject *, PyObject *),
+ PyObject *(*exp)(PyObject *, void *, size_t),
+ void (*drop)(void *));
+
+/*----- Bytestrings -------------------------------------------------------*/
+
+PyObject *bytestring_pywrap(const void *, size_t);
+PyObject *bytestring_pywrapbuf(buf *);
+
+/*----- Buffers -----------------------------------------------------------*/
+
+typedef struct buf_pyobj {
+ PyObject_HEAD
+ buf b;
+ PyObject *sub;
+ unsigned lk;
+} buf_pyobj;
+
+extern PyTypeObject *rbuf_pytype, *wbuf_pytype;
+#define RBUF_PYCHECK(o) PyObject_TypeCheck((o), rbuf_pytype)
+#define WBUF_PYCHECK(o) PyObject_TypeCheck((o), wbuf_pytype)
+#define BUF_B(o) (&((buf_pyobj *)(o))->b)
+#define BUF_SUB(o) (((buf_pyobj *)(o))->sub)
+#define BUF_LK(o) (((buf_pyobj *)(o))->lk)
+
+extern PyObject *buferr;
+#define BUFERR(str) do { PyErr_SetString(buferr, str); goto end; } while (0)
+
+extern int ensurebuf(PyObject *, size_t);
+
+/*----- Multiprecision arithmetic -----------------------------------------*/
+
+typedef struct mp_pyobj {
+ PyObject_HEAD
+ mp *x;
+} mp_pyobj;
+
+extern PyTypeObject *mp_pytype;
+extern PyTypeObject *gf_pytype;
+#define MP_X(o) (((mp_pyobj *)(o))->x)
+#define MP_PYCHECK(o) PyObject_TypeCheck((o), mp_pytype)
+#define GF_PYCHECK(o) PyObject_TypeCheck((o), gf_pytype)
+
+extern mp *mp_frompylong(PyObject *);
+extern PyObject *mp_topylong(mp *);
+extern mp *tomp(PyObject *);
+extern mp *implicitmp(PyObject *);
+extern mp *getmp(PyObject *);
+extern int convmp(PyObject *, void *);
+extern mp *implicitgf(PyObject *);
+extern mp *getgf(PyObject *);
+extern int convgf(PyObject *, void *);
+extern PyObject *mp_pywrap(mp *);
+extern PyObject *gf_pywrap(mp *);
+extern Py_hash_t mphash(mp *);
+extern mp *mp_frompyobject(PyObject *, int);
+extern PyObject *mp_topystring(mp *, int,
+ const char *, const char *, const char *);
+extern int mp_tolong_checked(mp *, long *, int);
+
+/*----- Abstract fields ---------------------------------------------------*/
+
+typedef struct field_pyobj {
+ PyHeapTypeObject ty;
+ field *f;
+} field_pyobj;
+
+extern PyTypeObject *field_pytype;
+extern PyTypeObject *primefield_pytype;
+extern PyTypeObject *niceprimefield_pytype;
+extern PyTypeObject *binfield_pytype;
+extern PyTypeObject *binpolyfield_pytype;
+extern PyTypeObject *binnormfield_pytype;
+#define FIELD_PYCHECK(o) PyObject_TypeCheck((o), field_pytype)
+#define FIELD_F(o) (((field_pyobj *)(o))->f)
+extern PyObject *field_pywrap(field *);
+extern field *field_copy(field *);
+
+typedef struct fe_pyobj {
+ PyObject_HEAD
+ field *f;
+ mp *x;
+} fe_pyobj;
+
+extern PyTypeObject *fe_pytype;
+#define FE_PYCHECK(o) PyObject_TypeCheck((o), fe_pytype)
+#define FE_F(o) (((fe_pyobj *)(o))->f)
+#define FE_FOBJ(o) ((PyObject *)Py_TYPE(o))
+#define FE_X(o) (((fe_pyobj *)(o))->x)
+extern PyObject *fe_pywrap(PyObject *, mp *);
+
+/*----- Elliptic curves ---------------------------------------------------*/
+
+typedef struct eccurve_pyobj {
+ PyHeapTypeObject ty;
+ ec_curve *c;
+ PyObject *fobj;
+} eccurve_pyobj;
+
+extern PyTypeObject *eccurve_pytype;
+extern PyTypeObject *ecprimecurve_pytype;
+extern PyTypeObject *ecprimeprojcurve_pytype;
+extern PyTypeObject *ecbincurve_pytype;
+extern PyTypeObject *ecbinprojcurve_pytype;
+#define ECCURVE_PYCHECK(o) PyObject_TypeCheck((o), eccurve_pytype)
+#define ECCURVE_C(o) (((eccurve_pyobj *)(o))->c)
+#define ECCURVE_FOBJ(o) (((eccurve_pyobj *)(o))->fobj)
+extern PyObject *eccurve_pywrap(PyObject *, ec_curve *);
+extern ec_curve *eccurve_copy(ec_curve *);
+
+typedef struct ecpt_pyobj {
+ PyObject_HEAD
+ ec_curve *c;
+ ec p;
+} ecpt_pyobj;
+
+extern PyTypeObject *ecpt_pytype, *ecptcurve_pytype;
+#define ECPT_PYCHECK(o) PyObject_TypeCheck((o), ecpt_pytype)
+#define ECPTCURVE_PYCHECK(o) PyObject_TypeCheck((o), ecptcurve_pytype)
+#define ECPT_C(o) (((ecpt_pyobj *)(o))->c)
+#define ECPT_COBJ(o) ((PyObject *)Py_TYPE(o))
+#define ECPT_FOBJ(o) ECCURVE_FOBJ(ECPT_COBJ((o)))
+#define ECPT_P(o) (&((ecpt_pyobj *)(o))->p)
+extern PyObject *ecpt_pywrap(PyObject *, ec *);
+extern PyObject *ecpt_pywrapout(void *, ec *);
+extern int toecpt(ec_curve *, ec *, PyObject *);
+extern int getecpt(ec_curve *, ec *, PyObject *);
+extern void getecptout(ec *, PyObject *);
+extern int convecpt(PyObject *, void *);
+
+typedef struct ecinfo_pyobj {
+ PyObject_HEAD
+ ec_info ei;
+ PyObject *cobj;
+} ecinfo_pyobj;
+
+extern PyTypeObject *ecinfo_pytype;
+#define ECINFO_PYCHECK(o) PyObject_TypeCheck((o), ecinfo_pytype)
+#define ECINFO_EI(o) (&((ecinfo_pyobj *)(o))->ei)
+#define ECINFO_COBJ(o) (((ecinfo_pyobj *)(o))->cobj)
+extern void ecinfo_copy(ec_info *, const ec_info *);
+extern PyObject *ecinfo_pywrap(ec_info *);
+
+/*----- Cyclic groups -----------------------------------------------------*/
+
+typedef struct ge_pyobj {
+ PyObject_HEAD
+ ge *x;
+ group *g;
+} ge_pyobj;
+
+extern PyTypeObject *ge_pytype;
+#define GE_PYCHECK(o) PyObject_TypeCheck((o), ge_pytype)
+#define GE_X(o) (((ge_pyobj *)(o))->x)
+#define GE_G(o) (((ge_pyobj *)(o))->g)
+#define GE_GOBJ(o) (PyObject *)(Py_TYPE(o))
+extern PyObject *ge_pywrap(PyObject *, ge *);
+
+typedef struct group_pyobj {
+ PyHeapTypeObject ty;
+ group *g;
+} group_pyobj;
+
+extern PyTypeObject *group_pytype;
+#define GROUP_G(o) (((group_pyobj *)(o))->g)
+extern PyObject *group_pywrap(group *);
+extern group *group_copy(group *);
+
+/*----- Random number generators ------------------------------------------*/
+
+#define f_freeme 1u
+
+typedef struct grand_pyobj {
+ PyObject_HEAD
+ unsigned f;
+ grand *r;
+} grand_pyobj;
+
+extern PyTypeObject *grand_pytype;
+extern PyObject *rand_pyobj;
+#define GRAND_PYCHECK(o) PyObject_TypeCheck((o), grand_pytype)
+#define GRAND_F(o) (((grand_pyobj *)(o))->f)
+#define GRAND_R(o) (((grand_pyobj *)(o))->r)
+extern PyObject *grand_pywrap(grand *, unsigned);
+extern int convgrand(PyObject *, void *);
+
+/*----- Symmetric cryptography --------------------------------------------*/
+
+extern PyObject *keysz_pywrap(const octet *);
+
+extern int convgccipher(PyObject *, void *);
+extern PyObject *gccipher_pywrap(gccipher *);
+
+typedef struct gchash_pyobj {
+ PyHeapTypeObject ty;
+ gchash *ch;
+} gchash_pyobj;
+
+extern PyTypeObject *gchash_pytype;
+extern PyObject *sha_pyobj, *has160_pyobj;
+#define GCHASH_PYCHECK(o) PyObject_TypeCheck((o), gchash_pytype)
+#define GCHASH_CH(o) (((gchash_pyobj *)(o))->ch)
+extern PyObject *ghash_pywrap(PyObject *, ghash *);
+extern int convgchash(PyObject *, void *);
+extern int convghash(PyObject *, void *);
+
+extern int convgcmac(PyObject *, void *);
+
+/*----- Key generation ----------------------------------------------------*/
+
+typedef struct pfilt_pyobj {
+ PyObject_HEAD
+ pfilt f;
+ int st;
+} pfilt_pyobj;
+
+extern PyTypeObject *pfilt_pytype;
+#define PFILT_PYCHECK(o) PyObject_TypeCheck(o, pfilt_pytype)
+#define PFILT_F(o) (&((pfilt_pyobj *)(o))->f)
+#define PFILT_ST(o) (((pfilt_pyobj *)(o))->st)
+
+typedef struct { pgen_proc *proc; void *ctx; } pgev;
+#define PGEV_HEAD PyObject_HEAD pgev pg;
+
+typedef struct pgev_pyobj {
+ PGEV_HEAD
+} pgev_pyobj;
+
+extern PyTypeObject *pgev_pytype;
+#define PGEV_PYCHECK(o) PyObject_TypeCheck(o, pgev_pytype)
+#define PGEV_PG(o) (&((pgev_pyobj *)(o))->pg)
+
+typedef struct pypgev {
+ pgev ev;
+ PyObject *obj;
+ struct excinfo *exc;
+} pypgev;
+
+extern int convpgev(PyObject *, void *);
+extern void droppgev(pypgev *);
+extern void pgenerr(struct excinfo *exc);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * Where the fun begins
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+PyObject *mexp_common(PyObject *me, PyObject *arg,
+ size_t efsz,
+ PyObject *(*id)(PyObject *),
+ int (*fill)(void *, PyObject *,
+ PyObject *, PyObject *),
+ PyObject *(*exp)(PyObject *, void *, size_t),
+ void (*drop)(void *))
+{
+ size_t i = 0, o, n;
+ int flat;
+ PyObject *qq = 0, *x = 0, *y = 0, *z = 0, *it = 0;
+ char *v = 0;
+
+ if (PyTuple_Size(arg) == 1) arg = PyTuple_GET_ITEM(arg, 0);
+ it = PyObject_GetIter(arg); if (!it) goto end;
+ qq = PyIter_Next(it);
+ if (!qq) {
+ if (!PyErr_Occurred()) z = id(me);
+ else goto end;
+ }
+ flat = !PySequence_Check(qq);
+ if (!PySequence_Check(arg))
+ n = 16;
+ else {
+ n = PySequence_Size(arg);
+ if (n == (size_t)-1 && PyErr_Occurred()) goto end;
+ if (flat) n /= 2;
+ if (!n) n = 16;
+ }
+
+ v = xmalloc(n*efsz);
+ o = 0;
+ for (;;) {
+ if (!flat) {
+ if (!PySequence_Check(qq) || PySequence_Size(qq) != 2)
+ TYERR("want a sequence of pairs");
+ x = PySequence_GetItem(qq, 0);
+ y = PySequence_GetItem(qq, 1);
+ } else {
+ x = qq; qq = 0;
+ y = PyIter_Next(it);
+ if (!y) {
+ if (PyErr_Occurred()) goto end;
+ VALERR("must have even number of operands");
+ }
+ }
+ if (!x || !y) goto end;
+
+ if (i >= n) { n *= 2; v = xrealloc(v, n*efsz, i*efsz); }
+ if (fill(v + o, me, x, y)) {
+ if (PyErr_Occurred()) goto end;
+ TYERR("type mismatch");
+ }
+ i++; o += efsz;
+ Py_DECREF(x); x = 0;
+ Py_DECREF(y); y = 0;
+ Py_XDECREF(qq);
+
+ qq = PyIter_Next(it);
+ if (!qq) {
+ if (PyErr_Occurred()) goto end;
+ else break;
+ }
+ }
+
+ z = exp(me, v, i);
+
+end:
+ while (i--) { o -= efsz; drop(v + o); }
+ xfree(v);
+ Py_XDECREF(it); Py_XDECREF(qq); Py_XDECREF(x); Py_XDECREF(y);
+ return (z);
+}
+
+int convmpw(PyObject *o, void *pp)
+{
+ unsigned long u;
+ unsigned *p = pp;
+
+ if (!convulong(o, &u)) goto end;
+ if (u > MPW_MAX) VALERR("out of range");
+ *p = u;
+ return (1);
+end:
+ return (0);
+}
+
+static PyTypeObject *thingtab_pytype;
+
+typedef struct thingentry {
+ sym_base _b;
+ PyObject *val;
+} thingentry;
+#define THING_VAL(x) (((thingentry *)(x))->val)
+
+typedef struct thingtab_pyobj {
+ GMAP_PYOBJ_HEAD
+ sym_table t;
+} thingtab_pyobj;
+#define THINGTAB_T(x) (&((thingtab_pyobj *)(x))->t)
+
+static void *thingtab_gmlookup(PyObject *me, PyObject *key, unsigned *f)
+{
+ const char *p;
+
+ p = TEXT_STR(key); if (!p) return (0);
+ return (sym_find(THINGTAB_T(me), p, -1, 0, f));
+}
+
+static void thingtab_gmiterinit(PyObject *me, void *i)
+ { sym_mkiter(i, THINGTAB_T(me)); }
+
+static void *thingtab_gmiternext(PyObject *me, void *i)
+ { sym_iter *it = i; void *e; SYM_NEXT(it, e); return (e); }
+
+static PyObject *thingtab_gmentrykey(PyObject *me, void *e)
+ { return (TEXT_FROMSTR(SYM_NAME(e))); }
+
+static PyObject *thingtab_gmentryvalue(PyObject *me, void *e)
+ { PyObject *rc = THING_VAL(e); RETURN_OBJ(rc); }
+
+static const gmap_ops thingtab_gmops = {
+ sizeof(sym_iter),
+ thingtab_gmlookup,
+ thingtab_gmiterinit,
+ thingtab_gmiternext,
+ thingtab_gmentrykey,
+ thingtab_gmentryvalue
+};
+
+static Py_ssize_t thing_pysize(PyObject *me)
+ { return gmap_pysize_from_sym(THINGTAB_T(me)); }
+
+static const PyMappingMethods thingtab_pymapping = {
+ thing_pysize,
+ gmap_pylookup,
+ 0
+};
+
+static thingtab_pyobj *make_thingtab(void)
+{
+ thingtab_pyobj *map = PyObject_NEW(thingtab_pyobj, thingtab_pytype);
+
+ map->gmops = &thingtab_gmops;
+ sym_create(&map->t);
+ return (map);
+}
+
+PyObject *make_algtab(const void *tab, size_t esz,
+ const char *(*namefn)(const void *),
+ PyObject *(*valfn)(const void *))
+{
+ thingtab_pyobj *map = make_thingtab();
+ const char *p = tab;
+ const char *name;
+ thingentry *e;
+ unsigned f;
+
+ for (;;) {
+ name = namefn(p); if (!name) break;
+ e = sym_find(&map->t, name, -1, sizeof(*e), &f); assert(!f);
+ e->val = valfn(p);
+ p += esz;
+ }
+ return ((PyObject *)map);
+}
+
+PyObject *make_grouptab(const void *tab, size_t esz,
+ const char *(*namefn)(const void *),
+ int (*ixfn)(const void *), PyObject *(*valfn)(int))
+{
+ thingtab_pyobj *map = make_thingtab();
+ struct { const char *name; int ix; } *ixtab = 0;
+ PyObject **valtab, **vv;
+ size_t i = 0, n = 0;
+ const char *p = tab;
+ const char *name;
+ thingentry *e;
+ unsigned f;
+
+ for (;;) {
+ name = namefn(p); if (!name) break;
+ if (i >= n) {
+ if (!n) n = 16;
+ else n *= 2;
+ ixtab = xrealloc(ixtab, n*sizeof(*ixtab), i*sizeof(*ixtab));
+ }
+ ixtab[i].name = name; ixtab[i].ix = ixfn(p); assert(ixtab[i].ix >= 0);
+ p += esz; i++;
+ }
+ n = i;
+
+ valtab = xmalloc(n*sizeof(*valtab));
+ for (i = 0; i < n; i++) valtab[i] = 0;
+
+ for (i = 0; i < n; i++) {
+ e = sym_find(&map->t, ixtab[i].name, -1, sizeof(*e), &f); assert(!f);
+ vv = &valtab[ixtab[i].ix];
+ if (*vv) Py_INCREF(*vv);
+ else *vv = valfn(ixtab[i].ix);
+ e->val = *vv;
+ }
+
+ xfree(ixtab); xfree(valtab);
+ return ((PyObject *)map);
+}
+
+static const PyTypeObject thingtab_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "_MiscTable", /* @tp_name@ */
+ sizeof(thingtab_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ PYSEQUENCE(gmap), /* @tp_as_sequence@ */
+ PYMAPPING(thingtab), /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Class for tables of algorithms and abstract-group data.\n"
+ " Not instantiable by users.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ gmap_pyiter, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gmapro), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *smallprimes(void)
+{
+ PyObject *v = PyList_New(NPRIME);
+ int i;
+
+ for (i = 0; i < NPRIME; i++)
+ PyList_SET_ITEM(v, i, PyInt_FromLong(primetab[i]));
+ return (v);
+}
+
+static PyObject *meth__ego(PyObject *me, PyObject *arg)
+{
+ char *argv0;
+ if (!PyArg_ParseTuple(arg, "s:_ego", &argv0))
+ return (0);
+ if (STRCMP(QUIS, ==, "<UNNAMED>"))
+ ego(argv0);
+ RETURN_NONE;
+}
+
+static const PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+ METH (_ego, "_ego(ARGV0)")
+#undef METHNAME
+ { 0 }
+};
+
+static void init_random(void)
+{
+#if PY_VERSION_HEX >= 0x02060000
+ char *seed;
+ uint32 r;
+
+ if (!Py_HashRandomizationFlag) return;
+ seed = getenv("PYTHONHASHSEED");
+ if (!seed || STRCMP(seed, ==, "random")) r = GR_WORD(&rand_global);
+ else r = strtoul(seed, 0, 0);
+ if (!r) r = 0xe011f220; /* zero doesn't work well */
+ unihash_setkey(&unihash_global, r);
+#endif
+}
+
+#ifdef PY3
+static PyModuleDef moddef = {
+ PyModuleDef_HEAD_INIT,
+ "catacomb._base", /* @m_name@ */
+ "Low-level module for Catacomb bindings. Use `catacomb' instead.",
+ /* @m_doc@ */
+ 0, /* @m_size@ */
+ 0, /* @m_methods@ */
+ 0, /* @m_slots@ */
+ 0, /* @m_traverse@ */
+ 0, /* @m_clear@ */
+ 0 /* @m_free@ */
+};
+#endif
+
+EXPORT PyMODINIT_FUNC PY23(init_base, PyInit__base)(void)
+{
+ PyObject *mod;
+
+ modname = TEXT_FROMSTR("catacomb");
+ addmethods(methods);
+ INIT_MODULES;
+ INITTYPE(thingtab, root);
+ init_random();
+#ifdef PY3
+ moddef.m_methods = donemethods();
+ mod = PyModule_Create(&moddef);
+#else
+ mod = Py_InitModule("catacomb._base", donemethods());
+#endif
+ INSERT_MODULES;
+ INSERT("_MiscTable", thingtab_pytype);
+ INSERT("smallprimes", smallprimes());
+#ifdef PY3
+ return (mod);
+#endif
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+### -*-python-*-
+###
+### Setup for Catacomb/Python bindings
+###
+### (c) 2004 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.
+
+from __future__ import with_statement
+
+from binascii import hexlify as _hexify, unhexlify as _unhexify
+from contextlib import contextmanager as _ctxmgr
+try: import DLFCN as _dlfcn
+except ImportError: _dlfcn = None
+import os as _os
+from struct import pack as _pack
+import sys as _sys
+import types as _types
+
+###--------------------------------------------------------------------------
+### Import the main C extension module.
+
+try:
+ _dlflags = _odlflags = _sys.getdlopenflags()
+except AttributeError:
+ _dlflags = _odlflags = -1
+
+## Set the `deep binding' flag. Python has its own different MD5
+## implementation, and some distributions export `md5_init' and friends so
+## they override our versions, which doesn't end well. Figure out how to
+## turn this flag on so we don't have the problem.
+if _dlflags >= 0:
+ try: _dlflags |= _dlfcn.RTLD_DEEPBIND
+ except AttributeError:
+ try: _dlflags |= _os.RTLD_DEEPBIND
+ except AttributeError:
+ if _os.uname()[0] == 'Linux': _dlflags |= 8 # magic knowledge
+ else: pass # can't do this.
+ _sys.setdlopenflags(_dlflags)
+
+if _sys.version_info >= (3,): from . import _base
+else: import _base
+
+if _odlflags >= 0:
+ _sys.setdlopenflags(_odlflags)
+
+del _dlflags, _odlflags
+
+###--------------------------------------------------------------------------
+### Basic stuff.
+
+## For the benefit of the default keyreporter, we need the program name.
+_base._ego(_sys.argv[0])
+
+## Register our module.
+_base._set_home_module(_sys.modules[__name__])
+def default_lostexchook(why, ty, val, tb):
+ """`catacomb.lostexchook(WHY, TY, VAL, TB)' reports lost exceptions."""
+ _sys.stderr.write("\n\n!!! LOST EXCEPTION: %s\n" % why)
+ _sys.excepthook(ty, val, tb)
+ _sys.stderr.write("\n")
+lostexchook = default_lostexchook
+
+## Text/binary conversions.
+if _sys.version_info >= (3,):
+ def _bin(s): return s.encode('iso8859-1')
+else:
+ def _bin(s): return s
+
+## Iterating over dictionaries.
+if _sys.version_info >= (3,):
+ def _iteritems(dict): return dict.items()
+ def _itervalues(dict): return dict.values()
+else:
+ def _iteritems(dict): return dict.iteritems()
+ def _itervalues(dict): return dict.itervalues()
+
+## The built-in bignum type.
+try: long
+except NameError: _long = int
+else: _long = long
+
+## How to fix a name back into the right identifier. Alas, the rules are not
+## consistent.
+def _fixname(name):
+
+ ## Hyphens consistently become underscores.
+ name = name.replace('-', '_')
+
+ ## But slashes might become underscores or just vanish.
+ if name.startswith('salsa20'): name = name.replace('/', '')
+ else: name = name.replace('/', '_')
+
+ ## Done.
+ return name
+
+## Initialize the module. Drag in the static methods of the various
+## classes; create names for the various known crypto algorithms.
+def _init():
+ d = globals()
+ b = _base.__dict__;
+ for i in b:
+ if i[0] != '_':
+ d[i] = b[i];
+ for i in [gcciphers, gcaeads, gchashes, gcmacs, gcprps]:
+ for c in _itervalues(i):
+ d[_fixname(c.name)] = c
+ for c in _itervalues(gccrands):
+ d[_fixname(c.name + 'rand')] = c
+_init()
+
+## A handy function for our work: add the methods of a named class to an
+## existing class. This is how we write the Python-implemented parts of our
+## mostly-C types.
+def _augment(c, cc):
+ for i in cc.__dict__:
+ a = cc.__dict__[i]
+ if type(a) is _types.MethodType:
+ a = a.im_func
+ elif type(a) not in (_types.FunctionType, staticmethod, classmethod):
+ continue
+ setattr(c, i, a)
+
+## Parsing functions tend to return the object parsed and the remainder of
+## the input. This checks that the remainder is input and, if so, returns
+## just the object.
+def _checkend(r):
+ x, rest = r
+ if rest != '':
+ raise SyntaxError('junk at end of string')
+ return x
+
+## Some pretty-printing utilities.
+PRINT_SECRETS = False
+def _clsname(me): return type(me).__name__
+def _repr_secret(thing, secretp = True):
+ if not secretp or PRINT_SECRETS: return repr(thing)
+ else: return '#<SECRET>'
+def _pp_str(me, pp, cyclep): pp.text(cyclep and '...' or str(me))
+def _pp_secret(pp, thing, secretp = True):
+ if not secretp or PRINT_SECRETS: pp.pretty(thing)
+ else: pp.text('#<SECRET>')
+def _pp_bgroup(pp, text):
+ ind = len(text)
+ pp.begin_group(ind, text)
+ return ind
+def _pp_bgroup_tyname(pp, obj, open = '('):
+ return _pp_bgroup(pp, _clsname(obj) + open)
+def _pp_kv(pp, k, v, secretp = False):
+ ind = _pp_bgroup(pp, k + ' = ')
+ _pp_secret(pp, v, secretp)
+ pp.end_group(ind, '')
+def _pp_commas(pp, printfn, items):
+ firstp = True
+ for i in items:
+ if firstp: firstp = False
+ else: pp.text(','); pp.breakable()
+ printfn(i)
+def _pp_dict(pp, items):
+ def p(kv):
+ k, v = kv
+ pp.begin_group(0)
+ pp.pretty(k)
+ pp.text(':')
+ pp.begin_group(2)
+ pp.breakable()
+ pp.pretty(v)
+ pp.end_group(2)
+ pp.end_group(0)
+ _pp_commas(pp, p, items)
+
+###--------------------------------------------------------------------------
+### Mappings.
+
+if _sys.version_info >= (3,):
+ class _tmp:
+ def __str__(me): return '%s(%r)' % (type(me).__name__, list(me))
+ __repr__ = __str__
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me, '([')
+ _pp_commas(pp, pp.pretty, me)
+ pp.end_group(ind, '])')
+ _augment(_base._KeyView, _tmp)
+ _augment(_base._ValueView, _tmp)
+ _augment(_base._ItemView, _tmp)
+
+###--------------------------------------------------------------------------
+### Bytestrings.
+
+class _tmp:
+ def fromhex(x):
+ return ByteString(_unhexify(x))
+ fromhex = staticmethod(fromhex)
+ if _sys.version_info >= (3,):
+ def hex(me): return _hexify(me).decode()
+ else:
+ def hex(me): return _hexify(me)
+ __hex__ = hex
+ def __repr__(me):
+ return 'bytes(%r)' % me.hex()
+_augment(ByteString, _tmp)
+ByteString.__hash__ = str.__hash__
+bytes = ByteString.fromhex
+
+###--------------------------------------------------------------------------
+### Symmetric encryption.
+
+class _tmp:
+ def encrypt(me, n, m, tsz = None, h = ByteString.zero(0)):
+ if tsz is None: tsz = me.__class__.tagsz.default
+ e = me.enc(n, len(h), len(m), tsz)
+ if not len(h): a = None
+ else: a = e.aad().hash(h)
+ c0 = e.encrypt(m)
+ c1, t = e.done(aad = a)
+ return c0 + c1, t
+ def decrypt(me, n, c, t, h = ByteString.zero(0)):
+ d = me.dec(n, len(h), len(c), len(t))
+ if not len(h): a = None
+ else: a = d.aad().hash(h)
+ m = d.decrypt(c)
+ m += d.done(t, aad = a)
+ return m
+_augment(GAEKey, _tmp)
+
+###--------------------------------------------------------------------------
+### Hashing.
+
+class _tmp:
+ def check(me, h):
+ hh = me.done()
+ return ctstreq(h, hh)
+_augment(GHash, _tmp)
+_augment(Poly1305Hash, _tmp)
+
+class _tmp:
+ def check(me, h):
+ return ctstreq(h, me.done(len(h)))
+_augment(Shake, _tmp)
+
+KMAC128.keysz = KeySZAny(16); KMAC128.tagsz = 16
+KMAC256.keysz = KeySZAny(32); KMAC256.tagsz = 32
+
+###--------------------------------------------------------------------------
+### NaCl `secretbox'.
+
+def secret_box(k, n, m):
+ y, t = salsa20_naclbox(k).encrypt(n, m)
+ return t + y
+
+def secret_unbox(k, n, c):
+ tsz = poly1305.tagsz
+ return salsa20_naclbox(k).decrypt(n, c[tsz:], c[0:tsz])
+
+###--------------------------------------------------------------------------
+### Multiprecision integers and binary polynomials.
+
+class BaseRat (object):
+ """Base class implementing fields of fractions over Euclidean domains."""
+ def __new__(cls, a, b):
+ a, b = cls.RING._implicit(a), cls.RING._implicit(b)
+ q, r = divmod(a, b)
+ if r == cls.ZERO: return q
+ g = b.gcd(r)
+ me = super(BaseRat, cls).__new__(cls)
+ me._n = a//g
+ me._d = b//g
+ return me
+ @property
+ def numer(me): return me._n
+ @property
+ def denom(me): return me._d
+ def __str__(me): return '%s/%s' % (me._n, me._d)
+ def __repr__(me): return '%s(%s, %s)' % (_clsname(me), me._n, me._d)
+ _repr_pretty_ = _pp_str
+
+ def _split_rat(me, x):
+ if isinstance(x, me.__class__): return x._n, x._d
+ else: return x, me.ONE
+ def __add__(me, you):
+ n, d = me._split_rat(you)
+ return type(me)(me._n*d + n*me._d, d*me._d)
+ __radd__ = __add__
+ def __sub__(me, you):
+ n, d = me._split_rat(you)
+ return type(me)(me._n*d - n*me._d, d*me._d)
+ def __rsub__(me, you):
+ n, d = me._split_rat(you)
+ return type(me)(n*me._d - me._n*d, d*me._d)
+ def __mul__(me, you):
+ n, d = me._split_rat(you)
+ return type(me)(me._n*n, me._d*d)
+ __rmul__ = __mul__
+ def __truediv__(me, you):
+ n, d = me._split_rat(you)
+ return type(me)(me._n*d, me._d*n)
+ def __rtruediv__(me, you):
+ n, d = me._split_rat(you)
+ return type(me)(me._d*n, me._n*d)
+ if _sys.version_info < (3,):
+ __div__ = __truediv__
+ __rdiv__ = __rtruediv__
+ def _order(me, you, op):
+ n, d = me._split_rat(you)
+ return op(me._n*d, n*me._d)
+ def __eq__(me, you): return me._order(you, lambda x, y: x == y)
+ def __ne__(me, you): return me._order(you, lambda x, y: x != y)
+ def __le__(me, you): return me._order(you, lambda x, y: x <= y)
+ def __lt__(me, you): return me._order(you, lambda x, y: x < y)
+ def __gt__(me, you): return me._order(you, lambda x, y: x > y)
+ def __ge__(me, you): return me._order(you, lambda x, y: x >= y)
+
+class IntRat (BaseRat):
+ RING = MP
+ ZERO, ONE = MP(0), MP(1)
+ def __new__(cls, a, b):
+ if isinstance(a, float) or isinstance(b, float): return a/b
+ return super(IntRat, cls).__new__(cls, a, b)
+ def __float__(me): return float(me._n)/float(me._d)
+
+class GFRat (BaseRat):
+ RING = GF
+ ZERO, ONE = GF(0), GF(1)
+
+class _tmp:
+ def negp(x): return x < 0
+ def posp(x): return x > 0
+ def zerop(x): return x == 0
+ def oddp(x): return x.testbit(0)
+ def evenp(x): return not x.testbit(0)
+ def mont(x): return MPMont(x)
+ def barrett(x): return MPBarrett(x)
+ def reduce(x): return MPReduce(x)
+ def __truediv__(me, you):
+ if isinstance(you, float): return _long(me)/you
+ else: return IntRat(me, you)
+ def __rtruediv__(me, you):
+ if isinstance(you, float): return you/_long(me)
+ else: return IntRat(you, me)
+ if _sys.version_info < (3,):
+ __div__ = __truediv__
+ __rdiv__ = __rtruediv__
+ _repr_pretty_ = _pp_str
+_augment(MP, _tmp)
+
+class _tmp:
+ def zerop(x): return x == 0
+ def reduce(x): return GFReduce(x)
+ def trace(x, y): return x.reduce().trace(y)
+ def halftrace(x, y): return x.reduce().halftrace(y)
+ def modsqrt(x, y): return x.reduce().sqrt(y)
+ def quadsolve(x, y): return x.reduce().quadsolve(y)
+ def __truediv__(me, you): return GFRat(me, you)
+ def __rtruediv__(me, you): return GFRat(you, me)
+ if _sys.version_info < (3,):
+ __div__ = __truediv__
+ __rdiv__ = __rtruediv__
+ _repr_pretty_ = _pp_str
+_augment(GF, _tmp)
+
+class _tmp:
+ def product(*arg):
+ 'product(ITERABLE) or product(I, ...) -> PRODUCT'
+ return MPMul(*arg).done()
+ product = staticmethod(product)
+_augment(MPMul, _tmp)
+
+###--------------------------------------------------------------------------
+### Abstract fields.
+
+class _tmp:
+ def fromstring(str): return _checkend(Field.parse(str))
+ fromstring = staticmethod(fromstring)
+_augment(Field, _tmp)
+
+class _tmp:
+ def __repr__(me): return '%s(%s)' % (_clsname(me), me.p)
+ def __hash__(me): return 0x114401de ^ hash(me.p)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep: pp.text('...')
+ else: pp.pretty(me.p)
+ pp.end_group(ind, ')')
+ def ec(me, a, b): return ECPrimeProjCurve(me, a, b)
+_augment(PrimeField, _tmp)
+
+class _tmp:
+ def __repr__(me): return '%s(%#x)' % (_clsname(me), me.p)
+ def ec(me, a, b): return ECBinProjCurve(me, a, b)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep: pp.text('...')
+ else: pp.text('%#x' % me.p)
+ pp.end_group(ind, ')')
+_augment(BinField, _tmp)
+
+class _tmp:
+ def __hash__(me): return 0x23e4701c ^ hash(me.p)
+_augment(BinPolyField, _tmp)
+
+class _tmp:
+ def __hash__(me):
+ h = 0x9a7d6240
+ h ^= hash(me.p)
+ h ^= 2*hash(me.beta) & 0xffffffff
+ return h
+_augment(BinNormField, _tmp)
+
+class _tmp:
+ def __str__(me): return str(me.value)
+ def __repr__(me): return '%s(%s)' % (repr(me.field), repr(me.value))
+ _repr_pretty_ = _pp_str
+_augment(FE, _tmp)
+
+###--------------------------------------------------------------------------
+### Elliptic curves.
+
+class _tmp:
+ def __repr__(me):
+ return '%s(%r, %s, %s)' % (_clsname(me), me.field, me.a, me.b)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ pp.pretty(me.field); pp.text(','); pp.breakable()
+ pp.pretty(me.a); pp.text(','); pp.breakable()
+ pp.pretty(me.b)
+ pp.end_group(ind, ')')
+ def fromstring(str): return _checkend(ECCurve.parse(str))
+ fromstring = staticmethod(fromstring)
+ def frombuf(me, s):
+ return ecpt.frombuf(me, s)
+ def fromraw(me, s):
+ return ecpt.fromraw(me, s)
+ def pt(me, *args):
+ return me(*args)
+_augment(ECCurve, _tmp)
+
+class _tmp:
+ def __hash__(me):
+ h = 0x6751d341
+ h ^= hash(me.field)
+ h ^= 2*hash(me.a) ^ 0xffffffff
+ h ^= 5*hash(me.b) ^ 0xffffffff
+ return h
+_augment(ECPrimeCurve, _tmp)
+
+class _tmp:
+ def __hash__(me):
+ h = 0x2ac203c5
+ h ^= hash(me.field)
+ h ^= 2*hash(me.a) ^ 0xffffffff
+ h ^= 5*hash(me.b) ^ 0xffffffff
+ return h
+_augment(ECBinCurve, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ if not me: return '%s()' % _clsname(me)
+ return '%s(%s, %s)' % (_clsname(me), me.ix, me.iy)
+ def __str__(me):
+ if not me: return 'inf'
+ return '(%s, %s)' % (me.ix, me.iy)
+ def _repr_pretty_(me, pp, cyclep):
+ if cyclep:
+ pp.text('...')
+ elif not me:
+ pp.text('inf')
+ else:
+ ind = _pp_bgroup(pp, '(')
+ pp.pretty(me.ix); pp.text(','); pp.breakable()
+ pp.pretty(me.iy)
+ pp.end_group(ind, ')')
+_augment(ECPt, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ return '%s(curve = %r, G = %r, r = %s, h = %s)' % \
+ (_clsname(me), me.curve, me.G, me.r, me.h)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_kv(pp, 'curve', me.curve); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'r', me.r); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'h', me.h)
+ pp.end_group(ind, ')')
+ def __hash__(me):
+ h = 0x9bedb8de
+ h ^= hash(me.curve)
+ h ^= 2*hash(me.G) & 0xffffffff
+ return h
+ def fromstring(str): return _checkend(ECInfo.parse(str))
+ fromstring = staticmethod(fromstring)
+ def group(me):
+ return ECGroup(me)
+_augment(ECInfo, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ if not me: return '%r()' % (me.curve)
+ return '%r(%s, %s)' % (me.curve, me.x, me.y)
+ def __str__(me):
+ if not me: return 'inf'
+ return '(%s, %s)' % (me.x, me.y)
+ def _repr_pretty_(me, pp, cyclep):
+ if cyclep:
+ pp.text('...')
+ elif not me:
+ pp.text('inf')
+ else:
+ ind = _pp_bgroup(pp, '(')
+ pp.pretty(me.x); pp.text(','); pp.breakable()
+ pp.pretty(me.y)
+ pp.end_group(ind, ')')
+_augment(ECPtCurve, _tmp)
+
+###--------------------------------------------------------------------------
+### Key sizes.
+
+class _tmp:
+ def __repr__(me): return '%s(%d)' % (_clsname(me), me.default)
+ def check(me, sz): return True
+ def best(me, sz): return sz
+ def pad(me, sz): return sz
+_augment(KeySZAny, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ return '%s(%d, %d, %d, %d)' % \
+ (_clsname(me), me.default, me.min, me.max, me.mod)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ pp.pretty(me.default); pp.text(','); pp.breakable()
+ pp.pretty(me.min); pp.text(','); pp.breakable()
+ pp.pretty(me.max); pp.text(','); pp.breakable()
+ pp.pretty(me.mod)
+ pp.end_group(ind, ')')
+ def check(me, sz): return me.min <= sz <= me.max and sz%me.mod == 0
+ def best(me, sz):
+ if sz < me.min: raise ValueError('key too small')
+ elif me.max is not None and sz > me.max: return me.max
+ else: return sz - sz%me.mod
+ def pad(me, sz):
+ if me.max is not None and sz > me.max: raise ValueError('key too large')
+ elif sz < me.min: return me.min
+ else: sz += me.mod - 1; return sz - sz%me.mod
+_augment(KeySZRange, _tmp)
+
+class _tmp:
+ def __repr__(me): return '%s(%d, %s)' % (_clsname(me), me.default, me.set)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ pp.pretty(me.default); pp.text(','); pp.breakable()
+ ind1 = _pp_bgroup(pp, '{')
+ _pp_commas(pp, pp.pretty, me.set)
+ pp.end_group(ind1, '}')
+ pp.end_group(ind, ')')
+ def check(me, sz): return sz in me.set
+ def best(me, sz):
+ found = -1
+ for i in me.set:
+ if found < i <= sz: found = i
+ if found < 0: raise ValueError('key too small')
+ return found
+ def pad(me, sz):
+ found = -1
+ for i in me.set:
+ if sz <= i and (found == -1 or i < found): found = i
+ if found < 0: raise ValueError('key too large')
+ return found
+_augment(KeySZSet, _tmp)
+
+###--------------------------------------------------------------------------
+### Key data objects.
+
+class _tmp:
+ def merge(me, file, report = None):
+ """KF.merge(FILE, [report = <built-in-reporter>])"""
+ name = file.name
+ lno = 1
+ for line in file:
+ me.mergeline(name, lno, line, report)
+ lno += 1
+ return me
+ def __repr__(me): return '%s(%r)' % (_clsname(me), me.name)
+_augment(KeyFile, _tmp)
+
+class _tmp:
+ def extract(me, file, filter = ''):
+ """KEY.extract(FILE, [filter = <any>])"""
+ line = me.extractline(filter)
+ file.write(line)
+ return me
+ def __repr__(me): return '%s(%r)' % (_clsname(me), me.fulltag)
+_augment(Key, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ return '%s({%s})' % (_clsname(me),
+ ', '.join(['%r: %r' % kv for kv in _iteritems(me)()]))
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep: pp.text('...')
+ else: _pp_dict(pp, _iteritems(me))
+ pp.end_group(ind, ')')
+_augment(KeyAttributes, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ return '%s(%s, %r)' % (_clsname(me),
+ _repr_secret(me._guts(),
+ not (me.flags & KF_NONSECRET)),
+ me.writeflags(me.flags))
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_secret(pp, me._guts(), not (me.flags & KF_NONSECRET))
+ pp.text(','); pp.breakable()
+ pp.pretty(me.writeflags(me.flags))
+ pp.end_group(ind, ')')
+ def __hash__(me): return me._HASHBASE ^ hash(me._guts())
+ def __eq__(me, kd):
+ return type(me) == type(kd) and \
+ me._guts() == kd._guts() and \
+ me.flags == kd.flags
+ def __ne__(me, kd):
+ return not me == kd
+_augment(KeyData, _tmp)
+
+class _tmp:
+ def _guts(me): return me.bin
+ def __eq__(me, kd):
+ return isinstance(kd, KeyDataBinary) and me.bin == kd.bin
+_augment(KeyDataBinary, _tmp)
+KeyDataBinary._HASHBASE = 0x961755c3
+
+class _tmp:
+ def _guts(me): return me.ct
+_augment(KeyDataEncrypted, _tmp)
+KeyDataEncrypted._HASHBASE = 0xffe000d4
+
+class _tmp:
+ def _guts(me): return me.mp
+_augment(KeyDataMP, _tmp)
+KeyDataMP._HASHBASE = 0x1cb64d69
+
+class _tmp:
+ def _guts(me): return me.str
+_augment(KeyDataString, _tmp)
+KeyDataString._HASHBASE = 0x349c33ea
+
+class _tmp:
+ def _guts(me): return me.ecpt
+_augment(KeyDataECPt, _tmp)
+KeyDataECPt._HASHBASE = 0x2509718b
+
+class _tmp:
+ def __repr__(me):
+ return '%s({%s})' % (_clsname(me),
+ ', '.join(['%r: %r' % kv for kv in _iteritems(me)]))
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me, '({ ')
+ if cyclep: pp.text('...')
+ else: _pp_dict(pp, _iteritems(me))
+ pp.end_group(ind, ' })')
+ def __hash__(me):
+ h = me._HASHBASE
+ for k, v in _iteritems(me):
+ h = ((h << 1) ^ 3*hash(k) ^ 5*hash(v))&0xffffffff
+ return h
+ def __eq__(me, kd):
+ if type(me) != type(kd) or me.flags != kd.flags or len(me) != len(kd):
+ return False
+ for k, v in _iteritems(me):
+ try: vv = kd[k]
+ except KeyError: return False
+ if v != vv: return False
+ return True
+_augment(KeyDataStructured, _tmp)
+KeyDataStructured._HASHBASE = 0x85851b21
+
+###--------------------------------------------------------------------------
+### Abstract groups.
+
+class _tmp:
+ def __repr__(me):
+ return '%s(p = %s, r = %s, g = %s)' % (_clsname(me), me.p, me.r, me.g)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'r', me.r); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'g', me.g)
+ pp.end_group(ind, ')')
+_augment(FGInfo, _tmp)
+
+class _tmp:
+ def group(me): return PrimeGroup(me)
+_augment(DHInfo, _tmp)
+
+class _tmp:
+ def group(me): return BinGroup(me)
+_augment(BinDHInfo, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ return '%s(%r)' % (_clsname(me), me.info)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep: pp.text('...')
+ else: pp.pretty(me.info)
+ pp.end_group(ind, ')')
+_augment(Group, _tmp)
+
+class _tmp:
+ def __hash__(me):
+ info = me.info
+ h = 0xbce3cfe6
+ h ^= hash(info.p)
+ h ^= 2*hash(info.r) & 0xffffffff
+ h ^= 5*hash(info.g) & 0xffffffff
+ return h
+ def _get_geval(me, x): return MP(x)
+_augment(PrimeGroup, _tmp)
+
+class _tmp:
+ def __hash__(me):
+ info = me.info
+ h = 0x80695949
+ h ^= hash(info.p)
+ h ^= 2*hash(info.r) & 0xffffffff
+ h ^= 5*hash(info.g) & 0xffffffff
+ return h
+ def _get_geval(me, x): return GF(x)
+_augment(BinGroup, _tmp)
+
+class _tmp:
+ def __hash__(me): return 0x0ec23dab ^ hash(me.info)
+ def _get_geval(me, x): return x.toec()
+_augment(ECGroup, _tmp)
+
+class _tmp:
+ def __repr__(me):
+ return '%r(%r)' % (me.group, str(me))
+ def _repr_pretty_(me, pp, cyclep):
+ pp.pretty(type(me)._get_geval(me))
+_augment(GE, _tmp)
+
+###--------------------------------------------------------------------------
+### RSA encoding techniques.
+
+class PKCS1Crypt (object):
+ def __init__(me, ep = _bin(''), rng = rand):
+ me.ep = ep
+ me.rng = rng
+ def encode(me, msg, nbits):
+ return _base._p1crypt_encode(msg, nbits, me.ep, me.rng)
+ def decode(me, ct, nbits):
+ return _base._p1crypt_decode(ct, nbits, me.ep, me.rng)
+
+class PKCS1Sig (object):
+ def __init__(me, ep = _bin(''), rng = rand):
+ me.ep = ep
+ me.rng = rng
+ def encode(me, msg, nbits):
+ return _base._p1sig_encode(msg, nbits, me.ep, me.rng)
+ def decode(me, msg, sig, nbits):
+ return _base._p1sig_decode(msg, sig, nbits, me.ep, me.rng)
+
+class OAEP (object):
+ def __init__(me, mgf = sha_mgf, hash = sha, ep = _bin(''), rng = rand):
+ me.mgf = mgf
+ me.hash = hash
+ me.ep = ep
+ me.rng = rng
+ def encode(me, msg, nbits):
+ return _base._oaep_encode(msg, nbits, me.mgf, me.hash, me.ep, me.rng)
+ def decode(me, ct, nbits):
+ return _base._oaep_decode(ct, nbits, me.mgf, me.hash, me.ep, me.rng)
+
+class PSS (object):
+ def __init__(me, mgf = sha_mgf, hash = sha, saltsz = None, rng = rand):
+ me.mgf = mgf
+ me.hash = hash
+ if saltsz is None:
+ saltsz = hash.hashsz
+ me.saltsz = saltsz
+ me.rng = rng
+ def encode(me, msg, nbits):
+ return _base._pss_encode(msg, nbits, me.mgf, me.hash, me.saltsz, me.rng)
+ def decode(me, msg, sig, nbits):
+ return _base._pss_decode(msg, sig, nbits,
+ me.mgf, me.hash, me.saltsz, me.rng)
+
+class _tmp:
+ def encrypt(me, msg, enc):
+ return me.pubop(enc.encode(msg, me.n.nbits))
+ def verify(me, msg, sig, enc):
+ if msg is None: return enc.decode(msg, me.pubop(sig), me.n.nbits)
+ try:
+ x = enc.decode(msg, me.pubop(sig), me.n.nbits)
+ return x is None or x == msg
+ except ValueError:
+ return False
+ def __repr__(me):
+ return '%s(n = %r, e = %r)' % (_clsname(me), me.n, me.e)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_kv(pp, 'n', me.n); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'e', me.e)
+ pp.end_group(ind, ')')
+_augment(RSAPub, _tmp)
+
+class _tmp:
+ def decrypt(me, ct, enc): return enc.decode(me.privop(ct), me.n.nbits)
+ def sign(me, msg, enc): return me.privop(enc.encode(msg, me.n.nbits))
+ def __repr__(me):
+ return '%s(n = %r, e = %r, d = %s, ' \
+ 'p = %s, q = %s, dp = %s, dq = %s, q_inv = %s)' % \
+ (_clsname(me), me.n, me.e,
+ _repr_secret(me.d), _repr_secret(me.p), _repr_secret(me.q),
+ _repr_secret(me.dp), _repr_secret(me.dq), _repr_secret(me.q_inv))
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_kv(pp, 'n', me.n); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'e', me.e); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'd', me.d, secretp = True); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'p', me.p, secretp = True); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'q', me.q, secretp = True); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'dp', me.dp, secretp = True); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'dq', me.dq, secretp = True); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'q_inv', me.q_inv, secretp = True)
+ pp.end_group(ind, ')')
+_augment(RSAPriv, _tmp)
+
+###--------------------------------------------------------------------------
+### DSA and related schemes.
+
+class _tmp:
+ def __repr__(me): return '%s(G = %r, p = %r, hash = %r)' % \
+ (_clsname(me), me.G, me.p, me.hash)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'hash', me.hash)
+ pp.end_group(ind, ')')
+_augment(DSAPub, _tmp)
+_augment(KCDSAPub, _tmp)
+
+class _tmp:
+ def __repr__(me): return '%s(G = %r, u = %s, p = %r, hash = %r)' % \
+ (_clsname(me), me.G, _repr_secret(me.u), me.p, me.hash)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep:
+ pp.text('...')
+ else:
+ _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'u', me.u, True); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
+ _pp_kv(pp, 'hash', me.hash)
+ pp.end_group(ind, ')')
+_augment(DSAPriv, _tmp)
+_augment(KCDSAPriv, _tmp)
+
+###--------------------------------------------------------------------------
+### Bernstein's elliptic curve crypto and related schemes.
+
+X25519_BASE = MP(9).storel(32)
+X448_BASE = MP(5).storel(56)
+
+Z128 = ByteString.zero(16)
+
+class _BasePub (object):
+ def __init__(me, pub, *args, **kw):
+ if not me._PUBSZ.check(len(pub)): raise ValueError('bad public key')
+ super(_BasePub, me).__init__(*args, **kw)
+ me.pub = pub
+ def __repr__(me): return '%s(pub = %r)' % (_clsname(me), me.pub)
+ def _pp(me, pp): _pp_kv(pp, 'pub', me.pub)
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup_tyname(pp, me)
+ if cyclep: pp.text('...')
+ else: me._pp(pp)
+ pp.end_group(ind, ')')
+
+class _BasePriv (object):
+ def __init__(me, priv, pub = None, *args, **kw):
+ if not me._KEYSZ.check(len(priv)): raise ValueError('bad private key')
+ if pub is None: pub = me._pubkey(priv)
+ super(_BasePriv, me).__init__(pub = pub, *args, **kw)
+ me.priv = priv
+ @classmethod
+ def generate(cls, rng = rand):
+ return cls(rng.block(cls._KEYSZ.default))
+ def __repr__(me):
+ return '%s(priv = %d, pub = %r)' % \
+ (_clsname(me), _repr_secret(me.priv), me.pub)
+ def _pp(me, pp):
+ _pp_kv(pp, 'priv', me.priv, secretp = True); pp.text(','); pp.breakable()
+ super(_BasePriv, me)._pp(pp)
+
+class _XDHPub (_BasePub): pass
+
+class _XDHPriv (_BasePriv):
+ def _pubkey(me, priv): return me._op(priv, me._BASE)
+ def agree(me, you): return me._op(me.priv, you.pub)
+ def boxkey(me, recip): return me._hashkey(me.agree(recip))
+ def box(me, recip, n, m): return secret_box(me.boxkey(recip), n, m)
+ def unbox(me, recip, n, c): return secret_unbox(me.boxkey(recip), n, c)
+
+class X25519Pub (_XDHPub):
+ _PUBSZ = KeySZSet(X25519_PUBSZ)
+ _BASE = X25519_BASE
+
+class X25519Priv (_XDHPriv, X25519Pub):
+ _KEYSZ = KeySZSet(X25519_KEYSZ)
+ def _op(me, k, X): return x25519(k, X)
+ def _hashkey(me, z): return hsalsa20_prf(z, Z128)
+
+class X448Pub (_XDHPub):
+ _PUBSZ = KeySZSet(X448_PUBSZ)
+ _BASE = X448_BASE
+
+class X448Priv (_XDHPriv, X448Pub):
+ _KEYSZ = KeySZSet(X448_KEYSZ)
+ def _op(me, k, X): return x448(k, X)
+ def _hashkey(me, z): return Shake256().hash(z).done(salsa20.keysz.default)
+
+class _EdDSAPub (_BasePub):
+ def beginhash(me): return me._HASH()
+ def endhash(me, h): return h.done()
+
+class _EdDSAPriv (_BasePriv, _EdDSAPub):
+ pass
+
+class Ed25519Pub (_EdDSAPub):
+ _PUBSZ = KeySZSet(ED25519_PUBSZ)
+ _HASH = sha512
+ def verify(me, msg, sig, **kw):
+ return ed25519_verify(me.pub, msg, sig, **kw)
+
+class Ed25519Priv (_EdDSAPriv, Ed25519Pub):
+ _KEYSZ = KeySZAny(ED25519_KEYSZ)
+ def _pubkey(me, priv): return ed25519_pubkey(priv)
+ def sign(me, msg, **kw):
+ return ed25519_sign(me.priv, msg, pub = me.pub, **kw)
+
+class Ed448Pub (_EdDSAPub):
+ _PUBSZ = KeySZSet(ED448_PUBSZ)
+ _HASH = shake256
+ def verify(me, msg, sig, **kw):
+ return ed448_verify(me.pub, msg, sig, **kw)
+
+class Ed448Priv (_EdDSAPriv, Ed448Pub):
+ _KEYSZ = KeySZAny(ED448_KEYSZ)
+ def _pubkey(me, priv): return ed448_pubkey(priv)
+ def sign(me, msg, **kw):
+ return ed448_sign(me.priv, msg, pub = me.pub, **kw)
+
+###--------------------------------------------------------------------------
+### Built-in algorithm and group tables.
+
+class _tmp:
+ def __repr__(me):
+ return '{%s}' % ', '.join(['%r: %r' % kv for kv in _iteritems(me)])
+ def _repr_pretty_(me, pp, cyclep):
+ ind = _pp_bgroup(pp, '{ ')
+ if cyclep: pp.text('...')
+ else: _pp_dict(pp, _iteritems(me))
+ pp.end_group(ind, ' }')
+_augment(_base._MiscTable, _tmp)
+
+###--------------------------------------------------------------------------
+### Prime number generation.
+
+class PrimeGenEventHandler (object):
+ def pg_begin(me, ev):
+ return me.pg_try(ev)
+ def pg_done(me, ev):
+ return PGEN_DONE
+ def pg_abort(me, ev):
+ return PGEN_TRY
+ def pg_fail(me, ev):
+ return PGEN_TRY
+ def pg_pass(me, ev):
+ return PGEN_TRY
+
+class SophieGermainStepJump (object):
+ def pg_begin(me, ev):
+ me.lf = PrimeFilter(ev.x)
+ me.hf = me.lf.muladd(2, 1)
+ return me.cont(ev)
+ def pg_try(me, ev):
+ me.step()
+ return me.cont(ev)
+ def cont(me, ev):
+ while me.lf.status == PGEN_FAIL or me.hf.status == PGEN_FAIL:
+ me.step()
+ if me.lf.status == PGEN_ABORT or me.hf.status == PGEN_ABORT:
+ return PGEN_ABORT
+ ev.x = me.lf.x
+ if me.lf.status == PGEN_DONE and me.hf.status == PGEN_DONE:
+ return PGEN_DONE
+ return PGEN_TRY
+ def pg_done(me, ev):
+ del me.lf
+ del me.hf
+
+class SophieGermainStepper (SophieGermainStepJump):
+ def __init__(me, step):
+ me.lstep = step;
+ me.hstep = 2 * step
+ def step(me):
+ me.lf.step(me.lstep)
+ me.hf.step(me.hstep)
+
+class SophieGermainJumper (SophieGermainStepJump):
+ def __init__(me, jump):
+ me.ljump = PrimeFilter(jump);
+ me.hjump = me.ljump.muladd(2, 0)
+ def step(me):
+ me.lf.jump(me.ljump)
+ me.hf.jump(me.hjump)
+ def pg_done(me, ev):
+ del me.ljump
+ del me.hjump
+ SophieGermainStepJump.pg_done(me, ev)
+
+class SophieGermainTester (object):
+ def __init__(me):
+ pass
+ def pg_begin(me, ev):
+ me.lr = RabinMiller(ev.x)
+ me.hr = RabinMiller(2 * ev.x + 1)
+ def pg_try(me, ev):
+ lst = me.lr.test(ev.rng.range(me.lr.x))
+ if lst != PGEN_PASS and lst != PGEN_DONE:
+ return lst
+ rst = me.hr.test(ev.rng.range(me.hr.x))
+ if rst != PGEN_PASS and rst != PGEN_DONE:
+ return rst
+ if lst == PGEN_DONE and rst == PGEN_DONE:
+ return PGEN_DONE
+ return PGEN_PASS
+ def pg_done(me, ev):
+ del me.lr
+ del me.hr
+
+class PrimitiveStepper (PrimeGenEventHandler):
+ def __init__(me):
+ pass
+ def pg_try(me, ev):
+ ev.x = me.i.next()
+ return PGEN_TRY
+ def pg_begin(me, ev):
+ me.i = iter(smallprimes)
+ return me.pg_try(ev)
+
+class PrimitiveTester (PrimeGenEventHandler):
+ def __init__(me, mod, hh = [], exp = None):
+ me.mod = MPMont(mod)
+ me.exp = exp
+ me.hh = hh
+ def pg_try(me, ev):
+ x = ev.x
+ if me.exp is not None:
+ x = me.mod.exp(x, me.exp)
+ if x == 1: return PGEN_FAIL
+ for h in me.hh:
+ if me.mod.exp(x, h) == 1: return PGEN_FAIL
+ ev.x = x
+ return PGEN_DONE
+
+class SimulStepper (PrimeGenEventHandler):
+ def __init__(me, mul = 2, add = 1, step = 2):
+ me.step = step
+ me.mul = mul
+ me.add = add
+ def _stepfn(me, step):
+ if step <= 0:
+ raise ValueError('step must be positive')
+ if step <= MPW_MAX:
+ return lambda f: f.step(step)
+ j = PrimeFilter(step)
+ return lambda f: f.jump(j)
+ def pg_begin(me, ev):
+ x = ev.x
+ me.lf = PrimeFilter(x)
+ me.hf = PrimeFilter(x * me.mul + me.add)
+ me.lstep = me._stepfn(me.step)
+ me.hstep = me._stepfn(me.step * me.mul)
+ SimulStepper._cont(me, ev)
+ def pg_try(me, ev):
+ me._step()
+ me._cont(ev)
+ def _step(me):
+ me.lstep(me.lf)
+ me.hstep(me.hf)
+ def _cont(me, ev):
+ while me.lf.status == PGEN_FAIL or me.hf.status == PGEN_FAIL:
+ me._step()
+ if me.lf.status == PGEN_ABORT or me.hf.status == PGEN_ABORT:
+ return PGEN_ABORT
+ ev.x = me.lf.x
+ if me.lf.status == PGEN_DONE and me.hf.status == PGEN_DONE:
+ return PGEN_DONE
+ return PGEN_TRY
+ def pg_done(me, ev):
+ del me.lf
+ del me.hf
+ del me.lstep
+ del me.hstep
+
+class SimulTester (PrimeGenEventHandler):
+ def __init__(me, mul = 2, add = 1):
+ me.mul = mul
+ me.add = add
+ def pg_begin(me, ev):
+ x = ev.x
+ me.lr = RabinMiller(x)
+ me.hr = RabinMiller(x * me.mul + me.add)
+ def pg_try(me, ev):
+ lst = me.lr.test(ev.rng.range(me.lr.x))
+ if lst != PGEN_PASS and lst != PGEN_DONE:
+ return lst
+ rst = me.hr.test(ev.rng.range(me.hr.x))
+ if rst != PGEN_PASS and rst != PGEN_DONE:
+ return rst
+ if lst == PGEN_DONE and rst == PGEN_DONE:
+ return PGEN_DONE
+ return PGEN_PASS
+ def pg_done(me, ev):
+ del me.lr
+ del me.hr
+
+def sgprime(start, step = 2, name = 'p', event = pgen_nullev, nsteps = 0):
+ start = MP(start)
+ return pgen(start, name, SimulStepper(step = step), SimulTester(), event,
+ nsteps, RabinMiller.iters(start.nbits))
+
+def findprimitive(mod, hh = [], exp = None, name = 'g', event = pgen_nullev):
+ return pgen(0, name, PrimitiveStepper(), PrimitiveTester(mod, hh, exp),
+ event, 0, 1)
+
+def kcdsaprime(pbits, qbits, rng = rand,
+ event = pgen_nullev, name = 'p', nsteps = 0):
+ hbits = pbits - qbits - 1
+ while True:
+ h = pgen(rng.mp(hbits, 1), name + ' [h]',
+ PrimeGenStepper(2), PrimeGenTester(),
+ event, nsteps, RabinMiller.iters(hbits))
+ while True:
+ q0 = rng.mp(qbits, 1)
+ p0 = 2*q0*h + 1
+ if p0.nbits == pbits: break
+ q = pgen(q0, name, SimulStepper(2*h, 1, 2),
+ SimulTester(2 * h, 1), event, nsteps, RabinMiller.iters(qbits))
+ p = 2*q*h + 1
+ if q.nbits == qbits and p.nbits == pbits: return p, q, h
+ elif nsteps: raise ValueError("prime generation failed")
+
+#----- That's all, folks ----------------------------------------------------
--- /dev/null
+### -*-python-*-
+###
+### Management of a secure password database
+###
+### (c) 2005 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.
+
+from __future__ import with_statement
+
+import binascii as _B
+import errno as _E
+import os as _OS
+import sys as _SYS
+
+if _SYS.version_info >= (3,): from io import StringIO as _StringIO
+else: from cStringIO import StringIO as _StringIO
+
+import catacomb as _C
+
+###--------------------------------------------------------------------------
+### Python version portability.
+
+if _SYS.version_info >= (3,):
+ def _iterkeys(dict): return dict.keys()
+ def _itervalues(dict): return dict.values()
+ def _iteritems(dict): return dict.items()
+ def _bin(text): return text.encode(errors = "surrogateescape")
+ def _text(bin): return bin.decode(errors = "surrogateescape")
+else:
+ def _iterkeys(dict): return dict.iterkeys()
+ def _itervalues(dict): return dict.itervalues()
+ def _iteritems(dict): return dict.iteritems()
+ def _bin(text): return text
+ def _text(bin): return bin
+
+_NUL = _bin('\0')
+_CIPHER = _bin('cipher:')
+_MAC = _bin('mac:')
+
+def _with_metaclass(meta, *supers):
+ return meta("#<anonymous base %s>" % meta.__name__,
+ supers or (object,), dict())
+
+def _excval(): return SYS.exc_info()[1]
+
+_M600 = int("600", 8)
+_M700 = int("700", 8)
+
+###--------------------------------------------------------------------------
+### Text encoding utilities.
+
+def _literalp(s):
+ """
+ Answer whether S can be represented literally.
+
+ If True, then S can be stored literally, as a metadata item name or
+ value; if False, then S requires some kind of encoding.
+ """
+ return all(ch.isalnum() or ch in '-_:' for ch in s)
+
+def _enc_metaname(name):
+ """Encode NAME as a metadata item name, returning the result."""
+ if _literalp(name):
+ return name
+ else:
+ sio = _StringIO()
+ sio.write('!')
+ for ch in name:
+ if _literalp(ch): sio.write(ch)
+ elif ch == ' ': sio.write('+')
+ else: sio.write('%%%02x' % ord(ch))
+ return sio.getvalue()
+
+def _dec_metaname(name):
+ """Decode NAME as a metadata item name, returning the result."""
+ if not name.startswith('!'):
+ return name
+ else:
+ sio = _StringIO()
+ i, n = 1, len(name)
+ while i < n:
+ ch = name[i]
+ i += 1
+ if ch == '+':
+ sio.write(' ')
+ elif ch == '%':
+ sio.write(chr(int(name[i:i + 2], 16)))
+ i += 2
+ else:
+ sio.write(ch)
+ return sio.getvalue()
+
+def _b64(s):
+ """Encode S as base64, without newlines, and trimming `=' padding."""
+ return _text(_B.b2a_base64(s)).replace('\n', '').rstrip('=')
+def _unb64(s):
+ """Decode S as base64 with trimmed `=' padding."""
+ return _B.a2b_base64(s + '='*((4 - len(s))%4))
+
+def _enc_metaval(val):
+ """Encode VAL as a metadata item value, returning the result."""
+ if _literalp(val): return val
+ else: return '?' + _b64(val)
+
+def _dec_metaval(val):
+ """Decode VAL as a metadata item value, returning the result."""
+ if not val.startswith('?'): return val
+ else: return _unb64(val[1:])
+
+###--------------------------------------------------------------------------
+### Underlying cryptography.
+
+class DecryptError (Exception):
+ """
+ I represent a failure to decrypt a message.
+
+ Usually this means that someone used the wrong key, though it can also
+ mean that a ciphertext has been modified.
+ """
+ pass
+
+class Crypto (object):
+ """
+ I represent a symmetric crypto transform.
+
+ There's currently only one transform implemented, which is the obvious
+ generic-composition construction: given a message m, and keys K0 and K1, we
+ choose an IV v, and compute:
+
+ * y = v || E(K0, v; m)
+ * t = M(K1; y)
+
+ The final ciphertext is t || y.
+ """
+
+ def __init__(me, c, h, m, ck, mk):
+ """
+ Initialize the Crypto object with a given algorithm selection and keys.
+
+ We need a GCipher subclass C, a GHash subclass H, a GMAC subclass M, and
+ keys CK and MK for C and M respectively.
+ """
+ me.c = c(ck)
+ me.m = m(mk)
+ me.h = h
+
+ def encrypt(me, pt):
+ """
+ Encrypt the message PT and return the resulting ciphertext.
+ """
+ blksz = me.c.__class__.blksz
+ b = _C.WriteBuffer()
+ if blksz:
+ iv = _C.rand.block(blksz)
+ me.c.setiv(iv)
+ b.put(iv)
+ b.put(me.c.encrypt(pt))
+ t = me.m().hash(b).done()
+ return t + str(buffer(b))
+
+ def decrypt(me, ct):
+ """
+ Decrypt the ciphertext CT, returning the plaintext.
+
+ Raises DecryptError if anything goes wrong.
+ """
+ blksz = me.c.__class__.blksz
+ tagsz = me.m.__class__.tagsz
+ b = _C.ReadBuffer(ct)
+ t = b.get(tagsz)
+ h = me.m()
+ if blksz:
+ iv = b.get(blksz)
+ me.c.setiv(iv)
+ h.hash(iv)
+ x = b.get(b.left)
+ h.hash(x)
+ if t != h.done(): raise DecryptError
+ return me.c.decrypt(x)
+
+class PPK (Crypto):
+ """
+ I represent a crypto transform whose keys are derived from a passphrase.
+
+ The password is salted and hashed; the salt is available as the `salt'
+ attribute.
+ """
+
+ def __init__(me, pp, c, h, m, salt = None):
+ """
+ Initialize the PPK object with a passphrase and algorithm selection.
+
+ We want a passphrase PP, a GCipher subclass C, a GHash subclass H, a GMAC
+ subclass M, and a SALT. The SALT may be None, if we're generating new
+ keys, indicating that a salt should be chosen randomly.
+ """
+ if not salt: salt = _C.rand.block(h.hashsz)
+ tag = pp + _NUL + salt
+ Crypto.__init__(me, c, h, m,
+ h().hash(_CIPHER).hash(tag).done(),
+ h().hash(_MAC).hash(tag).done())
+ me.salt = salt
+
+###--------------------------------------------------------------------------
+### Backend storage.
+
+class StorageBackendRefusal (Exception):
+ """
+ I signify that a StorageBackend subclass has refused to open a file.
+
+ This is used by the StorageBackend.open class method.
+ """
+ pass
+
+class StorageBackendClass (type):
+ """
+ I am a metaclass for StorageBackend classes.
+
+ My main feature is that I register my concrete instances (with a `NAME'
+ which is not `None') with the StorageBackend class.
+ """
+ def __init__(me, name, supers, dict):
+ """
+ Register a new concrete StorageBackend subclass.
+ """
+ super(StorageBackendClass, me).__init__(name, supers, dict)
+ try: name = me.NAME
+ except AttributeError: pass
+ else: StorageBackend.register_concrete_subclass(me)
+
+class StorageBackend (_with_metaclass(StorageBackendClass)):
+ """
+ I provide basic protocol for password storage backends.
+
+ I'm an abstract class: you want one of my subclasses if you actually want
+ to do something useful. But I maintain a list of my subclasses and can
+ choose an appropriate one to open a database file you've found lying about.
+
+ Backends are responsible for storing and retrieving stuff, but not for the
+ cryptographic details. Backends need to store two kinds of information:
+
+ * metadata, consisting of a number of property names and their values;
+ and
+
+ * password mappings, consisting of a number of binary labels and
+ payloads.
+
+ Backends need to implement the following ordinary methods. See the calling
+ methods for details of the subclass responsibilities.
+
+ BE._create(FILE) Create a new database in FILE; used by `create'.
+
+ BE._open(FILE, WRITEP)
+ Open the existing database FILE; used by `open'.
+
+ BE._close(ABRUPTP) Close the database, freeing up any resources. If
+ ABRUPTP then don't try to commit changes.
+
+ BE._get_meta(NAME, DEFAULT)
+ Return the value of the metadata item with the given
+ NAME, or DEFAULT if it doesn't exist; used by
+ `get_meta'.
+
+ BE._put_meta(NAME, VALUE)
+ Set the VALUE of the metadata item with the given
+ NAME, creating one if necessary; used by `put_meta'.
+
+ BE._del_meta(NAME) Forget the metadata item with the given NAME; raise
+ `KeyError' if there is no such item; used by
+ `del_meta'.
+
+ BE._iter_meta() Return an iterator over the metadata (NAME, VALUE)
+ pairs; used by `iter_meta'.
+
+ BE._get_passwd(LABEL)
+ Return the password payload stored with the (binary)
+ LABEL; used by `get_passwd'.
+
+ BE._put_passwd(LABEL, PAYLOAD)
+ Associate the (binary) PAYLOAD with the LABEL,
+ forgetting any previous payload for that LABEL; used
+ by `put_passwd'.
+
+ BE._del_passwd(LABEL) Forget the password record with the given LABEL; used
+ by `_del_passwd'.
+
+ BE._iter_passwds() Return an iterator over the password (LABEL, PAYLOAD)
+ pairs; used by `iter_passwds'.
+
+ Also, concrete subclasses should define the following class attributes.
+
+ NAME The name of the backend, so that the user can select
+ it when creating a new database.
+
+ PRIO An integer priority: backends are tried in decreasing
+ priority order when opening an existing database.
+ """
+
+ PRIO = 10
+
+ ## The registry of subclasses.
+ CLASSES = {}
+
+ FAIL = ['FAIL']
+
+ @staticmethod
+ def register_concrete_subclass(sub):
+ """Register a concrete subclass, so that `open' can try it."""
+ StorageBackend.CLASSES[sub.NAME] = sub
+
+ @staticmethod
+ def byname(name):
+ """
+ Return the concrete subclass with the given NAME.
+
+ Raise `KeyError' if the name isn't found.
+ """
+ return StorageBackend.CLASSES[name]
+
+ @staticmethod
+ def classes():
+ """Return an iterator over the concrete subclasses."""
+ return _itervalues(StorageBackend.CLASSES)
+
+ @staticmethod
+ def open(file, writep = False):
+ """Open a database FILE, using some appropriate backend."""
+ _OS.stat(file)
+ for cls in sorted(StorageBackend.CLASSES.values(), reverse = True,
+ key = lambda cls: cls.PRIO):
+ try: return cls(file, writep)
+ except StorageBackendRefusal: pass
+ raise StorageBackendRefusal
+
+ @classmethod
+ def create(cls, file):
+ """
+ Create a new database in the named FILE, using this backend.
+
+ Subclasses must implement the `_create' instance method.
+ """
+ return cls(writep = True, _magic = lambda me: me._create(file))
+
+ def __init__(me, file = None, writep = False, _magic = None, *args, **kw):
+ """
+ Main constructor.
+
+ Subclasses are not, in general, expected to override this: there's a
+ somewhat hairy protocol between the constructor and some of the class
+ methods. Instead, the main hook for customization is the subclass's
+ `_open' method, which is invoked in the usual case.
+ """
+ super(StorageBackend, me).__init__(*args, **kw)
+ if me.NAME is None: raise ValueError('abstract class')
+ if _magic is not None: _magic(me)
+ elif file is None: raise ValueError('missing file parameter')
+ else: me._open(file, writep)
+ me._writep = writep
+ me._livep = True
+
+ def close(me, abruptp = False):
+ """
+ Close the database.
+
+ It is harmless to attempt to close a database which has been closed
+ already. Calls the subclass's `_close' method.
+ """
+ if me._livep:
+ me._livep = False
+ me._close(abruptp)
+
+ ## Utilities.
+
+ def _check_live(me):
+ """Raise an error if the receiver has been closed."""
+ if not me._livep: raise ValueError('database is closed')
+
+ def _check_write(me):
+ """Raise an error if the receiver is not open for writing."""
+ me._check_live()
+ if not me._writep: raise ValueError('database is read-only')
+
+ def _check_meta_name(me, name):
+ """
+ Raise an error unless NAME is a valid name for a metadata item.
+
+ Metadata names may not start with `$': such names are reserved for
+ password storage.
+ """
+ if name.startswith('$'):
+ raise ValueError("invalid metadata key `%s'" % name)
+
+ ## Context protocol.
+
+ def __enter__(me):
+ """Context protocol: make sure the database is closed on exit."""
+ return me
+ def __exit__(me, exctype, excvalue, exctb):
+ """Context protocol: see `__enter__'."""
+ me.close(excvalue is not None)
+
+ ## Metadata.
+
+ def get_meta(me, name, default = FAIL):
+ """
+ Fetch the value for the metadata item NAME.
+
+ If no such item exists, then return DEFAULT if that was set; otherwise
+ raise a `KeyError'.
+
+ This calls the subclass's `_get_meta' method, which should return the
+ requested item or return the given DEFAULT value. It may assume that the
+ name is valid and the database is open.
+ """
+ me._check_meta_name(name)
+ me._check_live()
+ value = me._get_meta(name, default)
+ if value is StorageBackend.FAIL: raise KeyError(name)
+ return value
+
+ def put_meta(me, name, value):
+ """
+ Store VALUE in the metadata item called NAME.
+
+ This calls the subclass's `_put_meta' method, which may assume that the
+ name is valid and the database is open for writing.
+ """
+ me._check_meta_name(name)
+ me._check_write()
+ me._put_meta(name, value)
+
+ def del_meta(me, name):
+ """
+ Forget about the metadata item with the given NAME.
+
+ This calls the subclass's `_del_meta' method, which may assume that the
+ name is valid and the database is open for writing.
+ """
+ me._check_meta_name(name)
+ me._check_write()
+ me._del_meta(name)
+
+ def iter_meta(me):
+ """
+ Return an iterator over the name/value metadata items.
+
+ This calls the subclass's `_iter_meta' method, which may assume that the
+ database is open.
+ """
+ me._check_live()
+ return me._iter_meta()
+
+ def get_passwd(me, label):
+ """
+ Fetch and return the payload stored with the (opaque, binary) LABEL.
+
+ If there is no such payload then raise `KeyError'.
+
+ This calls the subclass's `_get_passwd' method, which may assume that the
+ database is open.
+ """
+ me._check_live()
+ return me._get_passwd(label)
+
+ def put_passwd(me, label, payload):
+ """
+ Associate the (opaque, binary) PAYLOAD with the (opaque, binary) LABEL.
+
+ Any previous payload for LABEL is forgotten.
+
+ This calls the subclass's `_put_passwd' method, which may assume that the
+ database is open for writing.
+ """
+ me._check_write()
+ me._put_passwd(label, payload)
+
+ def del_passwd(me, label):
+ """
+ Forget any PAYLOAD associated with the (opaque, binary) LABEL.
+
+ If there is no such payload then raise `KeyError'.
+
+ This calls the subclass's `_del_passwd' method, which may assume that the
+ database is open for writing.
+ """
+ me._check_write()
+ me._del_passwd(label)
+
+ def iter_passwds(me):
+ """
+ Return an iterator over the stored password label/payload pairs.
+
+ This calls the subclass's `_iter_passwds' method, which may assume that
+ the database is open.
+ """
+ me._check_live()
+ return me._iter_passwds()
+
+try: import gdbm as _G
+except ImportError: pass
+else:
+ class GDBMStorageBackend (StorageBackend):
+ """
+ My instances store password data in a GDBM database.
+
+ Metadata and password entries are mixed into the same database. The key
+ for a metadata item is simply its name; the key for a password entry is
+ the entry's label prefixed by `$', since we're guaranteed that no
+ metadata item name begins with `$'.
+ """
+
+ NAME = 'gdbm'
+
+ def _open(me, file, writep):
+ try: me._db = _G.open(file, writep and 'w' or 'r')
+ except _G.error: raise StorageBackendRefusal(_excval())
+
+ def _create(me, file):
+ me._db = _G.open(file, 'n', _M600)
+
+ def _close(me, abruptp):
+ me._db.close()
+ me._db = None
+
+ def _get_meta(me, name, default):
+ try: return me._db[name]
+ except KeyError: return default
+
+ def _put_meta(me, name, value):
+ me._db[name] = value
+
+ def _del_meta(me, name):
+ del me._db[name]
+
+ def _iter_meta(me):
+ k = me._db.firstkey()
+ while k is not None:
+ if not k.startswith('$'): yield k, me._db[k]
+ k = me._db.nextkey(k)
+
+ def _get_passwd(me, label):
+ return me._db['$' + label]
+
+ def _put_passwd(me, label, payload):
+ me._db['$' + label] = payload
+
+ def _del_passwd(me, label):
+ del me._db['$' + label]
+
+ def _iter_passwds(me):
+ k = me._db.firstkey()
+ while k is not None:
+ if k.startswith('$'): yield k[1:], me._db[k]
+ k = me._db.nextkey(k)
+
+try: import sqlite3 as _Q
+except ImportError: pass
+else:
+ class SQLiteStorageBackend (StorageBackend):
+ """
+ I represent a password database stored in SQLite.
+
+ Metadata and password items are stored in separate tables, so there's no
+ conflict. Some additional metadata is stored in the `meta' table, with
+ names beginning with `$' so as not to conflict with clients:
+
+ $version The schema version of the table.
+ """
+
+ NAME = 'sqlite'
+ VERSION = 0
+
+ def _open(me, file, writep):
+ try:
+ me._db = _Q.connect(file)
+ ver = me._query_scalar(
+ "SELECT value FROM meta WHERE name = '$version'",
+ "version check")
+ except (_Q.DatabaseError, _Q.OperationalError):
+ raise StorageBackendRefusal(_excval())
+ if ver is None: raise ValueError('database broken (missing $version)')
+ elif ver < me.VERSION: me._upgrade(ver)
+ elif ver > me.VERSION: raise ValueError \
+ ('unknown database schema version (%d > %d)' % (ver, me.VERSION))
+
+ def _create(me, file):
+ fd = _OS.open(file, _OS.O_WRONLY | _OS.O_CREAT | _OS.O_EXCL, _M600)
+ _OS.close(fd)
+ try:
+ me._db = _Q.connect(file)
+ c = me._db.cursor()
+ c.execute("""
+ CREATE TABLE meta (
+ name TEXT PRIMARY KEY NOT NULL,
+ value BLOB NOT NULL);
+ """)
+ c.execute("""
+ CREATE TABLE passwd (
+ label BLOB PRIMARY KEY NOT NULL,
+ payload BLOB NOT NULL);
+ """)
+ c.execute("""
+ INSERT INTO meta (name, value) VALUES ('$version', ?);
+ """, [me.VERSION])
+ except:
+ try: _OS.unlink(file)
+ except OSError: pass
+ raise
+
+ def _upgrade(me, ver):
+ """Upgrade the database from schema version VER."""
+ assert False, 'how embarrassing'
+
+ def _close(me, abruptp):
+ if not abruptp: me._db.commit()
+ me._db.close()
+ me._db = None
+
+ def _fetch_scalar(me, c, what, default = None):
+ try: row = next(c)
+ except StopIteration: val = default
+ else: val, = row
+ try: row = next(c)
+ except StopIteration: pass
+ else: raise ValueError('multiple matching records for %s' % what)
+ return val
+
+ def _query_scalar(me, query, what, default = None, args = []):
+ c = me._db.cursor()
+ c.execute(query, args)
+ return me._fetch_scalar(c, what, default)
+
+ def _get_meta(me, name, default):
+ v = me._query_scalar("SELECT value FROM meta WHERE name = ?",
+ "metadata item `%s'" % name,
+ default = default, args = [name])
+ if v is default: return v
+ else: return str(v)
+
+ def _put_meta(me, name, value):
+ c = me._db.cursor()
+ c.execute("INSERT OR REPLACE INTO meta (name, value) VALUES (?, ?)",
+ [name, buffer(value)])
+
+ def _del_meta(me, name):
+ c = me._db.cursor()
+ c.execute("DELETE FROM meta WHERE name = ?", [name])
+ if not c.rowcount: raise KeyError(name)
+
+ def _iter_meta(me):
+ c = me._db.cursor()
+ c.execute("SELECT name, value FROM meta WHERE name NOT LIKE '$%'")
+ for k, v in c: yield k, str(v)
+
+ def _get_passwd(me, label):
+ pld = me._query_scalar("SELECT payload FROM passwd WHERE label = ?",
+ "password", default = None,
+ args = [buffer(label)])
+ if pld is None: raise KeyError(label)
+ return str(pld)
+
+ def _put_passwd(me, label, payload):
+ c = me._db.cursor()
+ c.execute("INSERT OR REPLACE INTO passwd (label, payload) "
+ "VALUES (?, ?)",
+ [buffer(label), buffer(payload)])
+
+ def _del_passwd(me, label):
+ c = me._db.cursor()
+ c.execute("DELETE FROM passwd WHERE label = ?", [label])
+ if not c.rowcount: raise KeyError(label)
+
+ def _iter_passwds(me):
+ c = me._db.cursor()
+ c.execute("SELECT label, payload FROM passwd")
+ for k, v in c: yield str(k), str(v)
+
+class PlainTextBackend (StorageBackend):
+ """
+ I'm a utility base class for storage backends which use plain text files.
+
+ I provide subclasses with the following capabilities.
+
+ * Creating files, with given modes, optionally ensuring that the file
+ doesn't exist already.
+
+ * Parsing flat text files, checking leading magic, skipping comments, and
+ providing standard encodings of troublesome characters and binary
+ strings in metadata and password records. See below.
+
+ * Maintenance of metadata and password records in in-memory dictionaries,
+ with ready implementations of the necessary StorageBackend subclass
+ responsibility methods. (Subclasses can override these if they want to
+ make different arrangements.)
+
+ Metadata records are written with an optional prefix string chosen by the
+ caller, followed by a `NAME=VALUE' pair. The NAME is form-urlencoded and
+ prefixed with `!' if it contains strange characters; the VALUE is base64-
+ encoded (without the pointless trailing `=' padding) and prefixed with `?'
+ if necessary.
+
+ Password records are written with an optional prefix string chosen by the
+ caller, followed by a LABEL=PAYLOAD pair, both of which are base64-encoded
+ (without padding).
+
+ The following attributes are available for subclasses:
+
+ _meta Dictionary mapping metadata item names to their values.
+ Populated by `_parse_meta' and managed by `_get_meta' and
+ friends.
+
+ _pw Dictionary mapping password labels to encrypted payloads.
+ Populated by `_parse_passwd' and managed by `_get_passwd' and
+ friends.
+
+ _dirtyp Boolean: set if either of the dictionaries has been modified.
+ """
+
+ def __init__(me, *args, **kw):
+ """
+ Hook for initialization.
+
+ Sets up the published instance attributes.
+ """
+ me._meta = {}
+ me._pw = {}
+ me._dirtyp = False
+ super(PlainTextBackend, me).__init__(*args, **kw)
+
+ def _create_file(me, file, mode = _M600, freshp = False):
+ """
+ Make sure FILE exists, creating it with the given MODE if necessary.
+
+ If FRESHP is true, then make sure the file did not exist previously.
+ Return a file object for the newly created file.
+ """
+ flags = _OS.O_CREAT | _OS.O_WRONLY
+ if freshp: flags |= _OS.O_EXCL
+ else: flags |= _OS.O_TRUNC
+ fd = _OS.open(file, flags, mode)
+ return _OS.fdopen(fd, 'w')
+
+ def _mark_dirty(me):
+ """
+ Set the `_dirtyp' flag.
+
+ Subclasses might find it useful to intercept this method.
+ """
+ me._dirtyp = True
+
+ def _eqsplit(me, line):
+ """
+ Extract the KEY, VALUE pair from a LINE of the form `KEY=VALUE'.
+
+ Raise `ValueError' if there is no `=' in the LINE.
+ """
+ eq = line.index('=')
+ return line[:eq], line[eq + 1:]
+
+ def _parse_file(me, file, magic = None):
+ """
+ Parse a FILE.
+
+ Specifically:
+
+ * Raise `StorageBackendRefusal' if that the first line doesn't match
+ MAGIC (if provided). MAGIC should not contain the terminating
+ newline.
+
+ * Ignore comments (beginning `#') and blank lines.
+
+ * Call `_parse_line' (provided by the subclass) for other lines.
+ """
+ with open(file, 'r') as f:
+ if magic is not None:
+ if f.readline().rstrip('\n') != magic: raise StorageBackendRefusal
+ for line in f:
+ line = line.rstrip('\n')
+ if not line or line.startswith('#'): continue
+ me._parse_line(line)
+
+ def _write_file(me, file, writebody, mode = _M600, magic = None):
+ """
+ Update FILE atomically.
+
+ The newly created file will have the given MODE. If MAGIC is given, then
+ write that as the first line. Calls WRITEBODY(F) to write the main body
+ of the file where F is a file object for the new file.
+ """
+ new = file + '.new'
+ with me._create_file(new, mode) as f:
+ if magic is not None: f.write(magic + '\n')
+ writebody(f)
+ _OS.rename(new, file)
+
+ def _parse_meta(me, line):
+ """Parse LINE as a metadata NAME=VALUE pair, and updates `_meta'."""
+ k, v = me._eqsplit(line)
+ me._meta[_dec_metaname(k)] = _dec_metaval(v)
+
+ def _write_meta(me, f, prefix = ''):
+ """Write the metadata records to F, each with the given PREFIX."""
+ f.write('\n## Metadata.\n')
+ for k, v in _iteritems(me._meta):
+ f.write('%s%s=%s\n' % (prefix, _enc_metaname(k), _enc_metaval(v)))
+
+ def _get_meta(me, name, default):
+ return me._meta.get(name, default)
+ def _put_meta(me, name, value):
+ me._mark_dirty()
+ me._meta[name] = value
+ def _del_meta(me, name):
+ me._mark_dirty()
+ del me._meta[name]
+ def _iter_meta(me):
+ return _iteritems(me._meta)
+
+ def _parse_passwd(me, line):
+ """Parse LINE as a password LABEL=PAYLOAD pair, and updates `_pw'."""
+ k, v = me._eqsplit(line)
+ me._pw[_unb64(k)] = _unb64(v)
+
+ def _write_passwd(me, f, prefix = ''):
+ """Write the password records to F, each with the given PREFIX."""
+ f.write('\n## Password data.\n')
+ for k, v in _iteritems(me._pw):
+ f.write('%s%s=%s\n' % (prefix, _b64(k), _b64(v)))
+
+ def _get_passwd(me, label):
+ return me._pw[str(label)]
+ def _put_passwd(me, label, payload):
+ me._mark_dirty()
+ me._pw[str(label)] = payload
+ def _del_passwd(me, label):
+ me._mark_dirty()
+ del me._pw[str(label)]
+ def _iter_passwds(me):
+ return _iteritems(me._pw)
+
+class FlatFileStorageBackend (PlainTextBackend):
+ """
+ I maintain a password database in a plain text file.
+
+ The text file consists of lines, as follows.
+
+ * Empty lines, and lines beginning with `#' (in the leftmost column only)
+ are ignored.
+
+ * Lines of the form `$LABEL=PAYLOAD' store password data. Both LABEL and
+ PAYLOAD are base64-encoded, without `=' padding.
+
+ * Lines of the form `NAME=VALUE' store metadata. If the NAME contains
+ characters other than alphanumerics, hyphens, underscores, and colons,
+ then it is form-urlencoded, and prefixed wth `!'. If the VALUE
+ contains such characters, then it is base64-encoded, without `='
+ padding, and prefixed with `?'.
+
+ * Other lines are erroneous.
+
+ The file is rewritten from scratch when it's changed: any existing
+ commentary is lost, and items may be reordered. There is no file locking,
+ but the file is updated atomically, by renaming.
+
+ It is expected that the FlatFileStorageBackend is used mostly for
+ diagnostics and transfer, rather than for a live system.
+ """
+
+ NAME = 'flat'
+ PRIO = 0
+ MAGIC = '### pwsafe password database'
+
+ def _open(me, file, writep):
+ if not _OS.path.isfile(file): raise StorageBackendRefusal
+ me._parse_file(file, magic = me.MAGIC)
+ def _parse_line(me, line):
+ if line.startswith('$'): me._parse_passwd(line[1:])
+ else: me._parse_meta(line)
+
+ def _create(me, file):
+ with me._create_file(file, freshp = True) as f: pass
+ me._file = file
+ me._mark_dirty()
+
+ def _close(me, abruptp):
+ if not abruptp and me._dirtyp:
+ me._write_file(me._file, me._write_body, magic = me.MAGIC)
+
+ def _write_body(me, f):
+ me._write_meta(f)
+ me._write_passwd(f, '$')
+
+class DirectoryStorageBackend (PlainTextBackend):
+ """
+ I maintain a password database in a directory, with one file per password.
+
+ This makes password databases easy to maintain in a revision-control system
+ such as Git.
+
+ The directory is structured as follows.
+
+ dir/meta Contains metadata, similar to the `FlatFileBackend'.
+
+ dir/pw/LABEL Contains the (raw binary) payload for the given password
+ LABEL (base64-encoded, without the useless `=' padding, and
+ with `/' replaced by `.').
+
+ dir/tmp/ Contains temporary files used by the implementation.
+ """
+
+ NAME = 'dir'
+ METAMAGIC = '### pwsafe password directory metadata'
+
+ def _open(me, file, writep):
+ if not _OS.path.isdir(file) or \
+ not _OS.path.isdir(_OS.path.join(file, 'pw')) or \
+ not _OS.path.isdir(_OS.path.join(file, 'tmp')) or \
+ not _OS.path.isfile(_OS.path.join(file, 'meta')):
+ raise StorageBackendRefusal
+ me._dir = file
+ me._parse_file(_OS.path.join(file, 'meta'), magic = me.METAMAGIC)
+ def _parse_line(me, line):
+ me._parse_meta(line)
+
+ def _create(me, file):
+ _OS.mkdir(file, _M700)
+ _OS.mkdir(_OS.path.join(file, 'pw'), _M700)
+ _OS.mkdir(_OS.path.join(file, 'tmp'), _M700)
+ me._mark_dirty()
+ me._dir = file
+
+ def _close(me, abruptp):
+ if not abruptp and me._dirtyp:
+ me._write_file(_OS.path.join(me._dir, 'meta'),
+ me._write_meta, magic = me.METAMAGIC)
+
+ def _pwfile(me, label, dir = 'pw'):
+ return _OS.path.join(me._dir, dir, _b64(label).replace('/', '.'))
+ def _get_passwd(me, label):
+ try:
+ f = open(me._pwfile(label), 'rb')
+ except (OSError, IOError):
+ if _excval().errno == _E.ENOENT: raise KeyError(label)
+ else: raise
+ with f: return f.read()
+ def _put_passwd(me, label, payload):
+ new = me._pwfile(label, 'tmp')
+ fd = _OS.open(new, _OS.O_WRONLY | _OS.O_CREAT | _OS.O_TRUNC, _M600)
+ _OS.close(fd)
+ with open(new, 'wb') as f: f.write(payload)
+ _OS.rename(new, me._pwfile(label))
+ def _del_passwd(me, label):
+ try:
+ _OS.remove(me._pwfile(label))
+ except (OSError, IOError):
+ if _excval().errno == _E.ENOENT: raise KeyError(label)
+ else: raise
+ def _iter_passwds(me):
+ pw = _OS.path.join(me._dir, 'pw')
+ for i in _OS.listdir(pw):
+ with open(_OS.path.join(pw, i), 'rb') as f: pld = f.read()
+ yield _unb64(i.replace('.', '/')), pld
+
+###--------------------------------------------------------------------------
+### Password storage.
+
+class PW (object):
+ """
+ I represent a secure (ish) password store.
+
+ I can store short secrets, associated with textual names, in a way which
+ doesn't leak too much information about them.
+
+ I implement (some of) the Python mapping protocol.
+
+ I keep track of everything using a StorageBackend object. This contains
+ password entries, identified by cryptographic labels, and a number of
+ metadata items.
+
+ cipher Names the Catacomb cipher selected.
+
+ hash Names the Catacomb hash function selected.
+
+ key Cipher and MAC keys, each prefixed by a 16-bit big-endian
+ length and concatenated, encrypted using the master
+ passphrase.
+
+ mac Names the Catacomb message authentication code selected.
+
+ magic A magic string for obscuring password tag names.
+
+ salt The salt for hashing the passphrase.
+
+ tag The master passphrase's tag, for the Pixie's benefit.
+
+ Password entries are assigned labels of the form `$' || H(MAGIC || TAG);
+ the corresponding value consists of a pair (TAG, PASSWD), prefixed with
+ 16-bit lengths, concatenated, padded to a multiple of 256 octets, and
+ encrypted using the stored keys.
+ """
+
+ def __init__(me, file, writep = False):
+ """
+ Initialize a PW object from the database in FILE.
+
+ If WRITEP is false (the default) then the database is opened read-only;
+ if true then it may be written. Requests the database password from the
+ Pixie, which may cause interaction.
+ """
+
+ ## Open the database.
+ me.db = StorageBackend.open(file, writep)
+
+ ## Find out what crypto to use.
+ c = _C.gcciphers[me.db.get_meta('cipher')]
+ h = _C.gchashes[me.db.get_meta('hash')]
+ m = _C.gcmacs[me.db.get_meta('mac')]
+
+ ## Request the passphrase and extract the master keys.
+ tag = me.db.get_meta('tag')
+ ppk = PPK(_C.ppread(tag), c, h, m, me.db.get_meta('salt'))
+ try:
+ b = _C.ReadBuffer(ppk.decrypt(me.db.get_meta('key')))
+ except DecryptError:
+ _C.ppcancel(tag)
+ raise
+ me.ck = b.getblk16()
+ me.mk = b.getblk16()
+ if not b.endp: raise ValueError('trailing junk')
+
+ ## Set the key, and stash it and the tag-hashing secret.
+ me.k = Crypto(c, h, m, me.ck, me.mk)
+ me.magic = me.k.decrypt(me.db.get_meta('magic'))
+
+ @classmethod
+ def create(cls, dbcls, file, tag, c, h, m):
+ """
+ Create and initialize a new database FILE using StorageBackend DBCLS.
+
+ We want a GCipher subclass C, a GHash subclass H, and a GMAC subclass M;
+ and a Pixie passphrase TAG.
+
+ This doesn't return a working object: it just creates the database file
+ and gets out of the way.
+ """
+
+ ## Set up the cryptography.
+ pp = _C.ppread(tag, _C.PMODE_VERIFY)
+ ppk = PPK(pp, c, h, m)
+ ck = _C.rand.block(c.keysz.default)
+ mk = _C.rand.block(c.keysz.default)
+ k = Crypto(c, h, m, ck, mk)
+
+ ## Set up and initialize the database.
+ kct = ppk.encrypt(_C.WriteBuffer().putblk16(ck).putblk16(mk))
+ with dbcls.create(file) as db:
+ db.put_meta('tag', tag)
+ db.put_meta('salt', ppk.salt)
+ db.put_meta('cipher', c.name)
+ db.put_meta('hash', h.name)
+ db.put_meta('mac', m.name)
+ db.put_meta('key', kct)
+ db.put_meta('magic', k.encrypt(_C.rand.block(h.hashsz)))
+
+ def keyxform(me, key):
+ """Transform the KEY (actually a password tag) into a password label."""
+ return me.k.h().hash(me.magic).hash(key).done()
+
+ def changepp(me):
+ """
+ Change the database password.
+
+ Requests the new password from the Pixie, which will probably cause
+ interaction.
+ """
+ tag = me.db.get_meta('tag')
+ _C.ppcancel(tag)
+ ppk = PPK(_C.ppread(tag, _C.PMODE_VERIFY),
+ me.k.c.__class__, me.k.h, me.k.m.__class__)
+ kct = ppk.encrypt(_C.WriteBuffer().putblk16(me.ck).putblk16(me.mk))
+ me.db.put_meta('key', kct)
+ me.db.put_meta('salt', ppk.salt)
+
+ def pack(me, key, value):
+ """Pack the KEY and VALUE into a ciphertext, and return it."""
+ b = _C.WriteBuffer()
+ b.putblk16(key).putblk16(value)
+ b.zero(((b.size + 255) & ~255) - b.size)
+ return me.k.encrypt(b)
+
+ def unpack(me, ct):
+ """
+ Unpack a ciphertext CT and return a (KEY, VALUE) pair.
+
+ Might raise DecryptError, of course.
+ """
+ b = _C.ReadBuffer(me.k.decrypt(ct))
+ key = b.getblk16()
+ value = b.getblk16()
+ return key, value
+
+ ## Mapping protocol.
+
+ def __getitem__(me, key):
+ """Return the password for the given KEY."""
+ try: return me.unpack(me.db.get_passwd(me.keyxform(key)))[1]
+ except KeyError: raise KeyError(key)
+
+ def __setitem__(me, key, value):
+ """Associate the password VALUE with the KEY."""
+ me.db.put_passwd(me.keyxform(key), me.pack(key, value))
+
+ def __delitem__(me, key):
+ """Forget all about the KEY."""
+ try: me.db.del_passwd(me.keyxform(key))
+ except KeyError: raise KeyError(key)
+
+ def __iter__(me):
+ """Iterate over the known password tags."""
+ for _, pld in me.db.iter_passwds():
+ yield me.unpack(pld)[0]
+
+ ## Context protocol.
+
+ def __enter__(me):
+ return me
+ def __exit__(me, excty, excval, exctb):
+ me.db.close(excval is not None)
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+files
+tmp
+substvars
+*.substvars
+*.debhelper
+python-catacomb-bin
+python-catacomb
+*.log
--- /dev/null
+catacomb-python (1.3.99~) experimental; urgency=medium
+
+ * (placeholder for next release)
+
+ -- Mark Wooding <mdw@distorted.org.uk> Tue, 01 Oct 2019 12:57:58 +0100
+
+catacomb-python (1.3.0.1) experimental; urgency=medium
+
+ * Fix required Catacomb version in `setup.py' script. Only affects the
+ source package.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sun, 22 Sep 2019 01:21:28 +0100
+
+catacomb-python (1.3.0) experimental; urgency=medium
+
+ * catacomb: Bindings for new blockcipher-based MACs, and AEAD schemes.
+ * catacomb: Invalidate `grand' objects passed into Python through prime-
+ generation events.
+ * catacomb: Improve class docstrings. (They're still extremely terse.)
+ * catacomb: Add missing `copy' methods on hash and Keccak objects.
+ * catacomb: Add `WriteBuffer.contents' as a more convenient way to
+ extract the contents than coercing to `str' or `ByteString'.
+ * catacomb: Set `RTLD_DEEPBIND' while loading the native module to work
+ around #868366.
+ * pock: New program for generating efficiently verifiable prime numbers,
+ and for verifying their certificates.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sat, 21 Sep 2019 23:00:25 +0100
+
+catacomb-python (1.2.1.1) experimental; urgency=medium
+
+ * Fixing to build against Debian `stretch'.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Mon, 24 Dec 2018 15:21:08 +0000
+
+catacomb-python (1.2.1) experimental; urgency=low
+
+ * Fix use-after-free bug in ECPt hashing causing hash instability.
+ * Fix keywrd argument mismatch in ECDSAPriv.
+ * Coerce DSA private key values to `MP'.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Fri, 16 Jun 2017 01:01:05 +0100
+
+catacomb-python (1.2.0) experimental; urgency=low
+
+ * Bindings for HSalsa20 and HChaCha PRFs.
+ * Bindings for Poly1305.
+ * Bindings for X25519.
+ * Bindings for X448.
+ * Bindings for Ed25519.
+ * Bindings for Ed448.
+ * Bindings for Keccak[1600, n].
+ * Bindings for cSHAKE; Python implementation of KMAC.
+ * Improved pretty-printing for IPython.
+ * Allow selection of public exponent when generating RSA keys.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sun, 14 May 2017 17:29:53 +0100
+
+catacomb-python (1.1.2) experimental; urgency=low
+
+ * Further 64-bit compatibility improvements.
+ * Fixed docstrings for a number of native methods.
+ * Fix range checking for `GRand' methods.
+ * Fix crash when deleting natively implemented object attributes.
+ * Fix bug which assigned the wrong hash to bytestrings.
+ * Fix `__int__' methods to return `long' objects when necessary.
+ Without this, there are unnecessary failures and bizarrely timed
+ exceptions.
+ * A couple of patches to provide forward compatibility with upstream
+ library changes.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sun, 14 May 2017 04:25:35 +0100
+
+catacomb-python (1.1.1) experimental; urgency=low
+
+ * ByteString operators: fix crashes on 64-bit platforms resulting from
+ the use of the wrong length types.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sat, 04 Jun 2016 01:17:14 +0100
+
+catacomb-python (1.1.0.1) experimental; urgency=low
+
+ * Just Debian packaging fixes.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Fri, 19 Feb 2016 19:39:28 +0000
+
+catacomb-python (1.1.0) experimental; urgency=low
+
+ * Fix crash on trying to import RSA public and private keys with even
+ modulus.
+ * Add bindings for key-strength conversion functions.
+ * Add bindings for factorial and Fibonacci calculations.
+ * Implement exact field-of-fractions arithmetic for integers and binary
+ polynomials.
+ * Set module nameson types properly, so that IPython doesn't choke on
+ printing them.
+ * Implement hashing for comparable types. Again, this makes IPython
+ work better.
+ * Support Daniel Bernstein's Salsa20 and ChaCha stream ciphers.
+ * Stop abusing SyntaxError.
+ * Support elliptic curve point compression formats.
+ * Major overhaul of the `pwsafe' utility: multiple storage backends, so
+ (a) we no longer require GDBM, and (b) we can use a Git-friendly
+ directory-based format. Or SQLite.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Mon, 20 Jul 2015 16:07:51 +0100
+
+catacomb-python (1.0.6) experimental; urgency=low
+
+ * Fix stupid bug in WriteBuffer resizing.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sun, 21 Jul 2013 15:57:06 +0100
+
+catacomb-python (1.0.5) experimental; urgency=low
+
+ * Make Key constructor actually work.
+ * Include human-readable text in KeyError exceptions.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sat, 04 Jan 2014 01:38:26 +0000
+
+catacomb-python (1.0.4) experimental; urgency=low
+
+ * Build against Python 2.7.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Wed, 09 Jan 2013 02:47:59 +0000
+
+catacomb-python (1.0.3) experimental; urgency=low
+
+ * Remove catacomb-python-bin package, because it made building hard and
+ didn't provide any noticeable benefits.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Fri, 05 Nov 2010 15:12:35 +0000
+
+catacomb-python (1.0.2) experimental; urgency=low
+
+ * Overhaul build system with new support module.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sun, 04 Oct 2009 14:17:20 +0100
+
+catacomb-python (1.0.1) experimental; urgency=low
+
+ * Make it work with Python 2.6. Also switched over to the (increasingly
+ winning) CDBS.
+
+ -- Mark Wooding <mdw@distorted.org.uk> Sat, 03 Oct 2009 23:06:29 +0100
+
+catacomb-python (1.0.0) experimental; urgency=low
+
+ * Debianization!
+
+ -- Mark Wooding <mdw@distorted.org.uk> Mon, 26 Sep 2005 11:27:00 +0100
--- /dev/null
+Source: catacomb-python
+Section: python
+Priority: extra
+XS-Python-Version: >= 2.6, << 2.8
+XS-Python3-Version: >= 3.0
+Maintainer: Mark Wooding <mdw@distorted.org.uk>
+Build-Depends: debhelper (>= 10), dh-python, pkg-config,
+ python (>= 2.6.6-3~), python-all-dev, python3-all-dev,
+ mlib-dev (>= 2.4.99~), catacomb-dev (>= 2.5.99~)
+Standards-Version: 3.8.0
+
+Package: python-catacomb
+Architecture: any
+XB-Python-Version: ${python:Versions}
+Depends: ${shlibs:Depends}, ${python:Depends}
+Provides: ${python:Provides}, catacomb-python-bin
+Conflicts: catacomb-python-bin
+Description: Python bindings for the Catacomb cryptographic library.
+ Catacomb is a cryptographic library. It implements a large number of
+ encryption algorithms, hash functions, message authentication codes
+ and random number generators. It has a multi-precision maths library,
+ for implementing public key schemes such as RSA, DSA and Diffie-Hellman.
+ It contains rudimentary key-management tools.
+ .
+ The objective of Catacomb is to make a crypto library which is
+ relatively straightforward to audit for security. Its focus is on
+ clarity of source code and portability more than performance.
+ .
+ This package allows the Catacomb library to be used in Python programs.
+
+Package: python3-catacomb
+Architecture: any
+XB-Python-Version: ${python3:Versions}
+Depends: ${shlibs:Depends}, ${python3:Depends}
+Provides: ${python3:Provides}
+Description: Python 3 bindings for the Catacomb cryptographic library.
+ Catacomb is a cryptographic library. It implements a large number of
+ encryption algorithms, hash functions, message authentication codes
+ and random number generators. It has a multi-precision maths library,
+ for implementing public key schemes such as RSA, DSA and Diffie-Hellman.
+ It contains rudimentary key-management tools.
+ .
+ The objective of Catacomb is to make a crypto library which is
+ relatively straightforward to audit for security. Its focus is on
+ clarity of source code and portability more than performance.
+ .
+ This package allows the Catacomb library to be used in Python 3 programs.
--- /dev/null
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Copyright: 2006--2010, 2012--2017 Straylight/Edgeware
+Upstream-Name: catacomb-python
+Upstream-Contact: Mark Wooding <mdw@distorted.org.uk>
+Source: https://ftp.distorted.org.uk/pub/mdw/
+License: LGPL-2.0+
+
+Files: *
+Copyright: 2006--2010, 2012--2017 Straylight/Edgeware
+License: LGPL-2.0+
+
+License: LGPL-2.0+
+ Catacomb/Python is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library 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
+ Library General Public License for more details.
+ .
+ You should have a copy of the GNU Library General Public License in
+ /usr/share/common-licenses/LGPL-2; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+ .
+ On Debian systems, the full text of the GNU Library General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/LGPL-2'.
--- /dev/null
+debian/tmp/usr/bin
+debian/tmp/usr/share/man
+debian/tmp/usr/lib/python2.*
--- /dev/null
+debian/tmp/usr/lib/python3.*
--- /dev/null
+#! /usr/bin/make -f
+%:; dh $@ --parallel --with python2,python3
+
+export PYTHONS := $(shell pyversions -r && py3versions -r)
+
+override_dh_auto_test:
+ dh_auto_test -- OPTS-check=-V
+
+override_dh_auto_install:
+ dh_auto_install -- prefix=/usr
--- /dev/null
+/* -*-c-*-
+ *
+ * Elliptic curves
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Utility functions -------------------------------------------------*/
+
+PyTypeObject *ecpt_pytype;
+PyTypeObject *ecptcurve_pytype;
+PyTypeObject *eccurve_pytype;
+PyTypeObject *ecprimecurve_pytype;
+PyTypeObject *ecprimeprojcurve_pytype;
+PyTypeObject *ecbincurve_pytype;
+PyTypeObject *ecbinprojcurve_pytype;
+PyTypeObject *ecinfo_pytype;
+
+ec_curve *eccurve_copy(ec_curve *c)
+{
+ field *f;
+ mp *a, *b;
+
+ if ((f = field_copy(c->f)) == 0)
+ return (0);
+ a = F_OUT(f, MP_NEW, c->a);
+ b = F_OUT(f, MP_NEW, c->b);
+ if (STRCMP(EC_NAME(c), ==, "prime"))
+ c = ec_prime(f, a, b);
+ else if (STRCMP(EC_NAME(c), ==, "primeproj"))
+ c = ec_primeproj(f, a, b);
+ else if (STRCMP(EC_NAME(c), ==, "bin"))
+ c = ec_bin(f, a, b);
+ else if (STRCMP(EC_NAME(c), ==, "binproj"))
+ c = ec_binproj(f, a, b);
+ else
+ c = 0;
+ MP_DROP(a);
+ MP_DROP(b);
+ if (!c) F_DESTROY(f);
+ return (c);
+}
+
+static PyObject *ecpt_dopywrap(PyObject *cobj, ec_curve *c, ec *p)
+{
+ ecpt_pyobj *z = PyObject_New(ecpt_pyobj, (PyTypeObject *)cobj);
+ z->p = *p;
+ z->c = c;
+ Py_INCREF(cobj);
+ return ((PyObject *)z);
+}
+
+PyObject *ecpt_pywrap(PyObject *cobj, ec *p)
+ { return (ecpt_dopywrap(cobj, ECCURVE_C(cobj), p)); }
+
+PyObject *ecpt_pywrapout(void *cobj, ec *p)
+{
+ ec_curve *c;
+
+ if (!PyType_IsSubtype(cobj, ecptcurve_pytype))
+ c = 0;
+ else {
+ c = ECCURVE_C(cobj);
+ EC_IN(ECCURVE_C(cobj), p, p);
+ }
+ return (ecpt_dopywrap(cobj, c, p));
+}
+
+int toecpt(ec_curve *c, ec *d, PyObject *p)
+{
+ if (ECPTCURVE_PYCHECK(p)) {
+ if (ECPT_C(p) != c && !ec_samep(ECPT_C(p), c))
+ return (-1);
+ EC_COPY(d, ECPT_P(p));
+ } else if (ECPT_PYCHECK(p))
+ EC_IN(c, d, ECPT_P(p));
+ else
+ return (-1);
+ return (0);
+}
+
+int getecpt(ec_curve *c, ec *d, PyObject *p)
+{
+ if (toecpt(c, d, p)) {
+ PyErr_Format(PyExc_TypeError, "can't convert %.100s to ecpt",
+ p->ob_type->tp_name);
+ return (-1);
+ }
+ return (0);
+}
+
+void getecptout(ec *d, PyObject *p)
+{
+ if (ECPTCURVE_PYCHECK(p))
+ EC_OUT(ECPT_C(p), d, ECPT_P(p));
+ else {
+ assert(ECPT_PYCHECK(p));
+ EC_COPY(d, ECPT_P(p));
+ }
+}
+
+int convecpt(PyObject *o, void *p)
+{
+ if (!ECPT_PYCHECK(o))
+ TYERR("want elliptic curve point");
+ getecptout(p, o);
+ return (1);
+end:
+ return (0);
+}
+
+/*----- Curve points ------------------------------------------------------*/
+
+static int ecbinop(PyObject *x, PyObject *y,
+ ec_curve **c, PyObject **cobj, ec *xx, ec *yy)
+{
+ if (ECPTCURVE_PYCHECK(x)) *cobj = ECPT_COBJ(x);
+ else if (ECPTCURVE_PYCHECK(y)) *cobj = ECPT_COBJ(y);
+ else return (-1);
+ *c = ECCURVE_C(*cobj);
+ if (toecpt(*c, xx, x) || toecpt(*c, yy, y)) return (-1);
+ return (0);
+}
+
+#define BINOP(name) \
+ static PyObject *ecpt_py##name(PyObject *x, PyObject *y) { \
+ ec xx = EC_INIT, yy = EC_INIT, zz = EC_INIT; \
+ PyObject *cobj; \
+ ec_curve *c; \
+ if (ecbinop(x, y, &c, &cobj, &xx, &yy)) RETURN_NOTIMPL; \
+ c->ops->name(c, &zz, &xx, &yy); \
+ EC_DESTROY(&xx); EC_DESTROY(&yy); \
+ return (ecpt_pywrap(ECPT_COBJ(x), &zz)); \
+ }
+BINOP(add)
+BINOP(sub)
+#undef BINOP
+
+#define UNOP(name) \
+ static PyObject *ecpt_py##name(PyObject *x) { \
+ ec zz = EC_INIT; \
+ ec_curve *c = ECPT_C(x); \
+ c->ops->name(c, &zz, ECPT_P(x)); \
+ return (ecpt_pywrap(ECPT_COBJ(x), &zz)); \
+ }
+UNOP(neg)
+#undef UNOP
+
+static PyObject *ecpt_pyid(PyObject *x) { RETURN_OBJ(x); }
+
+static int ecpt_pynonzerop(PyObject *x) { return (!EC_ATINF(ECPT_P(x))); }
+
+static void ecpt_pydealloc(PyObject *x)
+{
+ EC_DESTROY(ECPT_P(x));
+ Py_DECREF(ECPT_COBJ(x));
+ FREEOBJ(x);
+}
+
+static PyObject *ecpt_pymul(PyObject *x, PyObject *y)
+{
+ mp *xx;
+ ec zz = EC_INIT;
+
+ if (ECPT_PYCHECK(x)) { PyObject *t; t = x; x = y; y = t; }
+ if (!ECPT_PYCHECK(y)) RETURN_NOTIMPL;
+ if (FE_PYCHECK(x) && FE_F(x)->ops->ty == FTY_PRIME)
+ xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));
+ else if ((xx = implicitmp(x)) == 0)
+ RETURN_NOTIMPL;
+ ec_imul(ECPT_C(y), &zz, ECPT_P(y), xx);
+ MP_DROP(xx);
+ return (ecpt_pywrap(ECPT_COBJ(y), &zz));
+}
+
+static Py_hash_t ecpt_pyhash(PyObject *me)
+{
+ uint32 h;
+ ec p = EC_INIT;
+
+ getecptout(&p, me);
+ if (EC_ATINF(&p)) h = 0x81d81a94;
+ else h = 0xe0fdd039 ^ (2*mphash(p.x)) ^ (3*mphash(p.y));
+ EC_DESTROY(&p);
+ return (h%LONG_MAX);
+}
+
+static PyObject *ecpt_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ ec p = EC_INIT, q = EC_INIT;
+ int b;
+ PyObject *rc = 0;
+
+ if (!ECPT_PYCHECK(y)) RETURN_NOTIMPL;
+ getecptout(&p, x);
+ getecptout(&q, y);
+ switch (op) {
+ case Py_EQ: b = EC_EQ(&p, &q); break;
+ case Py_NE: b = !EC_EQ(&p, &q); break;
+ default: TYERR("elliptic curve points are unordered");
+ }
+ rc = getbool(b);
+end:
+ EC_DESTROY(&p);
+ EC_DESTROY(&q);
+ return (rc);
+}
+
+static PyObject *epmeth_oncurvep(PyObject *me)
+{
+ return (getbool(EC_ATINF(ECPT_P(me)) ||
+ !EC_CHECK(ECPT_C(me), ECPT_P(me))));
+}
+
+static PyObject *epmeth_dbl(PyObject *me)
+{
+ ec p = EC_INIT;
+ EC_DBL(ECPT_C(me), &p, ECPT_P(me));
+ return (ecpt_pywrap(ECPT_COBJ(me), &p));
+}
+
+static PyObject *epmeth_tobuf(PyObject *me)
+{
+ buf b;
+ ec p = EC_INIT;
+ PyObject *rc;
+ size_t n;
+
+ getecptout(&p, me);
+ if (EC_ATINF(&p))
+ n = 2;
+ else
+ n = mp_octets(p.x) + mp_octets(p.y) + 6;
+ rc = bytestring_pywrap(0, n);
+ buf_init(&b, BIN_PTR(rc), n);
+ buf_putec(&b, &p);
+ assert(BOK(&b));
+ BIN_SETLEN(rc, BLEN(&b));
+ EC_DESTROY(&p);
+ return (rc);
+}
+
+static PyObject *epmeth_toraw(PyObject *me)
+{
+ buf b;
+ PyObject *rc;
+ char *p;
+ ec_curve *c = ECPT_C(me);
+ ec pp = EC_INIT;
+ int len;
+
+ len = c->f->noctets * 2 + 1;
+ rc = bytestring_pywrap(0, len);
+ p = BIN_PTR(rc);
+ buf_init(&b, p, len);
+ EC_OUT(c, &pp, ECPT_P(me));
+ ec_putraw(c, &b, &pp);
+ EC_DESTROY(&pp);
+ BIN_SETLEN(rc, BLEN(&b));
+ return (rc);
+}
+
+static PyObject *epmeth_ec2osp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ buf b;
+ PyObject *rc;
+ char *p;
+ ec_curve *c = ECPT_C(me);
+ ec pp = EC_INIT;
+ unsigned f = EC_EXPLY;
+ int len;
+ static const char *const kwlist[] = { "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:ec2osp", KWLIST,
+ convuint, &f))
+ return (0);
+ len = c->f->noctets * 2 + 1;
+ rc = bytestring_pywrap(0, len);
+ p = BIN_PTR(rc);
+ buf_init(&b, p, len);
+ EC_OUT(c, &pp, ECPT_P(me));
+ if (ec_ec2osp(c, f, &b, &pp)) {
+ Py_DECREF(rc); rc = 0;
+ VALERR("invalid flags");
+ }
+ EC_DESTROY(&pp);
+ BIN_SETLEN(rc, BLEN(&b));
+end:
+ return (rc);
+}
+
+static PyObject *epmeth_frombuf(PyObject *me, PyObject *arg)
+{
+ struct bin in;
+ buf b;
+ PyObject *rc = 0;
+ ec pp = EC_INIT;
+
+ if (!PyArg_ParseTuple(arg, "O&:frombuf", convbin, &in)) goto end;
+ buf_init(&b, (/*unconst*/ void *)in.p, in.sz);
+ if (buf_getec(&b, &pp)) VALERR("malformed data");
+ rc = Py_BuildValue("(NN)", ecpt_pywrapout(me, &pp),
+ bytestring_pywrapbuf(&b));
+end:
+ return (rc);
+}
+
+static PyObject *epmeth_parse(PyObject *me, PyObject *arg)
+{
+ char *p;
+ qd_parse qd;
+ PyObject *rc = 0;
+ int paren;
+ ec pp = EC_INIT;
+
+ if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+ qd.p = p; qd.e = 0;
+ qd_skipspc(&qd); paren = qd_delim(&qd, '(');
+ if (!ec_ptparse(&qd, &pp)) VALERR(qd.e);
+ qd_skipspc(&qd); if (paren && !qd_delim(&qd, ')'))
+ { EC_DESTROY(&pp); VALERR("missing `)'"); }
+ rc = Py_BuildValue("(Ns)", ecpt_pywrapout(me, &pp), qd.p);
+end:
+ return (rc);
+}
+
+static PyObject *epmeth_fromraw(PyObject *me, PyObject *arg)
+{
+ struct bin in;
+ buf b;
+ PyObject *rc = 0;
+ ec_curve *cc;
+ ec pp = EC_INIT;
+
+ if (!PyArg_ParseTuple(arg, "O&:fromraw", convbin, &in)) return (0);
+ buf_init(&b, (/*unconst*/ void *)in.p, in.sz);
+ cc = ECCURVE_C(me);
+ if (ec_getraw(cc, &b, &pp))
+ VALERR("bad point");
+ EC_IN(cc, &pp, &pp);
+ rc = Py_BuildValue("(NN)", ecpt_pywrap(me, &pp), bytestring_pywrapbuf(&b));
+end:
+ return (rc);
+}
+
+static PyObject *epmeth_os2ecp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ buf b;
+ PyObject *rc = 0;
+ ec_curve *cc;
+ unsigned f = EC_XONLY | EC_LSB | EC_SORT | EC_EXPLY;
+ ec pp = EC_INIT;
+ static const char *const kwlist[] = { "buf", "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:os2ecp", KWLIST,
+ convbin, &in, convuint, &f))
+ return (0);
+ buf_init(&b, (/*unconst*/ void *)in.p, in.sz);
+ cc = ECCURVE_C(me);
+ if (ec_os2ecp(cc, f, &b, &pp)) VALERR("bad point");
+ EC_IN(cc, &pp, &pp);
+ rc = Py_BuildValue("(NN)", ecpt_pywrap(me, &pp), bytestring_pywrapbuf(&b));
+end:
+ return (rc);
+}
+
+static PyObject *epncget_ix(PyObject *me, void *hunoz)
+{
+ ec p = EC_INIT;
+ PyObject *rc;
+ if (EC_ATINF(ECPT_P(me))) RETURN_NONE;
+ getecptout(&p, me);
+ rc = mp_pywrap(MP_COPY(p.x));
+ EC_DESTROY(&p);
+ return (rc);
+}
+
+static PyObject *epncget_iy(PyObject *me, void *hunoz)
+{
+ ec p = EC_INIT;
+ PyObject *rc;
+ if (EC_ATINF(ECPT_P(me))) RETURN_NONE;
+ getecptout(&p, me);
+ rc = mp_pywrap(MP_COPY(p.y));
+ EC_DESTROY(&p);
+ return (rc);
+}
+
+static PyObject *epncget_point(PyObject *me, void *hunoz)
+ { RETURN_ME; }
+
+static PyObject *epget_point(PyObject *me, void *hunoz)
+{
+ ec p = EC_INIT;
+ getecptout(&p, me);
+ return (ecpt_pywrapout(ecpt_pytype, &p));
+}
+
+static PyObject *epget_x(PyObject *me, void *hunoz)
+{
+ ec_curve *c = ECPT_C(me);
+ ec *pp = ECPT_P(me);
+ PyObject *fobj = ECPT_FOBJ(me);
+ ec p = EC_INIT;
+ PyObject *rc;
+
+ if (EC_ATINF(pp)) RETURN_NONE;
+ EC_OUT(c, &p, pp);
+ rc = fe_pywrap(fobj, F_IN(c->f, MP_NEW, p.x));
+ EC_DESTROY(&p);
+ return (rc);
+}
+
+static PyObject *epget_y(PyObject *me, void *hunoz)
+{
+ ec_curve *c = ECPT_C(me);
+ ec *pp = ECPT_P(me);
+ PyObject *fobj = ECPT_FOBJ(me);
+ ec p = EC_INIT;
+ PyObject *rc;
+
+ if (EC_ATINF(pp)) RETURN_NONE;
+ EC_OUT(c, &p, pp);
+ rc = fe_pywrap(fobj, F_IN(c->f, MP_NEW, p.y));
+ EC_DESTROY(&p);
+ return (rc);
+}
+
+static PyObject *epget__x(PyObject *me, void *hunoz)
+{
+ if (EC_ATINF(ECPT_P(me))) RETURN_NONE;
+ return (fe_pywrap(ECPT_FOBJ(me), MP_COPY(ECPT_P(me)->x)));
+}
+
+static PyObject *epget__y(PyObject *me, void *hunoz)
+{
+ if (EC_ATINF(ECPT_P(me))) RETURN_NONE;
+ return (fe_pywrap(ECPT_FOBJ(me), MP_COPY(ECPT_P(me)->y)));
+}
+
+static PyObject *epget__z(PyObject *me, void *hunoz)
+{
+ if (EC_ATINF(ECPT_P(me)) || !ECPT_P(me)->z) RETURN_NONE;
+ return (fe_pywrap(ECPT_FOBJ(me), MP_COPY(ECPT_P(me)->z)));
+}
+
+static mp *coord_in(field *f, PyObject *x)
+{
+ mp *xx;
+ if (FE_PYCHECK(x) && (FE_F(x) == f || field_samep(FE_F(x), f)))
+ return (MP_COPY(FE_X(x)));
+ else if ((xx = getmp(x)) == 0)
+ return (0);
+ else
+ return (F_IN(f, xx, xx));
+}
+
+static int ecptxl_3(ec_curve *c, ec *p,
+ PyObject *x, PyObject *y, PyObject *z)
+{
+ int rc = -1;
+
+ if (!x || !y || !z) TYERR("missing argument");
+ if (!c) VALERR("internal form with no curve!");
+ if ((p->x = coord_in(c->f, x)) == 0 ||
+ (p->y = coord_in(c->f, y)) == 0 ||
+ (z != Py_None && (p->z = coord_in(c->f, z)) == 0))
+ goto end;
+ if (!p->z) p->z = MP_COPY(c->f->one); /* just in case */
+ rc = 0;
+end:
+ return (rc);
+}
+
+static int ecptxl_2(ec_curve *c, ec *p, PyObject *x, PyObject *y)
+{
+ int rc = -1;
+
+ if (!x || !y) TYERR("missing argument");
+ if ((p->x = getmp(x)) == 0 ||
+ (p->y = getmp(y)) == 0)
+ goto end;
+ if (c) EC_IN(c, p, p);
+ rc = 0;
+end:
+ return (rc);
+}
+
+static int ecptxl_1(ec_curve *c, ec *p, PyObject *x)
+{
+ int rc = -1;
+ PyObject *y = 0, *z = 0, *t = 0, *u = 0;
+ mp *xx = 0;
+ qd_parse qd;
+ int paren;
+
+ Py_XINCREF(x);
+ if (!x || x == Py_None)
+ /*cool*/;
+ else if (ECPT_PYCHECK(x)) {
+ getecptout(p, x);
+ goto fix;
+ } else if (TEXT_CHECK(x)) {
+ qd.p = TEXT_PTR(x); qd.e = 0;
+ qd_skipspc(&qd); paren = qd_delim(&qd, '(');
+ if (!ec_ptparse(&qd, p)) VALERR(qd.e);
+ qd_skipspc(&qd); if (paren && !qd_delim(&qd, ')'))
+ { EC_DESTROY(p); VALERR("missing `)'"); }
+ qd_skipspc(&qd); if (!qd_eofp(&qd)) VALERR("junk at eof");
+ goto fix;
+ } else if (c && (xx = tomp(x)) != 0) {
+ xx = F_IN(c->f, xx, xx);
+ if (!EC_FIND(c, p, xx)) VALERR("not on the curve");
+ } else if ((t = PyObject_GetIter(x)) != 0) {
+ Py_DECREF(x);
+ x = PyIter_Next(t); if (!x) goto enditer;
+ y = PyIter_Next(t); if (!y) goto enditer;
+ z = PyIter_Next(t); if (!z && PyErr_Occurred()) goto end;
+ if (z) {
+ u = PyIter_Next(t);
+ if (u) goto enditer;
+ else if (PyErr_Occurred()) goto end;
+ }
+ rc = !z ? ecptxl_2(c, p, x, y) : ecptxl_3(c, p, x, y, z);
+ goto end;
+ } else {
+ PyErr_Clear();
+ TYERR("can't convert to curve point");
+ }
+ goto ok;
+
+enditer:
+ if (PyErr_Occurred()) goto end;
+ TYERR("expected sequence of 2 or 3 items");
+fix:
+ if (c) EC_IN(c, p, p);
+ok:
+ rc = 0;
+end:
+ Py_XDECREF(x); Py_XDECREF(y); Py_XDECREF(z); Py_XDECREF(t); Py_XDECREF(u);
+ mp_drop(xx);
+ return (rc);
+}
+
+static int ecptxl(ec_curve *c, ec *p, PyObject *x, PyObject *y, PyObject *z)
+{
+ if (z)
+ return (ecptxl_3(c, p, x, y, z));
+ else if (y)
+ return (ecptxl_2(c, p, x, y));
+ else
+ return (ecptxl_1(c, p, x));
+}
+
+static PyObject *ecptnc_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *x = 0, *y = 0, *z = 0;
+ ec p = EC_INIT;
+ static const char *const kwlist[] = { "x", "y", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:new", KWLIST, &x, &y) ||
+ ecptxl(0, &p, x, y, z))
+ goto end;
+ return (ecpt_pywrapout(ty, &p));
+end:
+ mp_drop(p.x); mp_drop(p.y); mp_drop(p.z);
+ return (0);
+}
+
+static PyObject *ecpt_pyint(PyObject *me)
+{
+ ec p = EC_INIT;
+ long l;
+ PyObject *rc = 0;
+ if (EC_ATINF(ECPT_P(me))) VALERR("point at infinity");
+ getecptout(&p, me);
+ if (!mp_tolong_checked(p.x, &l, 0)) rc = PyInt_FromLong(l);
+ else rc = mp_topylong(p.x);
+end:
+ EC_DESTROY(&p);
+ return (rc);
+}
+
+#ifdef PY2
+static PyObject *ecpt_pylong(PyObject *me)
+{
+ ec p = EC_INIT;
+ PyObject *rc = 0;
+ if (EC_ATINF(ECPT_P(me))) VALERR("point at infinity");
+ getecptout(&p, me);
+ rc = mp_topylong(p.x);
+end:
+ EC_DESTROY(&p);
+ return (rc);
+}
+#endif
+
+static PyObject *ecpt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *x = 0, *y = 0, *z = 0;
+ ec p = EC_INIT;
+ static const char *const kwlist[] = { "x", "y", "z", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OOO:new", KWLIST,
+ &x, &y, &z) ||
+ ecptxl(ECCURVE_C(ty), &p, x, y, z))
+ goto end;
+ return (ecpt_pywrap((PyObject *)ty, &p));
+end:
+ mp_drop(p.x); mp_drop(p.y); mp_drop(p.z);
+ return (0);
+}
+
+static const PyGetSetDef ecptnc_pygetset[] = {
+#define GETSETNAME(op, name) epnc##op##_##name
+ GET (ix, "P.ix -> integer x coordinate of P")
+ GET (iy, "P.iy -> integer y coordinate of P")
+ GET (point, "P.point -> standalone curve point (no-op)")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef ecptnc_pymethods[] = {
+#define METHNAME(func) epmeth_##func
+ NAMETH(tobuf, "X.tobuf() -> BIN")
+ CMTH (frombuf, "frombuf(STR) -> (P, REST)")
+ CMTH (parse, "parse(STR) -> (P, REST)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods ecpt_pynumber = {
+ 0, /* @nb_add@ */
+ 0, /* @nb_subtract@ */
+ 0, /* @nb_multiply@ */
+#ifdef PY2
+ 0, /* @nb_divide@ */
+#endif
+ 0, /* @nb_remainder@ */
+ 0, /* @nb_divmod@ */
+ 0, /* @nb_power@ */
+ 0, /* @nb_negative@ */
+ 0, /* @nb_positive@ */
+ 0, /* @nb_absolute@ */
+ ecpt_pynonzerop, /* @nb_nonzero@ */
+ 0, /* @nb_invert@ */
+ 0, /* @nb_lshift@ */
+ 0, /* @nb_rshift@ */
+ 0, /* @nb_and@ */
+ 0, /* @nb_xor@ */
+ 0, /* @nb_or@ */
+#ifdef PY2
+ 0, /* @nb_coerce@ */
+#endif
+ ecpt_pyint, /* @nb_int@ */
+ PY23(ecpt_pylong, 0), /* @nb_long@ */
+ 0, /* @nb_float@ */
+#ifdef PY2
+ 0, /* @nb_oct@ */
+ 0, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+#ifdef PY2
+ 0, /* @nb_inplace_divide@ */
+#endif
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ 0, /* @nb_floor_divide@ */
+ 0, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+};
+
+static const PyTypeObject ecpt_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECPt", /* @tp_name@ */
+ sizeof(ecpt_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ ecpt_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ PYNUMBER(ecpt), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ ecpt_pyhash, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECPt([X, [Y]]): elliptic curve points, not associated with any curve.\n"
+ " X alone may be None, an existing point, a string 'X, Y', an\n"
+ " x-coordinate, or a pair (X, Y); X and Y should be a coordinate pair.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ ecpt_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(ecptnc), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(ecptnc), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecptnc_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyMemberDef ecpt_pymembers[] = {
+#define MEMBERSTRUCT ecpt_pyobj
+ MEMRNM(curve, T_OBJECT, PY23(ob_type, ob_base.ob_type), READONLY,
+ "P.curve -> elliptic curve containing P")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyGetSetDef ecpt_pygetset[] = {
+#define GETSETNAME(op, name) ep##op##_##name
+ GET (point, "P.point -> standalone curve point")
+ GET (x, "P.x -> Cartesian x coordinate of P")
+ GET (y, "P.y -> Cartesian y coordinate of P")
+ GET (_x, "P._x -> internal x coordinate of P")
+ GET (_y, "P._y -> internal y coordinate of P")
+ GET (_z, "P._z -> internal z coordinate of P, or None")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef ecpt_pymethods[] = {
+#define METHNAME(func) epmeth_##func
+ NAMETH(toraw, "X.toraw() -> BIN")
+ KWMETH(ec2osp, "X.ec2osp([flags = EC_EXPLY]) -> BIN")
+ NAMETH(dbl, "X.dbl() -> X + X")
+ NAMETH(oncurvep, "X.oncurvep() -> BOOL")
+ CMTH (fromraw, "fromraw(STR) -> (P, REST)")
+ KWCMTH(os2ecp, "os2ecp(STR, [flags = ...]) -> (P, REST)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods ecptcurve_pynumber = {
+ ecpt_pyadd, /* @nb_add@ */
+ ecpt_pysub, /* @nb_subtract@ */
+ ecpt_pymul, /* @nb_multiply@ */
+#ifdef PY2
+ 0, /* @nb_divide@ */
+#endif
+ 0, /* @nb_remainder@ */
+ 0, /* @nb_divmod@ */
+ 0, /* @nb_power@ */
+ ecpt_pyneg, /* @nb_negative@ */
+ ecpt_pyid, /* @nb_positive@ */
+ 0, /* @nb_absolute@ */
+ 0, /* @nb_nonzero@ */
+ 0, /* @nb_invert@ */
+ 0, /* @nb_lshift@ */
+ 0, /* @nb_rshift@ */
+ 0, /* @nb_and@ */
+ 0, /* @nb_xor@ */
+ 0, /* @nb_or@ */
+#ifdef PY2
+ 0, /* @nb_coerce@ */
+#endif
+ 0, /* @nb_int@ */
+ 0, /* @nb_long@ */
+ 0, /* @nb_float@ */
+#ifdef PY2
+ 0, /* @nb_oct@ */
+ 0, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+ 0, /* @nb_inplace_divide@ */
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ 0, /* @nb_floor_divide@ */
+ 0, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+};
+
+static const PyTypeObject ecptcurve_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECPtCurve", /* @tp_name@ */
+ sizeof(ecpt_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ ecpt_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ PYNUMBER(ecptcurve), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Elliptic curve points; abstract base class for points on given curves.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(ecpt), /* @tp_methods@ */
+ PYMEMBERS(ecpt), /* @tp_members@ */
+ PYGETSET(ecpt), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Elliptic curves themselves ----------------------------------------*/
+
+static PyObject *eccurve_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ int b;
+
+ assert(ECCURVE_PYCHECK(x));
+ if (!ECCURVE_PYCHECK(y)) RETURN_NOTIMPL;
+ b = ec_samep(ECCURVE_C(x), ECCURVE_C(y));
+ switch (op) {
+ case Py_EQ: break;
+ case Py_NE: b = !b; break;
+ default: TYERR("can't order elliptic curves");
+ }
+ return (getbool(b));
+end:
+ return (0);
+}
+
+static PyObject *ecmmul_id(PyObject *me)
+ { ec p = EC_INIT; return (ecpt_pywrap(me, &p)); }
+
+static int ecmmul_fill(void *pp, PyObject *me, PyObject *x, PyObject *m)
+{
+ ec_mulfactor *f = pp;
+
+ EC_CREATE(&f->base);
+ if (getecpt(ECCURVE_C(me), &f->base, x) ||
+ (f->exp = getmp(m)) == 0)
+ return (-1);
+ return (0);
+}
+
+static PyObject *ecmmul_exp(PyObject *me, void *pp, size_t n)
+{
+ ec p = EC_INIT;
+ ec_immul(ECCURVE_C(me), &p, pp, n);
+ return (ecpt_pywrap(me, &p));
+}
+
+static void ecmmul_drop(void *pp)
+{
+ ec_mulfactor *f = pp;
+ EC_DESTROY(&f->base);
+ MP_DROP(f->exp);
+}
+
+static PyObject *ecmeth_mmul(PyObject *me, PyObject *arg)
+{
+ return (mexp_common(me, arg, sizeof(ec_mulfactor),
+ ecmmul_id, ecmmul_fill, ecmmul_exp, ecmmul_drop));
+}
+
+static void eccurve_pydealloc(PyObject *me)
+{
+ ec_destroycurve(ECCURVE_C(me));
+ Py_DECREF(ECCURVE_FOBJ(me));
+ PyType_Type.tp_dealloc(me);
+}
+
+static PyObject *ecmeth_find(PyObject *me, PyObject *arg)
+{
+ PyObject *x;
+ mp *xx = 0;
+ ec p = EC_INIT;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O:find", &x)) goto end;
+ if (FIELD_PYCHECK(x) && FE_F(x) == ECCURVE_C(me)->f)
+ xx = MP_COPY(FE_X(x));
+ else if ((xx = getmp(x)) == 0)
+ goto end;
+ else
+ xx = F_IN(ECCURVE_C(me)->f, xx, xx);
+ if (EC_FIND(ECCURVE_C(me), &p, xx) == 0)
+ VALERR("not on the curve");
+ rc = ecpt_pywrap(me, &p);
+end:
+ if (xx) MP_DROP(xx);
+ return (rc);
+}
+
+static PyObject *ecmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "rng", 0 };
+ grand *r = &rand_global;
+ ec p = EC_INIT;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
+ convgrand, &r))
+ return (0);
+ ec_rand(ECCURVE_C(me), &p, r);
+ EC_IN(ECCURVE_C(me), &p, &p);
+ return (ecpt_pywrap(me, &p));
+}
+
+static PyObject *ecmeth_parse(PyObject *me, PyObject *arg)
+{
+ char *p;
+ qd_parse qd;
+ ec_curve *c;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+ qd.p = p; qd.e = 0;
+ if ((c = ec_curveparse(&qd)) == 0) VALERR(qd.e);
+ rc = Py_BuildValue("(Ns)", eccurve_pywrap(0, c), qd.p);
+end:
+ return (rc);
+}
+
+static PyObject *eccurve_dopywrap(PyTypeObject *ty,
+ PyObject *fobj, ec_curve *c)
+{
+ eccurve_pyobj *cobj = newtype(ty, 0, c->ops->name);
+ cobj->c = c;
+ cobj->fobj = fobj;
+ cobj->ty.ht_type.tp_basicsize = sizeof(ecpt_pyobj);
+ cobj->ty.ht_type.tp_base = ecptcurve_pytype;
+ Py_INCREF(ecptcurve_pytype);
+ cobj->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_HEAPTYPE);
+ cobj->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ cobj->ty.ht_type.tp_free = 0;
+ cobj->ty.ht_type.tp_new = ecpt_pynew;
+ typeready(&cobj->ty.ht_type);
+ return ((PyObject *)cobj);
+}
+
+PyObject *eccurve_pywrap(PyObject *fobj, ec_curve *c)
+{
+ PyTypeObject *ty;
+
+ if (!fobj)
+ fobj = field_pywrap(c->f);
+ else
+ Py_INCREF(fobj);
+ assert(FIELD_F(fobj) == c->f);
+ if (STRCMP(EC_NAME(c), ==, "prime"))
+ ty = ecprimecurve_pytype;
+ else if (STRCMP(EC_NAME(c), ==, "primeproj"))
+ ty = ecprimeprojcurve_pytype;
+ else if (STRCMP(EC_NAME(c), ==, "bin"))
+ ty = ecbincurve_pytype;
+ else if (STRCMP(EC_NAME(c), ==, "binproj"))
+ ty = ecbinprojcurve_pytype;
+ else
+ abort();
+ return (eccurve_dopywrap(ty, fobj, c));
+}
+
+static PyObject *eccurve_pynew(PyTypeObject *ty,
+ ec_curve *(*make)(field *, mp *, mp *),
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *fobj;
+ PyObject *cobj = 0;
+ static const char *const kwlist[] = { "field", "a", "b", 0 };
+ mp *aa = 0, *bb = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&O&", KWLIST,
+ field_pytype, &fobj,
+ convmp, &aa, convmp, &bb))
+ goto end;
+ Py_INCREF(fobj);
+ cobj = eccurve_dopywrap(ty, fobj, make(FIELD_F(fobj), aa, bb));
+end:
+ if (aa) MP_DROP(aa);
+ if (bb) MP_DROP(bb);
+ return (cobj);
+}
+
+static PyObject *ecget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(EC_NAME(ECCURVE_C(me)))); }
+
+static PyObject *ecget_a(PyObject *me, void *hunoz)
+ { return (fe_pywrap(ECCURVE_FOBJ(me), MP_COPY(ECCURVE_C(me)->a))); }
+
+static PyObject *ecget_b(PyObject *me, void *hunoz)
+ { return (fe_pywrap(ECCURVE_FOBJ(me), MP_COPY(ECCURVE_C(me)->b))); }
+
+static PyObject *ecget_field(PyObject *me, void *hunoz)
+ { RETURN_OBJ(ECCURVE_FOBJ(me)); }
+
+static PyObject *ecget_inf(PyObject *me, void *hunoz)
+ { ec inf = EC_INIT; return (ecpt_pywrap(me, &inf)); }
+
+static const PyGetSetDef eccurve_pygetset[] = {
+#define GETSETNAME(op, name) ec##op##_##name
+ GET (name, "E.name -> name of this kind of curve")
+ GET (a, "E.a -> first parameter of curve")
+ GET (b, "E.b -> second parameter of curve")
+ GET (field, "E.field -> finite field containing this curve")
+ GET (inf, "E.inf -> point at infinity of this curve")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef eccurve_pymethods[] = {
+#define METHNAME(name) ecmeth_##name
+ METH (mmul, "E.mmul([(P0, N0), (P1, N1), ...]) = N0 P0 + N1 P1 + ...")
+ METH (find, "E.find(X) -> P")
+ KWMETH(rand, "E.rand([rng = rand]) -> P")
+ SMTH (parse, "parse(STR) -> (E, REST)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject eccurve_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECCurve", /* @tp_name@ */
+ sizeof(eccurve_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ eccurve_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "An elliptic curve. Abstract class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ eccurve_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(eccurve), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(eccurve), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *ecprimecurve_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ return (eccurve_pynew(ty, ec_prime, arg, kw));
+}
+
+static const PyTypeObject ecprimecurve_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECPrimeCurve", /* @tp_name@ */
+ sizeof(eccurve_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ eccurve_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECPrimeCurve(FIELD, A, B): an elliptic curve over a prime field.\n"
+ " Use ECPrimeProjCurve instead.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecprimecurve_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *ecprimeprojcurve_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ return (eccurve_pynew(ty, ec_primeproj, arg, kw));
+}
+
+static const PyTypeObject ecprimeprojcurve_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECPrimeProjCurve", /* @tp_name@ */
+ sizeof(eccurve_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ eccurve_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECPrimeProjCurve(FIELD, A, B): an elliptic curve over a prime field\n"
+ " using projective coordinates.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecprimeprojcurve_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *ecbincurve_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ return (eccurve_pynew(ty, ec_bin, arg, kw));
+}
+
+static const PyTypeObject ecbincurve_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECBinCurve", /* @tp_name@ */
+ sizeof(eccurve_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ eccurve_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECBinCurve(FIELD, A, B): an elliptic curve over a binary field.\n"
+ " Use ECBinProjCurve instead.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecbincurve_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *ecbinprojcurve_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ return (eccurve_pynew(ty, ec_binproj, arg, kw));
+}
+
+static const PyTypeObject ecbinprojcurve_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECBinProjCurve", /* @tp_name@ */
+ sizeof(eccurve_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ eccurve_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECBinProjCurve(FIELD, A, B): an elliptic curve over a binary field,\n"
+ " using projective coordinates.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecbinprojcurve_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Curve info --------------------------------------------------------*/
+
+void ecinfo_copy(ec_info *eic, const ec_info *ei)
+{
+ eic->c = eccurve_copy(ei->c);
+ EC_CREATE(&eic->g);
+ EC_COPY(&eic->g, &ei->g);
+ eic->r = MP_COPY(ei->r);
+ eic->h = MP_COPY(ei->h);
+}
+
+PyObject *ecinfo_pywrap(ec_info *ei)
+{
+ ecinfo_pyobj *o;
+
+ o = PyObject_NEW(ecinfo_pyobj, ecinfo_pytype);
+ o->ei = *ei;
+ o->cobj = eccurve_pywrap(0, o->ei.c);
+ Py_INCREF(o->cobj);
+ return ((PyObject *)o);
+}
+
+static void ecinfo_pydealloc(PyObject *me)
+{
+ ec_info *ei = ECINFO_EI(me);
+ EC_DESTROY(&ei->g);
+ MP_DROP(ei->r);
+ MP_DROP(ei->h);
+ Py_DECREF(ECINFO_COBJ(me));
+ FREEOBJ(me);
+}
+
+static PyObject *ecinfo_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ ec_info ei = { 0 };
+ PyObject *e, *g;
+ static const char *const kwlist[] = { "curve", "G", "r", "h", 0 };
+ ecinfo_pyobj *rc = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O&O&:new", KWLIST,
+ eccurve_pytype, &e, ecpt_pytype, &g,
+ convmp, &ei.r, convmp, &ei.h))
+ goto end;
+ if (ECPT_C(g) != ECCURVE_C(e) && !ec_samep(ECPT_C(g), ECCURVE_C(e)))
+ TYERR("point not from this curve");
+ ei.c = ECCURVE_C(e);
+ EC_CREATE(&ei.g);
+ EC_OUT(ei.c, &ei.g, ECPT_P(g));
+ rc = (ecinfo_pyobj *)ty->tp_alloc(ty, 0);
+ rc->ei = ei;
+ rc->cobj = e;
+ Py_INCREF(rc->cobj);
+ return ((PyObject *)rc);
+
+end:
+ mp_drop(ei.r);
+ mp_drop(ei.h);
+ return (0);
+}
+
+static PyObject *eimeth_parse(PyObject *me, PyObject *arg)
+{
+ char *p;
+ qd_parse qd;
+ ec_info ei;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+ qd.p = p; qd.e = 0;
+ if (ec_infoparse(&qd, &ei)) VALERR(qd.e);
+ rc = Py_BuildValue("(Ns)", ecinfo_pywrap(&ei), qd.p);
+end:
+ return (rc);
+}
+
+static PyObject *ecinfo_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ int b = ec_sameinfop(ECINFO_EI(x), ECINFO_EI(y));
+ switch (op) {
+ case Py_EQ: break;
+ case Py_NE: b = !b;
+ default: TYERR("can't order elliptic curve infos");
+ }
+ return (getbool(b));
+end:
+ return (0);
+}
+
+static PyObject *eimeth_check(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "rng", 0 };
+ grand *r = &rand_global;
+ const char *p;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:check", KWLIST,
+ convgrand, &r))
+ goto end;
+ if ((p = ec_checkinfo(ECINFO_EI(me), r)) != 0)
+ VALERR(p);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *eiget_curve(PyObject *me, void *hunoz)
+ { RETURN_OBJ(ECINFO_COBJ(me)); }
+
+static PyObject *eiget_G(PyObject *me, void *hunoz)
+{
+ ec_info *ei = ECINFO_EI(me);
+ ec p = EC_INIT;
+ EC_IN(ei->c, &p, &ei->g);
+ return (ecpt_pywrap(ECINFO_COBJ(me), &p));
+}
+
+static PyObject *eiget_r(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(ECINFO_EI(me)->r))); }
+
+static PyObject *eiget_h(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(ECINFO_EI(me)->h))); }
+
+static const PyGetSetDef ecinfo_pygetset[] = {
+#define GETSETNAME(op, name) ei##op##_##name
+ GET (curve, "I.curve -> the elliptic curve")
+ GET (G, "I.G -> generator point for the group")
+ GET (r, "I.r -> order of the group (and hence of G")
+ GET (h, "I.h -> cofactor of the group")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef ecinfo_pymethods[] = {
+#define METHNAME(name) eimeth_##name
+ KWMETH(check, "I.check([rng = rand]) -> None")
+ SMTH (parse, "parse(STR) -> (I, REST)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject ecinfo_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECInfo", /* @tp_name@ */
+ sizeof(ecinfo_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ ecinfo_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECInfo(CURVE, G, R, H): elliptic curve domain parameters.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ ecinfo_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(ecinfo), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(ecinfo), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecinfo_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Setup -------------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(EC_XONLY), CONST(EC_YBIT), CONST(EC_LSB),
+ CONST(EC_CMPR), CONST(EC_EXPLY), CONST(EC_SORT),
+ { 0 }
+};
+
+void ec_pyinit(void)
+{
+ INITTYPE(ecpt, root);
+ INITTYPE(ecptcurve, ecpt);
+ INITTYPE(eccurve, type);
+ INITTYPE(ecprimecurve, eccurve);
+ INITTYPE(ecprimeprojcurve, ecprimecurve);
+ INITTYPE(ecbincurve, eccurve);
+ INITTYPE(ecbinprojcurve, ecbincurve);
+ INITTYPE(ecinfo, root);
+}
+
+static const char *ec_namefn(const void *p)
+ { const ecentry *ec = p; return (ec->name); }
+
+static int ec_ixfn(const void *p)
+{
+ const ecentry *ec = p;
+ int i;
+
+ for (i = 0; ectab[i].name; i++)
+ if (ectab[i].data == ec->data) return (i);
+ return (-1);
+}
+
+static PyObject *ec_valfn(int i)
+{
+ ec_info ei;
+
+ ec_infofromdata(&ei, ectab[i].data);
+ return (ecinfo_pywrap(&ei));
+}
+
+void ec_pyinsert(PyObject *mod)
+{
+ INSERT("ECPt", ecpt_pytype);
+ INSERT("ECPtCurve", ecptcurve_pytype);
+ INSERT("ECCurve", eccurve_pytype);
+ INSERT("ECPrimeCurve", ecprimecurve_pytype);
+ INSERT("ECPrimeProjCurve", ecprimeprojcurve_pytype);
+ INSERT("ECBinCurve", ecbincurve_pytype);
+ INSERT("ECBinProjCurve", ecbinprojcurve_pytype);
+ INSERT("ECInfo", ecinfo_pytype);
+ INSERT("eccurves", make_grouptab(ectab, sizeof(*ectab),
+ ec_namefn, ec_ixfn, ec_valfn));
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Abstract fields
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Various utilities -------------------------------------------------*/
+
+PyTypeObject *field_pytype;
+PyTypeObject *primefield_pytype;
+PyTypeObject *niceprimefield_pytype;
+PyTypeObject *binfield_pytype;
+PyTypeObject *binpolyfield_pytype;
+PyTypeObject *binnormfield_pytype;
+PyTypeObject *fe_pytype;
+
+static PyObject *fe_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *x;
+ mp *z;
+ static const char *const kwlist[] = { "x", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", KWLIST, &x))
+ return (0);
+ if (FE_PYCHECK(x) && FE_F(x) == FIELD_F(ty)) RETURN_OBJ(x);
+ if ((z = getmp(x)) == 0) return (0);
+ z = F_IN(FIELD_F(ty), z, z);
+ return (fe_pywrap((PyObject *)ty, z));
+}
+
+static PyObject *field_dopywrap(PyTypeObject *ty, field *f)
+{
+ field_pyobj *fobj = newtype(ty, 0, f->ops->name);
+ fobj->f = f;
+ fobj->ty.ht_type.tp_basicsize = sizeof(fe_pyobj);
+ fobj->ty.ht_type.tp_base = fe_pytype;
+ Py_INCREF(fe_pytype);
+ fobj->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_HEAPTYPE);
+ fobj->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ fobj->ty.ht_type.tp_free = 0;
+ fobj->ty.ht_type.tp_new = fe_pynew;
+ typeready(&fobj->ty.ht_type);
+ return ((PyObject *)fobj);
+}
+
+PyObject *field_pywrap(field *f)
+{
+ PyTypeObject *ty;
+
+ if (STRCMP(F_NAME(f), ==, "prime")) ty = primefield_pytype;
+ else if (STRCMP(F_NAME(f), ==, "niceprime")) ty = niceprimefield_pytype;
+ else if (STRCMP(F_NAME(f), ==, "binpoly")) ty = binpolyfield_pytype;
+ else if (STRCMP(F_NAME(f), ==, "binnorm")) ty = binnormfield_pytype;
+ else abort();
+ return (field_dopywrap(ty, f));
+}
+
+field *field_copy(field *f)
+{
+ if (STRCMP(F_NAME(f), ==, "prime"))
+ f = field_prime(f->m);
+ else if (STRCMP(F_NAME(f), ==, "niceprime"))
+ f = field_niceprime(f->m);
+ else if (STRCMP(F_NAME(f), ==, "binpoly"))
+ f = field_binpoly(f->m);
+ else if (STRCMP(F_NAME(f), ==, "binnorm")) {
+ fctx_binnorm *fc = (fctx_binnorm *)f;
+ f = field_binnorm(f->m, fc->ntop.r[fc->ntop.n - 1]);
+ } else
+ abort();
+ return (f);
+}
+
+PyObject *fe_pywrap(PyObject *fobj, mp *x)
+{
+ fe_pyobj *z = PyObject_New(fe_pyobj, (PyTypeObject *)fobj);
+ z->f = FIELD_F(fobj);
+ Py_INCREF(fobj);
+ z->x = x;
+ return ((PyObject *)z);
+}
+
+static mp *implicitfe(field *f, PyObject *o)
+{
+ mp *x;
+
+ if (FE_PYCHECK(o) && FE_F(o) == f) return (MP_COPY(FE_X(o)));
+ switch (f->ops->ty) {
+ case FTY_PRIME: x = implicitmp(o); break;
+ case FTY_BINARY: x = implicitgf(o); break;
+ default: assert(!"huh?");
+ }
+ if (!x) return (0);
+ return (F_IN(f, MP_NEW, x));
+}
+
+/*----- Field elements ----------------------------------------------------*/
+
+static int febinop(PyObject *x, PyObject *y,
+ field **f, PyObject **fobj, mp **xx, mp **yy)
+{
+ if (FE_PYCHECK(x)) *fobj = FE_FOBJ(x);
+ else if (FE_PYCHECK(y)) *fobj = FE_FOBJ(y);
+ else return (-1);
+ *f = FIELD_F(*fobj);
+ if ((*xx = implicitfe(*f, x)) == 0)
+ return (-1);
+ if ((*yy = implicitfe(*f, y)) == 0) {
+ MP_DROP(*xx);
+ return (-1);
+ }
+ return (0);
+}
+
+#define BINOP(name) \
+ static PyObject *fe_py##name(PyObject *x, PyObject *y) { \
+ PyObject *fobj; \
+ field *ff; \
+ mp *xx, *yy, *zz; \
+ if (febinop(x, y, &ff, &fobj, &xx, &yy)) RETURN_NOTIMPL; \
+ zz = ff->ops->name(ff, MP_NEW, xx, yy); \
+ MP_DROP(xx); MP_DROP(yy); \
+ return (fe_pywrap(fobj, zz)); \
+ }
+BINOP(add)
+BINOP(sub)
+BINOP(mul)
+#undef BINOP
+
+static PyObject *fe_pydiv(PyObject *x, PyObject *y)
+{
+ PyObject *fobj;
+ field *ff;
+ mp *xx, *yy;
+ PyObject *z = 0;
+ if (febinop(x, y, &ff, &fobj, &xx, &yy)) RETURN_NOTIMPL;
+ if (F_ZEROP(ff, yy)) ZDIVERR("division by zero");
+ yy = F_INV(ff, yy, yy);
+ z = fe_pywrap(fobj, F_MUL(ff, MP_NEW, xx, yy));
+end:
+ MP_DROP(xx); MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *fe_pyexp(PyObject *x, PyObject *y, PyObject *z)
+{
+ field *ff;
+ mp *xx, *yy;
+
+ if (z != Py_None || !FE_PYCHECK(x)) RETURN_NOTIMPL;
+ if (FE_PYCHECK(y) && FE_F(y)->ops->ty == FTY_PRIME)
+ yy = F_OUT(FE_F(y), MP_NEW, FE_X(y));
+ else if ((yy = implicitmp(y)) == 0)
+ RETURN_NOTIMPL;
+ ff = FE_F(x); xx = FE_X(x); MP_COPY(xx);
+ if (MP_NEGP(yy) && F_ZEROP(ff, xx)) ZDIVERR("division by zero");
+ z = fe_pywrap(FE_FOBJ(x), field_exp(ff, MP_NEW, xx, yy));
+end:
+ MP_DROP(xx); MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *fe_pyneg(PyObject *x)
+{
+ return fe_pywrap(FE_FOBJ(x), FE_F(x)->ops->neg(FE_F(x), MP_NEW, FE_X(x)));
+}
+
+static PyObject *fe_pyid(PyObject *x) { RETURN_OBJ(x); }
+
+static int fe_pynonzerop(PyObject *x) { return !F_ZEROP(FE_F(x), FE_X(x)); }
+
+static PyObject *fe_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ PyObject *fobj;
+ field *ff;
+ mp *xx, *yy;
+ int b;
+ PyObject *rc = 0;
+
+ if (febinop(x, y, &ff, &fobj, &xx, &yy)) RETURN_NOTIMPL;
+ switch (op) {
+ case Py_EQ: b = MP_EQ(xx, yy); break;
+ case Py_NE: b = !MP_EQ(xx, yy); break;
+ default: TYERR("field elements are unordered");
+ }
+ rc = getbool(b);
+end:
+ MP_DROP(xx); MP_DROP(yy);
+ return (rc);
+}
+
+static Py_hash_t fe_pyhash(PyObject *me)
+ { return (mphash(FE_X(me))); }
+
+#ifdef PY2
+static int fe_pycoerce(PyObject **x, PyObject **y)
+{
+ mp *z;
+
+ if (FE_PYCHECK(*y)) {
+ if (FE_F(*x) != FE_F(*y) && !field_samep(FE_F(*x), FE_F(*y)))
+ TYERR("field mismatch");
+ Py_INCREF(*x); Py_INCREF(*y);
+ return (0);
+ }
+ if ((z = implicitfe(FE_F(*x), *y)) != 0) {
+ Py_INCREF(*x);
+ *y = fe_pywrap(FE_FOBJ(*x), z);
+ return (0);
+ }
+ return (1);
+
+end:
+ return (-1);
+}
+#endif
+
+static PyObject *fe_pyint(PyObject *x)
+{
+ long l;
+ PyObject *rc;
+ mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));
+ if (!mp_tolong_checked(xx, &l, 0)) rc = PyInt_FromLong(l);
+ else rc = mp_topylong(xx);
+ MP_DROP(xx);
+ return (rc);
+}
+
+#ifdef PY2
+static PyObject *fe_pylong(PyObject *x)
+{
+ mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));
+ PyObject *rc = mp_topylong(xx);
+ MP_DROP(xx);
+ return (rc);
+}
+#endif
+
+#define BASEOP(name, radix, pre) \
+ static PyObject *fe_py##name(PyObject *x) { \
+ mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x)); \
+ PyObject *rc = mp_topystring(xx, radix, 0, pre, 0); \
+ MP_DROP(xx); \
+ return (rc); \
+ }
+#ifdef PY2
+BASEOP(oct, 8, "0");
+#endif
+BASEOP(hex, 16, "0x");
+#undef BASEOP
+
+static void fe_pydealloc(PyObject *me)
+{
+ Py_DECREF(FE_FOBJ(me));
+ MP_DROP(FE_X(me));
+ FREEOBJ(me);
+}
+
+#define UNOP(name, check) \
+ static PyObject *femeth_##name(PyObject *me) { \
+ field *f = FE_F(me); \
+ mp *x = FE_X(me); \
+ if (!f->ops->name) TYERR(#name " not supported for this field"); \
+ check \
+ x = f->ops->name(f, MP_NEW, x); \
+ if (!x) RETURN_NONE; \
+ return (fe_pywrap(FE_FOBJ(me), x)); \
+ end: \
+ return (0); \
+ }
+UNOP(inv, if (F_ZEROP(f, x)) ZDIVERR("division by zero"); )
+UNOP(sqr, ; )
+UNOP(sqrt, ; )
+UNOP(quadsolve, ; )
+UNOP(dbl, ; )
+UNOP(tpl, ; )
+UNOP(qdl, ; )
+UNOP(hlv, ; )
+#undef UNOP
+
+static PyObject *feget_value(PyObject *me, void *hunoz)
+{
+ mp *x = F_OUT(FE_F(me), MP_NEW, FE_X(me));
+ if (F_TYPE(FE_F(me)) == FTY_BINARY)
+ return (gf_pywrap(x));
+ else
+ return (mp_pywrap(x));
+}
+
+static PyObject *feget__value(PyObject *me, void *hunoz)
+{
+ mp *x = FE_X(me);
+ MP_COPY(x);
+ if (F_TYPE(FE_F(me)) == FTY_BINARY)
+ return (gf_pywrap(x));
+ else
+ return (mp_pywrap(x));
+}
+
+static const PyMemberDef fe_pymembers[] = {
+#define MEMBERSTRUCT fe_pyobj
+ MEMRNM(field, T_OBJECT, PY23(ob_type, ob_base.ob_type), READONLY,
+ "X.field -> field containing X")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyGetSetDef fe_pygetset[] = {
+#define GETSETNAME(op, name) fe##op##_##name
+ GET (value, "X.value -> `natural' MP/GF representation of X")
+ GET (_value, "X._value -> internal MP/GF representation of X")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef fe_pymethods[] = {
+#define METHNAME(func) femeth_##func
+ NAMETH(inv, "X.inv() -> X^{-1}")
+ NAMETH(sqr, "X.sqr() -> X^2")
+ NAMETH(sqrt, "X.sqrt() -> sqrt(X)")
+ NAMETH(quadsolve, "X.quadsolve() -> Y where Y^2 + Y = X (binary only)")
+ NAMETH(dbl, "X.dbl() -> 2 * X (prime only)")
+ NAMETH(tpl, "X.tpl() -> 3 * X (prime only)")
+ NAMETH(qdl, "X.qdl() -> 4 * X (prime only)")
+ NAMETH(hlv, "X.hlv() -> X/2 (prime only)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods fe_pynumber = {
+ fe_pyadd, /* @nb_add@ */
+ fe_pysub, /* @nb_subtract@ */
+ fe_pymul, /* @nb_multiply@ */
+#ifdef PY2
+ fe_pydiv, /* @nb_divide@ */
+#endif
+ 0, /* @nb_remainder@ */
+ 0, /* @nb_divmod@ */
+ fe_pyexp, /* @nb_power@ */
+ fe_pyneg, /* @nb_negative@ */
+ fe_pyid, /* @nb_positive@ */
+ 0, /* @nb_absolute@ */
+ fe_pynonzerop, /* @nb_nonzero@ */
+ 0, /* @nb_invert@ */
+ 0, /* @nb_lshift@ */
+ 0, /* @nb_rshift@ */
+ 0, /* @nb_and@ */
+ 0, /* @nb_xor@ */
+ 0, /* @nb_or@ */
+#ifdef PY2
+ fe_pycoerce, /* @nb_coerce@ */
+#endif
+ fe_pyint, /* @nb_int@ */
+ PY23(fe_pylong, 0), /* @nb_long@ */
+ 0 /* meaningless */, /* @nb_float@ */
+#ifdef PY2
+ fe_pyoct, /* @nb_oct@ */
+ fe_pyhex, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+#ifdef PY2
+ 0, /* @nb_inplace_divide@ */
+#endif
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ 0, /* @nb_floor_divide@ */
+ fe_pydiv, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+
+ fe_pyint, /* @nb_index@ */
+};
+
+static const PyTypeObject fe_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "FE", /* @tp_name@ */
+ sizeof(fe_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ fe_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ PYNUMBER(fe), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ fe_pyhash, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ fe_pyhex, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Finite field elements, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ fe_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(fe), /* @tp_methods@ */
+ PYMEMBERS(fe), /* @tp_members@ */
+ PYGETSET(fe), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Fields ------------------------------------------------------------*/
+
+static PyObject *field_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ int b = field_samep(FIELD_F(x), FIELD_F(y));
+ switch (op) {
+ case Py_EQ: break;
+ case Py_NE: b = !b;
+ default: TYERR("can't order fields");
+ }
+ return (getbool(b));
+end:
+ return (0);
+}
+
+static PyObject *fmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "rng", 0 };
+ grand *r = &rand_global;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
+ convgrand, &r))
+ return (0);
+ return (fe_pywrap(me, F_RAND(FIELD_F(me), MP_NEW, r)));
+}
+
+static PyObject *fmeth__adopt(PyObject *me, PyObject *arg)
+{
+ mp *xx;
+ if (!PyArg_ParseTuple(arg, "O&:_adopt", convmp, &xx)) return (0);
+ return (fe_pywrap(me, xx));
+}
+
+static PyObject *fmeth_parse(PyObject *me, PyObject *arg)
+{
+ field *f;
+ char *p;
+ PyObject *rc = 0;
+ qd_parse qd;
+
+ if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+ qd.p = p; qd.e = 0;
+ if ((f = field_parse(&qd)) == 0) VALERR(qd.e);
+ rc = Py_BuildValue("(Ns)", field_pywrap(f), qd.p);
+end:
+ return (rc);
+}
+
+static void field_pydealloc(PyObject *me)
+{
+ F_DESTROY(FIELD_F(me));
+ PyType_Type.tp_dealloc(me);
+}
+
+static PyObject *fget_zero(PyObject *me, void *hunoz)
+ { return (fe_pywrap(me, MP_COPY(FIELD_F(me)->zero))); }
+
+static PyObject *fget_one(PyObject *me, void *hunoz)
+ { return (fe_pywrap(me, MP_COPY(FIELD_F(me)->one))); }
+
+static PyObject *fget_q(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(FIELD_F(me)->q))); }
+
+static PyObject *fget_nbits(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(FIELD_F(me)->nbits)); }
+
+static PyObject *fget_noctets(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(FIELD_F(me)->noctets)); }
+
+static PyObject *fget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(F_NAME(FIELD_F(me)))); }
+
+static PyObject *fget_type(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(F_TYPE(FIELD_F(me)))); }
+
+static const PyGetSetDef field_pygetset[] = {
+#define GETSETNAME(op, name) f##op##_##name
+ GET (zero, "F.zero -> field additive identity")
+ GET (one, "F.one -> field multiplicative identity")
+ GET (q, "F.q -> number of elements in field")
+ GET (nbits, "F.nbits -> bits needed to represent element")
+ GET (noctets, "F.noctets -> octetss needed to represent element")
+ GET (name, "F.name -> name of this kind of field")
+ GET (type, "F.type -> type code of this kind of field")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef field_pymethods[] = {
+#define METHNAME(name) fmeth_##name
+ METH (_adopt, "F._adopt(X) -> FE")
+ KWMETH(rand, "F.rand([rng = rand]) -> FE, uniformly distributed")
+ SMTH (parse, "parse(STR) -> F, REST")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject field_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Field", /* @tp_name@ */
+ sizeof(field_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ field_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "An abstract field. This is an abstract type.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ field_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(field), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(field), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Prime fields ------------------------------------------------------*/
+
+static PyObject *primefield_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *xx = 0;
+ field *f;
+ static const char *const kwlist[] = { "p", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", KWLIST,
+ convmp, &xx))
+ goto end;
+ if ((f = field_prime(xx)) == 0)
+ VALERR("bad prime for primefield");
+ MP_DROP(xx);
+ return (field_dopywrap(ty, f));
+end:
+ mp_drop(xx);
+ return (0);
+}
+
+static PyObject *pfget_p(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(FIELD_F(me)->m))); }
+
+static const PyGetSetDef primefield_pygetset[] = {
+#define GETSETNAME(op, name) pf##op##_##name
+ GET (p, "F.p -> prime field characteristic")
+#undef GETSETNAME
+};
+
+static const PyTypeObject primefield_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeField", /* @tp_name@ */
+ sizeof(field_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ field_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeField(P): prime fields.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(primefield), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ primefield_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *niceprimefield_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *xx = 0;
+ field *f;
+ static const char *const kwlist[] = { "p", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:niceprimefield",
+ KWLIST, convmp, &xx))
+ goto end;
+ if ((f = field_niceprime(xx)) == 0)
+ VALERR("bad prime for niceprimefield");
+ MP_DROP(xx);
+ return (field_dopywrap(ty, f));
+end:
+ mp_drop(xx);
+ return (0);
+}
+
+static const PyTypeObject niceprimefield_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "NicePrimeField", /* @tp_name@ */
+ sizeof(field_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ field_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "NicePrimeField(P): prime field using Solinas reduction.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ niceprimefield_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Binary fields -----------------------------------------------------*/
+
+static PyObject *bfget_m(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(FIELD_F(me)->nbits)); }
+
+static PyObject *bfget_p(PyObject *me, void *hunoz)
+ { return (gf_pywrap(MP_COPY(FIELD_F(me)->m))); }
+
+static const PyGetSetDef binfield_pygetset[] = {
+#define GETSETNAME(op, name) bf##op##_##name
+ GET (m, "F.m -> field polynomial degree")
+ GET (p, "F.p -> field polynomial")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject binfield_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BinField", /* @tp_name@ */
+ sizeof(field_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ field_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Binary fields. Abstract class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(binfield), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *binpolyfield_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *xx = 0;
+ field *f;
+ static const char *const kwlist[] = { "p", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", KWLIST,
+ convgf, &xx))
+ goto end;
+ if ((f = field_binpoly(xx)) == 0) VALERR("bad poly for binpolyfield");
+ MP_DROP(xx);
+ return (field_dopywrap(ty, f));
+end:
+ mp_drop(xx);
+ return (0);
+}
+
+static const PyTypeObject binpolyfield_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BinPolyField", /* @tp_name@ */
+ sizeof(field_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ field_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "BinPolyField(P): binary fields with polynomial basis representation.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ binpolyfield_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *binnormfield_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *xx = 0, *yy = 0;
+ field *f;
+ static const char *const kwlist[] = { "p", "beta", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:binnormfield",
+ KWLIST, convgf, &xx, convgf, &yy))
+ goto end;
+ if ((f = field_binnorm(xx, yy)) == 0) VALERR("bad args for binnormfield");
+ MP_DROP(xx); MP_DROP(yy);
+ return (field_dopywrap(ty, f));
+end:
+ mp_drop(xx); mp_drop(yy);
+ return (0);
+}
+
+static PyObject *bnfget_beta(PyObject *me, void *hunoz)
+{
+ fctx_binnorm *fc = (fctx_binnorm *)FIELD_F(me);
+ return (gf_pywrap(MP_COPY(fc->ntop.r[fc->ntop.n - 1])));
+}
+
+static const PyGetSetDef binnormfield_pygetset[] = {
+#define GETSETNAME(op, name) bnf##op##_##name
+ GET (beta, "F.beta -> conversion factor")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject binnormfield_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BinNormField", /* @tp_name@ */
+ sizeof(field_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ field_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "BinNormField(P, BETA): binary fields with normal basis representation.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(binnormfield), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ binnormfield_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Setup -------------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(FTY_PRIME), CONST(FTY_BINARY),
+ { 0 }
+};
+
+void field_pyinit(void)
+{
+ INITTYPE(fe, root);
+ INITTYPE(field, type);
+ INITTYPE(primefield, field);
+ INITTYPE(niceprimefield, primefield);
+ INITTYPE(binfield, field);
+ INITTYPE(binpolyfield, binfield);
+ INITTYPE(binnormfield, binfield);
+}
+
+void field_pyinsert(PyObject *mod)
+{
+ INSERT("FE", fe_pytype);
+ INSERT("Field", field_pytype);
+ INSERT("PrimeField", primefield_pytype);
+ INSERT("NicePrimeField", niceprimefield_pytype);
+ INSERT("BinField", binfield_pytype);
+ INSERT("BinPolyField", binpolyfield_pytype);
+ INSERT("BinNormField", binnormfield_pytype);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Abstract group inteface
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- DH and binary group infos -----------------------------------------*/
+
+static PyTypeObject *fginfo_pytype, *dhinfo_pytype, *bindhinfo_pytype;
+
+typedef struct fginfo_pyobj {
+ PyObject_HEAD
+ gprime_param dp;
+} fginfo_pyobj;
+
+#define FGINFO_DP(fg) (&((fginfo_pyobj *)(fg))->dp)
+
+static PyObject *fginfo_pywrap(gprime_param *dp, PyTypeObject *ty)
+{
+ fginfo_pyobj *z = PyObject_New(fginfo_pyobj, ty);
+ z->dp = *dp;
+ return ((PyObject *)z);
+}
+
+static PyObject *fginfo_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "p", "r", "g", 0 };
+ gprime_param dp = { 0 };
+ fginfo_pyobj *z = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&:new", KWLIST,
+ convmp, &dp.p,
+ convmp, &dp.q,
+ convmp, &dp.g))
+ goto end;
+ z = PyObject_New(fginfo_pyobj, ty);
+ z->dp = dp;
+ return ((PyObject *)z);
+end:
+ mp_drop(dp.p);
+ mp_drop(dp.q);
+ mp_drop(dp.g);
+ return (0);
+}
+
+static PyObject *figet_r(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(FGINFO_DP(me)->q)); }
+
+static PyObject *diget_p(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(FGINFO_DP(me)->p)); }
+
+static PyObject *diget_g(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(FGINFO_DP(me)->g)); }
+
+static PyObject *biget_p(PyObject *me, void *hunoz)
+ { return gf_pywrap(MP_COPY(FGINFO_DP(me)->p)); }
+
+static PyObject *biget_m(PyObject *me, void *hunoz)
+ { return PyInt_FromLong(mp_octets(FGINFO_DP(me)->p) - 1); }
+
+static PyObject *biget_g(PyObject *me, void *hunoz)
+ { return gf_pywrap(MP_COPY(FGINFO_DP(me)->g)); }
+
+static void fginfo_pydealloc(PyObject *me)
+{
+ mp_drop(FGINFO_DP(me)->p);
+ mp_drop(FGINFO_DP(me)->q);
+ mp_drop(FGINFO_DP(me)->g);
+ FREEOBJ(me);
+}
+
+static PyObject *dimeth_generate(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ dh_param dp;
+ unsigned ql = 0, pl;
+ unsigned steps = 0;
+ grand *r = &rand_global;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ static const char *const kwlist[] =
+ { "pbits", "qbits", "event", "rng", "nsteps", 0 };
+ PyObject *rc = 0;
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&O&O&O&:generate", KWLIST,
+ convuint, &pl, convuint, &ql,
+ convpgev, &evt, convgrand, &r,
+ convuint, &steps))
+ goto end;
+ if (dh_gen(&dp, ql, pl, steps, r, evt.ev.proc, evt.ev.ctx))
+ PGENERR(&exc);
+ rc = fginfo_pywrap(&dp, dhinfo_pytype);
+end:
+ droppgev(&evt);
+ return (rc);
+}
+
+static PyObject *dimeth_genlimlee(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ dh_param dp;
+ unsigned ql, pl;
+ unsigned steps = 0;
+ grand *r = &rand_global;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev oe = { { 0 } }, ie = { { 0 } };
+ int subgroupp = 1;
+ unsigned f = 0;
+ static const char *const kwlist[] = {
+ "pbits", "qbits", "event", "ievent",
+ "rng", "nsteps", "subgroupp", 0
+ };
+ size_t i, nf;
+ mp **v = 0;
+ PyObject *rc = 0, *vec = 0;
+
+ oe.exc = ie.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw,
+ "O&O&|O&O&O&O&O&:genlimlee", KWLIST,
+ convuint, &pl, convuint, &ql,
+ convpgev, &oe, convpgev, &ie,
+ convgrand, &r, convuint, &steps,
+ convbool, &subgroupp))
+ goto end;
+ if (subgroupp) f |= DH_SUBGROUP;
+ if (dh_limlee(&dp, ql, pl, f, steps, r,
+ oe.ev.proc, oe.ev.ctx, ie.ev.proc, ie.ev.ctx, &nf, &v))
+ PGENERR(&exc);
+ vec = PyList_New(nf);
+ for (i = 0; i < nf; i++)
+ PyList_SET_ITEM(vec, i, mp_pywrap(v[i]));
+ xfree(v);
+ rc = Py_BuildValue("(NN)", fginfo_pywrap(&dp, dhinfo_pytype), vec);
+end:
+ droppgev(&oe); droppgev(&ie);
+ return (rc);
+}
+
+static PyObject *dimeth_genkcdsa(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ dh_param dp;
+ unsigned ql, pl;
+ unsigned steps = 0;
+ grand *r = &rand_global;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ static const char *const kwlist[] =
+ { "pbits", "qbits", "event", "rng", "nsteps", 0 };
+ mp *v = MP_NEW;
+ PyObject *rc = 0;
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&O&:genkcdsa", KWLIST,
+ convuint, &pl, convuint, &ql,
+ convpgev, &evt, convgrand, &r,
+ convuint, &steps))
+ goto end;
+ if (dh_kcdsagen(&dp, ql, pl, 0, steps, r, evt.ev.proc, evt.ev.ctx))
+ PGENERR(&exc);
+ mp_div(&v, 0, dp.p, dp.q);
+ v = mp_lsr(v, v, 1);
+ rc = Py_BuildValue("(NN)", fginfo_pywrap(&dp, dhinfo_pytype),
+ mp_pywrap(v));
+end:
+ droppgev(&evt);
+ return (rc);
+}
+
+static PyObject *dimeth_gendsa(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ dsa_param dp;
+ unsigned ql, pl;
+ unsigned steps = 0;
+ dsa_seed ds;
+ struct bin k;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ static const char *const kwlist[] =
+ { "pbits", "qbits", "seed", "event", "nsteps", 0 };
+ PyObject *rc = 0;
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&|O&O&:gendsa", KWLIST,
+ convuint, &pl, convuint, &ql,
+ convbin, &k,
+ convpgev, &evt, convuint, &steps))
+ goto end;
+ if (dsa_gen(&dp, ql, pl, steps, k.p, k.sz, &ds, evt.ev.proc, evt.ev.ctx))
+ PGENERR(&exc);
+ rc = Py_BuildValue("(NNl)", fginfo_pywrap(&dp, dhinfo_pytype),
+ bytestring_pywrap(ds.p, ds.sz), (long)ds.count);
+ xfree(ds.p);
+end:
+ droppgev(&evt);
+ return (rc);
+}
+
+static PyObject *meth__parse(PyObject *me, PyObject *arg, PyTypeObject *ty,
+ int (*parse)(qd_parse *, gprime_param *))
+{
+ qd_parse qd;
+ char *p;
+ gprime_param gp;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+ qd.p = p; qd.e = 0;
+ if (parse(&qd, &gp)) VALERR(qd.e);
+ rc = fginfo_pywrap(&gp, ty);
+end:
+ return (rc);
+}
+
+static PyObject *dimeth_parse(PyObject *me, PyObject *arg)
+ { return (meth__parse(me, arg, dhinfo_pytype, dh_parse)); }
+
+static PyObject *bimeth_parse(PyObject *me, PyObject *arg)
+ { return (meth__parse(me, arg, bindhinfo_pytype, dhbin_parse)); }
+
+static const PyGetSetDef fginfo_pygetset[] = {
+#define GETSETNAME(op, name) fi##op##_##name
+ GET (r, "I.r -> group order")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyGetSetDef dhinfo_pygetset[] = {
+#define GETSETNAME(op, name) di##op##_##name
+ GET (p, "I.p -> prime")
+ GET (g, "I.g -> generator")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef dhinfo_pymethods[] = {
+#define METHNAME(name) dimeth_##name
+ SMTH (parse, "parse(STR) -> D, REST")
+ KWSMTH(generate,
+ "generate(PBITS, [qbits = 0], [event = pgen_nullev],\n"
+ " [rng = rand], [nsteps = 0]) -> D")
+ KWSMTH(genlimlee,
+ "genlimlee(PBITS, QBITS, [event = pgen_nullev], "
+ "[ievent = pgen_nullev],\n"
+ " [rng = rand], [nsteps = 0], [subgroupp = True]) "
+ "-> (D, [Q, ...])")
+ KWSMTH(gendsa,
+ "gendsa(PBITS, QBITS, SEED, [event = pgen_nullev], [nsteps = 0])\n"
+ " -> (D, SEED, COUNT)")
+ KWSMTH(genkcdsa,
+ "gendsa(PBITS, QBITS, [event = pgen_nullev], "
+ "[rng = rand], [nsteps = 0])\n"
+ " -> (D, V)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef bindhinfo_pygetset[] = {
+#define GETSETNAME(op, name) bi##op##_##name
+ GET (p, "I.p -> irreducible polynomial")
+ GET (m, "I.m -> degree of polynomial")
+ GET (g, "I.g -> generator")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef bindhinfo_pymethods[] = {
+#define METHNAME(name) bimeth_##name
+ SMTH (parse, "parse(STR) -> D, REST")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject fginfo_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "FGInfo", /* @tp_name@ */
+ sizeof(fginfo_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ fginfo_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Abstract base class for field-group information objects.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(fginfo), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject dhinfo_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "DHInfo", /* @tp_name@ */
+ sizeof(fginfo_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ fginfo_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "DHInfo(P, R, G): standard (integer) Diffie-Hellman group information.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(dhinfo), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(dhinfo), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ fginfo_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject bindhinfo_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BinDHInfo", /* @tp_name@ */
+ sizeof(fginfo_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ fginfo_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "BinDHInfo(P, R, G): binary-field Diffie-Hellman group information.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(bindhinfo), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(bindhinfo), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ fginfo_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- General utilities -------------------------------------------------*/
+
+PyTypeObject *ge_pytype, *group_pytype;
+static PyTypeObject *primegroup_pytype, *bingroup_pytype, *ecgroup_pytype;
+
+group *group_copy(group *g)
+{
+ if (STRCMP(G_NAME(g), ==, "prime")) {
+ gctx_prime *gc = (gctx_prime *)g;
+ gprime_param gp;
+ gp.g = G_TOINT(g, MP_NEW, g->g);
+ gp.p = gc->mm.m;
+ gp.q = gc->g.r;
+ g = group_prime(&gp);
+ MP_DROP(gp.g);
+ } else if (STRCMP(G_NAME(g), ==, "bin")) {
+ gctx_bin *gc = (gctx_bin *)g;
+ gbin_param gb;
+ gb.g = G_TOINT(g, MP_NEW, g->g);
+ gb.p = gc->r.p;
+ gb.q = gc->g.r;
+ g = group_binary(&gb);
+ MP_DROP(gb.g);
+ } else if (STRCMP(G_NAME(g), ==, "ec")) {
+ gctx_ec *gc = (gctx_ec *)g;
+ ec_info ei;
+ if ((ei.c = eccurve_copy(gc->ei.c)) == 0)
+ return (0);
+ EC_CREATE(&ei.g);
+ EC_COPY(&ei.g, &gc->ei.g);
+ ei.r = MP_COPY(gc->ei.r);
+ ei.h = MP_COPY(gc->ei.h);
+ g = group_ec(&ei);
+ } else
+ g = 0;
+ return (g);
+}
+
+PyObject *ge_pywrap(PyObject *gobj, ge *x)
+{
+ ge_pyobj *z = PyObject_New(ge_pyobj, (PyTypeObject *)gobj);
+ z->x = x;
+ z->g = GROUP_G(gobj);
+ Py_INCREF(gobj);
+ return ((PyObject *)z);
+}
+
+static PyObject *ge_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "x", 0 };
+ PyObject *x;
+ group *g;
+ ec p = EC_INIT;
+ mp *y = 0;
+ ge *xx = 0;
+ size_t n;
+ mptext_stringctx sc;
+
+ g = GROUP_G(ty);
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &x)) goto end;
+ xx = G_CREATE(g);
+ if (ECPT_PYCHECK(x)) {
+ getecptout(&p, x);
+ if (G_FROMEC(g, xx, &p))
+ TYERR("can't convert from elliptic curve point");
+ EC_DESTROY(&p);
+ } else if ((y = tomp(x)) != 0) {
+ if (G_FROMINT(g, xx, y))
+ TYERR("can't convert from integer");
+ MP_DROP(y);
+ } else if (TEXT_CHECK(x)) {
+ TEXT_PTRLEN(x, sc.buf, n); sc.lim = sc.buf + n;
+ if (G_READ(g, xx, &mptext_stringops, &sc) || sc.buf < sc.lim)
+ VALERR("malformed group element string");
+ } else
+ TYERR("can't convert to group element");
+ return (ge_pywrap((PyObject *)ty, xx));
+end:
+ mp_drop(y);
+ EC_DESTROY(&p);
+ if (xx) G_DESTROY(g, xx);
+ return (0);
+}
+
+static PyObject *group_dopywrap(PyTypeObject *ty, group *g)
+{
+ group_pyobj *gobj = newtype(ty, 0, g->ops->name);
+ gobj->g = g;
+ gobj->ty.ht_type.tp_basicsize = sizeof(ge_pyobj);
+ gobj->ty.ht_type.tp_base = ge_pytype;
+ Py_INCREF(group_pytype);
+ gobj->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_HEAPTYPE);
+ gobj->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ gobj->ty.ht_type.tp_free = 0;
+ gobj->ty.ht_type.tp_new = ge_pynew;
+ typeready(&gobj->ty.ht_type);
+ return ((PyObject *)gobj);
+}
+
+PyObject *group_pywrap(group *g)
+{
+ PyTypeObject *ty;
+
+ if (STRCMP(G_NAME(g), ==, "prime")) ty = primegroup_pytype;
+ else if (STRCMP(G_NAME(g), ==, "bin")) ty = bingroup_pytype;
+ else if (STRCMP(G_NAME(g), ==, "ec")) ty = ecgroup_pytype;
+ else abort();
+ return (group_dopywrap(ty, g));
+}
+
+/*----- Group elements ----------------------------------------------------*/
+
+#define BINOP(name) \
+ static PyObject *ge_py##name(PyObject *x, PyObject *y) \
+ { \
+ ge *z; \
+ group *g; \
+ if (!GE_PYCHECK(x) || !GE_PYCHECK(y) || \
+ (GE_G(x) != GE_G(y) && !group_samep(GE_G(x), GE_G(y)))) \
+ RETURN_NOTIMPL; \
+ g = GE_G(x); \
+ z = G_CREATE(g); \
+ g->ops->name(g, z, GE_X(x), GE_X(y)); \
+ return (ge_pywrap(GE_GOBJ(x), z)); \
+ }
+BINOP(mul)
+BINOP(div)
+#undef BINOP
+
+#define UNOP(name) \
+ static PyObject *gemeth_##name(PyObject *me) \
+ { \
+ group *g; \
+ ge *z; \
+ g = GE_G(me); \
+ z = G_CREATE(g); \
+ g->ops->name(g, z, GE_X(me)); \
+ return (ge_pywrap(GE_GOBJ(me), z)); \
+ }
+UNOP(sqr)
+UNOP(inv)
+#undef UNOP
+
+static PyObject *ge_pyexp(PyObject *x, PyObject *n, PyObject *m)
+{
+ mp *nn;
+ ge *z;
+
+ if (m != Py_None || !GE_PYCHECK(x)) RETURN_NOTIMPL;
+ if (FE_PYCHECK(n) && FE_F(n)->ops->ty == FTY_PRIME)
+ nn = F_OUT(FE_F(n), MP_NEW, FE_X(n));
+ else if ((nn = implicitmp(n)) == 0)
+ RETURN_NOTIMPL;
+ z = G_CREATE(GE_G(x));
+ G_EXP(GE_G(x), z, GE_X(x), nn);
+ MP_DROP(nn);
+ return (ge_pywrap(GE_GOBJ(x), z));
+}
+
+static void ge_pydealloc(PyObject *me)
+{
+ G_DESTROY(GE_G(me), GE_X(me));
+ Py_DECREF(GE_GOBJ(me));
+ FREEOBJ(me);
+}
+
+static void group_pydealloc(PyObject *me)
+{
+ G_DESTROYGROUP(GROUP_G(me));
+ PyType_Type.tp_dealloc(me);
+}
+
+static PyObject *gmexp_id(PyObject *me)
+{
+ group *g = GROUP_G(me); ge *x = G_CREATE(g);
+ G_COPY(g, x, g->i); return (ge_pywrap(me, x));
+}
+
+static int gmexp_fill(void *pp, PyObject *me, PyObject *x, PyObject *m)
+{
+ group_expfactor *f = pp;
+
+ if (!GE_PYCHECK(x) || GE_G(x) != GROUP_G(me) || (f->exp = getmp(m)) == 0)
+ return (-1);
+ f->base = GE_X(x);
+ return (0);
+}
+
+static PyObject *ge_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ int b;
+ PyObject *rc = 0;
+
+ if (!GE_PYCHECK(x) || !GE_PYCHECK(y) ||
+ (GE_G(x) != GE_G(y) && !group_samep(GE_G(x), GE_G(y))))
+ RETURN_NOTIMPL;
+ switch (op) {
+ case Py_EQ: b = G_EQ(GE_G(x), GE_X(x), GE_X(y)); break;
+ case Py_NE: b = !G_EQ(GE_G(x), GE_X(x), GE_X(y)); break;
+ default: TYERR("group elements are unordered");
+ }
+ rc = getbool(b);
+end:
+ return (rc);
+}
+
+static PyObject *gemeth_check(PyObject *me)
+{
+ if (group_check(GE_G(me), GE_X(me))) VALERR("bad group element");
+ RETURN_OBJ(me);
+end:
+ return (0);
+}
+
+static int ge_pynonzerop(PyObject *x)
+ { return (!G_IDENTP(GE_G(x), GE_X(x))); }
+
+static PyObject *ge_pystr(PyObject *me)
+{
+ dstr d = DSTR_INIT;
+ PyObject *rc;
+
+ group_writedstr(GE_G(me), GE_X(me), &d);
+ rc = TEXT_FROMSTRLEN(d.buf, d.len);
+ DDESTROY(&d);
+ return (rc);
+}
+
+#ifdef PY2
+static PyObject *ge_pylong(PyObject *me)
+{
+ mp *x = 0;
+ PyObject *rc = 0;
+
+ if ((x = G_TOINT(GE_G(me), MP_NEW, GE_X(me))) == 0)
+ TYERR("can't convert to integer");
+ rc = mp_topylong(x);
+end:
+ mp_drop(x);
+ return (rc);
+}
+#endif
+
+static PyObject *ge_pyint(PyObject *me)
+{
+ mp *x = 0;
+ PyObject *rc = 0;
+ long l;
+
+ if ((x = G_TOINT(GE_G(me), MP_NEW, GE_X(me))) == 0)
+ TYERR("can't convert to integer");
+ if (!mp_tolong_checked(x, &l, 0)) rc = PyInt_FromLong(l);
+ else rc = mp_topylong(x);
+end:
+ mp_drop(x);
+ return (rc);
+}
+
+static PyObject *gemeth_toint(PyObject *me)
+{
+ mp *x;
+
+ if ((x = G_TOINT(GE_G(me), MP_NEW, GE_X(me))) == 0)
+ TYERR("can't convert to integer");
+ return (mp_pywrap(x));
+end:
+ return (0);
+}
+
+static PyObject *gemeth_toec(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "curve", 0 };
+ PyTypeObject *cty = 0;
+ PyObject *rc = 0;
+ group *g;
+ ec_curve *c;
+ ec p = EC_INIT;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:toec", KWLIST,
+ &cty)) goto end;
+ g = GROUP_G(GE_GOBJ(me));
+ if (cty) {
+ if (!PyType_Check(cty) || !PyType_IsSubtype(cty, ecpt_pytype))
+ TYERR("want subtype of catacomb.ECPt");
+ Py_INCREF((PyObject *)cty);
+ } else if (STRCMP(G_NAME(g), ==, "ec")) {
+ c = eccurve_copy(((gctx_ec *)g)->ei.c);
+ cty = (PyTypeObject *)eccurve_pywrap(0, c);
+ } else {
+ cty = ecpt_pytype;
+ Py_INCREF((PyObject *)cty);
+ }
+ if (G_TOEC(GE_G(me), &p, GE_X(me))) {
+ Py_DECREF((PyObject *)cty);
+ TYERR("can't convert to ec point");
+ }
+ rc = ecpt_pywrapout(cty, &p);
+ Py_DECREF((PyObject *)cty);
+end:
+ return (rc);
+}
+
+static PyObject *gemeth_tobuf(PyObject *me)
+{
+ buf b;
+ PyObject *rc;
+ size_t n;
+
+ n = GE_G(me)->noctets + 4;
+ rc = bytestring_pywrap(0, n);
+ buf_init(&b, BIN_PTR(rc), n);
+ G_TOBUF(GE_G(me), &b, GE_X(me));
+ assert(BOK(&b));
+ BIN_SETLEN(rc, BLEN(&b));
+ return (rc);
+}
+
+static PyObject *gemeth_toraw(PyObject *me)
+{
+ buf b;
+ PyObject *rc;
+ size_t n;
+
+ n = GE_G(me)->noctets;
+ rc = bytestring_pywrap(0, n);
+ buf_init(&b, BIN_PTR(rc), n);
+ G_TORAW(GE_G(me), &b, GE_X(me));
+ assert(BOK(&b));
+ BIN_SETLEN(rc, BLEN(&b));
+ return (rc);
+}
+
+static PyObject *gmexp_exp(PyObject *me, void *pp, size_t n)
+{
+ ge *z = G_CREATE(GROUP_G(me));
+ G_MEXP(GROUP_G(me), z, pp, n);
+ return (ge_pywrap(me, z));
+}
+
+static void gmexp_drop(void *pp)
+{
+ group_expfactor *f = pp;
+ MP_DROP(f->exp);
+}
+
+static PyObject *gmeth_mexp(PyObject *me, PyObject *arg)
+{
+ return (mexp_common(me, arg, sizeof(group_expfactor),
+ gmexp_id, gmexp_fill, gmexp_exp, gmexp_drop));
+}
+
+static PyObject *gmeth_checkgroup(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "rng", 0 };
+ grand *r = &rand_global;
+ const char *p;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:checkgroup", KWLIST,
+ convgrand, &r))
+ goto end;
+ if ((p = G_CHECK(GROUP_G(me), r)) != 0)
+ VALERR(p);
+ RETURN_OBJ(me);
+end:
+ return (0);
+}
+
+static PyObject *group_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ int b = group_samep(GROUP_G(x), GROUP_G(y));
+ switch (op) {
+ case Py_EQ: break;
+ case Py_NE: b = !b;
+ default: TYERR("can't order groups");
+ }
+ return (getbool(b));
+end:
+ return (0);
+}
+
+static PyObject *gemeth_frombuf(PyObject *me, PyObject *arg)
+{
+ buf b;
+ struct bin in;
+ group *g;
+ ge *x = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:frombuf", convbin, &in)) return (0);
+ g = GROUP_G(me);
+ buf_init(&b, (/*unconst*/ void *)in.p, in.sz);
+ x = G_CREATE(g);
+ if (G_FROMBUF(g, &b, x)) VALERR("invalid data");
+ return (Py_BuildValue("(NN)", ge_pywrap(me, x), bytestring_pywrapbuf(&b)));
+end:
+ if (x) G_DESTROY(g, x);
+ return (0);
+}
+
+static PyObject *gemeth_fromraw(PyObject *me, PyObject *arg)
+{
+ buf b;
+ struct bin in;
+ group *g;
+ ge *x = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:fromraw", convbin, &in)) return (0);
+ g = GROUP_G(me);
+ buf_init(&b, (/*unconst*/ void *)in.p, in.sz);
+ x = G_CREATE(g);
+ if (G_FROMRAW(g, &b, x)) VALERR("invalid data");
+ return (Py_BuildValue("(NN)", ge_pywrap(me, x), bytestring_pywrapbuf(&b)));
+end:
+ if (x) G_DESTROY(g, x);
+ return (0);
+}
+
+static PyObject *gemeth_fromstring(PyObject *me, PyObject *arg)
+{
+ mptext_stringctx sc;
+ char *p;
+ Py_ssize_t n;
+ group *g;
+ ge *x = 0;
+
+ if (!PyArg_ParseTuple(arg, "s#:fromstring", &p, &n)) return (0);
+ sc.buf = p;
+ sc.lim = sc.buf + n;
+ g = GROUP_G(me);
+ x = G_CREATE(g);
+ if (G_READ(g, x, &mptext_stringops, &sc))
+ VALERR("bad group element string");
+ return (Py_BuildValue("(Ns#)", ge_pywrap(me, x),
+ sc.buf, (Py_ssize_t)(sc.lim - sc.buf)));
+end:
+ if (x) G_DESTROY(g, x);
+ return (0);
+}
+
+static PyObject *gmeth_parse(PyObject *me, PyObject *arg)
+{
+ char *p;
+ qd_parse qd;
+ group *g;
+
+ if (!PyArg_ParseTuple(arg, "s:parse", &p)) goto end;
+ qd.p = p; qd.e = 0;
+ if ((g = group_parse(&qd)) == 0) VALERR(qd.e);
+ return (group_pywrap(g));
+end:
+ return (0);
+}
+
+static PyObject *geget_group(PyObject *me, void *hunoz)
+ { RETURN_OBJ(GE_GOBJ(me)); }
+
+static PyObject *gget_nbits(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GROUP_G(me)->nbits)); }
+
+static PyObject *gget_noctets(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GROUP_G(me)->noctets)); }
+
+static PyObject *gget_i(PyObject *me, void *hunoz)
+{
+ group *g = GROUP_G(me); ge *x = G_CREATE(g);
+ G_COPY(g, x, g->i); return (ge_pywrap(me, x));
+}
+
+static PyObject *gget_g(PyObject *me, void *hunoz)
+{
+ group *g = GROUP_G(me); ge *x = G_CREATE(g);
+ G_COPY(g, x, g->g); return (ge_pywrap(me, x));
+}
+
+static Py_hash_t ge_pyhash(PyObject *me)
+{
+ buf b;
+ size_t sz = GE_G(me)->noctets + 4;
+ uint32 h = 0xf672c776 + GE_G(me)->ops->ty;
+ octet *p = xmalloc(sz);
+ buf_init(&b, p, sz);
+ G_TOBUF(GE_G(me), &b, GE_X(me));
+ assert(BOK(&b));
+ h = unihash_hash(&unihash_global, h, BBASE(&b), BLEN(&b));
+ xfree(p);
+ return (h % LONG_MAX);
+}
+
+static PyObject *gget_r(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(GROUP_G(me)->r))); }
+
+static PyObject *gget_h(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(GROUP_G(me)->h))); }
+
+static const PyGetSetDef ge_pygetset[] = {
+#define GETSETNAME(op, name) ge##op##_##name
+ GET (group, "X.group -> group containing X")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef ge_pymethods[] = {
+#define METHNAME(name) gemeth_##name
+ NAMETH(inv, "X.inv() -> inverse element of X")
+ NAMETH(sqr, "X.sqr() -> X^2 = X * X")
+ NAMETH(check, "X.check() -> check X really belongs to its group")
+ NAMETH(toint, "X.toint() -> X converted to an integer")
+ KWMETH(toec, "X.toec([curve = ECPt]) -> "
+ "X converted to elliptic curve point")
+ NAMETH(tobuf, "X.tobuf() -> X in buffer representation")
+ NAMETH(toraw, "X.toraw() -> X in raw representation")
+ CMTH (frombuf, "frombuf(BUF) -> X, REST")
+ CMTH (fromraw, "fromraw(BUF) -> X, REST")
+ CMTH (fromstring, "fromstring(STR) -> X, REST")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods ge_pynumber = {
+ 0, /* @nb_add@ */
+ 0, /* @nb_subtract@ */
+ ge_pymul, /* @nb_multiply@ */
+#ifdef PY2
+ ge_pydiv, /* @nb_divide@ */
+#endif
+ 0, /* @nb_remainder@ */
+ 0, /* @nb_divmod@ */
+ ge_pyexp, /* @nb_power@ */
+ 0, /* @nb_negative@ */
+ 0, /* @nb_positive@ */
+ 0, /* @nb_absolute@ */
+ ge_pynonzerop, /* @nb_nonzero@ */
+ 0, /* @nb_invert@ */
+ 0, /* @nb_lshift@ */
+ 0, /* @nb_rshift@ */
+ 0, /* @nb_and@ */
+ 0, /* @nb_xor@ */
+ 0, /* @nb_or@ */
+#ifdef PY2
+ 0, /* @nb_coerce@ */
+#endif
+ ge_pyint, /* @nb_int@ */
+ PY23(ge_pylong, 0), /* @nb_long@ */
+ 0 /* meaningless */, /* @nb_float@ */
+#ifdef PY2
+ 0, /* @nb_oct@ */
+ 0, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+#ifdef PY2
+ 0, /* @nb_inplace_divide@ */
+#endif
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ 0, /* @nb_floor_divide@ */
+ ge_pydiv, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+};
+
+static const PyTypeObject ge_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GE", /* @tp_name@ */
+ sizeof(ge_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ ge_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ PYNUMBER(ge), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ ge_pyhash, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ ge_pystr, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Group elements, abstract base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ ge_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(ge), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(ge), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyGetSetDef group_pygetset[] = {
+#define GETSETNAME(op, name) g##op##_##name
+ GET (noctets, "G.noctets -> size in octets of element")
+ GET (nbits, "G.nbits -> size in bits of element")
+ GET (i, "G.i -> group identity")
+ GET (g, "G.g -> group generator")
+ GET (r, "G.r -> group order")
+ GET (h, "G.h -> group cofactor")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef group_pymethods[] = {
+#define METHNAME(name) gmeth_##name
+ METH (mexp, "G.mexp([(X0, N0), (X1, N1), ...]) -> X0^N0 X1^N1 ...")
+ KWMETH(checkgroup, "G.checkgroup([rng = rand]): check group is good")
+ SMTH (parse, "parse(STR) -> G, REST")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject group_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Group", /* @tp_name@ */
+ sizeof(group_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ group_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Abstract base class for groups.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ group_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(group), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(group), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *pgget_info(PyObject *me, void *hunoz)
+{
+ gprime_param dp;
+ gctx_prime *gg = (gctx_prime *)GROUP_G(me);
+ dp.p = MP_COPY(gg->mm.m);
+ dp.q = MP_COPY(gg->g.r);
+ dp.g = mpmont_reduce(&gg->mm, MP_NEW, gg->gen.x);
+ return (fginfo_pywrap(&dp, dhinfo_pytype));
+}
+
+static const PyGetSetDef primegroup_pygetset[] = {
+#define GETSETNAME(op, name) pg##op##_##name
+ GET (info, "G.info -> information about the group")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *primegroup_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *i;
+ static const char *const kwlist[] = { "info", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
+ dhinfo_pytype, &i))
+ return (0);
+ return (group_dopywrap(ty, group_prime(FGINFO_DP(i))));
+}
+
+static const PyTypeObject primegroup_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeGroup", /* @tp_name@ */
+ sizeof(group_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ group_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeGroup(INFO): subgroups of prime fields.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(primegroup), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ primegroup_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *bgget_info(PyObject *me, void *hunoz)
+{
+ gbin_param dp;
+ gctx_bin *gg = (gctx_bin *)GROUP_G(me);
+ dp.p = MP_COPY(gg->r.p);
+ dp.q = MP_COPY(gg->g.r);
+ dp.g = MP_COPY(gg->gen.x);
+ return (fginfo_pywrap(&dp, bindhinfo_pytype));
+}
+
+static const PyGetSetDef bingroup_pygetset[] = {
+#define GETSETNAME(op, name) bg##op##_##name
+ GET (info, "G.info -> information about the group")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *bingroup_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *i;
+ static const char *const kwlist[] = { "info", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
+ bindhinfo_pytype, &i))
+ return (0);
+ return (group_dopywrap(ty, group_binary(FGINFO_DP(i))));
+}
+
+static const PyTypeObject bingroup_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BinGroup", /* @tp_name@ */
+ sizeof(group_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ group_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "BinGroup(INFO): subgroups of binary fields.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(bingroup), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ bingroup_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *egget_info(PyObject *me, void *hunoz)
+{
+ ec_info ei;
+ gctx_ec *gg = (gctx_ec *)GROUP_G(me);
+
+ ecinfo_copy(&ei, &gg->ei);
+ return (ecinfo_pywrap(&ei));
+}
+
+static const PyGetSetDef ecgroup_pygetset[] = {
+#define GETSETNAME(op, name) eg##op##_##name
+ GET (info, "G.info -> information about the group")
+#undef GETSETNAME
+ { 0 }
+};
+
+static PyObject *ecgroup_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *i;
+ ec_info ei;
+ static const char *const kwlist[] = { "info", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
+ ecinfo_pytype, &i))
+ return (0);
+ ecinfo_copy(&ei, ECINFO_EI(i));
+ return (group_dopywrap(ty, group_ec(&ei)));
+}
+
+static const PyTypeObject ecgroup_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ECGroup", /* @tp_name@ */
+ sizeof(group_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ group_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ECGroup(INFO): elliptic curve groups.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(ecgroup), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ ecgroup_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Global stuff ------------------------------------------------------*/
+
+void group_pyinit(void)
+{
+ INITTYPE(fginfo, root);
+ INITTYPE(dhinfo, fginfo);
+ INITTYPE(bindhinfo, dhinfo);
+ INITTYPE(ge, root);
+ INITTYPE(group, type);
+ INITTYPE(primegroup, group);
+ INITTYPE(bingroup, group);
+ INITTYPE(ecgroup, group);
+}
+
+static const char *grp_namefn(const void *p)
+ { const pentry *pt = p; return (pt->name); }
+
+static int grp_ixfn(const pentry *tab, const pentry *pt)
+{
+ int i;
+
+ for (i = 0; tab[i].name; i++)
+ if (tab[i].data == pt->data) return (i);
+ return (-1);
+}
+static int pgrp_ixfn(const void *p) { return (grp_ixfn(ptab, p)); }
+static int bgrp_ixfn(const void *p) { return (grp_ixfn(bintab, p)); }
+
+static PyObject *grp_valfn(const pentry *tab, PyTypeObject *ty, int i)
+{
+ gprime_param gp;
+
+ dh_infofromdata(&gp, tab[i].data);
+ return (fginfo_pywrap(&gp, ty));
+}
+static PyObject *pgrp_valfn(int i)
+ { return (grp_valfn(ptab, dhinfo_pytype, i)); }
+static PyObject *bgrp_valfn(int i)
+ { return (grp_valfn(bintab, bindhinfo_pytype, i)); }
+
+void group_pyinsert(PyObject *mod)
+{
+ INSERT("FGInfo", fginfo_pytype);
+ INSERT("DHInfo", dhinfo_pytype);
+ INSERT("BinDHInfo", bindhinfo_pytype);
+ INSERT("GE", ge_pytype);
+ INSERT("Group", group_pytype);
+ INSERT("PrimeGroup", primegroup_pytype);
+ INSERT("BinGroup", bingroup_pytype);
+ INSERT("ECGroup", ecgroup_pytype);
+ INSERT("primegroups", make_grouptab(ptab, sizeof(*ptab),
+ grp_namefn, pgrp_ixfn, pgrp_valfn));
+ INSERT("bingroups", make_grouptab(bintab, sizeof(*bintab),
+ grp_namefn, bgrp_ixfn, bgrp_valfn));
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Key files and data
+ *
+ * (c) 2005 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Exceptions --------------------------------------------------------*/
+
+static PyObject *keyexc;
+static PyObject *keyioexc;
+static PyObject *keyfilebrokenexc;
+
+static PyObject *kxmeth___init__(PyObject *me, PyObject *arg)
+{
+ long err;
+ PyObject *x = 0;
+ Py_ssize_t n;
+
+ n = PyTuple_GET_SIZE(arg);
+ if (n < 2) TYERR("__init__() takes at least two arguments");
+ me = PyTuple_GET_ITEM(arg, 0);
+ err = PyInt_AsLong(PyTuple_GET_ITEM(arg, 1));
+ if (err == -1 && PyErr_Occurred()) goto end;
+ if (INT_MIN > err || err > INT_MAX) OVFERR("error code out of range");
+
+ x = PyInt_FromLong(err); if (!x) goto end;
+ if (PyObject_SetAttrString(me, "err", x)) goto end;
+ Py_DECREF(x); x = 0;
+
+ x = TEXT_FROMSTR(key_strerror(err)); if (!x) goto end;
+ if (PyObject_SetAttrString(me, "errstring", x)) goto end;
+ Py_DECREF(x); x = 0;
+
+ x = PyTuple_GetSlice(arg, 1, n); if (!x) goto end;
+ if (PyObject_SetAttrString(me, "args", x)) goto end;
+ Py_DECREF(x); x = 0;
+ RETURN_NONE;
+
+end:
+ Py_XDECREF(x);
+ return (0);
+}
+
+static PyObject *kxmeth___str__(PyObject *me, PyObject *arg)
+{
+ long err;
+ const char *errstr, *errtag;
+ PyObject *x = 0;
+ PyObject *rc = 0;
+
+ static const char *const tab[] = {
+#define ENTRY(tag, num, str) "KERR_" #tag,
+ KEY_ERRORS(ENTRY)
+#undef ENTRY
+ };
+
+ if (!PyArg_ParseTuple(arg, "O:__str__", &me) ||
+ (x = PyObject_GetAttrString(me, "err")) == 0 ||
+ (err = PyInt_AsLong(x), PyErr_Occurred()))
+ goto done;
+ Py_DECREF(x); x = 0;
+ err = -err;
+ if (err >= 0 && err < N(tab)) errtag = tab[err];
+ else errtag = "<unknown>";
+ if ((x = PyObject_GetAttrString(me, "errstring")) == 0 ||
+ (errstr = TEXT_STR(x)) == 0)
+ goto done;
+ rc = TEXT_FORMAT("%s (%ld): %s", errtag, -err, errstr);
+
+done:
+ Py_XDECREF(x);
+ return (rc);
+}
+
+static const PyMethodDef keyexc_pymethods[] = {
+#define METHNAME(func) kxmeth_##func
+ METH (__init__, "KeyError(CODE)")
+ METH (__str__, "E.__str__() -> STRING")
+#undef METHNAME
+ { 0 }
+};
+
+static void keyexc_raise(int err)
+{
+ PyObject *arg = Py_BuildValue("(i)", err);
+ if (arg) PyErr_SetObject(keyexc, arg);
+ Py_XDECREF(arg);
+}
+#define KEYERR(err) do { keyexc_raise(err); goto end; } while (0)
+#define KEYIOERR(name) do { \
+ PyErr_SetFromErrnoWithFilename(keyioexc, name); \
+ goto end; \
+} while (0)
+#define KEYFILEBROKEN(name) do { \
+ PyErr_SetFromErrnoWithFilename(keyfilebrokenexc, name); \
+ goto end; \
+} while (0)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct keydata_pyobj {
+ GMAP_PYOBJ_HEAD
+ key_data *kd;
+} keydata_pyobj;
+
+static PyTypeObject *keydata_pytype;
+static PyTypeObject *keydatabin_pytype;
+static PyTypeObject *keydataenc_pytype;
+static PyTypeObject *keydatamp_pytype;
+static PyTypeObject *keydatastruct_pytype;
+static PyTypeObject *keydatastr_pytype;
+static PyTypeObject *keydataec_pytype;
+#define KEYDATA_PYCHECK(o) PyObject_TypeCheck(o, keydata_pytype)
+#define KEYDATA_KD(o) (((keydata_pyobj *)(o))->kd)
+
+typedef struct keyfile_pyobj {
+ GMAP_PYOBJ_HEAD
+ key_file kf;
+} keyfile_pyobj;
+
+static PyTypeObject *keyfile_pytype;
+#define KEYFILE_PYCHECK(o) PyObject_TypeCheck(o, keyfile_pytype)
+#define KEYFILE_KF(o) (&((keyfile_pyobj *)(o))->kf)
+
+typedef struct key_pyobj {
+ PyObject_HEAD
+ key *k;
+ PyObject *kfobj;
+} key_pyobj;
+
+static PyTypeObject *key_pytype;
+#define KEY_PYCHECK(o) PyObject_TypeCheck(o, key_pytype)
+#define KEY_K(o) (((key_pyobj *)(o))->k)
+#define KEY_KFOBJ(o) (((key_pyobj *)(o))->kfobj)
+#define KEY_KF(o) KEYFILE_KF(KEY_KFOBJ(o))
+
+typedef struct keyattrs_pyobj {
+ GMAP_PYOBJ_HEAD
+ PyObject *kobj;
+} keyattrs_pyobj;
+
+static PyTypeObject *keyattrs_pytype;
+#define KEYATTRS_PYCHECK(o) PyObject_TypeCheck(o, keyattrs_pytype)
+#define KEYATTRS_KOBJ(o) (((keyattrs_pyobj *)(o))->kobj)
+#define KEYATTRS_KF(o) KEY_KF(KEYATTRS_KOBJ(o))
+#define KEYATTRS_K(o) KEY_K(KEYATTRS_KOBJ(o))
+
+/*----- Filters -----------------------------------------------------------*/
+
+static int convfilter(PyObject *x, void *p)
+{
+ key_filter *f = p;
+ const char *fs;
+ char *end;
+ int n;
+ PyObject *a = 0, *b = 0;
+ int err;
+ int rc = 0;
+
+ if ((fs = TEXT_STR(x)) != 0) {
+ if ((err = key_readflags(fs, &end, &f->f, &f->m)) != 0)
+ KEYERR(err);
+ if (*end)
+ KEYERR(KERR_BADFLAGS);
+ } else {
+ PyErr_Clear();
+ if (!PySequence_Check(x))
+ goto tyerr;
+ else if ((n = PySequence_Size(x)) < 0)
+ goto end;
+ else if (n != 2)
+ goto tyerr;
+ else if ((a = PySequence_GetItem(x, 0)) == 0 || !convuint(a, &f->f) ||
+ (b = PySequence_GetItem(x, 1)) == 0 || !convuint(b, &f->m))
+ goto end;
+ }
+ rc = 1;
+ goto end;
+tyerr:
+ TYERR("expected flag string or flag/mask pair");
+end:
+ Py_XDECREF(a);
+ Py_XDECREF(b);
+ return (rc);
+}
+
+static int convflags(PyObject *x, void *p)
+{
+ unsigned *f = p;
+ const char *fs;
+ char *end;
+ int err;
+ int rc = 0;
+
+ if (convuint(x, p))
+ return (1);
+ else {
+ PyErr_Clear();
+ if ((fs = TEXT_STR(x)) != 0) {
+ if ((err = key_readflags(fs, &end, f, 0)) != 0)
+ KEYERR(err);
+ if (*end)
+ KEYERR(KERR_BADFLAGS);
+ } else {
+ PyErr_Clear();
+ goto tyerr;
+ }
+ }
+ rc = 1;
+ goto end;
+tyerr:
+ TYERR("expected flag string or integer bitfield");
+end:
+ return (rc);
+}
+
+static PyObject *kdmeth_readflags(PyObject *me, PyObject *arg)
+{
+ const char *p;
+ char *end;
+ unsigned f, m;
+ PyObject *rc = 0;
+ int err;
+
+ if (!PyArg_ParseTuple(arg, "s:readflags", &p)) goto end;
+ if ((err = key_readflags(p, &end, &f, &m)) != 0) KEYERR(err);
+ rc = Py_BuildValue("(NNs)", getulong(f), getulong(m), end);
+end:
+ return (rc);
+}
+
+static PyObject *kdmeth_writeflags(PyObject *me, PyObject *arg)
+{
+ dstr d = DSTR_INIT;
+ PyObject *rc;
+ unsigned f;
+
+ if (!PyArg_ParseTuple(arg, "O&:key_writeflags", convuint, &f)) return (0);
+ key_writeflags(f, &d);
+ rc = TEXT_FROMSTRLEN(d.buf, d.len);
+ dstr_destroy(&d);
+ return (rc);
+}
+
+/*----- Key data ----------------------------------------------------------*/
+
+static const gmap_ops keydatastruct_gmops;
+
+static PyObject *keydata_pywrap(key_data *kd)
+{
+ PyTypeObject *ty;
+ keydata_pyobj *kdobj;
+ const gmap_ops *gmops = 0;
+
+ switch (kd->e & KF_ENCMASK) {
+ case KENC_BINARY: ty = keydatabin_pytype; break;
+ case KENC_ENCRYPT: ty = keydataenc_pytype; break;
+ case KENC_MP: ty = keydatamp_pytype; break;
+ case KENC_STRUCT:
+ ty = keydatastruct_pytype;
+ gmops = &keydatastruct_gmops;
+ break;
+ case KENC_STRING: ty = keydatastr_pytype; break;
+ case KENC_EC: ty = keydataec_pytype; break;
+ default: abort();
+ }
+ kdobj = PyObject_NEW(keydata_pyobj, ty);
+ kdobj->gmops = gmops;
+ kdobj->kd = kd;
+ return ((PyObject *)kdobj);
+}
+
+static void keydata_pydealloc(PyObject *me)
+{
+ key_drop(KEYDATA_KD(me));
+ FREEOBJ(me);
+}
+
+static PyObject *kdmeth_matchp(PyObject *me, PyObject *arg)
+{
+ key_filter f;
+
+ if (!PyArg_ParseTuple(arg, "O&:matchp", convfilter, &f))
+ return (0);
+ return (getbool(KEY_MATCH(KEYDATA_KD(me), &f)));
+}
+
+static PyObject *kdmeth_split(PyObject *me)
+ { key_split(&KEYDATA_KD(me)); RETURN_ME; }
+
+static PyObject *kdmeth_copy(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ key_filter f = { 0, 0 };
+ static const char *const kwlist[] = { "filter", 0 };
+ key_data *kd;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:copy", KWLIST,
+ convfilter, &f))
+ return (0);
+ if ((kd = key_copydata(KEYDATA_KD(me), &f)) == 0)
+ RETURN_NONE;
+ else
+ return (keydata_pywrap(kd));
+}
+
+static PyObject *kdmeth_write(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ key_filter f = { 0, 0 };
+ dstr d = DSTR_INIT;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "filter", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:write", KWLIST,
+ convfilter, &f))
+ return (0);
+ key_write(KEYDATA_KD(me), &d, &f);
+ rc = TEXT_FROMSTRLEN(d.buf, d.len);
+ dstr_destroy(&d);
+ return (rc);
+}
+
+static PyObject *kdmeth_encode(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ key_filter f = { 0, 0 };
+ dstr d = DSTR_INIT;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "filter", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:encode", KWLIST,
+ convfilter, &f))
+ return (0);
+ key_encode(KEYDATA_KD(me), &d, &f);
+ rc = bytestring_pywrap(d.buf, d.len);
+ dstr_destroy(&d);
+ return (rc);
+}
+
+static PyObject *kdmeth_plock(PyObject *me, PyObject *arg)
+{
+ char *tag;
+ int err;
+ PyObject *rc = 0;
+ key_data *kd;
+
+ if (!PyArg_ParseTuple(arg, "s:plock", &tag))
+ goto end;
+ if ((err = key_plock(&kd, KEYDATA_KD(me), tag)) != 0)
+ KEYERR(err);
+ rc = keydata_pywrap(kd);
+end:
+ return (rc);
+}
+
+static PyObject *kdmeth_lock(PyObject *me, PyObject *arg)
+{
+ struct bin pp;
+ PyObject *rc = 0;
+ key_data *kd;
+
+ if (!PyArg_ParseTuple(arg, "O&:lock", convbin, &pp))
+ goto end;
+ key_lock(&kd, KEYDATA_KD(me), pp.p, pp.sz);
+ rc = keydata_pywrap(kd);
+end:
+ return (rc);
+}
+
+static PyObject *kdmeth_read(PyObject *me, PyObject *arg)
+{
+ const char *p;
+ char *end;
+ key_data *kd;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "s:read", &p)) goto end;
+ if ((kd = key_read(p, &end)) == 0) KEYERR(KERR_MALFORMED);
+ rc = Py_BuildValue("(Ns)", keydata_pywrap(kd), end);
+end:
+ return (rc);
+}
+
+static PyObject *kdmeth_decode(PyObject *me, PyObject *arg)
+{
+ struct bin in;
+ key_data *kd;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:decode", convbin, &in)) goto end;
+ if ((kd = key_decode(in.p, in.sz)) == 0) KEYERR(KERR_MALFORMED);
+ rc = keydata_pywrap(kd);
+end:
+ return (rc);
+}
+
+static PyObject *kdget_flags(PyObject *me, void *hunoz)
+ { return (getulong(KEYDATA_KD(me)->e)); }
+
+static const PyMethodDef keydata_pymethods[] = {
+#define METHNAME(func) kdmeth_##func
+ METH (matchp, "KD.matchp(FILTER) -> BOOL")
+ NAMETH(split, "KD.split()")
+ KWMETH(write, "KD.write([filter = <any>]) -> STRING")
+ KWMETH(encode, "KD.encode([filter = <any>]) -> BYTES")
+ KWMETH(copy, "KD.copy([filter = <any>]) -> KD")
+ METH (plock, "KD.plock(TAG) -> ENCRYPTED-KD")
+ METH (lock, "KD.lock(KEY) -> ENCRYPTED-KD")
+ SMTH (readflags, "readflags(STRING) -> (FLAGS, MASK, REST)")
+ SMTH (writeflags, "writeflags(FLAGS) -> STRING")
+ SMTH (read, "read(STRING) -> (KD, REST)")
+ SMTH (decode, "decode(BYTES) -> KD")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef keydata_pygetset[] = {
+#define GETSETNAME(op, name) kd##op##_##name
+ GET (flags, "KD.flags -> FLAGS")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject keydata_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyData", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ keydata_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Key data base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(keydata), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keydata), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *keydatabin_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ unsigned f = 0;
+ keydata_pyobj *me = 0;
+ static const char *const kwlist[] = { "key", "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convbin, &in, convflags, &f))
+ goto end;
+ me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+ me->kd = key_newbinary(f & ~KF_ENCMASK, in.p, in.sz);
+end:
+ return ((PyObject *)me);
+}
+
+static PyObject *kdbget_bin(PyObject *me, void *hunoz)
+ { return (bytestring_pywrap(KEYDATA_KD(me)->u.k.k,
+ KEYDATA_KD(me)->u.k.sz)); }
+
+static const PyGetSetDef keydatabin_pygetset[] = {
+#define GETSETNAME(op, name) kdb##op##_##name
+ GET (bin, "KD.bin -> BYTES")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject keydatabin_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyDataBinary", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyDataBinary(KEY, [flags = 0]): key data for binary keys.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keydatabin), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keydatabin_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *keydataenc_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ unsigned f = 0;
+ keydata_pyobj *me = 0;
+ static const char *const kwlist[] = { "key", "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convbin, &in, convflags, &f))
+ goto end;
+ me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+ me->kd = key_newencrypted(f & ~KF_ENCMASK, in.p, in.sz);
+end:
+ return ((PyObject *)me);
+}
+
+static PyObject *kdemeth_plock(PyObject *me, PyObject *arg)
+{
+ char *hunoz;
+ if (!PyArg_ParseTuple(arg, "s:plock", &hunoz)) goto end;
+ KEYERR(KERR_WRONGTYPE);
+end:
+ return (0);
+}
+
+static PyObject *kdemeth_lock(PyObject *me, PyObject *arg)
+{
+ struct bin hunoz;
+ if (!PyArg_ParseTuple(arg, "O&:lock", convbin, &hunoz)) goto end;
+ KEYERR(KERR_WRONGTYPE);
+end:
+ return (0);
+}
+
+static PyObject *kdemeth_punlock(PyObject *me, PyObject *arg)
+{
+ char *tag;
+ int err;
+ PyObject *rc = 0;
+ key_data *kd;
+
+ if (!PyArg_ParseTuple(arg, "s:punlock", &tag))
+ goto end;
+ if ((err = key_punlock(&kd, KEYDATA_KD(me), tag)) != 0)
+ KEYERR(err);
+ rc = keydata_pywrap(kd);
+end:
+ return (rc);
+}
+
+static PyObject *kdemeth_unlock(PyObject *me, PyObject *arg)
+{
+ struct bin pp;
+ int err;
+ PyObject *rc = 0;
+ key_data *kd;
+
+ if (!PyArg_ParseTuple(arg, "O&:unlock", convbin, &pp))
+ goto end;
+ if ((err = key_unlock(&kd, KEYDATA_KD(me), pp.p, pp.sz)) != 0)
+ KEYERR(err);
+ rc = keydata_pywrap(kd);
+end:
+ return (rc);
+}
+
+#define kdeget_ct kdbget_bin
+
+static const PyMethodDef keydataenc_pymethods[] = {
+#define METHNAME(func) kdemeth_##func
+ METH (plock, "KD.plock(TAG) -> ENCRYPTED-KD")
+ METH (lock, "KD.lock(KEY) -> ENCRYPTED-KD")
+ METH (punlock, "KD.punlock(TAG) -> KD")
+ METH (unlock, "KD.unlock(KEY) -> KD")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef keydataenc_pygetset[] = {
+#define GETSETNAME(op, name) kde##op##_##name
+ GET (ct, "KD.ct -> BYTES")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject keydataenc_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyDataEncrypted", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyDataEncrypted(KEY, [flags = 0]): key data for encrypted keys.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(keydataenc), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keydataenc), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keydataenc_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *keydatamp_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *x = 0;
+ unsigned f = 0;
+ keydata_pyobj *me = 0;
+ static const char *const kwlist[] = { "key", "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convmp, &x, convflags, &f))
+ goto end;
+ me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+ me->kd = key_newmp(f & ~KF_ENCMASK, x);
+end:
+ mp_drop(x);
+ return ((PyObject *)me);
+}
+
+static PyObject *kdmget_mp(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(KEYDATA_KD(me)->u.m))); }
+
+static const PyGetSetDef keydatamp_pygetset[] = {
+#define GETSETNAME(op, name) kdm##op##_##name
+ GET (mp, "KD.mp -> X")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject keydatamp_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyDataMP", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyDataMP(KEY, [flags = 0]): key data for large-integer keys.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keydatamp), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keydatamp_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *keydatastr_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ char *p;
+ unsigned f = 0;
+ keydata_pyobj *me = 0;
+ static const char *const kwlist[] = { "key", "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:new", KWLIST,
+ &p, convflags, &f))
+ goto end;
+ me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+ me->kd = key_newstring(f & ~KF_ENCMASK, p);
+end:
+ return ((PyObject *)me);
+}
+
+static PyObject *kdsget_str(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(KEYDATA_KD(me)->u.p)); }
+
+static const PyGetSetDef keydatastr_pygetset[] = {
+#define GETSETNAME(op, name) kds##op##_##name
+ GET (str, "KD.str -> STRING")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject keydatastr_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyDataString", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyDataString(KEY, [flags = 0]): key data for string keys.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keydatastr), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keydatastr_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *keydataec_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ ec x = EC_INIT;
+ unsigned f = 0;
+ keydata_pyobj *me = 0;
+ static const char *const kwlist[] = { "key", "flags", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convecpt, &x, convflags, &f))
+ goto end;
+ me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+ me->kd = key_newec(f & ~KF_ENCMASK, &x);
+end:
+ EC_DESTROY(&x);
+ return ((PyObject *)me);
+}
+
+static PyObject *kdeget_ecpt(PyObject *me, void *hunoz)
+{
+ ec pt = EC_INIT;
+ EC_COPY(&pt, &KEYDATA_KD(me)->u.e);
+ return (ecpt_pywrapout(ecpt_pytype, &pt));
+}
+
+static const PyGetSetDef keydataec_pygetset[] = {
+#define GETSETNAME(op, name) kde##op##_##name
+ GET (ecpt, "KD.ecpt -> ECPT")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject keydataec_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyDataECPt", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyDataECPt(KEY, [flags = 0]): key data for elliptic-curve keys.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keydataec), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keydataec_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static void *keydatastruct_gmlookup(PyObject *me, PyObject *k, unsigned *f)
+{
+ key_data *kd = KEYDATA_KD(me);
+ const char *tag;
+ key_struct *ks;
+
+ assert((kd->e&KF_ENCMASK) == KENC_STRUCT);
+ if ((tag = TEXT_STR(k)) == 0) return (0);
+ if (f) { key_split(&kd); KEYDATA_KD(me) = kd; }
+ ks = sym_find(&kd->u.s, tag, -1, f ? sizeof(key_struct) : 0, f);
+ if (ks && f && !*f) ks->k = 0;
+ return (ks);
+}
+
+static void keydatastruct_gmiterinit(PyObject *me, void *i)
+{
+ key_data *kd = KEYDATA_KD(me);
+
+ assert((kd->e&KF_ENCMASK) == KENC_STRUCT);
+ sym_mkiter(i, &kd->u.s);
+}
+
+static void *keydatastruct_gmiternext(PyObject *me, void *i)
+ { return (sym_next(i)); }
+
+static PyObject *keydatastruct_gmentrykey(PyObject *me, void *e)
+ { key_struct *ks = e; return (TEXT_FROMSTR(SYM_NAME(ks))); }
+
+static PyObject *keydatastruct_gmentryvalue(PyObject *me, void *e)
+{
+ key_struct *ks = e;
+ key_data *kd = ks->k;
+
+ key_incref(kd);
+ return (keydata_pywrap(kd));
+}
+
+static key_struct *maybe_split_and_reprobe(PyObject *me, key_struct *ks)
+{
+ key_data *kd = KEYDATA_KD(me);
+
+ if (kd->ref == 1) return (ks);
+ key_split(&kd); KEYDATA_KD(me) = kd;
+ ks = sym_find(&kd->u.s, SYM_NAME(ks), SYM_LEN(ks), 0, 0); assert(ks);
+ return (ks);
+}
+
+static int keydatastruct_gmsetentry(PyObject *me, void *e, PyObject *val)
+{
+ key_struct *ks;
+ key_data *kd;
+ int rc = -1;
+
+ if (!KEYDATA_PYCHECK(val)) TYERR("expected KeyData value");
+ ks = maybe_split_and_reprobe(me, e);
+ if (ks->k) key_drop(ks->k);
+ ks->k = kd = KEYDATA_KD(val); key_incref(kd);
+ rc = 0;
+end:
+ return (rc);
+}
+
+static int keydatastruct_gmdelentry(PyObject *me, void *e)
+{
+ key_struct *ks;
+
+ ks = maybe_split_and_reprobe(me, e);
+ if (ks->k) key_drop(ks->k);
+ sym_remove(&KEYDATA_KD(me)->u.s, ks);
+ return (0);
+}
+
+static const gmap_ops keydatastruct_gmops = {
+ sizeof(sym_iter),
+ keydatastruct_gmlookup,
+ keydatastruct_gmiterinit,
+ keydatastruct_gmiternext,
+ keydatastruct_gmentrykey,
+ keydatastruct_gmentryvalue,
+ keydatastruct_gmsetentry,
+ keydatastruct_gmdelentry
+};
+
+static int populate_struct(key_data *kd, PyObject *map)
+{
+ PyObject *it = 0, *name = 0, *val = 0;
+ const char *p;
+ int rc = -1;
+
+ if (!PyMapping_Check(map)) TYERR("subkeys must be an iterable mapping");
+ if ((it = PyObject_GetIter(map)) == 0) goto end;
+ while ((name = PyIter_Next(it)) != 0) {
+ if ((p = TEXT_STR(name)) == 0 ||
+ (val = PyObject_GetItem(map, name)) == 0)
+ goto end;
+ if (!KEYDATA_PYCHECK(val))
+ TYERR("subkey objects must be instances of KeyData");
+ if (key_structfind(kd, p)) VALERR("duplicate tag");
+ key_structset(kd, p, KEYDATA_KD(val));
+ Py_DECREF(name); name = 0;
+ Py_DECREF(val); val = 0;
+ }
+ if (PyErr_Occurred()) goto end;
+ rc = 0;
+end:
+ Py_XDECREF(it); Py_XDECREF(name); Py_XDECREF(val);
+ return (rc);
+}
+
+static PyObject *keydatastruct_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *sub = 0;
+ keydata_pyobj *me = 0;
+ key_data *kd = 0;
+
+ if (!PyArg_ParseTuple(arg, "|O:new", &sub)) goto end;
+ kd = key_newstruct();
+ if (sub && populate_struct(kd, sub)) goto end;
+ if (kw && populate_struct(kd, kw)) goto end;
+ me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+ me->gmops = &keydatastruct_gmops;
+ me->kd = kd; kd = 0;
+end:
+ if (kd) key_drop(kd);
+ return ((PyObject *)me);
+}
+
+static Py_ssize_t keydatastruct_pysize(PyObject *me)
+ { return gmap_pysize_from_sym(&KEYDATA_KD(me)->u.s); }
+
+static const PyMappingMethods keydatastruct_pymapping = {
+ keydatastruct_pysize,
+ gmap_pylookup,
+ gmap_pystore
+};
+
+static const PyTypeObject keydatastruct_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyDataStructured", /* @tp_name@ */
+ sizeof(keydata_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ PYSEQUENCE(gmap), /* @tp_as_sequence@ */
+ PYMAPPING(keydatastruct), /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyDataStructured([subkeys = []]): key data for structured keys.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ gmap_pyiter, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gmap), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keydatastruct_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Key attributes ----------------------------------------------------*/
+
+static void *keyattrs_gmlookup(PyObject *me, PyObject *k, unsigned *f)
+{
+ char *name = TEXT_STR(k);
+ key_attr *a = 0;
+
+ if (!name) goto end;
+ if (f && !(KEYATTRS_KF(me)->f&KF_WRITE)) KEYERR(KERR_READONLY);
+ a = sym_find(&KEYATTRS_K(me)->a, name, -1, f ? sizeof(key_attr) : 0, f);
+ if (f && !*f) a->p = 0;
+end:
+ return (a);
+}
+
+static void keyattrs_gmiterinit(PyObject *me, void *i)
+ { sym_mkiter(i, &KEYATTRS_K(me)->a); }
+
+static void *keyattrs_gmiternext(PyObject *me, void *i)
+ { return (sym_next(i)); }
+
+static PyObject *keyattrs_gmentrykey(PyObject *me, void *e)
+ { return (TEXT_FROMSTR(SYM_NAME(e))); }
+
+static PyObject *keyattrs_gmentryvalue(PyObject *me, void *e)
+ { return (TEXT_FROMSTR(((key_attr *)e)->p)); }
+
+static int keyattrs_gmsetentry(PyObject *me, void *e, PyObject *val)
+{
+ key_attr *a = e;
+ const char *p;
+ Py_ssize_t n;
+ int rc = -1;
+
+ if (!TEXT_CHECK(val)) TYERR("expected string");
+ TEXT_PTRLEN(val, p, n);
+ if (n > 255) VALERR("attribute too long");
+ if (memchr(p, 0, n)) VALERR("attribute must not contain nul");
+ if (a->p) xfree(a->p);
+ a->p = xmalloc(n + 1); memcpy(a->p, p, n + 1);
+ KEYATTRS_KF(me)->f |= KF_MODIFIED;
+ rc = 0;
+end:
+ return (rc);
+}
+
+static int keyattrs_gmdelentry(PyObject *me, void *e)
+{
+ key_attr *a = e;
+ int rc = -1;
+
+ if (!(KEYATTRS_KF(me)->f&KF_WRITE)) KEYERR(KERR_READONLY);
+ if (a->p) { KEYATTRS_KF(me)->f |= KF_MODIFIED; xfree(a->p); }
+ sym_remove(&KEYATTRS_K(me)->a, a);
+ rc = 0;
+end:
+ return (rc);
+}
+
+static const gmap_ops keyattrs_gmops = {
+ sizeof(sym_iter),
+ keyattrs_gmlookup,
+ keyattrs_gmiterinit,
+ keyattrs_gmiternext,
+ keyattrs_gmentrykey,
+ keyattrs_gmentryvalue,
+ keyattrs_gmsetentry,
+ keyattrs_gmdelentry
+};
+
+static PyObject *keyattrs_make(PyObject *kobj)
+{
+ keyattrs_pyobj *me = PyObject_NEW(keyattrs_pyobj, keyattrs_pytype);
+ me->gmops = &keyattrs_gmops;
+ me->kobj = kobj;
+ Py_INCREF(kobj);
+ return ((PyObject *)me);
+}
+
+static void keyattrs_pydealloc(PyObject *me)
+{
+ Py_DECREF(KEYATTRS_KOBJ(me));
+ FREEOBJ(me);
+}
+
+static Py_ssize_t keyattrs_pysize(PyObject *me)
+ { return gmap_pysize_from_sym(&KEYATTRS_K(me)->a); }
+
+static const PyMappingMethods keyattrs_pymapping = {
+ keyattrs_pysize,
+ gmap_pylookup,
+ gmap_pystore
+};
+
+static const PyTypeObject keyattrs_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyAttributes", /* @tp_name@ */
+ sizeof(keyattrs_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ keyattrs_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ PYSEQUENCE(gmap), /* @tp_as_sequence@ */
+ PYMAPPING(keyattrs), /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Proxy thing for talking about key attributes.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ gmap_pyiter, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gmap), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Key objects -------------------------------------------------------*/
+
+static PyObject *key_dowrap(PyTypeObject *ty, PyObject *kfobj, key *k)
+{
+ key_pyobj *kobj = (key_pyobj *)ty->tp_alloc(ty, 0);
+ kobj->kfobj = kfobj;
+ Py_INCREF(kfobj);
+ kobj->k = k;
+ return ((PyObject *)kobj);
+}
+
+static PyObject *key_pywrap(PyObject *kfobj, key *k)
+ { return (key_dowrap(key_pytype, kfobj, k)); }
+
+static PyObject *key_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *kfobj;
+ uint32 id;
+ char *type;
+ unsigned long exptime = KEXP_FOREVER;
+ static const char *const kwlist[] =
+ { "keyfile", "id", "type", "exptime", 0 };
+ key *k;
+ int err;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&s|O&:new", KWLIST,
+ keyfile_pytype, &kfobj, convu32, &id,
+ &type, convulong, &exptime))
+ goto end;
+ if ((err = key_new(KEYFILE_KF(kfobj), id, type, exptime, &k)) != 0)
+ KEYERR(err);
+ return (key_dowrap(ty, kfobj, k));
+end:
+ return (0);
+}
+
+static void key_pydealloc(PyObject *me)
+{
+ Py_DECREF(KEY_KFOBJ(me));
+ FREEOBJ(me);
+}
+
+static Py_hash_t key_pyhash(PyObject *me)
+ { return ((Py_hash_t)KEY_K(me)); }
+
+static PyObject *key_pyrichcompare(PyObject *me, PyObject *you, int op)
+{
+ if (!KEY_PYCHECK(you)) RETURN_NOTIMPL;
+ switch (op) {
+ case Py_EQ: return (getbool(KEY_K(me) == KEY_K(you)));
+ case Py_NE: return (getbool(KEY_K(me) == KEY_K(you)));
+ default: TYERR("ordering makes no sense");
+ }
+end:
+ return (0);
+}
+
+static PyObject *kmeth_delete(PyObject *me)
+{
+ int err;
+
+ if ((err = key_delete(KEY_KF(me), KEY_K(me))) != 0) KEYERR(err);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *kmeth_expire(PyObject *me)
+{
+ int err;
+
+ if ((err = key_expire(KEY_KF(me), KEY_K(me))) != 0) KEYERR(err);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *kmeth_used(PyObject *me, PyObject *arg)
+{
+ long t;
+ int err;
+
+ if (!PyArg_ParseTuple(arg, "l:used", &t)) goto end;
+ if ((err = key_used(KEY_KF(me), KEY_K(me), t)) != 0) KEYERR(err);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *kmeth_extractline(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ key_filter f = { 0, 0 };
+ dstr d = DSTR_INIT;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "filter", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:extract", KWLIST,
+ convfilter, &f))
+ goto end;
+ key_extractline(KEY_KF(me), KEY_K(me), &d, &f);
+ rc = TEXT_FROMSTRLEN(d.buf, d.len);
+end:
+ dstr_destroy(&d);
+ return (rc);
+}
+
+static PyObject *kmeth_fingerprint(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ ghash *h;
+ key_filter f = { KF_NONSECRET, KF_NONSECRET };
+ static const char *const kwlist[] = { "hash", "filter", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:fingerprint", KWLIST,
+ convghash, &h, convfilter, &f))
+ return (0);
+ return (getbool(key_fingerprint(KEY_K(me), h, &f)));
+}
+
+static PyObject *kget_id(PyObject *me, void *hunoz)
+ { return (getulong(KEY_K(me)->id)); }
+static PyObject *kget_file(PyObject *me, void *hunoz)
+ { RETURN_OBJ(KEY_KFOBJ(me)); }
+static PyObject *kget_type(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(KEY_K(me)->type)); }
+static PyObject *kget_exptime(PyObject *me, void *hunoz)
+ { return (getulong(KEY_K(me)->exp)); }
+static PyObject *kget_deltime(PyObject *me, void *hunoz)
+ { return (getulong(KEY_K(me)->del)); }
+static PyObject *kget_expiredp(PyObject *me, void *hunoz)
+ { return (getbool(key_expired(KEY_K(me)))); }
+static PyObject *kget_attr(PyObject *me, void *hunoz)
+ { return (keyattrs_make(me)); }
+
+static int kset_exptime(PyObject *me, PyObject *x, void *hunoz)
+{
+ key *k = KEY_K(me);
+ unsigned long et;
+
+ if (!x) NIERR("__del__");
+ if (!convulong(x, &et))
+ goto end;
+ if (!(KEY_KF(me)->f & KF_WRITE))
+ KEYERR(KERR_READONLY);
+ k->exp = et;
+ KEY_KF(me)->f |= KF_MODIFIED;
+ return (0);
+end:
+ return (-1);
+}
+
+static int kset_deltime(PyObject *me, PyObject *x, void *hunoz)
+{
+ key *k = KEY_K(me);
+ unsigned long dt;
+
+ if (!x) NIERR("__del__");
+ if (!convulong(x, &dt))
+ goto end;
+ if (dt == KEXP_FOREVER && k->exp != KEXP_FOREVER)
+ VALERR("key will eventually expire");
+ if (!(KEY_KF(me)->f & KF_WRITE))
+ KEYERR(KERR_READONLY);
+ k->del = dt;
+ KEY_KF(me)->f |= KF_MODIFIED;
+ return (0);
+end:
+ return (-1);
+}
+
+static PyObject *kget_data(PyObject *me, void *hunoz)
+{
+ key_data *kd = KEY_K(me)->k;
+ key_incref(kd);
+ return (keydata_pywrap(kd));
+}
+static int kset_data(PyObject *me, PyObject *x, void *hunoz)
+{
+ int err;
+
+ if (!x) NIERR("__del__");
+ if (!KEYDATA_PYCHECK(x)) TYERR("expected KeyData object");
+ if ((err = key_setkeydata(KEY_KF(me), KEY_K(me), KEYDATA_KD(x))) != 0)
+ KEYERR(err);
+ return (0);
+end:
+ return (-1);
+}
+
+static PyObject *kget_fulltag(PyObject *me, void *hunoz)
+{
+ dstr d = DSTR_INIT;
+ PyObject *rc;
+
+ key_fulltag(KEY_K(me), &d);
+ rc = TEXT_FROMSTRLEN(d.buf, d.len);
+ dstr_destroy(&d);
+ return (rc);
+}
+
+static PyObject *kget_tag(PyObject *me, void *hunoz)
+{
+ if (!KEY_K(me)->tag) RETURN_NONE;
+ return (TEXT_FROMSTR(KEY_K(me)->tag));
+}
+static int kset_tag(PyObject *me, PyObject *x, void *hunoz)
+{
+ int err;
+ char *tag;
+
+ if (!x || x == Py_None) tag = 0;
+ else if ((tag = TEXT_STR(x)) == 0) goto end;
+ if ((err = key_settag(KEY_KF(me), KEY_K(me), tag)) != 0) KEYERR(err);
+ return (0);
+end:
+ return (-1);
+}
+
+static PyObject *kget_comment(PyObject *me, void *hunoz)
+{
+ if (!KEY_K(me)->c) RETURN_NONE;
+ return (TEXT_FROMSTR(KEY_K(me)->c));
+}
+static int kset_comment(PyObject *me, PyObject *x, void *hunoz)
+{
+ int err;
+ char *c;
+
+ if (!x || x == Py_None) c = 0;
+ else if ((c = TEXT_STR(x)) == 0) goto end;
+ if ((err = key_setcomment(KEY_KF(me), KEY_K(me), c)) != 0) KEYERR(err);
+ return (0);
+end:
+ return (-1);
+}
+
+static const PyMethodDef key_pymethods[] = {
+#define METHNAME(func) kmeth_##func
+ NAMETH(delete, "KEY.delete()")
+ NAMETH(expire, "KEY.expire()")
+ METH (used, "KEY.used(TIME)")
+ KWMETH(extractline, "KEY.extractline([filter = <any>])")
+ KWMETH(fingerprint, "KEY.fingerprint(HASH, [filter = '-secret'])")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef key_pygetset[] = {
+#define GETSETNAME(op, name) k##op##_##name
+ GET (file, "KEY.file -> KF")
+ GET (id, "KEY.id -> ID")
+ GETSET(tag, "KEY.tag -> TAG")
+ GET (type, "KEY.type -> TYPE")
+ GETSET(exptime, "KEY.exptime -> TIME")
+ GETSET(deltime, "KEY.deltime -> TIME")
+ GET (expiredp, "KEY.expiredp -> BOOL")
+ GET (attr, "KEY.attr -> ATTRIBUTES")
+ GETSET(data, "KEY.data -> KD")
+ GETSET(comment, "KEY.comment -> COMMENT")
+ GET (fulltag, "KEY.fulltag -> FULLTAG")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject key_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Key", /* @tp_name@ */
+ sizeof(key_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ key_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ key_pyhash, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Key(KF, ID, TYPE, [exptime = KEXP_FOREVER]): key object.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ key_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(key), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(key), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ key_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Key files ---------------------------------------------------------*/
+
+static key *bytag(PyObject *me, PyObject *tagobj)
+{
+ uint32 id;
+ char *tag;
+ key *k = 0;
+
+ if (convu32(tagobj, &id))
+ k = key_byid(KEYFILE_KF(me), id);
+ else {
+ PyErr_Clear();
+ if ((tag = TEXT_STR(tagobj)) == 0)
+ goto end;
+ k = key_bytag(KEYFILE_KF(me), tag);
+ }
+end:
+ return (k);
+}
+
+static void *keyfile_gmlookup(PyObject *me, PyObject *k, unsigned *f)
+ { key *kk = bytag(me, k); if (f) *f = !!kk; return (kk); }
+
+static void keyfile_gmiterinit(PyObject *me, void *i)
+ { key_mkiter(i, KEYFILE_KF(me)); }
+
+static void *keyfile_gmiternext(PyObject *me, void *i)
+ { return (key_next(i)); }
+
+static PyObject *keyfile_gmentrykey(PyObject *me, void *e)
+ { key *k = e; return (getulong(k->id)); }
+
+static PyObject *keyfile_gmentryvalue(PyObject *me, void *e)
+ { return (key_pywrap(me, e)); }
+
+static const gmap_ops keyfile_gmops = {
+ sizeof(key_iter),
+ keyfile_gmlookup,
+ keyfile_gmiterinit,
+ keyfile_gmiternext,
+ keyfile_gmentrykey,
+ keyfile_gmentryvalue,
+ 0,
+ 0
+};
+
+struct reportinfo {
+ PyObject *func;
+ int stop;
+};
+
+static void pythonreporter(const char *file, int line,
+ const char *msg, void *p)
+{
+ struct reportinfo *ri = p;
+ PyObject *res = 0;
+
+ if (ri->stop)
+ return;
+ if (ri->func == Py_None)
+ key_moan(file, line, msg, 0);
+ else if ((res = PyObject_CallFunction(ri->func, "sis",
+ file, line, msg)) == 0)
+ ri->stop = 1;
+ else
+ Py_DECREF(res);
+}
+
+static PyObject *keyfile_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ struct reportinfo ri = { Py_None, 0 };
+ char *file = 0;
+ unsigned how = KOPEN_READ;
+ keyfile_pyobj *rc = 0;
+ static const char *const kwlist[] = { "file", "how", "report", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", KWLIST,
+ &file, &how, &ri.func))
+ goto end;
+ if (ri.func != Py_None && !PyCallable_Check(ri.func))
+ TYERR("reporter function not callable");
+ if ((rc = (keyfile_pyobj *)ty->tp_alloc(ty, 0)) == 0)
+ goto end;
+ rc->gmops = &keyfile_gmops;
+ if (key_open(&rc->kf, file, how, pythonreporter, &ri))
+ OSERR(file);
+ if (ri.stop) {
+ key_discard(&rc->kf);
+ goto end;
+ }
+ goto done;
+
+end:
+ if (rc) {
+ FREEOBJ(rc);
+ rc = 0;
+ }
+done:
+ return ((PyObject *)rc);
+}
+
+static void keyfile_pydealloc(PyObject *me)
+{
+ key_discard(KEYFILE_KF(me));
+ FREEOBJ(me);
+}
+
+static PyObject *kfmeth_save(PyObject *me)
+{
+ switch (key_save(KEYFILE_KF(me))) {
+ case KWRITE_OK:
+ RETURN_ME;
+ case KWRITE_FAIL:
+ KEYIOERR(KEYFILE_KF(me)->name);
+ case KWRITE_BROKEN:
+ KEYFILEBROKEN(KEYFILE_KF(me)->name);
+ default:
+ abort();
+ }
+end:
+ return (0);
+}
+
+static PyObject *kfmeth_mergeline(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ struct reportinfo ri = { Py_None, 0 };
+ const char *file, *line;
+ int lno, rc;
+ static const char *const kwlist[] = { "name", "lno", "line", "report", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "sis|O:merge", KWLIST,
+ &file, &lno, &line, &ri.func))
+ goto end;
+ if (ri.func != Py_None && !PyCallable_Check(ri.func))
+ TYERR("reporter function not callable");
+ rc = key_mergeline(KEYFILE_KF(me), file, lno, line, pythonreporter, &ri);
+ if (ri.stop)
+ goto end;
+ if (rc != 0)
+ KEYERR(rc);
+ RETURN_ME;
+
+end:
+ return (0);
+}
+
+static PyObject *kfmeth_byid(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ uint32 id;
+ key *k;
+ PyObject *failp = Py_True;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "id", "fail", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:byid", KWLIST,
+ convu32, &id, &failp))
+ goto end;
+ if ((k = key_byid(KEYFILE_KF(me), id)) != 0) rc = key_pywrap(me, k);
+ else if (PyObject_IsTrue(failp)) KEYERR(KERR_NOTFOUND);
+ else RETURN_NONE;
+end:
+ return (rc);
+}
+
+static PyObject *kfmeth_bytype(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ char *type;
+ key *k;
+ PyObject *failp = Py_True;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "type", "fail", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O:bytype", KWLIST,
+ &type, &failp))
+ goto end;
+ if ((k = key_bytype(KEYFILE_KF(me), type)) != 0) rc = key_pywrap(me, k);
+ else if (PyObject_IsTrue(failp)) KEYERR(KERR_NOTFOUND);
+ else RETURN_NONE;
+end:
+ return (rc);
+}
+
+static PyObject *kfmeth_bytag(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *tagobj;
+ key *k;
+ PyObject *failp = Py_True;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "type", "fail", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:bytag", KWLIST,
+ &tagobj, &failp))
+ goto end;
+ if ((k = bytag(me, tagobj)) != 0) rc = key_pywrap(me, k);
+ else if (PyObject_IsTrue(failp)) KEYERR(KERR_NOTFOUND);
+ else RETURN_NONE;
+end:
+ return (rc);
+}
+
+static PyObject *kfmeth_newkey(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ uint32 id;
+ char *type;
+ long exptime = KEXP_FOREVER;
+ static const char *const kwlist[] = { "id", "type", "exptime", 0 };
+ key *k;
+ int err;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s|l:newkey", KWLIST,
+ convu32, &id, &type, &exptime))
+ goto end;
+ if ((err = key_new(KEYFILE_KF(me), id, type, exptime, &k)) != 0)
+ KEYERR(err);
+ return (key_pywrap(me, k));
+end:
+ return (0);
+}
+
+static PyObject *kfmeth_qtag(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ key *k;
+ key_data **kd, *okd;
+ PyObject *newkdobj = 0;
+ char *tag;
+ dstr d = DSTR_INIT;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "tag", "new", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O!:qtag", KWLIST,
+ &tag, keydata_pytype, &newkdobj))
+ goto end;
+ if (key_qtag(KEYFILE_KF(me), tag, &d, &k, &kd))
+ KEYERR(KERR_NOTFOUND);
+ okd = *kd;
+ if (newkdobj) {
+ if (!(KEYFILE_KF(me)->f & KF_WRITE))
+ KEYERR(KERR_READONLY);
+ KEYFILE_KF(me)->f |= KF_MODIFIED;
+ *kd = KEYDATA_KD(newkdobj);
+ }
+ key_incref(*kd);
+ rc = Py_BuildValue("(s#NN)",
+ d.buf, (Py_ssize_t)d.len,
+ key_pywrap(me, k),
+ keydata_pywrap(okd));
+end:
+ return (rc);
+}
+
+static PyObject *kfget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(KEYFILE_KF(me)->name)); }
+static PyObject *kfget_modifiedp(PyObject *me, void *hunoz)
+ { return (getbool(KEYFILE_KF(me)->f & KF_MODIFIED)); }
+static PyObject *kfget_writep(PyObject *me, void *hunoz)
+ { return (getbool(KEYFILE_KF(me)->f & KF_WRITE)); }
+static PyObject *kfget_filep(PyObject *me, void *hunoz)
+ { return (getbool(!!KEYFILE_KF(me)->fp)); }
+
+static const PyMethodDef keyfile_pymethods[] = {
+#define METHNAME(func) kfmeth_##func
+ NAMETH(save, "KF.save()")
+ KWMETH(mergeline, "KF.mergeline(NAME, LNO, LINE, "
+ "[report = <built-in-reporter>])")
+ KWMETH(newkey, "KF.newkey(ID, TYPE, [exptime = KEXP_FOREVER]) "
+ "-> KEY")
+ KWMETH(byid, "KF.byid(KEYID, [fail = True]) -> KEY|None")
+ KWMETH(bytype, "KF.bytype(TYPE, [fail = True]) -> KEY|None")
+ KWMETH(bytag, "KF.bytag(TAG, [fail = True]) -> KEY|None")
+ KWMETH(qtag, "KF.qtag(TAG, [new = KD]) -> FULLTAG, KEY, OLDKD")
+ GMAP_ROMETHODS
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef keyfile_pygetset[] = {
+#define GETSETNAME(op, name) kf##op##_##name
+ GET (name, "KF.name -> file name")
+ GET (modifiedp, "KF.modifiedp -> has keyring been modified?")
+ GET (writep, "KF.writep -> is keyring modifiable?")
+ GET (filep, "KF.filep -> does keyring have a backing file?")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMappingMethods keyfile_pymapping = {
+ gmap_pysize,
+ gmap_pylookup,
+ 0
+};
+
+static const PyTypeObject keyfile_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KeyFile", /* @tp_name@ */
+ sizeof(keyfile_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ keyfile_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ PYSEQUENCE(gmap), /* @tp_as_sequence@ */
+ PYMAPPING(keyfile), /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KeyFile(FILE, [how = KOPEN_READ], [report = ?]): Keyring file.\n"
+ " calls REPORT(FILE, LINE, MSG) on problems",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ gmap_pyiter, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(keyfile), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(keyfile), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ keyfile_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Initialization ----------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(KOPEN_READ), CONST(KOPEN_WRITE), CONST(KOPEN_NOFILE),
+ CONST(KEXP_FOREVER), CONST(KEXP_EXPIRE),
+ CONST(KF_ENCMASK), CONST(KENC_BINARY), CONST(KENC_MP), CONST(KENC_STRUCT),
+ CONST(KENC_ENCRYPT), CONST(KENC_STRING), CONST(KENC_EC),
+ CONST(KF_CATMASK), CONST(KCAT_SYMM), CONST(KCAT_PRIV), CONST(KCAT_PUB),
+ CONST(KCAT_SHARE),
+ CONST(KF_NONSECRET),
+ CONST(KF_BURN), CONST(KF_OPT),
+#define ENTRY(tag, val, str) CONSTFLAG(CF_SIGNED, KERR_##tag),
+ KEY_ERRORS(ENTRY)
+#undef ENTRY
+ { 0 }
+};
+
+void key_pyinit(void)
+{
+ INITTYPE(keyfile, root);
+ INITTYPE(key, root);
+ INITTYPE(keydata, root);
+ INITTYPE(keydatabin, keydata);
+ INITTYPE(keydataenc, keydata);
+ INITTYPE(keydatastr, keydata);
+ INITTYPE(keydatamp, keydata);
+ INITTYPE(keydataec, keydata);
+ INITTYPE(keydatastruct, keydata);
+ INITTYPE(keyattrs, root);
+}
+
+void key_pyinsert(PyObject *mod)
+{
+ INSEXC("KeyError", keyexc, PyExc_Exception, keyexc_pymethods);
+ INSEXC("KeyFileIOError", keyioexc, PyExc_OSError, 0);
+ INSEXC("KeyFileBroken", keyfilebrokenexc, keyioexc, 0);
+ INSERT("KeyFile", keyfile_pytype);
+ INSERT("Key", key_pytype);
+ INSERT("KeyAttributes", keyattrs_pytype);
+ INSERT("KeyData", keydata_pytype);
+ INSERT("KeyDataBinary", keydatabin_pytype);
+ INSERT("KeyDataEncrypted", keydataenc_pytype);
+ INSERT("KeyDataMP", keydatamp_pytype);
+ INSERT("KeyDataECPt", keydataec_pytype);
+ INSERT("KeyDataString", keydatastr_pytype);
+ INSERT("KeyDataStructured", keydatastruct_pytype);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Multiprecision arithmetic
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- General utilities -------------------------------------------------*/
+
+PyTypeObject *mp_pytype = 0;
+PyTypeObject *gf_pytype = 0;
+
+#ifndef PyLong_SHIFT
+# define PyLong_SHIFT SHIFT
+#endif
+
+#ifndef PyLong_MASK
+# define PyLong_MASK MASK
+#endif
+
+STATIC_ASSERT(MPW_BITS >= PyLong_SHIFT,
+ "Catacomb's limbs are now narrower than than Python's!");
+
+mp *mp_frompylong(PyObject *obj)
+{
+ unsigned long bits;
+ PyLongObject *l = (PyLongObject *)obj;
+ int sz;
+ size_t w;
+ mpd r = 0;
+ int b = 0;
+ int i;
+ mp *x;
+ mpw *p;
+
+#ifdef PY3
+ int ov;
+ long j = PyLong_AsLongAndOverflow(obj, &ov);
+ if (!ov) return mp_fromlong(MP_NEW, j);
+#endif
+
+ sz = Py_SIZE(l);
+ if (sz < 0) sz = -sz;
+ bits = (unsigned long)sz * PyLong_SHIFT;
+ w = (bits + MPW_BITS - 1)/MPW_BITS;
+ x = mp_new(w, Py_SIZE(l) < 0 ? MP_NEG : 0);
+ p = x->v;
+ for (i = 0; i < sz; i++) {
+ r |= (mpd)l->ob_digit[i] << b;
+ b += PyLong_SHIFT;
+ while (b >= MPW_BITS) {
+ *p++ = MPW(r);
+ r >>= MPW_BITS;
+ b -= MPW_BITS;
+ }
+ }
+ while (r) {
+ *p++ = MPW(r);
+ r >>= MPW_BITS;
+ }
+ x->vl = p;
+ MP_SHRINK(x);
+ return (x);
+}
+
+PyObject *mp_topylong(mp *x)
+{
+ unsigned long bits = mp_bits(x);
+ int sz = (bits + PyLong_SHIFT - 1)/PyLong_SHIFT;
+ PyLongObject *l = _PyLong_New(sz);
+ mpd r = 0;
+ int b = 0;
+ mpw *p = x->v;
+ int i = 0;
+
+ while (i < sz && p < x->vl) {
+ r |= (mpd)*p++ << b;
+ b += MPW_BITS;
+ while (i < sz && b >= PyLong_SHIFT) {
+ l->ob_digit[i++] = r & PyLong_MASK;
+ r >>= PyLong_SHIFT;
+ b -= PyLong_SHIFT;
+ }
+ }
+ while (i < sz && r) {
+ l->ob_digit[i++] = r & PyLong_MASK;
+ r >>= PyLong_SHIFT;
+ }
+ Py_SIZE(l) = (x->f & MP_NEG) ? -sz : sz;
+ return ((PyObject *)l);
+}
+
+mp *mp_frompyobject(PyObject *o, int radix)
+{
+ mp *x;
+
+ if (TEXT_CHECK(o)) {
+ mptext_stringctx sc;
+ mp *x;
+ size_t sz;
+ TEXT_PTRLEN(o, sc.buf, sz); sc.lim = sc.buf + sz;
+ if (sc.buf + 2 < sc.lim && sc.buf[0] == '0' &&
+ (radix == 16 ? (sc.buf[1] == 'x' || sc.buf[1] == 'X') :
+ radix == 8 ? (sc.buf[1] == 'o' || sc.buf[1] == 'O') :
+ radix == 2 ? (sc.buf[1] == 'b' || sc.buf[1] == 'B') :
+ 0))
+ sc.buf += 2;
+ x = mp_read(MP_NEW, radix, &mptext_stringops, &sc);
+ if (!x) return (0);
+ if (sc.buf < sc.lim) { MP_DROP(x); return (0); }
+ return (x);
+ }
+ if ((x = tomp(o)) != 0)
+ return (x);
+ return (0);
+}
+
+PyObject *mp_topystring(mp *x, int radix, const char *xpre,
+ const char *pre, const char *post)
+{
+ int len = mptext_len(x, radix) + 1;
+ mptext_stringctx sc;
+ PyObject *o;
+ size_t xprelen = xpre ? strlen(xpre) : 0;
+ size_t prelen = pre ? strlen(pre) : 0;
+ size_t postlen = post ? strlen(post) : 0;
+ char *p;
+ MP_COPY(x);
+ TEXT_PREPAREWRITE(o, p, len + 1 + xprelen + prelen + postlen);
+ sc.buf = p;
+ if (xpre) { memcpy(sc.buf, xpre, xprelen); sc.buf += xprelen; }
+ if (MP_NEGP(x)) { *sc.buf++ = '-'; x = mp_neg(x, x); }
+ if (pre) { memcpy(sc.buf, pre, prelen); sc.buf += prelen; }
+ sc.lim = sc.buf + len;
+ mp_write(x, radix, &mptext_stringops, &sc);
+ if (post) { memcpy(sc.buf, post, postlen); sc.buf += postlen; }
+ MP_DROP(x);
+ TEXT_DONEWRITE(o, sc.buf - p);
+ return (o);
+}
+
+static int good_radix_p(int r, int readp)
+{
+ return ((r >= -255 && r <= -2) ||
+ (readp && r == 0) ||
+ (r >= 2 && r <= 62));
+}
+
+PyObject *mp_pywrap(mp *x)
+{
+ mp_pyobj *z = PyObject_New(mp_pyobj, mp_pytype);
+ z->x = x;
+ return ((PyObject *)z);
+}
+
+PyObject *gf_pywrap(mp *x)
+{
+ mp_pyobj *z = PyObject_New(mp_pyobj, gf_pytype);
+ z->x = x;
+ return ((PyObject *)z);
+}
+
+int mp_tolong_checked(mp *x, long *l, int must)
+{
+ static mp *longmin = 0, *longmax = 0;
+ int rc = -1;
+
+ if (!longmax) {
+ longmin = mp_fromlong(MP_NEW, LONG_MIN);
+ longmax = mp_fromlong(MP_NEW, LONG_MAX);
+ }
+ if (MP_CMP(x, <, longmin) || MP_CMP(x, >, longmax)) {
+ if (must) VALERR("mp out of range for int");
+ else goto end;
+ }
+ *l = mp_tolong(x);
+ rc = 0;
+end:
+ return (rc);
+}
+
+/*----- Arbitrary-precision integers --------------------------------------*/
+
+static void mp_pydealloc(PyObject *o)
+{
+ MP_DROP(MP_X(o));
+ FREEOBJ(o);
+}
+
+static PyObject *mp_pyrepr(PyObject *o)
+ { return mp_topystring(MP_X(o), 10, "MP(", 0, ")"); }
+
+static PyObject *mp_pystr(PyObject *o)
+ { return mp_topystring(MP_X(o), 10, 0, 0, 0); }
+
+mp *tomp(PyObject *o)
+{
+ PyObject *l;
+ mp *x;
+
+ if (!o || PyFloat_Check(o))
+ return (0);
+ else if (MP_PYCHECK(o) || GF_PYCHECK(o))
+ return (MP_COPY(MP_X(o)));
+ else if (FE_PYCHECK(o))
+ return (F_OUT(FE_F(o), MP_NEW, FE_X(o)));
+ else if (PFILT_PYCHECK(o))
+ return (MP_COPY(PFILT_F(o)->m));
+ else if (ECPT_PYCHECK(o)) {
+ ec p = EC_INIT;
+ if (EC_ATINF(ECPT_P(o))) return (0);
+ getecptout(&p, o);
+ x = MP_COPY(p.x);
+ EC_DESTROY(&p);
+ return (x);
+ } else if (GE_PYCHECK(o)) {
+ if ((x = G_TOINT(GE_G(o), MP_NEW, GE_X(o))) == 0)
+ return (0);
+ return (x);
+ }
+#ifdef PY2
+ else if (PyInt_Check(o))
+ return (mp_fromlong(MP_NEW, PyInt_AS_LONG(o)));
+#endif
+ else if ((l = PyNumber_Long(o)) != 0) {
+ x = mp_frompylong(l);
+ Py_DECREF(l);
+ return (x);
+ } else {
+ PyErr_Clear();
+ return (0);
+ }
+}
+
+mp *getmp(PyObject *o)
+{
+ mp *x = 0;
+ if (!o) return (0);
+ if ((x = tomp(o)) == 0) {
+ PyErr_Format(PyExc_TypeError, "can't convert %.100s to mp",
+ Py_TYPE(o)->tp_name);
+ }
+ return (x);
+}
+
+int convmp(PyObject *o, void *p)
+{
+ mp *x;
+ if ((x = getmp(o)) == 0) return (0);
+ *(mp **)p = x;
+ return (1);
+}
+
+mp *getgf(PyObject *o)
+{
+ mp *x = 0;
+ if (!o) return (0);
+ if ((x = tomp(o)) == 0) {
+ PyErr_Format(PyExc_TypeError, "can't convert %.100s to gf",
+ Py_TYPE(o)->tp_name);
+ }
+ return (x);
+}
+
+int convgf(PyObject *o, void *p)
+{
+ mp *x;
+ if ((x = getgf(o)) == 0) return (0);
+ *(mp **)p = x;
+ return (1);
+}
+
+mp *implicitmp(PyObject *o)
+{
+ PyObject *l;
+
+ if (!o || GF_PYCHECK(o) || FE_PYCHECK(o)) return (0);
+ else if (MP_PYCHECK(o)) return (MP_COPY(MP_X(o)));
+ else if (PFILT_PYCHECK(o)) return (MP_COPY(PFILT_F(o)->m));
+#ifdef PY2
+ else if (PyInt_Check(o)) return (mp_fromlong(MP_NEW, PyInt_AS_LONG(o)));
+#endif
+ else if ((l = PyNumber_Index(o)) != 0) {
+#ifdef PY2
+ if (PyInt_Check(o)) return (mp_fromlong(MP_NEW, PyInt_AS_LONG(o)));
+#endif
+ if (PyLong_Check(o)) return (mp_frompylong(o));
+ }
+ PyErr_Clear(); return (0);
+}
+
+mp *implicitgf(PyObject *o)
+{
+ if (GF_PYCHECK(o)) return (MP_COPY(MP_X(o)));
+ return (0);
+}
+
+static int mpbinop(PyObject *x, PyObject *y, mp **xx, mp **yy)
+{
+ if ((*xx = implicitmp(x)) == 0)
+ return (-1);
+ if ((*yy = implicitmp(y)) == 0) {
+ MP_DROP(*xx);
+ return (-1);
+ }
+ return (0);
+}
+
+static int gfbinop(PyObject *x, PyObject *y, mp **xx, mp **yy)
+{
+ if ((*xx = implicitgf(x)) == 0)
+ return (-1);
+ if ((*yy = implicitgf(y)) == 0) {
+ MP_DROP(*xx);
+ return (-1);
+ }
+ return (0);
+}
+
+#define FPBINOP(name, pyop) \
+ static PyObject *mp_py##name(PyObject *x, PyObject *y) { \
+ mp *xx, *yy, *zz; \
+ PyObject *l, *rc; \
+ if (PyFloat_Check(x)) { \
+ l = mp_topylong(MP_X(y)); rc = PyNumber_##pyop(x, l); \
+ Py_DECREF(l); return (rc); \
+ } else if (PyFloat_Check(y)) { \
+ l = mp_topylong(MP_X(x)); rc = PyNumber_##pyop(l, y); \
+ Py_DECREF(l); return (rc); \
+ } \
+ if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL; \
+ zz = mp_##name(MP_NEW, xx, yy); \
+ MP_DROP(xx); MP_DROP(yy); \
+ return (mp_pywrap(zz)); \
+ }
+FPBINOP(add, Add)
+FPBINOP(sub, Subtract)
+FPBINOP(mul, Multiply)
+
+#define gf_and mp_and
+#define gf_or mp_or
+#define gf_xor mp_xor
+#define BINOP(pre, name) \
+ static PyObject *pre##_py##name(PyObject *x, PyObject *y) { \
+ mp *xx, *yy, *zz; \
+ if (pre##binop(x, y, &xx, &yy)) RETURN_NOTIMPL; \
+ zz = pre##_##name(MP_NEW, xx, yy); \
+ MP_DROP(xx); MP_DROP(yy); \
+ return (pre##_pywrap(zz)); \
+ }
+BINOP(mp, and2c)
+BINOP(mp, or2c)
+BINOP(mp, xor2c)
+BINOP(gf, add)
+BINOP(gf, sub)
+BINOP(gf, mul)
+BINOP(gf, and)
+BINOP(gf, or)
+BINOP(gf, xor)
+#undef BINOP
+
+static mp *mp_abs(mp *d, mp *x)
+{
+ if (MP_NEGP(x))
+ return (mp_neg(d, x));
+ MP_COPY(x);
+ if (d) MP_DROP(d);
+ return (x);
+}
+
+#define UNOP(pre, name) \
+ static PyObject *pre##_py##name(PyObject *x) \
+ { return mp_pywrap(pre##_##name(MP_NEW, MP_X(x))); }
+UNOP(mp, neg)
+UNOP(mp, abs)
+UNOP(mp, not2c)
+#undef UNOP
+
+static PyObject *mp_pyid(PyObject *x) { RETURN_OBJ(x); }
+
+#define gf_lsr mp_lsr
+#define SHIFTOP(pre, PRE, name, rname) \
+ static PyObject *pre##_py##name(PyObject *x, PyObject *y) { \
+ PyObject *yix = 0; \
+ PyObject *z = 0; \
+ long n; \
+ if (!PRE##_PYCHECK(x)) RETURN_NOTIMPL; \
+ if (GF_PYCHECK(y) || FE_PYCHECK(y)) RETURN_NOTIMPL; \
+ yix = PyNumber_Index(y); if (!yix) { PyErr_Clear(); RETURN_NOTIMPL; } \
+ n = PyInt_AsLong(yix); Py_DECREF(yix); \
+ if (n == -1 && PyErr_Occurred()) { PyErr_Clear(); RETURN_NOTIMPL; } \
+ if (n < 0) z = pre##_pywrap(mp_##rname(MP_NEW, MP_X(x), -n)); \
+ else z = pre##_pywrap(mp_##name(MP_NEW, MP_X(x), n)); \
+ return (z); \
+ }
+SHIFTOP(mp, MP, lsl2c, lsr2c)
+SHIFTOP(mp, MP, lsr2c, lsl2c)
+SHIFTOP(gf, GF, lsl, lsr)
+SHIFTOP(gf, GF, lsr, lsl)
+#undef SHIFTOP
+
+#define DIVOP(pre, name, qq, rr, gather) \
+ static PyObject *pre##_py##name(PyObject *x, PyObject *y) { \
+ mp *xx, *yy; \
+ PyObject *z = 0; \
+ INIT_##qq(q) INIT_##rr(r) \
+ if (pre##binop(x, y, &xx, &yy)) RETURN_NOTIMPL; \
+ if (MP_ZEROP(yy)) \
+ ZDIVERR("division by zero"); \
+ pre##_div(ARG_##qq(q), ARG_##rr(r), xx, yy); \
+ z = gather; \
+ end: \
+ MP_DROP(xx); MP_DROP(yy); \
+ return (z); \
+ }
+#define INIT_YES(p) mp *p = MP_NEW;
+#define INIT_NO(p)
+#define ARG_YES(p) &p
+#define ARG_NO(p) 0
+DIVOP(mp, divmod, YES, YES,
+ Py_BuildValue("(NN)", mp_pywrap(q), mp_pywrap(r)))
+DIVOP(mp, div, YES, NO, mp_pywrap(q))
+DIVOP(mp, mod, NO, YES, mp_pywrap(r))
+DIVOP(gf, divmod, YES, YES,
+ Py_BuildValue("(NN)", gf_pywrap(q), gf_pywrap(r)))
+DIVOP(gf, div, YES, NO, gf_pywrap(q))
+DIVOP(gf, mod, NO, YES, gf_pywrap(r))
+#undef INIT_YES
+#undef INIT_NO
+#undef ARG_YES
+#undef ARG_NO
+#undef DIVOP
+
+static mp *mp_modinv_checked(mp *d, mp *x, mp *p)
+{
+ mp *g = MP_NEW;
+ mp_gcd(&g, 0, &d, p, x);
+ if (!MP_EQ(g, MP_ONE)) {
+ MP_DROP(g); MP_DROP(d);
+ PyErr_SetString(PyExc_ZeroDivisionError, "no modular inverse");
+ return (0);
+ }
+ MP_DROP(g); return (d);
+}
+
+static PyObject *mp_pyexp(PyObject *x, PyObject *y, PyObject *z)
+{
+ mp *xx = 0, *yy = 0, *zz = 0;
+ mp *r = 0;
+ PyObject *rc = 0;
+
+ if ((xx = implicitmp(x)) == 0 || (yy = implicitmp(y)) == 0 ||
+ (z && z != Py_None && (zz = implicitmp(z)) == 0)) {
+ mp_drop(xx); mp_drop(yy); mp_drop(zz);
+ RETURN_NOTIMPL;
+ }
+ if (!z || z == Py_None) {
+ if (MP_NEGP(yy)) VALERR("negative exponent");
+ r = mp_exp(MP_NEW, xx, yy);
+ } else {
+ if (!MP_POSP(zz)) VALERR("modulus must be positive");
+ if (MP_NEGP(yy)) {
+ if ((xx = mp_modinv_checked(xx, xx, zz)) == 0) goto end;
+ yy = mp_neg(yy, yy);
+ }
+ if (MP_ODDP(zz)) {
+ mpmont mm;
+ mpmont_create(&mm, zz);
+ r = mpmont_exp(&mm, MP_NEW, xx, yy);
+ mpmont_destroy(&mm);
+ } else {
+ mpbarrett mb;
+ mpbarrett_create(&mb, zz);
+ r = mpbarrett_exp(&mb, MP_NEW, xx, yy);
+ mpbarrett_destroy(&mb);
+ }
+ }
+ rc = mp_pywrap(r);
+end:
+ mp_drop(xx); mp_drop(yy); mp_drop(zz);
+ return (rc);
+}
+
+static mp *gf_modinv_checked(mp *d, mp *x, mp *p)
+{
+ mp *g = MP_NEW;
+ gf_gcd(&g, 0, &d, p, x);
+ if (!MP_EQ(g, MP_ONE)) {
+ MP_DROP(g); MP_DROP(d);
+ PyErr_SetString(PyExc_ZeroDivisionError, "no modular inverse");
+ return (0);
+ }
+ MP_DROP(g); return (d);
+}
+
+#define BASEOP(name, radix, pre) \
+ static PyObject *mp_py##name(PyObject *x) \
+ { return mp_topystring(MP_X(x), radix, 0, pre, 0); }
+#ifdef PY2
+BASEOP(oct, 8, "0");
+#endif
+BASEOP(hex, 16, "0x");
+#undef BASEOP
+
+static int mp_pynonzerop(PyObject *x) { return !MP_ZEROP(MP_X(x)); }
+
+static PyObject *mp_pyint(PyObject *x)
+{
+ long l;
+ if (!mp_tolong_checked(MP_X(x), &l, 0)) return (PyInt_FromLong(l));
+ else return mp_topylong(MP_X(x));
+}
+#ifdef PY2
+static PyObject *mp_pylong(PyObject *x)
+ { return (mp_topylong(MP_X(x))); }
+#endif
+static PyObject *mp_pyfloat(PyObject *x)
+{
+ PyObject *l = mp_topylong(MP_X(x));
+ double f = PyLong_AsDouble(l);
+ Py_DECREF(l);
+ return (PyFloat_FromDouble(f));
+}
+
+#ifdef PY2
+#define COERCE(pre, PRE) \
+ static int pre##_pycoerce(PyObject **x, PyObject **y) \
+ { \
+ mp *z; \
+ \
+ if (PRE##_PYCHECK(*y)) { \
+ Py_INCREF(*x); Py_INCREF(*y); \
+ return (0); \
+ } \
+ if ((z = implicit##pre(*y)) != 0) { \
+ Py_INCREF(*x); \
+ *y = pre##_pywrap(z); \
+ return (0); \
+ } \
+ return (1); \
+ }
+COERCE(mp, MP)
+COERCE(gf, GF)
+#undef COERCE
+#endif
+
+static PyObject *mp_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ mp *xx, *yy;
+ PyObject *l, *rc;
+ if (PyFloat_Check(y)) {
+ l = mp_topylong(MP_X(x)); rc = PyObject_RichCompare(l, y, op);
+ Py_DECREF(l); return (rc);
+ }
+ if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;
+ rc = enrich_compare(op, mp_cmp(xx, yy));
+ MP_DROP(xx); MP_DROP(yy);
+ return (rc);
+}
+
+#ifdef PY2
+static int mp_pycompare(PyObject *x, PyObject *y)
+ { return mp_cmp(MP_X(x), MP_X(y)); }
+#endif
+
+static PyObject *mp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *x;
+ mp *z;
+ mp_pyobj *zz = 0;
+ int radix = 0;
+ static const char *const kwlist[] = { "x", "radix", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", KWLIST, &x, &radix))
+ goto end;
+ if (MP_PYCHECK(x)) RETURN_OBJ(x);
+ if (!good_radix_p(radix, 1)) VALERR("bad radix");
+ if ((z = mp_frompyobject(x, radix)) == 0) {
+ PyErr_Format(PyExc_TypeError, "can't convert %.100s to mp",
+ Py_TYPE(x)->tp_name);
+ goto end;
+ }
+ zz = (mp_pyobj *)ty->tp_alloc(ty, 0);
+ zz->x = z;
+end:
+ return ((PyObject *)zz);
+}
+
+#define IMPLICIT(pre) \
+ static PyObject *pre##meth__implicit(PyObject *me, PyObject *arg) \
+ { \
+ PyObject *x, *rc = 0; \
+ mp *y = MP_NEW; \
+ if (!PyArg_ParseTuple(arg, "O:_implicit", &x)) goto end; \
+ y = implicit##pre(x); \
+ if (!y) TYERR("can't convert implicitly to " #pre); \
+ rc = pre##_pywrap(y); \
+ end: \
+ return (rc); \
+ }
+IMPLICIT(mp)
+IMPLICIT(gf)
+#undef IMPLICIT
+
+Py_hash_t mphash(mp *x)
+{
+ PyObject *l = mp_topylong(x);
+ Py_hash_t h = PyObject_Hash(l);
+ Py_DECREF(l); return (h);
+}
+
+static Py_hash_t mp_pyhash(PyObject *me) { return (mphash(MP_X(me))); }
+
+static PyObject *mpmeth_jacobi(PyObject *me, PyObject *arg)
+{
+ mp *y = 0;
+ PyObject *z = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:jacobi", convmp, &y)) goto end;
+ z = PyInt_FromLong(mp_jacobi(y, MP_X(me)));
+end:
+ if (y) MP_DROP(y);
+ return (z);
+}
+
+#define BITOP(pre, name, c) \
+ static PyObject *pre##meth_##name(PyObject *me, PyObject *arg) \
+ { \
+ unsigned long i; \
+ if (!PyArg_ParseTuple(arg, "O&:" #name, convulong, &i)) return (0); \
+ return (pre##_pywrap(mp_##name##c(MP_NEW, MP_X(me), i))); \
+ }
+BITOP(mp, setbit, 2c);
+BITOP(mp, clearbit, 2c);
+BITOP(gf, setbit, );
+BITOP(gf, clearbit, );
+#undef BITOP
+
+static PyObject *mpmeth_testbit(PyObject *me, PyObject *arg)
+{
+ unsigned long i;
+ if (!PyArg_ParseTuple(arg, "O&:testbit", convulong, &i)) return (0);
+ return (getbool(mp_testbit2c(MP_X(me), i)));
+}
+
+static PyObject *gfmeth_testbit(PyObject *me, PyObject *arg)
+{
+ unsigned long i;
+ if (!PyArg_ParseTuple(arg, "O&:testbit", convulong, &i)) return (0);
+ return (getbool(mp_testbit(MP_X(me), i)));
+}
+
+static PyObject *mpmeth_odd(PyObject *me)
+{
+ mp *t;
+ size_t s;
+
+ t = mp_odd(MP_NEW, MP_X(me), &s);
+ return (Py_BuildValue("(lN)", (long)s, mp_pywrap(t)));
+}
+
+static PyObject *mpmeth_sqr(PyObject *me)
+ { return (mp_pywrap(mp_sqr(MP_NEW, MP_X(me)))); }
+
+static PyObject *mpmeth_sqrt(PyObject *me)
+{
+ if (MP_NEGP(MP_X(me))) VALERR("negative root");
+ return (mp_pywrap(mp_sqrt(MP_NEW, MP_X(me))));
+end:
+ return (0);
+}
+
+static PyObject *mpmeth_gcd(PyObject *me, PyObject *arg)
+{
+ mp *y = 0, *zz = MP_NEW;
+ PyObject *z = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:gcd", convmp, &y)) goto end;
+ mp_gcd(&zz, 0, 0, MP_X(me), y);
+ z = mp_pywrap(zz);
+end:
+ if (y) MP_DROP(y);
+ return (z);
+}
+
+static PyObject *mpmeth_gcdx(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0, *zz = MP_NEW, *uu = MP_NEW, *vv = MP_NEW;
+
+ if (!PyArg_ParseTuple(arg, "O&:gcdx", convmp, &yy)) goto end;
+ mp_gcd(&zz, &uu, &vv, MP_X(me), yy);
+ z = Py_BuildValue("(NNN)",
+ mp_pywrap(zz), mp_pywrap(uu), mp_pywrap(vv));
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *mpmeth_modinv(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0, *zz = MP_NEW;
+
+ if (!PyArg_ParseTuple(arg, "O&:modinv", convmp, &yy) ||
+ (zz = mp_modinv_checked(MP_NEW, yy, MP_X(me))) == 0)
+ goto end;
+ z = mp_pywrap(zz);
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *mpmeth_tostring(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ int radix = 10;
+ static const char *const kwlist[] = { "radix", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", KWLIST, &radix))
+ goto end;
+ if (!good_radix_p(radix, 0)) VALERR("bad radix");
+ return (mp_topystring(MP_X(me), radix, 0, 0, 0));
+end:
+ return (0);
+}
+
+static PyObject *mpmeth_modsqrt(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0, *zz = MP_NEW;
+
+ if (!PyArg_ParseTuple(arg, "O&:modsqrt", convmp, &yy)) goto end;
+ if ((zz = mp_modsqrt(MP_NEW, yy, MP_X(me))) == 0)
+ VALERR("no modular square root");
+ z = mp_pywrap(zz);
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *mpmeth_leastcongruent(PyObject *me, PyObject *arg)
+{
+ mp *z, *b, *m;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:leastcongruent", convmp, &b, convmp, &m))
+ goto end;
+ z = mp_leastcongruent(MP_NEW, b, MP_X(me), m);
+ rc = mp_pywrap(z);
+end:
+ return (rc);
+}
+
+#define STOREOP(name, c) \
+ static PyObject *mpmeth_##name(PyObject *me, \
+ PyObject *arg, PyObject *kw) \
+ { \
+ long len = -1; \
+ static const char *const kwlist[] = { "len", 0 }; \
+ PyObject *rc = 0; \
+ \
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|l:" #name, \
+ KWLIST, &len)) \
+ goto end; \
+ if (len < 0) { \
+ len = mp_octets##c(MP_X(me)); \
+ if (!len) len = 1; \
+ } \
+ rc = bytestring_pywrap(0, len); \
+ mp_##name(MP_X(me), BIN_PTR(rc), len); \
+ end: \
+ return (rc); \
+ }
+STOREOP(storel, )
+STOREOP(storeb, )
+STOREOP(storel2c, 2c)
+STOREOP(storeb2c, 2c)
+#undef STOREOP
+
+#define BUFOP(ty) \
+ static PyObject *ty##meth_frombuf(PyObject *me, PyObject *arg) \
+ { \
+ buf b; \
+ struct bin in; \
+ PyObject *rc = 0; \
+ mp *x; \
+ \
+ if (!PyArg_ParseTuple(arg, "O&:frombuf", convbin, &in)) goto end; \
+ buf_init(&b, (/*unconst*/ void *)in.p, in.sz); \
+ if ((x = buf_getmp(&b)) == 0) VALERR("malformed data"); \
+ rc = Py_BuildValue("(NN)", ty##_pywrap(x), \
+ bytestring_pywrapbuf(&b)); \
+ end: \
+ return (rc); \
+ }
+BUFOP(mp)
+BUFOP(gf)
+#undef BUFOP
+
+static PyObject *mpmeth_tobuf(PyObject *me)
+{
+ buf b;
+ PyObject *rc;
+ mp *x;
+ size_t n;
+
+ x = MP_X(me);
+ n = mp_octets(x) + 3;
+ rc = bytestring_pywrap(0, n);
+ buf_init(&b, BIN_PTR(rc), n);
+ buf_putmp(&b, x);
+ assert(BOK(&b));
+ BIN_SETLEN(rc, BLEN(&b));
+ return (rc);
+}
+
+static PyObject *mpmeth_primep(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ grand *r = &rand_global;
+ static const char *const kwlist[] = { "rng", 0 };
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", KWLIST, convgrand, &r))
+ goto end;
+ rc = getbool(pgen_primep(MP_X(me), r));
+end:
+ return (rc);
+}
+
+static PyObject *mpmeth_fromstring(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ int r = 0;
+ char *p;
+ Py_ssize_t len;
+ PyObject *z = 0;
+ mp *zz;
+ mptext_stringctx sc;
+ static const char *const kwlist[] = { "x", "radix", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|i:fromstring", KWLIST,
+ &p, &len, &r))
+ goto end;
+ if (!good_radix_p(r, 1)) VALERR("bad radix");
+ sc.buf = p; sc.lim = p + len;
+ if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0)
+ VALERR("bad integer");
+ z = Py_BuildValue("(Ns#)", mp_pywrap(zz),
+ sc.buf, (Py_ssize_t)(sc.lim - sc.buf));
+end:
+ return (z);
+}
+
+static PyObject *mpmeth_factorial(PyObject *me, PyObject *arg)
+{
+ unsigned long i;
+ mp *x;
+ if (!PyArg_ParseTuple(arg, "O&:factorial", convulong, &i)) return (0);
+ x = mp_factorial(i);
+ return mp_pywrap(x);
+}
+
+static PyObject *mpmeth_fibonacci(PyObject *me, PyObject *arg)
+{
+ long i;
+ mp *x;
+ if (!PyArg_ParseTuple(arg, "l:fibonacci", &i)) return (0);
+ x = mp_fibonacci(i);
+ return mp_pywrap(x);
+}
+
+#define LOADOP(pre, name) \
+ static PyObject *pre##meth_##name(PyObject *me, PyObject *arg) \
+ { \
+ struct bin in; \
+ if (!PyArg_ParseTuple(arg, "O&:" #name, convbin, &in)) return (0); \
+ return (pre##_pywrap(mp_##name(MP_NEW, in.p, in.sz))); \
+ }
+LOADOP(mp, loadl)
+LOADOP(mp, loadb)
+LOADOP(mp, loadl2c)
+LOADOP(mp, loadb2c)
+LOADOP(gf, loadl)
+LOADOP(gf, loadb)
+#undef LOADOP
+
+static PyObject *mpget_nbits(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(mp_bits(MP_X(me)))); }
+
+static PyObject *mpget_noctets(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(mp_octets(MP_X(me)))); }
+
+static PyObject *mpget_noctets2c(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(mp_octets2c(MP_X(me)))); }
+
+static const PyGetSetDef mp_pygetset[] = {
+#define GETSETNAME(op, func) mp##op##_##func
+ GET (nbits, "X.nbits -> bit length of X")
+ GET (noctets, "X.noctets -> octet length of X")
+ GET (noctets2c, "X.noctets2c -> two's complement octet length of X")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef mp_pymethods[] = {
+#define METHNAME(func) mpmeth_##func
+ METH (jacobi, "X.jacobi(Y) -> Jacobi symbol (Y|X) (NB inversion!)")
+ METH (setbit, "X.setbit(N) -> X with bit N set")
+ METH (clearbit, "X.clearbit(N) -> X with bit N clear")
+ METH (testbit, "X.testbit(N) -> true/false if bit N set/clear in X")
+ NAMETH(odd, "X.odd() -> S, T where X = 2^S T with T odd")
+ NAMETH(sqr, "X.sqr() -> X^2")
+ NAMETH(sqrt, "X.sqrt() -> largest integer <= sqrt(X)")
+ METH (gcd, "X.gcd(Y) -> gcd(X, Y)")
+ METH (gcdx, "X.gcdx(Y) -> (gcd(X, Y), U, V) "
+ "with X U + Y V = gcd(X, Y)")
+ METH (modinv, "X.modinv(Y) -> multiplicative inverse of Y mod X")
+ METH (modsqrt, "X.modsqrt(Y) -> square root of Y mod X, if X prime")
+ METH (leastcongruent, "X.leastcongruent(B, M) -> "
+ "smallest Z >= B with Z == X (mod M)")
+ KWMETH(primep, "X.primep([rng = rand]) -> X is prime?")
+ KWMETH(tostring, "X.tostring([radix = 10]) -> STR")
+ KWMETH(storel, "X.storel([len = -1]) -> little-endian bytes")
+ KWMETH(storeb, "X.storeb([len = -1]) -> big-endian bytes")
+ KWMETH(storel2c, "X.storel2c([len = -1]) -> "
+ "little-endian bytes, two's complement")
+ KWMETH(storeb2c, "X.storeb2c([len = -1]) -> "
+ "big-endian bytes, two's complement")
+ NAMETH(tobuf, "X.tobuf() -> buffer format")
+ KWSMTH(fromstring, "fromstring(STR, [radix = 0]) -> (X, REST)\n"
+ " Parse STR as a large integer, according to RADIX. If RADIX is\n"
+ " zero, read a prefix from STR to decide radix: allow `0b' for binary,\n"
+ " `0' or `0o' for octal, `0x' for hex, or `R_' for other radix R.")
+ SMTH (_implicit, 0)
+ SMTH (factorial, "factorial(I) -> I!: compute factorial")
+ SMTH (fibonacci, "fibonacci(I) -> F(I): compute Fibonacci number")
+ SMTH (loadl, "loadl(STR) -> X: read little-endian bytes")
+ SMTH (loadb, "loadb(STR) -> X: read big-endian bytes")
+ SMTH (loadl2c, "loadl2c(STR) -> X: "
+ "read little-endian bytes, two's complement")
+ SMTH (loadb2c, "loadb2c(STR) -> X: "
+ "read big-endian bytes, two's complement")
+ SMTH (frombuf, "frombuf(STR) -> (X, REST): read buffer format")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods mp_pynumber = {
+ mp_pyadd, /* @nb_add@ */
+ mp_pysub, /* @nb_subtract@ */
+ mp_pymul, /* @nb_multiply@ */
+#ifdef PY2
+ 0, /* @nb_divide@ */
+#endif
+ mp_pymod, /* @nb_remainder@ */
+ mp_pydivmod, /* @nb_divmod@ */
+ mp_pyexp, /* @nb_power@ */
+ mp_pyneg, /* @nb_negative@ */
+ mp_pyid, /* @nb_positive@ */
+ mp_pyabs, /* @nb_absolute@ */
+ mp_pynonzerop, /* @nb_nonzero@ */
+ mp_pynot2c, /* @nb_invert@ */
+ mp_pylsl2c, /* @nb_lshift@ */
+ mp_pylsr2c, /* @nb_rshift@ */
+ mp_pyand2c, /* @nb_and@ */
+ mp_pyxor2c, /* @nb_xor@ */
+ mp_pyor2c, /* @nb_or@ */
+#ifdef PY2
+ mp_pycoerce, /* @nb_coerce@ */
+#endif
+ mp_pyint, /* @nb_int@ */
+ PY23(mp_pylong, 0), /* @nb_long@ */
+ mp_pyfloat, /* @nb_float@ */
+#ifdef PY2
+ mp_pyoct, /* @nb_oct@ */
+ mp_pyhex, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+#ifdef PY2
+ 0, /* @nb_inplace_divide@ */
+#endif
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ mp_pydiv, /* @nb_floor_divide@ */
+ 0, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+
+ mp_pyint, /* @nb_index@ */
+};
+
+static const PyTypeObject mp_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "MP", /* @tp_name@ */
+ sizeof(mp_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mp_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ PY23(mp_pycompare, 0), /* @tp_compare@/@tp_as_async@ */
+ mp_pyrepr, /* @tp_repr@ */
+ PYNUMBER(mp), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ mp_pyhash, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ mp_pystr, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Multiprecision integers, similar to `long' but more efficient and\n"
+ "versatile. Support all the standard arithmetic operations, with\n"
+ "implicit conversions from `PrimeFilter', and other objects which\n"
+ "convert to `" PY23("long", "int") "'.\n"
+ "\n"
+ "Constructor MP(X, [radix = R]) attempts to convert X to an `MP'. If\n"
+ "X is a string, it's read in radix-R form, or we look for a prefix\n"
+ "if R = 0. Other acceptable things are field elements, elliptic curve\n"
+ PY23(
+ "points, group elements, Python `int' and `long' objects, and anything\n"
+ "with an integer conversion.\n",
+ "points, group elements, Python `int' objects, and anything with an\n"
+ "integer conversion.\n")
+ "\n"
+ "Notes:\n"
+ "\n"
+ " * Use `//' for integer division: `/' gives exact rational division.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ mp_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(mp), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(mp), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ mp_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Products of small integers ----------------------------------------*/
+
+static PyTypeObject *mpmul_pytype;
+
+typedef struct mpmul_pyobj {
+ PyObject_HEAD
+ int livep;
+ mpmul mm;
+} mpmul_pyobj;
+
+#define MPMUL_LIVEP(o) (((mpmul_pyobj *)(o))->livep)
+#define MPMUL_PY(o) (&((mpmul_pyobj *)(o))->mm)
+
+static void mpmul_pydealloc(PyObject *me)
+{
+ if (MPMUL_LIVEP(me))
+ mp_drop(mpmul_done(MPMUL_PY(me)));
+ FREEOBJ(me);
+}
+
+static PyObject *mmmeth_factor(PyObject *me, PyObject *arg)
+{
+ PyObject *q, *i;
+ mp *x;
+
+ if (!MPMUL_LIVEP(me)) VALERR("MPMul object invalid");
+ if (PyTuple_GET_SIZE(arg) != 1)
+ i = PyObject_GetIter(arg);
+ else {
+ if ((q = PyTuple_GET_ITEM(arg, 0)) == 0) goto end;
+ if ((i = PyObject_GetIter(q)) == 0) {
+ PyErr_Clear(); /* that's ok */
+ i = PyObject_GetIter(arg);
+ }
+ }
+ if (!i) goto end;
+ while ((q = PyIter_Next(i)) != 0) {
+ x = getmp(q); Py_DECREF(q); if (!x) {
+ Py_DECREF(i);
+ goto end;
+ }
+ mpmul_add(MPMUL_PY(me), x);
+ MP_DROP(x);
+ }
+ Py_DECREF(i);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *mmmeth_done(PyObject *me)
+{
+ mp *x;
+
+ if (!MPMUL_LIVEP(me)) VALERR("MPMul object invalid");
+ x = mpmul_done(MPMUL_PY(me));
+ MPMUL_LIVEP(me) = 0;
+ return (mp_pywrap(x));
+end:
+ return (0);
+}
+
+static PyObject *mpmul_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mpmul_pyobj *mm;
+
+ if (kw) TYERR("keyword arguments not allowed here");
+ mm = (mpmul_pyobj *)ty->tp_alloc(ty, 0);
+ mpmul_init(&mm->mm);
+ mm->livep = 1;
+ if (mmmeth_factor((PyObject *)mm, arg) == 0) {
+ Py_DECREF(mm);
+ goto end;
+ }
+ return ((PyObject *)mm);
+end:
+ return (0);
+}
+
+static PyObject *mmget_livep(PyObject *me, void *hunoz)
+ { return (getbool(MPMUL_LIVEP(me))); }
+
+static const PyGetSetDef mpmul_pygetset[] = {
+#define GETSETNAME(op, name) mm##op##_##name
+ GET (livep, "MM.livep -> flag: object still valid?")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef mpmul_pymethods[] = {
+#define METHNAME(name) mmmeth_##name
+ METH (factor, "MM.factor(ITERABLE) or MM.factor(I, ...)")
+ NAMETH(done, "MM.done() -> PRODUCT")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject mpmul_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "MPMul", /* @tp_name@ */
+ sizeof(mpmul_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mpmul_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "MPMul(N_0, N_1, ....): an object for multiplying many small integers.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(mpmul), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(mpmul), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ mpmul_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Montgomery reduction ----------------------------------------------*/
+
+static PyTypeObject *mpmont_pytype;
+
+typedef struct mpmont_pyobj {
+ PyObject_HEAD
+ mpmont mm;
+} mpmont_pyobj;
+
+#define MPMONT_PY(o) (&((mpmont_pyobj *)(o))->mm)
+
+static PyObject *mmmeth_int(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0;
+ mpmont *mm = MPMONT_PY(me);
+
+ if (!PyArg_ParseTuple(arg, "O&:in", convmp, &yy))
+ goto end;
+ mp_div(0, &yy, yy, mm->m);
+ z = mp_pywrap(mpmont_mul(mm, MP_NEW, yy, mm->r2));
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *mmmeth_mul(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *yy = 0, *zz = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:mul", convmp, &yy, convmp, &zz))
+ goto end;
+ rc = mp_pywrap(mpmont_mul(MPMONT_PY(me), MP_NEW, yy, zz));
+end:
+ if (yy) MP_DROP(yy); if (zz) MP_DROP(zz);
+ return (rc);
+}
+
+static PyObject *mmmeth_exp(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *yy = 0, *zz = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:exp", convmp, &yy, convmp, &zz))
+ goto end;
+ if (MP_NEGP(zz)) {
+ if ((yy = mp_modinv_checked(yy, yy, MPMONT_PY(me)->m)) == 0) goto end;
+ zz = mp_neg(zz, zz);
+ }
+ rc = mp_pywrap(mpmont_exp(MPMONT_PY(me), MP_NEW, yy, zz));
+end:
+ if (yy) MP_DROP(yy); if (zz) MP_DROP(zz);
+ return (rc);
+}
+
+static PyObject *mmmeth_expr(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *yy = 0, *zz = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:expr", convmp, &yy, convmp, &zz))
+ goto end;
+ if (MP_NEGP(zz)) {
+ yy = mpmont_reduce(MPMONT_PY(me), yy, yy);
+ if ((yy = mp_modinv_checked(yy, yy, MPMONT_PY(me)->m)) == 0) goto end;
+ yy = mpmont_mul(MPMONT_PY(me), yy, yy, MPMONT_PY(me)->r2);
+ zz = mp_neg(zz, zz);
+ }
+ rc = mp_pywrap(mpmont_expr(MPMONT_PY(me), MP_NEW, yy, zz));
+end:
+ if (yy) MP_DROP(yy); if (zz) MP_DROP(zz);
+ return (rc);
+}
+
+static PyObject *mm_mexpr_id(PyObject *me)
+ { return mp_pywrap(MP_COPY(MPMONT_PY(me)->r)); }
+
+static int mm_mexpr_fill(void *p, PyObject *me, PyObject *x, PyObject *y)
+{
+ mp *xx = 0, *yy = 0;
+ mp_expfactor *f = p;
+ mpmont *mm = MPMONT_PY(me);
+
+ if ((xx = getmp(x)) == 0 || (yy = getmp(y)) == 0)
+ goto fail;
+ if (MP_NEGP(yy)) {
+ xx = mpmont_reduce(mm, xx, xx);
+ if ((xx = mp_modinv_checked(xx, xx, yy)) == 0)
+ goto fail;
+ xx = mpmont_mul(mm, xx, xx, mm->r2);
+ yy = mp_neg(yy, yy);
+ }
+ f->base = xx;
+ f->exp = yy;
+ return (0);
+
+fail:
+ mp_drop(xx); mp_drop(yy);
+ return (-1);
+}
+
+static PyObject *mm_mexpr(PyObject *me, void *v, size_t n)
+ { return mp_pywrap(mpmont_mexpr(MPMONT_PY(me), MP_NEW, v, n)); }
+
+static void mp_mexp_drop(void *p)
+{
+ mp_expfactor *f = p;
+ mp_drop(f->base);
+ mp_drop(f->exp);
+}
+
+static PyObject *mmmeth_mexpr(PyObject *me, PyObject *arg)
+{
+ return mexp_common(me, arg, sizeof(mp_expfactor),
+ mm_mexpr_id, mm_mexpr_fill, mm_mexpr, mp_mexp_drop);
+}
+
+static PyObject *mp_mexp_id(PyObject *me)
+ { return mp_pywrap(MP_ONE); }
+
+static int mp_mexp_fill(void *p, PyObject *me, PyObject *x, PyObject *y)
+{
+ mp *xx = 0, *yy = 0;
+ mp_expfactor *f = p;
+
+ if ((xx = getmp(x)) == 0 || (yy = getmp(y)) == 0)
+ goto fail;
+ if (MP_NEGP(yy)) {
+ if ((xx = mp_modinv_checked(xx, xx, yy)) == 0)
+ goto fail;
+ yy = mp_neg(yy, yy);
+ }
+ f->base = xx;
+ f->exp = yy;
+ return (0);
+
+fail:
+ mp_drop(xx); mp_drop(yy);
+ return (-1);
+}
+
+static PyObject *mm_mexp(PyObject *me, void *v, size_t n)
+ { return mp_pywrap(mpmont_mexp(MPMONT_PY(me), MP_NEW, v, n)); }
+
+static PyObject *mmmeth_mexp(PyObject *me, PyObject *arg)
+{
+ return mexp_common(me, arg, sizeof(mp_expfactor),
+ mp_mexp_id, mp_mexp_fill, mm_mexp, mp_mexp_drop);
+}
+
+#define mmmeth_ext mmmeth_reduce
+static PyObject *mmmeth_reduce(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&", convmp, &yy)) goto end;
+ z = mp_pywrap(mpmont_reduce(MPMONT_PY(me), MP_NEW, yy));
+end:
+ return (z);
+}
+
+static void mpmont_pydealloc(PyObject *me)
+{
+ mpmont_destroy(MPMONT_PY(me));
+ FREEOBJ(me);
+}
+
+static PyObject *mpmont_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mpmont_pyobj *mm = 0;
+ static const char *const kwlist[] = { "m", 0 };
+ mp *xx = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
+ goto end;
+ if (!MP_POSP(xx) || !MP_ODDP(xx)) VALERR("m must be positive and odd");
+ mm = (mpmont_pyobj *)ty->tp_alloc(ty, 0);
+ mpmont_create(&mm->mm, xx);
+end:
+ if (xx) MP_DROP(xx);
+ return ((PyObject *)mm);
+}
+
+static PyObject *mmget_m(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(MPMONT_PY(me)->m))); }
+
+static PyObject *mmget_r(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(MPMONT_PY(me)->r))); }
+
+static PyObject *mmget_r2(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(MPMONT_PY(me)->r2))); }
+
+static const PyGetSetDef mpmont_pygetset[] = {
+#define GETSETNAME(op, name) mm##op##_##name
+ GET (m, "M.m -> modulus for reduction")
+ GET (r, "M.r -> multiplicative identity")
+ GET (r2, "M.r2 -> M.r^2, Montgomerization factor")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef mpmont_pymethods[] = {
+#define METHNAME(name) mmmeth_##name
+ METH (int, "M.int(X) -> XR")
+ METH (mul, "M.mul(XR, YR) -> ZR where Z = X Y")
+ METH (expr, "M.expr(XR, N) -> ZR where Z = X^N mod M.m")
+ METH (mexpr, "M.mexpr([(XR0, N0), (XR1, N1), ...]) = ZR "
+ "where Z = X0^N0 X1^N1 ... mod M.m\n"
+ "\t(the list may be flattened if this more convenient.)")
+ METH (reduce, "M.reduce(XR) -> X")
+ METH (ext, "M.ext(XR) -> X")
+ METH (exp, "M.exp(X, N) -> X^N mod M.m")
+ METH (mexp, "M.mexp([(X0, N0), (X1, N1), ...]) = "
+ "X0^N0 X1^N1 ... mod M.m\n"
+ "\t(the list may be flattened if this more convenient.)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject mpmont_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "MPMont", /* @tp_name@ */
+ sizeof(mpmont_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mpmont_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "MPMont(N): a Montgomery reduction context.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(mpmont), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(mpmont), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ mpmont_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Barrett reduction -------------------------------------------------*/
+
+static PyTypeObject *mpbarrett_pytype;
+
+typedef struct mpbarrett_pyobj {
+ PyObject_HEAD
+ mpbarrett mb;
+} mpbarrett_pyobj;
+
+#define MPBARRETT_PY(o) (&((mpbarrett_pyobj *)(o))->mb)
+
+static PyObject *mbmeth_exp(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *yy = 0, *zz = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:exp", convmp, &yy, convmp, &zz))
+ goto end;
+ if (MP_NEGP(zz)) {
+ if ((yy = mp_modinv_checked(yy, yy, MPBARRETT_PY(me)->m)) == 0) goto end;
+ zz = mp_neg(zz, zz);
+ }
+ rc = mp_pywrap(mpbarrett_exp(MPBARRETT_PY(me), MP_NEW, yy, zz));
+end:
+ if (yy) MP_DROP(yy); if (zz) MP_DROP(zz);
+ return (rc);
+}
+
+static PyObject *mb_mexp(PyObject *me, void *v, size_t n)
+ { return mp_pywrap(mpbarrett_mexp(MPBARRETT_PY(me), MP_NEW, v, n)); }
+
+static PyObject *mbmeth_mexp(PyObject *me, PyObject *arg)
+{
+ return mexp_common(me, arg, sizeof(mp_expfactor),
+ mp_mexp_id, mp_mexp_fill, mb_mexp, mp_mexp_drop);
+}
+
+static PyObject *mbmeth_reduce(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:reduce", convmp, &yy))
+ goto end;
+ z = mp_pywrap(mpbarrett_reduce(MPBARRETT_PY(me), MP_NEW, yy));
+end:
+ return (z);
+}
+
+static void mpbarrett_pydealloc(PyObject *me)
+{
+ mpbarrett_destroy(MPBARRETT_PY(me));
+ FREEOBJ(me);
+}
+
+static PyObject *mpbarrett_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mpbarrett_pyobj *mb = 0;
+ static const char *const kwlist[] = { "m", 0 };
+ mp *xx = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
+ goto end;
+ if (!MP_POSP(xx)) VALERR("m must be positive");
+ mb = (mpbarrett_pyobj *)ty->tp_alloc(ty, 0);
+ mpbarrett_create(&mb->mb, xx);
+end:
+ if (xx) MP_DROP(xx);
+ return ((PyObject *)mb);
+}
+
+static PyObject *mbget_m(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(MPBARRETT_PY(me)->m))); }
+
+static const PyGetSetDef mpbarrett_pygetset[] = {
+#define GETSETNAME(op, name) mb##op##_##name
+ GET (m, "B.m -> modulus for reduction")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef mpbarrett_pymethods[] = {
+#define METHNAME(name) mbmeth_##name
+ METH (reduce, "B.reduce(X) -> X mod B.m")
+ METH (exp, "B.exp(X, N) -> X^N mod B.m")
+ METH (mexp, "B.mexp([(X0, N0), (X1, N1), ...]) = "
+ "X0^N0 X1^N1 ... mod B.m\n"
+ "\t(the list may be flattened if this more convenient.)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject mpbarrett_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "MPBarrett", /* @tp_name@ */
+ sizeof(mpbarrett_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mpbarrett_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "MPBarrett(N): a Barrett reduction context.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(mpbarrett), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(mpbarrett), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ mpbarrett_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Nice prime reduction ----------------------------------------------*/
+
+static PyTypeObject *mpreduce_pytype;
+
+typedef struct mpreduce_pyobj {
+ PyObject_HEAD
+ mpreduce mr;
+} mpreduce_pyobj;
+
+#define MPREDUCE_PY(o) (&((mpreduce_pyobj *)(o))->mr)
+
+static PyObject *mrmeth_exp(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *yy = 0, *zz = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:exp", convmp, &yy, convmp, &zz))
+ goto end;
+ if (MP_NEGP(zz)) {
+ if ((yy = mp_modinv_checked(yy, yy, MPREDUCE_PY(me)->p)) == 0) goto end;
+ zz = mp_neg(zz, zz);
+ }
+ rc = mp_pywrap(mpreduce_exp(MPREDUCE_PY(me), MP_NEW, yy, zz));
+end:
+ if (yy) MP_DROP(yy); if (zz) MP_DROP(zz);
+ return (rc);
+}
+
+static PyObject *mrmeth_reduce(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:reduce", convmp, &yy)) goto end;
+ z = mp_pywrap(mpreduce_do(MPREDUCE_PY(me), MP_NEW, yy));
+end:
+ return (z);
+}
+
+static void mpreduce_pydealloc(PyObject *me)
+{
+ mpreduce_destroy(MPREDUCE_PY(me));
+ FREEOBJ(me);
+}
+
+static PyObject *mpreduce_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mpreduce_pyobj *mr = 0;
+ mpreduce r;
+ static const char *const kwlist[] = { "m", 0 };
+ mp *xx = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
+ goto end;
+ if (!MP_POSP(xx)) VALERR("m must be positive");
+ if (mpreduce_create(&r, xx)) VALERR("bad modulus (must be 2^k - ...)");
+ mr = (mpreduce_pyobj *)ty->tp_alloc(ty, 0);
+ mr->mr = r;
+end:
+ if (xx) MP_DROP(xx);
+ return ((PyObject *)mr);
+}
+
+static PyObject *mrget_m(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(MPREDUCE_PY(me)->p))); }
+
+static const PyGetSetDef mpreduce_pygetset[] = {
+#define GETSETNAME(op, name) mr##op##_##name
+ GET (m, "R.m -> modulus for reduction")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const const PyMethodDef mpreduce_pymethods[] = {
+#define METHNAME(name) mrmeth_##name
+ METH (reduce, "R.reduce(X) -> X mod B.m")
+ METH (exp, "R.exp(X, N) -> X^N mod B.m")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject mpreduce_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "MPReduce", /* @tp_name@ */
+ sizeof(mpreduce_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mpreduce_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "MPReduce(N): a reduction context for reduction modulo Solinas primes.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(mpreduce), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(mpreduce), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ mpreduce_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Chinese Remainder Theorem solution --------------------------------*/
+
+static PyTypeObject *mpcrt_pytype;
+
+typedef struct mpcrt_pyobj {
+ PyObject_HEAD
+ mpcrt c;
+} mpcrt_pyobj;
+
+#define MPCRT_PY(o) (&((mpcrt_pyobj *)(o))->c)
+
+static PyObject *mcmeth_solve(PyObject *me, PyObject *arg)
+{
+ mpcrt *c = MPCRT_PY(me);
+ PyObject *q = 0, *it, *x, *z = 0;
+ mp *xx;
+ mp **v = 0;
+ Py_ssize_t i = 0, n = c->k;
+
+ if (PyTuple_GET_SIZE(arg) == n)
+ q = arg;
+ else if (!PyArg_ParseTuple(arg, "O:solve", &q))
+ goto end;
+ v = xmalloc(n*sizeof(*v));
+ it = PyObject_GetIter(q); if (!it) goto end;
+ while (i < n) {
+ x = PyIter_Next(it);
+ if (!x) {
+ if (PyErr_Occurred()) goto end;
+ VALERR("residue count mismatch");
+ }
+ xx = getmp(x); Py_DECREF(x); if (!xx) goto end;
+ v[i++] = xx; xx = 0;
+ }
+ x = PyIter_Next(it);
+ if (x) { Py_DECREF(x); VALERR("residue count mismatch"); }
+ else if (PyErr_Occurred()) goto end;
+ z = mp_pywrap(mpcrt_solve(c, MP_NEW, v));
+end:
+ if (v) {
+ while (i--) MP_DROP(v[i]);
+ xfree(v);
+ }
+ return (z);
+}
+
+static void mpcrt_pydealloc(PyObject *me)
+{
+ mpcrt *c = MPCRT_PY(me);
+ mpcrt_destroy(c);
+ xfree(c->v);
+}
+
+static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mpcrt_mod *v = 0;
+ Py_ssize_t n, i = 0, j;
+ static const char *const kwlist[] = { "mv", 0 };
+ PyObject *q, *it = 0, *x;
+ mp *xx = MP_NEW, *y = MP_NEW, *g = MP_NEW;
+ mpmul mm;
+ mpcrt_pyobj *c = 0;
+
+ if (PyTuple_GET_SIZE(arg) > 1)
+ q = arg;
+ else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &q))
+ goto end;
+
+ if (!PySequence_Check(q))
+ n = 16;
+ else {
+ n = PySequence_Size(arg);
+ if (n == (size_t)-1 && PyErr_Occurred()) goto end;
+ }
+
+ v = xmalloc(n*sizeof(*v));
+ it = PyObject_GetIter(q); if (!it) goto end;
+ for (;;) {
+ x = PyIter_Next(it); if (!x) break;
+ xx = getmp(x); Py_DECREF(x); if (!xx) goto end;
+ if (MP_CMP(xx, <=, MP_ZERO)) VALERR("moduli must be positive");
+ if (i >= n) { n *= 2; v = xrealloc(v, n*sizeof(*v), i*sizeof(*v)); }
+ v[i].m = xx; v[i].n = 0; v[i].ni = 0; v[i].nni = 0; i++; xx = MP_NEW;
+ }
+ if (PyErr_Occurred()) goto end;
+ Py_DECREF(it); it = 0;
+
+ mpmul_init(&mm);
+ for (j = 0; j < i; j++) mpmul_add(&mm, v[j].m);
+ xx = mpmul_done(&mm);
+ for (j = 0; j < i; j++) {
+ mp_div(&y, 0, xx, v[j].m);
+ mp_gcd(&g, 0, 0, y, v[j].m);
+ if (!MP_EQ(g, MP_ONE)) VALERR("moduli must be pairwise coprime");
+ }
+
+ c = (mpcrt_pyobj *)ty->tp_alloc(ty, 0);
+ mpcrt_create(&c->c, v, i, 0);
+ mp_drop(xx); mp_drop(y); mp_drop(g);
+ return ((PyObject *)c);
+
+end:
+ if (v) {
+ while (i--) MP_DROP(v[i].m);
+ xfree(v);
+ }
+ mp_drop(xx); mp_drop(y); mp_drop(g);
+ Py_XDECREF(it);
+ return (0);
+}
+
+static PyObject *mcget_product(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(MPCRT_PY(me)->mb.m))); }
+
+static PyObject *mcget_moduli(PyObject *me, void *hunoz)
+{
+ int i;
+ PyObject *q;
+ mpcrt *c = MPCRT_PY(me);
+
+ if ((q = PyList_New(c->k)) == 0) return (0);
+ for (i = 0; i < c->k; i++)
+ PyList_SET_ITEM(q, i, mp_pywrap(c->v[i].m));
+ return (q);
+}
+
+static const PyGetSetDef mpcrt_pygetset[] = {
+#define GETSETNAME(op, name) mc##op##_##name
+ GET (product, "C.product -> product of moduli")
+ GET (moduli, "C.moduli -> list of individual moduli")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef mpcrt_pymethods[] = {
+#define METHNAME(name) mcmeth_##name
+ METH (solve, "C.solve([R0, R1]) -> X mod C.product")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject mpcrt_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "MPCRT", /* @tp_name@ */
+ sizeof(mpcrt_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mpcrt_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "MPCRT(SEQ): a context for solving Chinese Remainder Theorem problems.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(mpcrt), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(mpcrt), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ mpcrt_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Binary polynomials ------------------------------------------------*/
+
+static PyObject *gf_pyrepr(PyObject *o)
+ { return mp_topystring(MP_X(o), 16, "GF(", "0x", ")"); }
+
+static PyObject *gf_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+ mp *xx, *yy;
+ int xl, yl;
+ int b;
+
+ if (gfbinop(x, y, &xx, &yy)) RETURN_NOTIMPL;
+ switch (op) {
+ case Py_EQ: b = MP_EQ(xx, yy); break;
+ case Py_NE: b = !MP_EQ(xx, yy); break;
+ default:
+ xl = mp_bits(xx);
+ yl = mp_bits(yy);
+ switch (op) {
+ case Py_LT: b = xl < yl; break;
+ case Py_LE: b = xl <= yl; break;
+ case Py_GT: b = xl > yl; break;
+ case Py_GE: b = xl >= yl; break;
+ default: abort();
+ }
+ break;
+ }
+ MP_DROP(xx); MP_DROP(yy);
+ return (getbool(b));
+}
+
+static PyObject *gf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *x;
+ mp *z;
+ mp_pyobj *zz = 0;
+ int radix = 0;
+ static const char *const kwlist[] = { "x", "radix", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", KWLIST, &x, &radix))
+ goto end;
+ if (GF_PYCHECK(x)) RETURN_OBJ(x);
+ if (!good_radix_p(radix, 1)) VALERR("radix out of range");
+ if ((z = mp_frompyobject(x, radix)) == 0) {
+ PyErr_Format(PyExc_TypeError, "can't convert %.100s to gf",
+ Py_TYPE(x)->tp_name);
+ goto end;
+ }
+ if (MP_NEGP(z)) {
+ MP_DROP(z);
+ VALERR("gf cannot be negative");
+ }
+ zz = (mp_pyobj *)ty->tp_alloc(ty, 0);
+ zz->x = z;
+end:
+ return ((PyObject *)zz);
+}
+
+static PyObject *gf_pyexp(PyObject *x, PyObject *y, PyObject *z)
+{
+ mp *xx = 0, *yy = 0, *zz = 0;
+ mp *r = 0;
+ PyObject *rc = 0;
+
+ if ((xx = implicitgf(x)) == 0 || (yy = implicitmp(y)) == 0 ||
+ (z && z != Py_None && (zz = implicitgf(z)) == 0)) {
+ mp_drop(xx); mp_drop(yy); mp_drop(zz);
+ RETURN_NOTIMPL;
+ }
+ if (!z || z == Py_None) {
+ if (MP_NEGP(yy)) VALERR("negative exponent");
+ r = gf_exp(MP_NEW, xx, yy);
+ } else {
+ gfreduce gr;
+ if (MP_ZEROP(zz)) ZDIVERR("zero modulus");
+ if (MP_NEGP(yy)) {
+ if ((xx = gf_modinv_checked(xx, xx, zz)) == 0) goto end;
+ yy = mp_neg(yy, yy);
+ }
+ gfreduce_create(&gr, zz);
+ r = gfreduce_exp(&gr, MP_NEW, xx, yy);
+ gfreduce_destroy(&gr);
+ }
+ rc = gf_pywrap(r);
+end:
+ mp_drop(xx); mp_drop(yy); mp_drop(zz);
+ return (rc);
+}
+
+static PyObject *gfmeth_sqr(PyObject *me)
+ { return (gf_pywrap(gf_sqr(MP_NEW, MP_X(me)))); }
+
+static PyObject *gfmeth_gcd(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0, *zz = MP_NEW;
+
+ if (!PyArg_ParseTuple(arg, "O&:gcd", convgf, &yy)) goto end;
+ gf_gcd(&zz, 0, 0, MP_X(me), yy);
+ z = gf_pywrap(zz);
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *gfmeth_gcdx(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0, *zz = MP_NEW, *uu = MP_NEW, *vv = MP_NEW;
+
+ if (!PyArg_ParseTuple(arg, "O&:gcdx", convgf, &yy))
+ goto end;
+ gf_gcd(&zz, &uu, &vv, MP_X(me), yy);
+ z = Py_BuildValue("(NNN)",
+ gf_pywrap(zz), gf_pywrap(uu), gf_pywrap(vv));
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *gfmeth_modinv(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0, *zz = MP_NEW;
+
+ if (!PyArg_ParseTuple(arg, "O&:modinv", convgf, &yy) ||
+ (zz = gf_modinv_checked(MP_NEW, yy, MP_X(me))) == 0)
+ goto end;
+ z = gf_pywrap(zz);
+end:
+ if (yy) MP_DROP(yy);
+ return (z);
+}
+
+static PyObject *gfmeth_fromstring(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ int r = 0;
+ char *p;
+ Py_ssize_t len;
+ PyObject *z = 0;
+ mp *zz;
+ mptext_stringctx sc;
+ static const char *const kwlist[] = { "x", "radix", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|i:fromstring", KWLIST,
+ &p, &len, &r))
+ goto end;
+ if (!good_radix_p(r, 1)) VALERR("bad radix");
+ sc.buf = p; sc.lim = p + len;
+ if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0 ||
+ MP_NEGP(zz)) {
+ if (zz) MP_DROP(zz);
+ VALERR("bad binary polynomial");
+ }
+ z = Py_BuildValue("(Ns#)", gf_pywrap(zz),
+ sc.buf, (Py_ssize_t)(sc.lim - sc.buf));
+end:
+ return (z);
+}
+
+static PyObject *gfmeth_irreduciblep(PyObject *me)
+ { return getbool(gf_irreduciblep(MP_X(me))); }
+
+static PyObject *gfget_degree(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(mp_bits(MP_X(me)) - 1)); }
+
+static const PyGetSetDef gf_pygetset[] = {
+#define GETSETNAME(op, name) gf##op##_##name
+ GET (degree, "X.degree -> polynomial degree of X")
+#undef GETSETNAME
+#define GETSETNAME(op, name) mp##op##_##name
+ GET (nbits, "X.nbits -> bit length of X")
+ GET (noctets, "X.noctets -> octet length of X")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef gf_pymethods[] = {
+#define METHNAME(func) gfmeth_##func
+ METH (setbit, "X.setbit(N) -> X with bit N set")
+ METH (clearbit, "X.clearbit(N) -> X with bit N clear")
+ METH (testbit, "X.testbit(N) -> true/false if bit N set/clear in X")
+ NAMETH(sqr, "X.sqr() -> X^2")
+ METH (gcd, "X.gcd(Y) -> gcd(X, Y)")
+ METH (gcdx, "X.gcdx(Y) -> (gcd(X, Y), U, V) with X U + Y V = gcd(X, Y)")
+ METH (modinv, "X.modinv(Y) -> multiplicative inverse of Y mod X")
+ NAMETH(irreduciblep, "X.irreduciblep() -> true/false")
+ KWSMTH(fromstring, "fromstring(STR, [radix = 0]) -> (X, REST)\n"
+ " Parse STR as a binary polynomial, according to RADIX. If RADIX is\n"
+ " zero, read a prefix from STR to decide radix: allow `0b' for binary,\n"
+ " `0' or `0o' for octal, `0x' for hex, or `R_' for other radix R.")
+ SMTH (_implicit, 0)
+ SMTH (loadl, "loadl(STR) -> X: read little-endian bytes")
+ SMTH (loadb, "loadb(STR) -> X: read big-endian bytes")
+ SMTH (frombuf, "frombuf(STR) -> (X, REST): read buffer format")
+#undef METHNAME
+#define METHNAME(func) mpmeth_##func
+ KWMETH(tostring, "X.tostring([radix = 10]) -> STR")
+ KWMETH(storel, "X.storel([len = -1]) -> little-endian bytes")
+ KWMETH(storeb, "X.storeb([len = -1]) -> big-endian bytes")
+ KWMETH(storel2c, "X.storel2c([len = -1]) -> "
+ "little-endian bytes, two's complement")
+ KWMETH(storeb2c, "X.storeb2c([len = -1]) -> "
+ "big-endian bytes, two's complement")
+ NAMETH(tobuf, "X.tobuf() -> buffer format")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods gf_pynumber = {
+ gf_pyadd, /* @nb_add@ */
+ gf_pysub, /* @nb_subtract@ */
+ gf_pymul, /* @nb_multiply@ */
+#ifdef PY2
+ 0, /* @nb_divide@ */
+#endif
+ gf_pymod, /* @nb_remainder@ */
+ gf_pydivmod, /* @nb_divmod@ */
+ gf_pyexp, /* @nb_power@ */
+ mp_pyid, /* @nb_negative@ */
+ mp_pyid, /* @nb_positive@ */
+ mp_pyid, /* @nb_absolute@ */
+ mp_pynonzerop, /* @nb_nonzero@ */
+ 0 /* doesn't make any sense */, /* @nb_invert@ */
+ gf_pylsl, /* @nb_lshift@ */
+ gf_pylsr, /* @nb_rshift@ */
+ gf_pyand, /* @nb_and@ */
+ gf_pyxor, /* @nb_xor@ */
+ gf_pyor, /* @nb_or@ */
+#ifdef PY2
+ gf_pycoerce, /* @nb_coerce@ */
+#endif
+ mp_pyint, /* @nb_int@ */
+ PY23(mp_pylong, 0), /* @nb_long@ */
+ 0 /* doesn't make any sense */, /* @nb_float@ */
+#ifdef PY2
+ mp_pyoct, /* @nb_oct@ */
+ mp_pyhex, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+#ifdef PY2
+ 0, /* @nb_inplace_divide@ */
+#endif
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ gf_pydiv, /* @nb_floor_divide@ */
+ 0, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+
+ mp_pyint, /* @nb_index@ */
+};
+
+static const PyTypeObject gf_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GF", /* @tp_name@ */
+ sizeof(mp_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ mp_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ gf_pyrepr, /* @tp_repr@ */
+ PYNUMBER(gf), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ mp_pyhash, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ mp_pyhex, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Binary polynomials. Support almost all the standard arithmetic\n"
+ "operations.\n"
+ "\n"
+ "Constructor GF(X, [radix = R]) attempts to convert X to a `GF'. If\n"
+ "X is a string, it's read in radix-R form, or we look for a prefix\n"
+ "if R = 0. Other acceptable things are field elements, elliptic curve\n"
+ PY23(
+ "points, group elements, Python `int' and `long' objects, and anything\n"
+ "with an integer conversion.\n",
+ "points, group elements, Python `int' objects, and anything with an\n"
+ "integer conversion.\n")
+ "\n"
+ "The name is hopelessly wrong from a technical point of view, but\n"
+ "but it's much easier to type than `p2' or `c2' or whatever.\n"
+ "\n"
+ "Notes:\n"
+ "\n"
+ " * Use `//' for Euclidean division: `/' gives exact rational division.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ gf_pyrichcompare, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gf), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gf), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ gf_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Sparse poly reduction ---------------------------------------------*/
+
+static PyTypeObject *gfreduce_pytype;
+
+typedef struct gfreduce_pyobj {
+ PyObject_HEAD
+ gfreduce mr;
+} gfreduce_pyobj;
+
+#define GFREDUCE_PY(o) (&((gfreduce_pyobj *)(o))->mr)
+
+static PyObject *grmeth_exp(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *yy = 0, *zz = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:exp", convgf, &yy, convgf, &zz))
+ goto end;
+ if (MP_NEGP(zz)) {
+ if ((yy = gf_modinv_checked(yy, yy, GFREDUCE_PY(me)->p)) == 0) goto end;
+ zz = mp_neg(zz, zz);
+ }
+ rc = gf_pywrap(gfreduce_exp(GFREDUCE_PY(me), MP_NEW, yy, zz));
+end:
+ if (yy) MP_DROP(yy); if (zz) MP_DROP(zz);
+ return (rc);
+}
+
+static PyObject *grmeth_trace(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *xx = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:trace", convgf, &xx)) goto end;
+ rc = PyInt_FromLong(gfreduce_trace(GFREDUCE_PY(me), xx));
+end:
+ if (xx) MP_DROP(xx);
+ return (rc);
+}
+
+static PyObject *grmeth_halftrace(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *xx = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:halftrace", convgf, &xx)) goto end;
+ rc = gf_pywrap(gfreduce_halftrace(GFREDUCE_PY(me), MP_NEW, xx));
+end:
+ if (xx) MP_DROP(xx);
+ return (rc);
+}
+
+static PyObject *grmeth_sqrt(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *xx = 0, *yy;
+
+ if (!PyArg_ParseTuple(arg, "O&:sqrt", convgf, &xx)) goto end;
+ if ((yy = gfreduce_sqrt(GFREDUCE_PY(me), MP_NEW, xx)) == 0)
+ VALERR("no modular square root");
+ rc = gf_pywrap(yy);
+end:
+ if (xx) MP_DROP(xx);
+ return (rc);
+}
+
+static PyObject *grmeth_quadsolve(PyObject *me, PyObject *arg)
+{
+ PyObject *rc = 0;
+ mp *xx = 0, *yy;
+
+ if (!PyArg_ParseTuple(arg, "O&:quadsolve", convgf, &xx)) goto end;
+ if ((yy = gfreduce_quadsolve(GFREDUCE_PY(me), MP_NEW, xx)) == 0)
+ VALERR("no solution found");
+ rc = gf_pywrap(yy);
+end:
+ if (xx) MP_DROP(xx);
+ return (rc);
+}
+
+static PyObject *grmeth_reduce(PyObject *me, PyObject *arg)
+{
+ PyObject *z = 0;
+ mp *yy = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:reduce", convgf, &yy)) goto end;
+ z = gf_pywrap(gfreduce_do(GFREDUCE_PY(me), MP_NEW, yy));
+end:
+ return (z);
+}
+
+static void gfreduce_pydealloc(PyObject *me)
+{
+ gfreduce_destroy(GFREDUCE_PY(me));
+ FREEOBJ(me);
+}
+
+static PyObject *gfreduce_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ gfreduce_pyobj *mr = 0;
+ gfreduce r;
+ static const char *const kwlist[] = { "m", 0 };
+ mp *xx = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convgf, &xx))
+ goto end;
+ if (MP_ZEROP(xx)) ZDIVERR("modulus is zero!");
+ gfreduce_create(&r, xx);
+ mr = (gfreduce_pyobj *)ty->tp_alloc(ty, 0);
+ mr->mr = r;
+end:
+ if (xx) MP_DROP(xx);
+ return ((PyObject *)mr);
+}
+
+static PyObject *grget_m(PyObject *me, void *hunoz)
+ { return (gf_pywrap(MP_COPY(GFREDUCE_PY(me)->p))); }
+
+static const PyGetSetDef gfreduce_pygetset[] = {
+#define GETSETNAME(op, name) gr##op##_##name
+ GET (m, "R.m -> reduction polynomial")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef gfreduce_pymethods[] = {
+#define METHNAME(name) grmeth_##name
+ METH (reduce, "R.reduce(X) -> X mod B.m")
+ METH (trace, "R.trace(X) -> Tr(X) = x + x^2 + ... + x^{2^{m - 1}}")
+ METH (halftrace, "R.halftrace(X) -> x + x^{2^2} + ... + x^{2^{m - 1}}")
+ METH (sqrt, "R.sqrt(X) -> Y where Y^2 = X mod R")
+ METH (quadsolve, "R.quadsolve(X) -> Y where Y^2 + Y = X mod R")
+ METH (exp, "R.exp(X, N) -> X^N mod B.m")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gfreduce_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GFReduce", /* @tp_name@ */
+ sizeof(gfreduce_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gfreduce_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "GFReduce(N): a context for reduction modulo sparse polynomials.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gfreduce), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gfreduce), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ gfreduce_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Normal/poly transformation ----------------------------------------*/
+
+static PyTypeObject *gfn_pytype;
+
+typedef struct gfn_pyobj {
+ PyObject_HEAD
+ mp *p;
+ gfn ntop, pton;
+} gfn_pyobj;
+
+#define GFN_P(o) (((gfn_pyobj *)(o))->p)
+#define GFN_PTON(o) (&((gfn_pyobj *)(o))->pton)
+#define GFN_NTOP(o) (&((gfn_pyobj *)(o))->ntop)
+
+static PyObject *gfn_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mp *p = 0, *beta = 0;
+ gfn_pyobj *gg = 0;
+ static const char *const kwlist[] = { "p", "beta", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
+ convgf, &p, convgf, &beta))
+ goto end;
+ gg = PyObject_New(gfn_pyobj, ty);
+ gg->p = 0;
+ if (gfn_create(p, beta, &gg->ntop, &gg->pton)) {
+ Py_DECREF(gg);
+ gg = 0;
+ VALERR("can't invert transformation matrix");
+ }
+ gg->p = MP_COPY(p);
+end:
+ mp_drop(p);
+ mp_drop(beta);
+ return ((PyObject *)gg);
+}
+
+static PyObject *gfnget_p(PyObject *me, void *hunoz)
+ { return (gf_pywrap(MP_COPY(GFN_P(me)))); }
+
+static PyObject *gfnget_beta(PyObject *me, void *hunoz)
+{
+ gfn *n = GFN_NTOP(me);
+ mp *x = n->r[n->n - 1];
+ return (gf_pywrap(MP_COPY(x)));
+}
+
+#define XFORMOP(name, NAME) \
+ static PyObject *gfnmeth_##name(PyObject *me, PyObject *arg) \
+ { \
+ mp *xx = 0; \
+ mp *z = 0; \
+ \
+ if (!PyArg_ParseTuple(arg, "O&:" #name, convgf, &xx)) goto end; \
+ z = gfn_transform(GFN_##NAME(me), MP_NEW, xx); \
+ end: \
+ mp_drop(xx); \
+ if (!z) return (0); \
+ return (gf_pywrap(z)); \
+ }
+XFORMOP(pton, PTON)
+XFORMOP(ntop, NTOP)
+#undef XFORMOP
+
+static void gfn_pydealloc(PyObject *me)
+{
+ if (GFN_P(me)) {
+ MP_DROP(GFN_P(me));
+ gfn_destroy(GFN_PTON(me));
+ gfn_destroy(GFN_NTOP(me));
+ }
+ FREEOBJ(me);
+}
+
+static const PyGetSetDef gfn_pygetset[] = {
+#define GETSETNAME(op, name) gfn##op##_##name
+ GET (p, "X.p -> polynomial basis, as polynomial")
+ GET (beta, "X.beta -> normal basis element, in poly form")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef gfn_pymethods[] = {
+#define METHNAME(name) gfnmeth_##name
+ METH (pton, "X.pton(A) -> normal-basis representation of A")
+ METH (ntop, "X.ntop(A) -> polynomial-basis representation of A")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gfn_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GFN", /* @tp_name@ */
+ sizeof(gfn_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gfn_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "GFN(P, BETA): an object for transforming elements of binary fields\n"
+ " between polynomial and normal basis representations.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gfn), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gfn), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ gfn_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Glue --------------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(MPW_MAX),
+ { 0 }
+};
+
+void mp_pyinit(void)
+{
+ INITTYPE(mp, root);
+ INITTYPE(gf, root);
+ INITTYPE(mpmul, root);
+ INITTYPE(mpmont, root);
+ INITTYPE(mpbarrett, root);
+ INITTYPE(mpreduce, root);
+ INITTYPE(mpcrt, root);
+ INITTYPE(gfreduce, root);
+ INITTYPE(gfn, root);
+}
+
+void mp_pyinsert(PyObject *mod)
+{
+ INSERT("MP", mp_pytype);
+ INSERT("MPMul", mpmul_pytype);
+ INSERT("MPMont", mpmont_pytype);
+ INSERT("MPBarrett", mpbarrett_pytype);
+ INSERT("MPReduce", mpreduce_pytype);
+ INSERT("MPCRT", mpcrt_pytype);
+ INSERT("GF", gf_pytype);
+ INSERT("GFReduce", gfreduce_pytype);
+ INSERT("GFN", gfn_pytype);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Reading and writing passphrases
+ *
+ * (c) 2005 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Pixie stuff -------------------------------------------------------*/
+
+typedef struct pixie_pyobj {
+ PyObject_HEAD
+ int fd;
+} pixie_pyobj;
+
+static PyTypeObject *pixie_pytype;
+#define PIXIE_PYCHECK(o) PyObject_TypeCheck((o), pixie_pytype)
+#define PIXIE_FD(o) (((pixie_pyobj *)(o))->fd)
+
+#ifdef WANT_CONVPIXIE
+static int convpixie(PyObject *o, void *p)
+{
+ if (!PIXIE_PYCHECK(o))
+ TYERR("want pixie");
+ *(int *)p = PIXIE_FD(o);
+ return (1);
+end:
+ return (0);
+}
+#endif
+
+static PyObject *pixie_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ pixie_pyobj *rc = 0;
+ static const char *const kwlist[] = { "socket", 0 };
+ char *sock = 0;
+ int fd;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s:new", KWLIST, &sock))
+ goto end;
+ if ((fd = pixie_open(sock)) < 0)
+ OSERR(sock);
+ rc = (pixie_pyobj *)ty->tp_alloc(ty, 0);
+ rc->fd = fd;
+end:
+ return ((PyObject *)rc);
+}
+
+static void pixie_pydealloc(PyObject *me)
+{
+ close(PIXIE_FD(me));
+ FREEOBJ(me);
+}
+
+static PyObject *pixmeth_read(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ unsigned mode = PMODE_READ;
+ char *tag;
+ static const char *const kwlist[] = { "tag", "mode", 0 };
+ PyObject *rc = 0;
+ int r;
+ char buf[1024];
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:read", KWLIST,
+ &tag, convuint, &mode))
+ goto end;
+ r = pixie_read(PIXIE_FD(me), tag, mode, buf, sizeof(buf));
+ if (r < 0)
+ OSERR(0);
+ else if (r > 0)
+ RETURN_NONE;
+ else
+ rc = BIN_FROMSTR(buf);
+end:
+ return (rc);
+}
+
+static PyObject *pixmeth_set(PyObject *me, PyObject *arg)
+{
+ char *tag;
+ char *phrase;
+
+ if (!PyArg_ParseTuple(arg, "s"Y":set", &tag, &phrase))
+ return (0);
+ pixie_set(PIXIE_FD(me), tag, phrase);
+ RETURN_ME;
+}
+
+static PyObject *pixmeth_cancel(PyObject *me, PyObject *arg)
+{
+ char *tag;
+
+ if (!PyArg_ParseTuple(arg, "s:cancel", &tag))
+ return (0);
+ pixie_cancel(PIXIE_FD(me), tag);
+ RETURN_ME;
+}
+
+static const PyMethodDef pixie_pymethods[] = {
+#define METHNAME(name) pixmeth_##name
+ KWMETH(read, "P.read(TAG, [mode = PMODE_READ]) -> STRING")
+ METH (set, "P.set(TAG, PHRASE)")
+ METH (cancel, "P.cancel(TAG)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject pixie_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Pixie", /* @tp_name@ */
+ sizeof(pixie_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ pixie_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Pixie([socket = ?]): passphrase pixie connection.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(pixie), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ pixie_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(PMODE_READ), CONST(PMODE_VERIFY),
+ { 0 }
+};
+
+static PyObject *meth_ppread(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ char *tag;
+ unsigned f = PMODE_READ;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "tag", "mode", 0 };
+ char buf[1024];
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:ppread", KWLIST,
+ &tag, convuint, &f))
+ goto end;
+ if (passphrase_read(tag, f, buf, sizeof(buf)))
+ SYSERR("passphrase read failed");
+ rc = BIN_FROMSTR(buf);
+end:
+ return (rc);
+}
+
+static PyObject *meth_ppcancel(PyObject *me, PyObject *arg)
+{
+ char *tag;
+
+ if (!PyArg_ParseTuple(arg, "s:ppcancel", &tag))
+ return (0);
+ passphrase_cancel(tag);
+ RETURN_NONE;
+}
+
+static PyObject *meth_getpass(PyObject *me, PyObject *arg)
+{
+ char *prompt;
+ char buf[1024];
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "s:getpass", &prompt))
+ goto end;
+ if (pixie_getpass(prompt, buf, sizeof(buf)))
+ OSERR(0);
+ rc = BIN_FROMSTR(buf);
+end:
+ return (rc);
+}
+
+static const PyMethodDef methods[] = {
+#define METHNAME(name) meth_##name
+ KWMETH(ppread, "ppread(TAG, [mode = PMODE_READ]) -> STRING")
+ METH (ppcancel, "ppcancel(TAG)")
+ METH (getpass, "getpass(PROMPT) -> STRING")
+#undef METHNAME
+ { 0 }
+};
+
+void passphrase_pyinit(void)
+{
+ INITTYPE(pixie, root);
+ addmethods(methods);
+}
+
+void passphrase_pyinsert(PyObject *mod)
+{
+ INSERT("Pixie", pixie_pytype);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Prime number generation
+ *
+ * (c) 2005 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Filters -----------------------------------------------------------*/
+
+PyTypeObject *pfilt_pytype;
+
+static PyObject *pfilt_pywrap(pfilt *f)
+{
+ pfilt_pyobj *o = 0;
+ o = PyObject_New(pfilt_pyobj, pfilt_pytype);
+ o->f = *f;
+ o->st = pfilt_step(f, 0);
+ return ((PyObject *)o);
+}
+
+static PyObject *pfilt_pymake(PyTypeObject *ty, PyObject *xobj)
+{
+ mp *x = 0;
+ pfilt_pyobj *o = 0;
+
+ if (PFILT_PYCHECK(xobj)) RETURN_OBJ(xobj);
+ if ((x = getmp(xobj)) == 0) goto end;
+ o = (pfilt_pyobj *)ty->tp_alloc(ty, 0);
+ o->st = pfilt_create(&o->f, x);
+end:
+ mp_drop(x);
+ return ((PyObject *)o);
+}
+
+static PyObject *pfilt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "x", 0 };
+ PyObject *xobj;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &xobj))
+ return (0);
+ return (pfilt_pymake(ty, xobj));
+}
+
+static void pfilt_pydealloc(PyObject *me)
+ { pfilt_destroy(PFILT_F(me)); FREEOBJ(me); }
+
+static PyObject *pfmeth_step(PyObject *me, PyObject *arg)
+{
+ mpw x;
+
+ if (!PyArg_ParseTuple(arg, "O&:step", convmpw, &x)) return (0);
+ PFILT_ST(me) = pfilt_step(PFILT_F(me), x);
+ RETURN_ME;
+}
+
+static PyObject *pfmeth_muladd(PyObject *me, PyObject *arg)
+{
+ mpw m, a;
+ pfilt_pyobj *o = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&O&:muladd", convmpw, &m, convmpw, &a))
+ return (0);
+ o = PyObject_New(pfilt_pyobj, pfilt_pytype);
+ o->st = pfilt_muladd(&o->f, PFILT_F(me), m, a);
+ return ((PyObject *)o);
+}
+
+static CONVFUNC(pfilt, pfilt *, PFILT_F)
+
+static PyObject *pfmeth_jump(PyObject *me, PyObject *arg)
+{
+ pfilt *f;
+
+ if (!PyArg_ParseTuple(arg, "O&:jump", convpfilt, &f)) return (0);
+ PFILT_ST(me) = pfilt_jump(PFILT_F(me), f);
+ RETURN_ME;
+}
+
+static PyObject *pfmeth_smallfactor(PyObject *me, PyObject *arg)
+{
+ mp *x = 0;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:smallfactor", convmp, &x)) goto end;
+ rc = PyInt_FromLong(pfilt_smallfactor(x));
+end:
+ mp_drop(x);
+ return (rc);
+}
+
+static int pfilt_pynonzerop(PyObject *me)
+ { return (PFILT_ST(me) != PGEN_FAIL); }
+
+static PyObject *pfilt_pyint(PyObject *me)
+{
+ long l;
+ PyObject *rc = 0;
+
+ if (!mp_tolong_checked(PFILT_F(me)->m, &l, 0)) rc = PyInt_FromLong(l);
+ else rc = mp_topylong(PFILT_F(me)->m);
+ return (rc);
+}
+
+#ifdef PY2
+static PyObject *pfilt_pylong(PyObject *me)
+ { return (mp_topylong(PFILT_F(me)->m)); }
+#endif
+
+static PyObject *pfget_x(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(PFILT_F(me)->m))); }
+
+static PyObject *pfget_status(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(PFILT_ST(me))); }
+
+static const PyGetSetDef pfilt_pygetset[] = {
+#define GETSETNAME(op, name) pf##op##_##name
+ GET (x, "F.x -> current position of filter")
+ GET (status, "F.status -> primality status of filter")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef pfilt_pymethods[] = {
+#define METHNAME(name) pfmeth_##name
+ METH (step, "F.step(N)")
+ METH (muladd, "F.muladd(M, A)")
+ METH (jump, "F.jump(FF)")
+ SMTH (smallfactor, "smallfactor(X) -> PGST")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyNumberMethods pfilt_pynumber = {
+ 0, /* @nb_add@ */
+ 0, /* @nb_subtract@ */
+ 0, /* @nb_multiply@ */
+#ifdef PY2
+ 0, /* @nb_divide@ */
+#endif
+ 0, /* @nb_remainder@ */
+ 0, /* @nb_divmod@ */
+ 0, /* @nb_power@ */
+ 0, /* @nb_negative@ */
+ 0, /* @nb_positive@ */
+ 0, /* @nb_absolute@ */
+ pfilt_pynonzerop, /* @nb_nonzero@ */
+ 0, /* @nb_invert@ */
+ 0, /* @nb_lshift@ */
+ 0, /* @nb_rshift@ */
+ 0, /* @nb_and@ */
+ 0, /* @nb_xor@ */
+ 0, /* @nb_or@ */
+#ifdef PY2
+ 0, /* @nb_coerce@ */
+#endif
+ pfilt_pyint, /* @nb_int@ */
+ PY23(pfilt_pylong, 0), /* @nb_long@ */
+ 0, /* @nb_float@ */
+#ifdef PY2
+ 0, /* @nb_oct@ */
+ 0, /* @nb_hex@ */
+#endif
+
+ 0, /* @nb_inplace_add@ */
+ 0, /* @nb_inplace_subtract@ */
+ 0, /* @nb_inplace_multiply@ */
+#ifdef PY2
+ 0, /* @nb_inplace_divide@ */
+#endif
+ 0, /* @nb_inplace_remainder@ */
+ 0, /* @nb_inplace_power@ */
+ 0, /* @nb_inplace_lshift@ */
+ 0, /* @nb_inplace_rshift@ */
+ 0, /* @nb_inplace_and@ */
+ 0, /* @nb_inplace_xor@ */
+ 0, /* @nb_inplace_or@ */
+
+ 0, /* @nb_floor_divide@ */
+ 0, /* @nb_true_divide@ */
+ 0, /* @nb_inplace_floor_divide@ */
+ 0, /* @nb_inplace_true_divide@ */
+};
+
+static const PyTypeObject pfilt_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeFilter", /* @tp_name@ */
+ sizeof(pfilt_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ pfilt_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ PYNUMBER(pfilt), /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeFilter(X): small-primes filter.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(pfilt), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(pfilt), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ pfilt_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Rabin-Miller testing ----------------------------------------------*/
+
+typedef struct rabin_pyobj {
+ PyObject_HEAD
+ rabin r;
+} rabin_pyobj;
+
+static PyTypeObject *rabin_pytype;
+#define RABIN_R(o) (&((rabin_pyobj *)(o))->r)
+
+static PyObject *rabin_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mp *x = 0;
+ rabin_pyobj *o = 0;
+ static const char *const kwlist[] = { "x", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &x))
+ goto end;
+ if (!MP_POSP(x) || MP_EVENP(x)) VALERR("must be positive and odd");
+ o = (rabin_pyobj *)ty->tp_alloc(ty, 0);
+ rabin_create(&o->r, x);
+end:
+ return ((PyObject *)o);
+}
+
+static void rabin_pydealloc(PyObject *me)
+{
+ rabin_destroy(RABIN_R(me));
+ FREEOBJ(me);
+}
+
+static PyObject *rmeth_test(PyObject *me, PyObject *arg)
+{
+ mp *w = 0;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:test", convmp, &w)) goto end;
+ rc = PyInt_FromLong(rabin_test(RABIN_R(me), w));
+end:
+ mp_drop(w);
+ return (rc);
+}
+
+static PyObject *rmeth_rtest(PyObject *me, PyObject *arg)
+{
+ mp *w = 0;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:rtest", convmp, &w)) goto end;
+ rc = PyInt_FromLong(rabin_rtest(RABIN_R(me), w));
+end:
+ mp_drop(w);
+ return (rc);
+}
+
+static PyObject *rget_niters(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(rabin_iters(mp_bits(RABIN_R(me)->mm.m)))); }
+
+static PyObject *rget_x(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(RABIN_R(me)->mm.m))); }
+
+static PyObject *rmeth_iters(PyObject *me, PyObject *arg)
+{
+ unsigned n;
+
+ if (!PyArg_ParseTuple(arg, "O&:iters", convuint, &n)) return (0);
+ return (PyInt_FromLong(rabin_iters(n)));
+}
+
+static const PyGetSetDef rabin_pygetset[] = {
+#define GETSETNAME(op, name) r##op##_##name
+ GET (x, "R.x -> number under test")
+ GET (niters, "R.niters -> suggested number of tests")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef rabin_pymethods[] = {
+#define METHNAME(name) rmeth_##name
+ METH (test, "R.test(W) -> PGST")
+ METH (rtest, "R.rtest(W) -> PGST")
+ SMTH (iters, "iters(NBITS) -> NITERS")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject rabin_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "RabinMiller", /* @tp_name@ */
+ sizeof(rabin_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ rabin_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "RabinMiller(X): Rabin-Miller strong primality test.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(rabin), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(rabin), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ rabin_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Events ------------------------------------------------------------*/
+
+typedef struct pgevent_pyobj {
+ PyObject_HEAD
+ PyObject *r;
+ pgen_event *ev;
+} pgevent_pyobj;
+
+static PyTypeObject *pgevent_pytype;
+#define PGEVENT_EV(o) (((pgevent_pyobj *)(o))->ev)
+
+static PyObject *pgevent_pywrap(pgen_event *ev)
+{
+ pgevent_pyobj *o = PyObject_New(pgevent_pyobj, pgevent_pytype);
+ o->ev = ev; o->r = 0;
+ return ((PyObject *)o);
+}
+
+static CONVFUNC(pgevent, pgen_event *, PGEVENT_EV)
+
+static void pgevent_kill(PyObject *me)
+{
+ pgevent_pyobj *ev = (pgevent_pyobj *)me;
+
+ ev->ev = 0;
+ if (ev->r) GRAND_R(ev->r) = 0;
+}
+
+static void pgevent_pydealloc(PyObject *me)
+{
+ pgevent_pyobj *ev = (pgevent_pyobj *)me;
+ Py_XDECREF(ev->r); FREEOBJ(me);
+}
+
+#define PGEVENT_CHECK(me) do { \
+ if (!PGEVENT_EV(me)) { \
+ PyErr_SetString(PyExc_ValueError, "event object is no longer valid"); \
+ return (0); \
+ } \
+} while (0)
+
+static PyObject *peget_name(PyObject *me, void *hunoz)
+ { PGEVENT_CHECK(me); return (TEXT_FROMSTR(PGEVENT_EV(me)->name)); }
+
+static PyObject *peget_x(PyObject *me, void *hunoz)
+ { PGEVENT_CHECK(me); return (mp_pywrap(MP_COPY(PGEVENT_EV(me)->m))); }
+
+static PyObject *peget_steps(PyObject *me, void *hunoz)
+ { PGEVENT_CHECK(me); return (PyInt_FromLong(PGEVENT_EV(me)->steps)); }
+
+static PyObject *peget_tests(PyObject *me, void *hunoz)
+ { PGEVENT_CHECK(me); return (PyInt_FromLong(PGEVENT_EV(me)->tests)); }
+
+static PyObject *peget_rng(PyObject *me, void *hunoz)
+{
+ pgevent_pyobj *ev = (pgevent_pyobj *)me;
+
+ PGEVENT_CHECK(me);
+ if (!ev->r) ev->r = grand_pywrap(ev->ev->r, 0);
+ Py_INCREF(ev->r); return ((PyObject *)ev->r);
+}
+
+static int peset_x(PyObject *me, PyObject *xobj, void *hunoz)
+{
+ mp *x = 0;
+ pgen_event *ev = PGEVENT_EV(me);
+ int rc = -1;
+ if (!xobj) NIERR("__del__");
+ PGEVENT_CHECK(me);
+ if ((x = getmp(xobj)) == 0) goto end;
+ mp_drop(ev->m);
+ ev->m = MP_COPY(x);
+ rc = 0;
+end:
+ mp_drop(x);
+ return (rc);
+}
+
+static const PyGetSetDef pgevent_pygetset[] = {
+#define GETSETNAME(op, name) pe##op##_##name
+ GET (name, "EV.name -> value being generated")
+ GETSET(x, "EV.x -> value under test")
+ GET (steps, "EV.steps -> number of steps left")
+ GET (tests, "EV.tests -> tests before passing")
+ GET (rng, "EV.rng -> (noncrypto) random number generator")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject pgevent_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeGenEvent", /* @tp_name@ */
+ sizeof(pgevent_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ pgevent_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Prime-generation event.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(pgevent), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Event handlers ----------------------------------------------------*/
+
+PyTypeObject *pgev_pytype;
+
+typedef struct pgstep_pyobj {
+ PGEV_HEAD
+ pgen_filterctx f;
+} pgstep_pyobj;
+
+static PyTypeObject *pgstep_pytype;
+#define PGSTEP_STEP(o) (((pgstep_pyobj *)(o))->f.step)
+
+typedef struct pgjump_pyobj {
+ PGEV_HEAD
+ PyObject *fobj;
+ pgen_jumpctx j;
+} pgjump_pyobj;
+
+static PyTypeObject *pgjump_pytype;
+#define PGJUMP_FOBJ(o) (((pgjump_pyobj *)(o))->fobj)
+#define PGJUMP_J(o) (&((pgjump_pyobj *)(o))->j)
+
+typedef struct pgtest_pyobj {
+ PGEV_HEAD
+ rabin r;
+} pgtest_pyobj;
+
+static PyTypeObject *pgtest_pytype;
+
+static int pgev_python(int rq, pgen_event *ev, void *p)
+{
+ pypgev *pg = p;
+ PyObject *pyev = 0;
+ PyObject *rc = 0;
+ int st = PGEN_ABORT;
+ long l;
+ static const char *const meth[] =
+ { "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass" };
+
+ rq++;
+ if (rq > N(meth)) SYSERR("event code out of range");
+ pyev = pgevent_pywrap(ev);
+ if ((rc = PyObject_CallMethod(pg->obj, (/*unconst*/ char *)meth[rq],
+ "(O)", pyev)) == 0)
+ goto end;
+ if (rc == Py_None)
+ st = PGEN_TRY;
+ else if ((l = PyInt_AsLong(rc)) == -1 && PyErr_Occurred())
+ goto end;
+ else if (l < PGEN_ABORT || l > PGEN_PASS)
+ VALERR("return code out of range");
+ else
+ st = l;
+end:
+ if (PyErr_Occurred())
+ stash_exception(pg->exc, "exception from `pgen' handler");
+ if (pyev) {
+ pgevent_kill(pyev);
+ Py_DECREF(pyev);
+ }
+ Py_XDECREF(rc);
+ return (st);
+}
+
+static PyObject *pgev_pywrap(const pgev *pg)
+{
+ pgev_pyobj *o;
+
+ o = PyObject_New(pgev_pyobj, pgev_pytype);
+ o->pg = *pg;
+ return ((PyObject *)o);
+}
+
+int convpgev(PyObject *o, void *p)
+{
+ pypgev *pg = p;
+
+ if (PGEV_PYCHECK(o))
+ pg->ev = *PGEV_PG(o);
+ else {
+ pg->ev.proc = pgev_python;
+ pg->ev.ctx = pg;
+ pg->obj = o; Py_INCREF(o);
+ }
+ return (1);
+}
+
+void droppgev(pypgev *pg)
+{
+ if (pg->ev.proc == pgev_python)
+ { assert(pg->ev.ctx == pg); Py_DECREF(pg->obj); }
+}
+
+static PyObject *pgmeth_common(PyObject *me, PyObject *arg, int rq)
+{
+ pgen_event *ev;
+ pgev *pg = PGEV_PG(me);
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&", convpgevent, &ev)) goto end;
+ rc = PyInt_FromLong(!pg->proc ? rq : pg->proc(rq, ev, pg->ctx));
+end:
+ return (rc);
+}
+
+#define PGMETH(lc, uc) \
+ static PyObject *pgmeth_pg_##lc(PyObject *me, PyObject *arg) \
+ { return pgmeth_common(me, arg, PGEN_##uc); }
+PGMETH(abort, ABORT)
+PGMETH(done, DONE)
+PGMETH(begin, BEGIN)
+PGMETH(try, TRY)
+PGMETH(pass, PASS)
+PGMETH(fail, FAIL)
+#undef PGMETH
+
+static PyObject *pgev_stdev(pgen_proc *proc)
+ { pgev pg; pg.proc = proc; pg.ctx = 0; return (pgev_pywrap(&pg)); }
+
+static const PyMethodDef pgev_pymethods[] = {
+#define METHNAME(name) pgmeth_##name
+ METH (pg_abort, "E.pg_abort(EV) -> PGST -- prime generation aborted")
+ METH (pg_done, "E.pg_done(EV) -> PGST -- prime generation finished")
+ METH (pg_begin, "E.pg_begin(EV) -> PGST -- commence stepping/testing")
+ METH (pg_try, "E.pg_try(EV) -> PGST -- found new candidate")
+ METH (pg_pass, "E.pg_pass(EV) -> PGST -- passed primality test")
+ METH (pg_fail, "E.pg_fail(EV) -> PGST -- failed primality test")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject pgev_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeGenBuiltinHandler", /* @tp_name@ */
+ sizeof(pgev_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Built-in prime-generation event handler, base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(pgev), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *pgstep_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mpw s;
+ pgstep_pyobj *rc = 0;
+ static const char *const kwlist[] = { "step", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmpw, &s))
+ goto end;
+ rc = (pgstep_pyobj *)ty->tp_alloc(ty, 0);
+ rc->f.step = s;
+ rc->pg.proc = pgen_filter;
+ rc->pg.ctx = &rc->f;
+end:
+ return ((PyObject *)rc);
+}
+
+static PyObject *psget_step(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(PGSTEP_STEP(me))); }
+
+static const PyGetSetDef pgstep_pygetset[] = {
+#define GETSETNAME(op, name) ps##op##_##name
+ GET (step, "S.step -> step size for the stepper")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject pgstep_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeGenStepper", /* @tp_name@ */
+ sizeof(pgstep_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeGenStepper(STEP): simple stepper with small-factors filter.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(pgstep), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ pgstep_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *pgjump_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ PyObject *o, *fobj;
+ pgjump_pyobj *rc = 0;
+ static const char *const kwlist[] = { "jump", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &o) ||
+ (fobj = pfilt_pymake(pfilt_pytype, o)) == 0)
+ goto end;
+ rc = (pgjump_pyobj *)ty->tp_alloc(ty, 0);
+ rc->fobj = fobj;
+ rc->j.j = PFILT_F(fobj);
+ rc->pg.proc = pgen_jump;
+ rc->pg.ctx = &rc->j;
+end:
+ return ((PyObject *)rc);
+}
+
+static void pgjump_pydealloc(PyObject *me)
+{
+ Py_DECREF(PGJUMP_FOBJ(me));
+ FREEOBJ(me);
+}
+
+static PyObject *pjget_jump(PyObject *me, void *hunoz)
+ { RETURN_OBJ(PGJUMP_FOBJ(me)); }
+
+static const PyGetSetDef pgjump_pygetset[] = {
+#define GETSETNAME(op, name) pj##op##_##name
+ GET (jump, "S.jump -> jump size for the stepper")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject pgjump_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeGenJumper", /* @tp_name@ */
+ sizeof(pgjump_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ pgjump_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeGenJumper(JUMP): "
+ "stepper for larger steps with small-factors filter.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(pgjump), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ pgjump_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *pgtest_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ pgtest_pyobj *rc = 0;
+ static const char *const kwlist[] = { 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
+ rc = (pgtest_pyobj *)ty->tp_alloc(ty, 0);
+ rc->pg.proc = pgen_test;
+ rc->pg.ctx = &rc->r;
+end:
+ return ((PyObject *)rc);
+}
+
+static const PyTypeObject pgtest_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeGenTester", /* @tp_name@ */
+ sizeof(pgtest_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeGenTester(): Rabin-Miller tester.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ pgtest_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Prime iteration ---------------------------------------------------*/
+
+static PyTypeObject *piter_pytype;
+
+typedef struct piter_pyobj {
+ PyObject_HEAD
+ primeiter i;
+} piter_pyobj;
+#define PITER_I(o) (&((piter_pyobj *)(o))->i)
+
+static PyObject *piter_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ piter_pyobj *rc;
+ mp *n = 0;
+ static const char *const kwlist[] = { "start", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convmp, &n))
+ return (0);
+ rc = (piter_pyobj *)ty->tp_alloc(ty, 0);
+ primeiter_create(&rc->i, n);
+ return ((PyObject *)rc);
+}
+
+static void piter_pydealloc(PyObject *me)
+ { primeiter_destroy(PITER_I(me)); FREEOBJ(me); }
+
+static PyObject *piter_pynext(PyObject *me)
+ { return (mp_pywrap(primeiter_next(PITER_I(me), 0))); }
+
+static const PyTypeObject piter_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "PrimeIter", /* @tp_name@ */
+ sizeof(piter_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ piter_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "PrimeIter([start = N]): Prime-number iterator.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ PyObject_SelfIter, /* @tp_iter@ */
+ piter_pynext, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ piter_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Prime generation functions ----------------------------------------*/
+
+void pgenerr(struct excinfo *exc)
+{
+ if (exc->ty) RESTORE_EXCINFO(exc);
+ else PyErr_SetString(PyExc_ValueError, "prime generation failed");
+}
+
+static PyObject *meth_pgen(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ mp *x = 0;
+ mp *r = 0;
+ PyObject *rc = 0;
+ char *p = "p";
+ pgen_filterctx fc = { 2 };
+ rabin tc;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev step = { { 0 } }, test = { { 0 } }, evt = { { 0 } };
+ unsigned nsteps = 0, ntests = 0;
+ static const char *const kwlist[] =
+ { "start", "name", "stepper", "tester", "event", "nsteps", "ntests", 0 };
+
+ step.exc = &exc; step.ev.proc = pgen_filter; step.ev.ctx = &fc;
+ test.exc = &exc; test.ev.proc = pgen_test; test.ev.ctx = &tc;
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&O&O&:pgen", KWLIST,
+ convmp, &x, &p, convpgev, &step,
+ convpgev, &test, convpgev, &evt,
+ convuint, &nsteps, convuint, &ntests))
+ goto end;
+ if (!ntests) ntests = rabin_iters(mp_bits(x));
+ if ((r = pgen(p, MP_NEW, x, evt.ev.proc, evt.ev.ctx,
+ nsteps, step.ev.proc, step.ev.ctx,
+ ntests, test.ev.proc, test.ev.ctx)) == 0)
+ PGENERR(&exc);
+ rc = mp_pywrap(r); r = 0;
+end:
+ mp_drop(r); mp_drop(x);
+ droppgev(&step); droppgev(&test); droppgev(&evt);
+ return (rc);
+}
+
+static PyObject *meth_strongprime_setup(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ mp *x = 0;
+ pfilt f;
+ grand *r = &rand_global;
+ unsigned nbits;
+ char *name = "p";
+ unsigned n = 0;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ PyObject *rc = 0;
+ static const char *const kwlist[] =
+ { "nbits", "name", "event", "rng", "nsteps", 0 };
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
+ convuint, &nbits, &name,
+ convpgev, &evt, convgrand, &r,
+ convuint, &n))
+ goto end;
+ if ((x = strongprime_setup(name, MP_NEW, &f, nbits,
+ r, n, evt.ev.proc, evt.ev.ctx)) == 0)
+ PGENERR(&exc);
+ rc = Py_BuildValue("(NN)", mp_pywrap(x), pfilt_pywrap(&f));
+ x = 0;
+end:
+ mp_drop(x);
+ droppgev(&evt);
+ return (rc);
+}
+
+static PyObject *meth_strongprime(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ mp *x = 0;
+ grand *r = &rand_global;
+ unsigned nbits;
+ char *name = "p";
+ unsigned n = 0;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ PyObject *rc = 0;
+ static const char *const kwlist[] =
+ { "nbits", "name", "event", "rng", "nsteps", 0 };
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
+ convuint, &nbits, &name,
+ convpgev, &evt, convgrand, &r,
+ convuint, &n))
+ goto end;
+ if ((x = strongprime(name, MP_NEW, nbits,
+ r, n, evt.ev.proc, evt.ev.ctx)) == 0)
+ PGENERR(&exc);
+ rc = mp_pywrap(x);
+ x = 0;
+end:
+ mp_drop(x);
+ droppgev(&evt);
+ return (rc);
+}
+
+static PyObject *meth_limlee(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ char *p = "p";
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev ie = { { 0 } }, oe = { { 0 } };
+ unsigned ql, pl;
+ grand *r = &rand_global;
+ unsigned on = 0;
+ size_t i, nf = 0;
+ PyObject *rc = 0, *vec;
+ static const char *const kwlist[] =
+ { "pbits", "qbits", "name", "event", "ievent", "rng", "nsteps", 0 };
+ mp *x = 0, **v = 0;
+
+ ie.exc = oe.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|sO&O&O&O&:limlee", KWLIST,
+ convuint, &pl, convuint, &ql,
+ &p, convpgev, &oe, convpgev, &ie,
+ convgrand, &r, convuint, &on))
+ goto end;
+ if ((x = limlee(p, MP_NEW, MP_NEW, ql, pl, r, on,
+ oe.ev.proc, oe.ev.ctx, ie.ev.proc, ie.ev.ctx,
+ &nf, &v)) == 0)
+ PGENERR(&exc);;
+ vec = PyList_New(nf);
+ for (i = 0; i < nf; i++)
+ PyList_SET_ITEM(vec, i, mp_pywrap(v[i]));
+ xfree(v);
+ rc = Py_BuildValue("(NN)", mp_pywrap(x), vec);
+end:
+ droppgev(&oe); droppgev(&ie);
+ return (rc);
+}
+
+/*----- Global stuff ------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(PGEN_PASS), CONST(PGEN_FAIL), CONST(PGEN_BEGIN), CONST(PGEN_TRY),
+ CONST(PGEN_DONE), CONST(PGEN_ABORT),
+ { 0 }
+};
+
+static const PyMethodDef methods[] = {
+#define METHNAME(name) meth_##name
+ KWMETH(pgen,
+ "pgen(START, [name = 'p'], [stepper = PrimeGenStepper(2)],\n"
+ " [tester = PrimeGenTester()], [event = pgen_nullev],\n"
+ " [nsteps = 0], [ntests = RabinMiller.iters(START.nbits)]) -> P")
+ KWMETH(strongprime_setup,
+ "strongprime_setup(NBITS, [name = 'p'], [event = pgen_nullev],\n"
+ " [rng = rand], [nsteps = 0]) -> (START, JUMP)")
+ KWMETH(strongprime,
+ "strongprime(NBITS, [name = 'p'], [event = pgen_nullev],\n"
+ " [rng = rand], [nsteps = 0]) -> P")
+ KWMETH(limlee,
+ "limlee(PBITS, QBITS, [name = 'p'], [event = pgen_nullev],\n"
+ " [ievent = pgen_nullev], [rng = rand], [nsteps = 0]) "
+ "-> (P, [Q, ...])")
+#undef METHNAME
+ { 0 }
+};
+
+void pgen_pyinit(void)
+{
+ INITTYPE(pfilt, root);
+ INITTYPE(rabin, root);
+ INITTYPE(pgevent, root);
+ INITTYPE(pgev, root);
+ INITTYPE(pgstep, pgev);
+ INITTYPE(pgjump, pgev);
+ INITTYPE(pgtest, pgev);
+ INITTYPE(piter, root);
+ addmethods(methods);
+}
+
+static PyObject *obj;
+
+void pgen_pyinsert(PyObject *mod)
+{
+ INSERT("PrimeFilter", pfilt_pytype);
+ INSERT("RabinMiller", rabin_pytype);
+ INSERT("PrimeGenEvent", pgevent_pytype);
+ INSERT("PrimeGenBuiltinHandler", pgev_pytype);
+ INSERT("PrimeGenStepper", pgstep_pytype);
+ INSERT("PrimeGenJumper", pgjump_pytype);
+ INSERT("PrimeGenTester", pgtest_pytype);
+ INSERT("pgen_nullev", obj = pgev_stdev(0));
+ INSERT("pgen_stdev", pgev_stdev(pgen_ev));
+ INSERT("pgen_spinev", pgev_stdev(pgen_evspin));
+ INSERT("pgen_subev", pgev_stdev(pgen_subev));
+ INSERT("PrimeIter", piter_pytype);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+#! /usr/bin/python
+### -*- mode: python, coding: utf-8 -*-
+###
+### Tool for generating and verifying primality certificates
+###
+### (c) 2017 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 sys as SYS; from sys import argv, stdin, stdout, stderr
+import os as OS
+import itertools as I
+import math as M
+import optparse as OP
+
+import catacomb as C
+
+if SYS.version_info >= (3,):
+ xrange = range
+ range = lambda *args: list(xrange(*args))
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+def _excval():
+ """Return the most recent exception object."""
+ return SYS.exc_info()[1]
+
+class ExpectedError (Exception):
+ """
+ I represent an expected error, which should be reported in a friendly way.
+ """
+ pass
+
+def prod(ff, one = 1):
+ """Return ONE times the product of the elements of FF."""
+ return C.MPMul().factor(one).factor(ff).done()
+
+def parse_label(line):
+ """
+ Split LINE at an `=' character and return the left and right halves.
+
+ The returned pieces have leading and trailing whitespace trimmed.
+ """
+ eq = line.find('=')
+ if eq < 0: raise ExpectedError('expected `LABEL = ...\'')
+ return line[:eq].strip(), line[eq + 1:].strip()
+
+def parse_list(s, n):
+ l = s.split(',', n - 1)
+ if n is not None and len(l) != n:
+ raise ExpectedError('expected `,\'-separated list of %d items' % n)
+ return l
+
+def conv_int(s):
+ """Convert S to a integer."""
+ try: return C.MP(s, 0)
+ except TypeError: raise ExpectedError('invalid integer `%s\'' % s)
+
+VERBOSITY = 1
+
+class ProgressReporter (object):
+ """
+ I keep users amused while the program looks for large prime numbers.
+
+ My main strategy is the printing of incomprehensible runes. I can be
+ muffled by lowering by verbosity level.
+
+ Prime searches are recursive in nature. When a new recursive level is
+ started, call `push'; and call `pop' when the level is finished. This must
+ be done around the top level too.
+ """
+ def __init__(me):
+ """Initialize the ProgressReporter."""
+ me._level = -1
+ me._update()
+ def _update(me):
+ """
+ Update our idea of whether we're active.
+
+ We don't write inscrutable runes when inactive. The current policy is to
+ write nothing if verbosity is zero, to write runes for the top level only
+ if verbosity is 1, and to write runes always if verbosity is higher than
+ that.
+ """
+ me._active = VERBOSITY >= 2 or (VERBOSITY == 1 and me._level == 0)
+ def push(me):
+ """Push a new search level."""
+ me._level += 1
+ me._update()
+ if me._level > 0: me.p('[')
+ else: me.p(';; ')
+ def pop(me):
+ """Pop a search level."""
+ if me._level > 0: me.p(']')
+ else: me.p('\n')
+ me._level -= 1
+ me._update()
+ def p(me, ch):
+ """Print CH as a progress rune."""
+ if me._active: stderr.write(ch); stderr.flush()
+
+def combinations(r, v):
+ """
+ Return an iterator which yields all combinations of R elements from V.
+
+ V must be an indexable sequence. The each combination is returned as a
+ list, containing elements from V in their original order.
+ """
+
+ ## Set up the selection vector. C will contain the indices of the items of
+ ## V we've selected for the current combination. At all times, C contains
+ ## a strictly increasing sequence of integers in the interval [0, N).
+ n = len(v)
+ c = range(r)
+
+ while True:
+
+ ## Yield up the current combination.
+ vv = [v[i] for i in c]
+ yield vv
+
+ ## Now advance to the next one. Find the last index in C which we can
+ ## increment subject to the rules. As we iterate downwards, i will
+ ## contain the index into C, and j will be the maximum acceptable value
+ ## for the corresponding item. We'll step the last index until it
+ ## reaches the limit, and then step the next one down, resetting the last
+ ## index, and so on.
+ i, j = r, n
+ while True:
+
+ ## If i is zero here, then we've advanced everything as far as it will
+ ## go. We're done.
+ if i == 0: return
+
+ ## Move down to the next index.
+ i -= 1; j -= 1
+
+ ## If this index isn't at its maximum value, then we've found the place
+ ## to step.
+ if c[i] != j: break
+
+ ## Step this index on by one, and set the following indices to the
+ ## immediately following values.
+ j = c[i] + 1
+ while i < r: c[i] = j; i += 1; j += 1
+
+class ArgFetcher (object):
+ """
+ I return arguments from a list, reporting problems when they occur.
+ """
+ def __init__(me, argv, errfn):
+ """
+ Initialize, returning successive arguments from ARGV.
+
+ Errors are reported to ERRFN.
+ """
+ me._argv = argv
+ me._argc = len(argv)
+ me._errfn = errfn
+ me._i = 0
+ def arg(me, default = None, must = True):
+ """
+ Return the next argument.
+
+ If MUST is true, then report an error (to the ERRFN) if there are no more
+ arguments; otherwise, return the DEFAULT.
+ """
+ if me._i >= me._argc:
+ if must: me._errfn('missing argument')
+ return default
+ arg = me._argv[me._i]; me._i += 1
+ return arg
+ def int(me, default = None, must = True, min = None, max = None):
+ """
+ Return the next argument converted to an integer.
+
+ If MUST is true, then report an error (to the ERRFN) if there are no more
+ arguments; otherwise return the DEFAULT. Report an error if the next
+ argument is not a valid integer, or if the integer is beyond the MIN and
+ MAX bounds.
+ """
+ arg = me.arg(default = None, must = must)
+ if arg is None: return default
+ try: arg = int(arg)
+ except ValueError: me._errfn('bad integer')
+ if (min is not None and arg < min) or (max is not None and arg > max):
+ me._errfn('out of range')
+ return arg
+
+###--------------------------------------------------------------------------
+### Sieving for small primes.
+
+class Sieve (object):
+ """
+ I represent a collection of small primes, up to some chosen limit.
+
+ The limit is available as the `limit' attribute. Let L be this limit;
+ then, if N < L^2 is some composite, then N has at least one prime factor
+ less than L.
+ """
+
+ ## Figure out the number of bits in a (nonnegative) primitive `int'. We'll
+ ## use a list of these as our sieve.
+ try: _MAX = SYS.maxint
+ except AttributeError: _MAX = SYS.maxsize
+ _NBIT = 15
+ while 1 << (_NBIT + 1) < _MAX: _NBIT += 1
+
+ def __init__(me, limit):
+ """
+ Initialize a sieve holding all primes below LIMIT.
+ """
+
+ ## The sieve is maintained in the `_bits' attribute. This is a list of
+ ## integers, used as a bitmask: let 2 < n < L be an odd integer; then bit
+ ## (n - 3)/2 will be clear iff n is prime. Let W be the value of
+ ## `_NBIT', above; then bit W i + j in the sieve is stored in bit j of
+ ## `_bits[i]'.
+
+ ## Store the limit for later inspection.
+ me.limit = limit
+
+ ## Calculate the size of sieve we'll need and initialize the bit list.
+ n = (limit - 2)//2
+ sievesz = (n + me._NBIT - 1)//me._NBIT
+ me._sievemax = sievesz*me._NBIT
+ me._bits = sievesz*[0]
+
+ ## This is standard Sieve of Eratosthenes. For each index i: if
+ ## bit i is clear, then p = 2 i + 3 is prime, so set the bits
+ ## corresponding to each multiple of p, i.e., bits (k p - 3)/2 =
+ ## (2 k i + 3 - 3)/2 = k i for k > 1.
+ for i in xrange(me._sievemax):
+ if me._bitp(i): i += 1; continue
+ p = 2*i + 3
+ if p >= limit: break
+ for j in xrange(i + p, me._sievemax, p): me._setbit(j)
+ i += 1
+
+ def _bitp(me, i): i, j = divmod(i, me._NBIT); return (me._bits[i] >> j)&1
+ def _setbit(me, i): i, j = divmod(i, me._NBIT); me._bits[i] |= 1 << j
+
+ def smallprimes(me):
+ """
+ Return an iterator over the known small primes.
+ """
+ yield 2
+ n = 3
+ for b in me._bits:
+ for j in xrange(me._NBIT):
+ if not (b&1): yield n
+ b >>= 1; n += 2
+
+## We generate the sieve on demand.
+SIEVE = None
+
+def initsieve(sievebits):
+ """
+ Generate the sieve.
+
+ Ensure that it can be used to check the primality of numbers up to (but not
+ including) 2^SIEVEBITS.
+ """
+ global SIEVE
+ if SIEVE is not None: raise ValueError('sieve already defined')
+ if sievebits < 6: sievebits = 6
+ SIEVE = Sieve(1 << (sievebits + 1)//2)
+
+###--------------------------------------------------------------------------
+### Primality checking.
+
+def small_test(p):
+ """
+ Check that P is a small prime.
+
+ If not, raise an `ExpectedError'. The `SIEVE' variable must have been
+ initialized.
+ """
+ if p < 2: raise ExpectedError('%d too small' % p)
+ if SIEVE.limit*SIEVE.limit < p:
+ raise ExpectedError('%d too large for small prime' % p)
+ for q in SIEVE.smallprimes():
+ if q*q > p: return
+ if p%q == 0: raise ExpectedError('%d divides %d' % (q, p))
+
+def pock_test(p, a, qq):
+ """
+ Check that P is prime using Pocklington's criterion.
+
+ If not, raise an `ExpectedError'.
+
+ Let Q be the product of the elements of the sequence QQ. The test works as
+ follows. Suppose p is the smallest prime factor of P. If A^{P-1} /== 1
+ (mod P) then P is certainly composite (Fermat's test); otherwise, we have
+ established that the order of A in (Z/pZ)^* divides P - 1. Next, let t =
+ A^{(P-1)/q} for some prime factor q of Q, and let g = gcd(t - 1, P). If g
+ = P then the proof is inconclusive; if 1 < g < P then g is a nontrivial
+ factor of P, so P is composite; otherwise, t has order q in (Z/pZ)^*, so
+ (Z/pZ)^* contains a subgroup of size q, and therefore q divides p - 1. If
+ QQ is a sequence of distinct primes, and the preceding criterion holds for
+ all q in QQ, then Q divides p - 1. If Q^2 < P then the proof is
+ inconclusive; otherwise, let p' be any prime dividing P/p. Then p' >= p >
+ Q, so p p' > Q^2 > P; but p p' divides P, so this is a contradiction.
+ Therefore P/p has no prime factors, and P is prime.
+ """
+
+ ## We don't actually need the distinctness criterion. Suppose that q^e
+ ## divides Q. Then gcd(t - 1, P) = 1 implies that A^{(P-1)/q^{e-1}} has
+ ## order q^e in (Z/pZ)^*, which accounts for the multiplicity.
+
+ Q = prod(qq)
+ if p < 2: raise ExpectedError('%d too small' % p)
+ if Q*Q <= p:
+ raise ExpectedError('too few Pocklington factors for %d' % p)
+ if pow(a, p - 1, p) != 1:
+ raise ExpectedError('%d is Fermat witness for %d' % (a, p))
+ for q in qq:
+ if Q%(q*q) == 0:
+ raise ExpectedError('duplicate Pocklington factor %d for %d' % (q, p))
+ g = p.gcd(pow(a, (p - 1)/q, p) - 1)
+ if g == p:
+ raise ExpectedError('%d order not multiple of %d mod %d' % (a, q, p))
+ elif g != 1:
+ raise ExpectedError('%d divides %d' % (g, p))
+
+def ecpp_test(p, a, b, x, y, qq):
+ """
+ Check that P is prime using Goldwasser and Kilian's ECPP method.
+
+ If not, raise an `ExpectedError'.
+
+ Let Q be the product of the elements of the sequence QQ. Suppose p is the
+ smallest prime factor of P. Let g = gcd(4 A^3 + 27 B^2, P). If g = P then
+ the test is inconclusive; otherwise, if g /= 1 then g is a nontrivial
+ factor of P. Define E(GF(p)) = { (x, y) | y^2 = x^3 + A x + B } U { inf }
+ to be the elliptic curve over p with short-Weierstraß coefficients A and B;
+ we have just checked that this curve is not singular. If R = (X, Y) is not
+ a point on this curve, then the test is inconclusive. If Q R is not the
+ point at infinity, then the test fails; otherwise we deduce that P has
+ Q-torsion in E. Let S = (Q/q) R for some prime factor q of Q. If S is the
+ point at infinity then the test is inconclusive; otherwise, q divides the
+ order of S in E. If QQ is a sequence of distinct primes, and the preceding
+ criterion holds for all q in QQ, then Q divides the order of S. Therefore
+ #E(p) >= Q. If Q <= (qrrt(P) + 1)^2 then the test is inconclusive.
+ Otherwise, Hasse's theorem tells us that |p + 1 - #E(p)| <= 2 sqrt(p);
+ hence we must have p + 1 + 2 sqrt(p) = (sqrt(p) + 1)^2 >= #E(p) >= Q >
+ (qrrt(P) + 1)^2; so sqrt(p) + 1 > qrrt(P) + 1, i.e., p^2 > P. As for
+ Pocklington above, if p' is any prime factor of P/p, then p p' >= p^2 > P,
+ which is a contradiction, and we conclude that P is prime.
+ """
+
+ ## This isn't going to work if gcd(P, 6) /= 1: we're going to use the
+ ## large-characteristic addition formulae.
+ g = p.gcd(6)
+ if g != 1: raise ExpectedError('%d divides %d' % (g, p))
+
+ ## We want to check that Q > (qrrt(P) + 1)^2 iff sqrt(Q) > qrrt(P) + 1; but
+ ## calculating square roots is not enjoyable (partly because we have to
+ ## deal with the imprecision). Fortunately, some algebra will help: the
+ ## condition holds iff qrrt(P) < sqrt(Q) - 1 iff P < Q^2 - 4 Q sqrt(Q) +
+ ## 6 Q - 4 sqrt(Q) + 1 = Q (Q + 6) + 1 - 4 sqrt(Q) (Q + 1) iff Q (Q + 6) -
+ ## P + 1 > 4 sqrt(Q) (Q + 1) iff (Q (Q + 6) - P + 1)^2 > 16 Q (Q + 1)^2
+ Q = prod(qq)
+ t, u = Q*(Q + 6) - p + 1, 4*(Q + 1)
+ if t*t <= Q*u*u: raise ExpectedError('too few subgroups for ECPP')
+
+ ## Construct the curve.
+ E = C.PrimeField(p).ec(a, b) # careful: may not be a prime!
+
+ ## Find the base point.
+ R = E(x, y)
+ if not R.oncurvep():
+ raise ExpectedError('(%d, %d) is not on the curve' % (x, y))
+
+ ## Check that it has Q-torsion.
+ if Q*R: raise ExpectedError('(%d, %d) not a %d-torsion point' % (x, y, Q))
+
+ ## Now check the individual factors.
+ for q in qq:
+ if Q%(q*q) == 0:
+ raise ExpectedError('duplicate ECPP factor %d for %d' % (q, p))
+ S = (Q/q)*R
+ if not S:
+ raise ExpectedError('(%d, %d) order not a multiple of %d' % (x, y, q))
+ g = p.gcd(S._z)
+ if g != 1:
+ raise ExpectedError('%d divides %d' % (g, p))
+
+###--------------------------------------------------------------------------
+### Proof steps and proofs.
+
+class BaseStep (object):
+ """
+ I'm a step in a primality proof.
+
+ I assert that a particular number is prime, and can check this.
+
+ This class provides basic protocol for proof steps, mostly to do with
+ handling labels.
+
+ The step's label is kept in its `label' attribute. It can be set by the
+ constructor, and is `None' by default. Users can modify this attribute if
+ they like. Labels beginning `$' are assumed to be internal and
+ uninteresting; other labels cause `check' lines to be written to the output
+ listing the actual number of interest.
+
+ Protocol that proof steps should provide:
+
+ label A string labelling the proof step and the associated prime
+ number.
+
+ p The prime number which this step proves to be prime.
+
+ check() Check that the proof step is actually correct, assuming that
+ any previous steps have already been verified.
+
+ out(FILE) Write an appropriate encoding of the proof step to the output
+ FILE.
+ """
+ def __init__(me, label = None, *arg, **kw):
+ """Initialize a proof step, setting a default label if necessary."""
+ super(BaseStep, me).__init__(*arg, **kw)
+ me.label = label
+ def out(me, file):
+ """
+ Write the proof step to an output FILE.
+
+ Subclasses must implement a method `_out' which actually does the work.
+ Here, we write a `check' line to verify that the proof actually applies
+ to the number we wanted, if the label says that this is an interesting
+ step.
+ """
+ me._out(file)
+ if me.label is not None and not me.label.startswith('$'):
+ file.write('check %s, %d, %d\n' % (me.label, me.p.nbits, me.p))
+
+class SmallStep (BaseStep):
+ """
+ I represent a claim that a number is a small prime.
+
+ Such claims act as the base cases in a complicated primality proof. When
+ verifying, the claim is checked by trial division using a collection of
+ known small primes.
+ """
+ def __init__(me, pp, p, *arg, **kw):
+ """
+ Initialize a small-prime step.
+
+ PP is the overall PrimeProof object of which this is a step; P is the
+ small number whose primality is asserted.
+ """
+ super(SmallStep, me).__init__(*arg, **kw)
+ me.p = p
+ def check(me):
+ """Check that the number is indeed a small prime."""
+ return small_test(me.p)
+ def _out(me, file):
+ """Write a small-prime step to the FILE."""
+ file.write('small %s = %d\n' % (me.label, me.p))
+ def __repr__(me): return 'SmallStep(%d)' % (me.p)
+ @classmethod
+ def parse(cls, pp, line):
+ """
+ Parse a small-prime step from a LINE in a proof file.
+
+ SMALL-STEP ::= `small' LABEL `=' P
+
+ PP is a PrimeProof object holding the results from the previous steps.
+ """
+ if SIEVE is None: raise ExpectedError('missing `sievebits\' line')
+ label, p = parse_label(line)
+ return cls(pp, conv_int(p), label = label)
+
+class PockStep (BaseStep):
+ """
+ I represent a Pocklington certificate for a number.
+
+ The number is not explicitly represented in a proof file. See `pock_test'
+ for the underlying mathematics.
+ """
+ def __init__(me, pp, a, R, qqi, *arg, **kw):
+ """
+ Inititialize a Pocklington step.
+
+ PP is the overall PrimeProof object of which this is a step; A is the
+ generator of a substantial subgroup of units; R is a cofactor; and QQI is
+ a sequence of labels for previous proof steps. If Q is the product of
+ the primes listed in QQI, then the number whose primality is asserted is
+ 2 Q R + 1.
+ """
+ super(PockStep, me).__init__(*arg, **kw)
+ me._a = a
+ me._R = R
+ me._qqi = qqi
+ me._qq = [pp.get_step(qi).p for qi in qqi]
+ me.p = prod(me._qq, 2*R) + 1
+ def check(me):
+ """Verify a proof step based on Pocklington's theorem."""
+ return pock_test(me.p, me._a, me._qq)
+ def _out(me, file):
+ """Write a Pocklington step to the FILE."""
+ file.write('pock %s = %d, %d, [%s]\n' % \
+ (me.label, me._a,
+ me._R, ', '.join('%s' % qi for qi in me._qqi)))
+ def __repr__(me): return 'PockStep(%d, %d, %s)' % (me._a, me._R, me._qqi)
+ @classmethod
+ def parse(cls, pp, line):
+ """
+ Parse a Pocklington step from a LINE in a proof file.
+
+ POCK-STEP ::= `pock' LABEL `=' A `,' R `,' `[' Q-LIST `]'
+ Q-LIST ::= Q [`,' Q-LIST]
+
+ PP is a PrimeProof object holding the results from the previous steps.
+ """
+ label, rest = parse_label(line)
+ a, R, qq = parse_list(rest, 3)
+ qq = qq.strip()
+ if not qq.startswith('[') or not qq.endswith(']'):
+ raise ExpectedError('missing `[...]\' around Pocklington factors')
+ return cls(pp, conv_int(a), conv_int(R),
+ [q.strip() for q in qq[1:-1].split(',')], label = label)
+
+class ECPPStep (BaseStep):
+ """
+ I represent a Goldwasser--Kilian ECPP certificate for a number.
+ """
+ def __init__(me, pp, p, a, b, x, y, qqi, *arg, **kw):
+ """
+ Inititialize an ECPP step.
+
+ PP is the overall PrimeProof object of which this is a step; P is the
+ number whose primality is asserted; A and B are the short Weierstraß
+ curve coefficients; X and Y are the base point coordinates; and QQI is a
+ sequence of labels for previous proof steps.
+ """
+ super(ECPPStep, me).__init__(*arg, **kw)
+ me._a, me._b = a, b
+ me._x, me._y = x, y
+ me._qqi = qqi
+ me._qq = [pp.get_step(qi).p for qi in qqi]
+ me.p = p
+ def check(me):
+ """Verify a proof step based on Goldwasser and Kilian's theorem."""
+ return ecpp_test(me.p, me._a, me._b, me._x, me._y, me._qq)
+ def _out(me, file):
+ """Write an ECPP step to the FILE."""
+ file.write('ecpp %s = %d, %d, %d, %d, %d, [%s]\n' % \
+ (me.label, me.p, me._a, me._b, me._x, me._y,
+ ', '.join('%s' % qi for qi in me._qqi)))
+ def __repr__(me):
+ return 'ECPPstep(%d, %d, %d, %d, %d, %s)' % \
+ (me.p, me._a, me._b, me._x, me._y, me._qqi)
+ @classmethod
+ def parse(cls, pp, line):
+ """
+ Parse an ECPP step from a LINE in a proof file.
+
+ ECPP-STEP ::= `ecpp' LABEL `=' P `,' A `,' B `,' X `,' Y `,'
+ `[' Q-LIST `]'
+ Q-LIST ::= Q [`,' Q-LIST]
+
+ PP is a PrimeProof object holding the results from the previous steps.
+ """
+ label, rest = parse_label(line)
+ p, a, b, x, y, qq = parse_list(rest, 6)
+ qq = qq.strip()
+ if not qq.startswith('[') or not qq.endswith(']'):
+ raise ExpectedError('missing `[...]\' around ECPP factors')
+ return cls(pp, conv_int(p), conv_int(a), conv_int(b),
+ conv_int(x), conv_int(y),
+ [q.strip() for q in qq[1:-1].split(',')], label = label)
+
+def check(pp, line):
+ """
+ Handle a `check' line in a proof file.
+
+ CHECK ::= `check' LABEL, B, N
+
+ Verify that the proof step with the given LABEL asserts the primality of
+ the integer N, and that 2^{B-1} <= N < 2^B.
+ """
+ label, nb, p = parse_list(line, 3)
+ label, nb, p = label.strip(), conv_int(nb), conv_int(p)
+ pi = pp.get_step(label).p
+ if pi != p:
+ raise ExpectedError('check failed: %s = %d /= %d' % (label, pi, p))
+ if p.nbits != nb:
+ raise ExpectedError('check failed: nbits(%s) = %d /= %d' % \
+ (label, p.nbits, nb))
+ if VERBOSITY: print(';; %s = %d [%d]' % (label, p, nb))
+
+def setsievebits(pp, line):
+ """
+ Handle a `sievebits' line in a proof file.
+
+ SIEVEBITS ::= `sievebits' N
+
+ Ensure that the verifier is willing to accept small primes up to 2^N.
+ """
+ initsieve(int(line))
+
+class PrimeProof (object):
+ """
+ I represent a proof of primality for one or more numbers.
+
+ I can encode my proof as a line-oriented text file, in a simple format, and
+ read such a proof back to check it.
+ """
+
+ ## A table to dispatch on keywords read from a file.
+ STEPMAP = { 'small': SmallStep.parse,
+ 'pock': PockStep.parse,
+ 'ecpp': ECPPStep.parse,
+ 'sievebits': setsievebits,
+ 'check': check }
+
+ def __init__(me):
+ """
+ Initialize a proof object.
+ """
+ me._steps = {} # Maps labels to steps.
+ me._stepseq = [] # Sequence of labels, in order.
+ me._pmap = {} # Maps primes to steps.
+ me._i = 0
+
+ def addstep(me, step):
+ """
+ Add a new STEP to the proof.
+
+ The STEP may have a label already. If not, a new internal label is
+ chosen. The proof step is checked before being added to the proof. The
+ label is returned.
+ """
+
+ ## If there's already a step for this prime, and the new step doesn't
+ ## have a label, then return the old one instead.
+ if step.label is None:
+ try: return me._pmap[step.p]
+ except KeyError: pass
+
+ ## Make sure the step is actually correct.
+ step.check()
+
+ ## Generate a label if the step doesn't have one already.
+ if step.label is None: step.label = '$t%d' % me._i; me._i += 1
+
+ ## If the label is already taken then we have a problem.
+ if step.label in me._steps:
+ raise ExpectedError('duplicate label `%s\'' % step.label)
+
+ ## Store the proof step.
+ me._pmap[step.p] = step.label
+ me._steps[step.label] = step
+ me._stepseq.append(step.label)
+ return step.label
+
+ def get_step(me, label):
+ """
+ Check that LABEL labels a known step, and return that step.
+ """
+ try: return me._steps[label]
+ except KeyError: raise ExpectedError('unknown label `%s\'' % label)
+
+ def write(me, file):
+ """
+ Write the proof to the given FILE.
+ """
+
+ ## Prefix the main steps with a `sievebits' line.
+ file.write('sievebits %d\n' % (2*(SIEVE.limit.bit_length() - 1)))
+
+ ## Write the steps out one by one.
+ for label in me._stepseq: me._steps[label].out(file)
+
+ def read(me, file):
+ """
+ Read a proof from a given FILE.
+
+ FILE ::= {STEP | CHECK | SIEVEBITS} [FILE]
+ STEP ::= SMALL-STEP | POCK-STEP
+
+ Comments (beginning `;') and blank lines are ignored. Other lines begin
+ with a keyword.
+ """
+ lastp = None
+ for lno, line in enumerate(file, 1):
+ line = line.strip()
+ if line.startswith(';'): continue
+ ww = line.split(None, 1)
+ if not ww: continue
+ w = ww[0]
+ if len(ww) > 1: tail = ww[1]
+ else: tail = ''
+ try:
+ try: op = me.STEPMAP[w]
+ except KeyError:
+ raise ExpectedError('unrecognized keyword `%s\'' % w)
+ step = op(me, tail)
+ if step is not None:
+ me.addstep(step)
+ lastp = step.p
+ except ExpectedError:
+ raise ExpectedError('%s:%d: %s' %
+ (file.name, lno, _excval().message))
+ return lastp
+
+###--------------------------------------------------------------------------
+### Finding provable primes.
+
+class BasePrime (object):
+ """
+ I represent a prime number which has been found and can be proven.
+
+ This object can eventually be turned into a sequence of proof steps and
+ added to a PrimeProof. This isn't done immediately, because some
+ prime-search strategies want to build a pool of provable primes and will
+ then select some subset of them to actually construct the number of final
+ interest. This way, we avoid cluttering the output proof with proofs of
+ uninteresting numbers.
+
+ Protocol required.
+
+ p The prime number in question.
+
+ label(LABEL) Associate LABEL with this prime, and the corresponding proof
+ step. A label can be set in the constructor, or later using
+ this method.
+
+ register(PP) Register the prime with a PrimeProof, adding any necessary
+ proof steps. Returns the label of the proof step for this
+ number.
+
+ _mkstep(PP, **KW)
+ Return a proof step for this prime.
+ """
+ def __init__(me, label = None, *args, **kw):
+ """Initialize a provable prime number object."""
+ super(BasePrime, me).__init__(*args, **kw)
+ me._index = me._pp = None
+ me._label = label
+ def label(me, label):
+ """Set this number's LABEL."""
+ me._label = label
+ def register(me, pp):
+ """
+ Register the prime's proof steps with PrimeProof PP.
+
+ Return the final step's label.
+ """
+ if me._pp is not None:
+ assert me._pp == pp
+ else:
+ me._pp = pp
+ me._index = pp.addstep(me._mkstep(pp, label = me._label))
+ ##try: me._index = pp.addstep(me._mkstep(pp, label = me._label))
+ ##except: raise RuntimeError('generated proof failed sanity check')
+ return me._index
+
+class SmallPrime (BasePrime):
+ """I represent a prime small enough to be checked in isolation."""
+ def __init__(me, p, *args, **kw):
+ super(SmallPrime, me).__init__(*args, **kw)
+ me.p = p
+ def _mkstep(me, pp, **kw):
+ return SmallStep(pp, me.p, **kw)
+
+class PockPrime (BasePrime):
+ """I represent a prime proven using Pocklington's theorem."""
+ def __init__(me, p, a, qq, *args, **kw):
+ super(PockPrime, me).__init__(*args, **kw)
+ me.p = p
+ me._a = a
+ me._qq = qq
+ def _mkstep(me, pp, **kw):
+ return PockStep(pp, me._a, (me.p - 1)/prod((q.p for q in me._qq), 2),
+ [q.register(pp) for q in me._qq], **kw)
+
+def gen_small(nbits, label = None, p = None):
+ """
+ Return a new small prime.
+
+ The prime will be exactly NBITS bits long. The proof step will have the
+ given LABEL attached. Report progress to the ProgressReporter P.
+ """
+ while True:
+
+ ## Pick a random NBITS-bit number.
+ n = C.rand.mp(nbits, 1)
+ assert n.nbits == nbits
+
+ ## If it's probably prime, then check it against the small primes we
+ ## know. If it passes then we're done. Otherwise, try again.
+ if n.primep():
+ for q in SIEVE.smallprimes():
+ if q*q > n: return SmallPrime(n, label = label)
+ if n%q == 0: break
+
+def gen_pock(nbits, nsubbits = 0, label = None, p = ProgressReporter()):
+ """
+ Return a new prime provable using Pocklington's theorem.
+
+ The prime N will be exactly NBITS long, of the form N = 2 Q R + 1. If
+ NSUBBITS is nonzero, then each prime factor of Q will be NSUBBITS bits
+ long; otherwise a suitable default will be chosen. The proof step will
+ have the given LABEL attached. Report progress to the ProgressReporter P.
+
+ The prime numbers this function returns are a long way from being uniformly
+ distributed.
+ """
+
+ ## Pick a suitable value for NSUBBITS if we don't have one.
+ if not nsubbits:
+
+ ## This is remarkably tricky. Picking about 1/3 sqrt(NBITS) factors
+ ## seems about right for large numbers, but there's serious trouble
+ ## lurking for small sizes.
+ nsubbits = int(3*M.sqrt(nbits))
+ if nbits < nsubbits + 3: nsubbits = nbits//2 + 1
+ if nbits == 2*nsubbits: nsubbits += 1
+
+ ## Figure out how many subgroups we'll need.
+ npiece = ((nbits + 1)//2 + nsubbits - 1)//nsubbits
+ p.push()
+
+ ## Keep searching...
+ while True:
+
+ ## Come up with a collection of known prime factors.
+ p.p('!'); qq = [gen(nsubbits, p = p) for i in xrange(npiece)]
+ Q = prod(q.p for q in qq)
+
+ ## Come up with bounds on the cofactor. If we're to have N = 2 Q R + 1,
+ ## and 2^{B-1} <= N < 2^B, then we must have 2^{B-2}/Q <= R < 2^{B-1}/Q.
+ Rbase = (C.MP(0).setbit(nbits - 2) + Q - 1)//Q
+ Rwd = C.MP(0).setbit(nbits - 2)//Q
+
+ ## Probe the available space of cofactors. If the space is kind of
+ ## narrow, then we want to give up quickly if we're not finding anything
+ ## suitable.
+ step = 0
+ while step < Rwd:
+ step += 1
+
+ ## Pick a random cofactor and examine the number we ended up with.
+ ## Make sure it really does have the length we expect.
+ R = C.rand.range(Rwd) + Rbase
+ n = 2*Q*R + 1
+ assert n.nbits == nbits
+
+ ## As a complication, if NPIECE is 1, it's just about possible that Q^2
+ ## <= n, in which case this isn't going to work.
+ if Q*Q < n: continue
+
+ ## If n has small factors, then pick another cofactor.
+ if C.PrimeFilter.smallfactor(n) == C.PGEN_FAIL: continue
+
+ ## Work through the small primes to find a suitable generator. The
+ ## value 2 is almost always acceptable, so don't try too hard here.
+ for a in I.islice(SIEVE.smallprimes(), 16):
+
+ ## First, try the Fermat test. If that fails, then n is definitely
+ ## composite.
+ if pow(a, n - 1, n) != 1: p.p('.'); break
+ p.p('*')
+
+ ## Work through the subgroup orders, checking that suitable powers of
+ ## a generate the necessary subgroups.
+ for q in qq:
+ if n.gcd(pow(a, (n - 1)/q.p, n) - 1) != 1:
+ p.p('@'); ok = False; break
+ else:
+ ok = True
+
+ ## we're all good.
+ if ok: p.pop(); return PockPrime(n, a, qq, label = label)
+
+def gen(nbits, label = None, p = ProgressReporter()):
+ """
+ Generate a prime number with NBITS bits.
+
+ Give it the LABEL, and report progress to P.
+ """
+ if SIEVE.limit >> (nbits + 1)//2: g = gen_small
+ else: g = gen_pock
+ return g(nbits, label = label, p = p)
+
+def gen_limlee(nbits, nsubbits,
+ label = None, qlfmt = None, p = ProgressReporter()):
+ """
+ Generate a Lim--Lee prime with NBITS bits.
+
+ Let p be the prime. Then we'll have p = 2 q_0 q_1 ... q_k, with all q_i at
+ least NSUBBITS bits long, and all but q_k exactly that long.
+
+ The prime will be given the LABEL; progress is reported to P. The factors
+ q_i will be labelled by filling in the `printf'-style format string QLFMT
+ with the argument i.
+ """
+
+ ## Figure out how many factors (p - 1)/2 will have.
+ npiece = nbits//nsubbits
+ if npiece < 2: raise ExpectedError('too few pieces')
+
+ ## Decide how big to make the pool of factors.
+ poolsz = max(3*npiece + 5, 25) # Heuristic from GnuPG
+
+ ## Prepare for the main loop.
+ disp = nstep = 0
+ qbig = None
+ p.push()
+
+ ## Try to make a prime.
+ while True:
+ p.p('!')
+
+ ## Construct a pool of NSUBBITS-size primes. There's a problem with very
+ ## small sizes: we might not be able to build a pool of distinct primes.
+ pool = []; qmap = {}
+ for i in xrange(poolsz):
+ for j in xrange(64):
+ q = gen(nsubbits, p = p)
+ if q.p not in qmap: break
+ else:
+ raise ExpectedError('insufficient diversity')
+ qmap[q.p] = q
+ pool.append(q)
+
+ ## Work through combinations of factors from the pool.
+ for qq in combinations(npiece - 1, pool):
+
+ ## Construct the product of the selected factors.
+ qsmall = prod(q.p for q in qq)
+
+ ## Maybe we'll need to replace the large factor. Try not to do this
+ ## too often. DISP measures the large factor's performance at
+ ## producing candidates with the right length. If it looks bad then
+ ## we'll have to replace it.
+ if 3*disp*disp > nstep*nstep:
+ qbig = None
+ if disp < 0: p.p('<')
+ else: p.p('>')
+
+ ## If we don't have a large factor, then make one.
+ if qbig is None:
+ qbig = gen(nbits - qsmall.nbits, p = p)
+ disp = 0; nstep = 0
+
+ ## We have a candidate. Calculate it and make sure it has the right
+ ## length.
+ n = 2*qsmall*qbig.p + 1
+ nstep += 1
+ if n.nbits < nbits: disp -= 1
+ elif n.nbits > nbits: disp += 1
+ elif C.PrimeFilter.smallfactor(n) == C.PGEN_FAIL: pass
+ else:
+
+ ## The candidate has passed the small-primes test. Now check it
+ ## against Pocklington.
+ for a in I.islice(SIEVE.smallprimes(), 16):
+
+ ## Fermat test.
+ if pow(a, n - 1, n) != 1: p.p('.'); break
+ p.p('*')
+
+ ## Find a generator of a sufficiently large subgroup.
+ if n.gcd(pow(a, (n - 1)/qbig.p, n) - 1) != 1: p.p('@'); continue
+ ok = True
+ for q in qq:
+ if n.gcd(pow(a, (n - 1)/q.p, n) - 1) != 1:
+ p.p('@'); ok = False; break
+
+ ## We're done.
+ if ok:
+
+ ## Label the factors.
+ qq.append(qbig)
+ if qlfmt:
+ for i, q in enumerate(qq): q.label(qlfmt % i)
+
+ ## Return the number we found.
+ p.pop(); return PockPrime(n, a, qq, label = label)
+
+###--------------------------------------------------------------------------
+### Main program.
+
+def __main__():
+ global VERBOSITY
+
+ ## Prepare an option parser.
+ op = OP.OptionParser(
+ usage = '''\
+pock [-qv] [-s SIEVEBITS] CMD ARGS...
+ gen NBITS
+ ll NBITS NSUBBITS
+ check [FILE]''',
+ description = 'Generate or verify certified prime numbers.')
+ op.add_option('-v', '--verbose', dest = 'verbosity',
+ action = 'count', default = 1,
+ help = 'print mysterious runes while looking for prime numbers')
+ op.add_option('-q', '--quiet', dest = 'quietude',
+ action = 'count', default = 0,
+ help = 'be quiet while looking for prime numbers')
+ op.add_option('-s', '--sievebits', dest = 'sievebits',
+ type = 'int', default = 32,
+ help = 'size (in bits) of largest small prime')
+ opts, argv = op.parse_args()
+ VERBOSITY = opts.verbosity - opts.quietude
+ p = ProgressReporter()
+ a = ArgFetcher(argv, op.error)
+
+ ## Process arguments and do what the user asked.
+ w = a.arg()
+
+ if w == 'gen':
+ ## Generate a prime with no special structure.
+ initsieve(opts.sievebits)
+ nbits = a.int(min = 4)
+ pp = PrimeProof()
+ p = gen(nbits, 'p', p = p)
+ p.register(pp)
+ pp.write(stdout)
+
+ elif w == 'll':
+ ## Generate a Lim--Lee prime.
+ initsieve(opts.sievebits)
+ nbits = a.int(min = 4)
+ nsubbits = a.int(min = 4, max = nbits)
+ pp = PrimeProof()
+ p = gen_limlee(nbits, nsubbits, 'p', 'q_%d', p = p)
+ p.register(pp)
+ pp.write(stdout)
+
+ elif w == 'check':
+ ## Check an existing certificate.
+ fn = a.arg(default = '-', must = False)
+ if fn == '-': f = stdin
+ else: f = open(fn, 'r')
+ pp = PrimeProof()
+ p = pp.read(f)
+
+ else:
+ raise ExpectedError("unknown command `%s'" % w)
+
+if __name__ == '__main__':
+ prog = OS.path.basename(argv[0])
+ try: __main__()
+ except ExpectedError: exit('%s: %s' % (prog, _excval().message))
+ except IOError: exit('%s: %s' % (prog, _excval()))
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+.\" -*-nroff-*-
+.\"
+.\" Describe the primality certificate generator and checker
+.\"
+.\" (c) 2016 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.
+.
+.ie t \{\
+. if \n(.g \{\
+. fam P
+. \}
+. ds o \(bu
+. ds ss \s7\v'-4p'
+. ds se \v'4p'\s0
+. ds us \s7\v'2p'
+. ds ue \v'-2p'\s0
+. ds *e \(*e
+. ds mo \(mo
+. ds sr \(sr
+. ds cu \(cu
+. ds ca \(ca
+. ds iy \(if
+. ds == \(==
+. ds .. \&.\h'2p'.\h'2p'.\&
+. ds /= \h'(\w'\*(=='-\w'/')/2'/\h'-(\w'\*(=='+\w'/')/2'\*(==
+.\}
+.el \{\
+. ds o o
+. ds ss ^
+. ds se
+. ds us _
+. ds ue
+. ds *e \fIepsilon\fP
+. ds mo in
+. ds sr sqrt
+. ds cu union
+. ds ca isect
+. ds iy infty
+. ds == ==
+. ds .. \&...\&
+. ds /= /==
+.\}
+.de hP
+.IP
+\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c
+..
+.
+.TH pock 1 "28 May 2017" "Straylight/Edgeware" "Catacomb cryptographic library"
+.SH NAME
+pock \- generate and verify primality certificates
+.
+.\"--------------------------------------------------------------------------
+.SH SYNOPSIS
+.
+.B pock
+.RB [ \-qv ]
+.I command
+.IR [ arguments ...]
+.PP
+Subcommands:
+.RS
+.br
+.B gen
+.I nbits
+.br
+.B ll
+.I nbits
+.I nsubbits
+.br
+.B check
+.RI [ file ]
+.RE
+.
+.\"--------------------------------------------------------------------------
+.SH DESCRIPTION
+.
+Many cryptographic protocols make use of large prime numbers.
+The usual way of determining primality in such circumstances
+is due to Rabin and Miller.
+Given a candidate
+.I n
+and a
+.I witness
+2 \(<=
+.I a
+<
+.IR n ,
+the test answers either `composite' or `unknown'.
+If
+.I n
+is prime then the test answers `unknown' for every witness
+.IR a ;
+if
+.I n
+is in fact composite
+then the test answers `composite'
+for at least three quarters of the possible witnesses.
+If
+.I n
+is a composite,
+then the witnesses
+.I a
+for which the test reports `unknown'
+are called
+.IR liars .
+.PP
+Naively, then,
+to reduce the probability of falsely accepting a composite
+below some bound \*(*e,
+one must perform
+\-(log\*(us2\*(ue \*(*e)/2
+iterations of the test,
+with independent, uniformly distributed witnesses.
+This is especially slow when high security levels are wanted,
+both because tests take longer on larger candidate numbers,
+and because one must do more tests
+to reach the necessary higher confidence level.
+.PP
+The above is a worst-case bound:
+very few composite numbers
+.I n
+have anywhere near
+.IR n /4
+liars.
+In situations such as RSA key generation,
+the user generating the prime number is the only one
+who must be convinced of the number's primality,
+and they have valuable additional knowledge:
+specifically that the candidate has been chosen at random
+according to some suitable probability distribution.
+In this case, one needs many fewer iterations,
+and the number of iterations needed
+.I decreases
+with the size of the candidate being tested.
+.PP
+But in cases where many users must share some large prime parameter,
+each of them must test the proposed prime separately,
+and often they must pessimistically assume
+that the number was chosen maliciously,
+and the worst-case
+.IR n /4
+bound is the best one can do using the Rabin\(enMiller test.
+For large candidates,
+this is inconveniently slow.
+(Also, many implementations incorrectly use
+a number of iterations suitable for randomly chosen primes
+for testing candidates of unknown provenance.)
+.PP
+There
+.I are
+stronger probabilistic tests.
+A combination of Rabin\(enMiller and
+Grantham's Frobenius test
+is known as the
+Baillie\(enPSW test
+(after Baillie, Pomerance, Selfridge, and Wagstaff);
+there are
+.I no
+known composites which pass this test,
+nor is it known how to construct any.
+On the other hand, it's been conjectured that
+infinitely many Baillie\(enPSW pseudoprimes exist.
+While it may be reasonable to assume
+the strength of the Baillie\(enPSW test,
+it must be borne in mind that this
+.I does
+constitute a security assumption.
+.PP
+By contrast,the
+.B pock
+program will generate prime numbers
+of sizes suitable for use in cryptography,
+along with a
+.I certificate of primality
+which can be independently verified fairly efficiently.
+Specifically, verifying a proof takes a little longer
+than a single iteration of the Rabin\(enMiller probabilistic test.
+It can also verify such certificates.
+.PP
+Note that the primes selected by
+.B pock
+are a long way from uniformly distributed.
+Indeed, they have somewhat unusual structure,
+but it seems unlikely that this structure
+renders them unsuitable for e.g., discrete-logarithm cryptography.
+.
+.SS "Command line"
+The following options are recognized.
+.TP
+.B "\-h, \-\-help"
+Write help text to standard output and exit with status 0.
+.TP
+.B "\-q, \-\-quiet"
+Be less verbose during prime generation or certificate verification.
+.TP
+.B "\-v, \-\-verbose"
+Be more verbose during prime generation or certificate verification.
+.TP
+.BI "\-s, \-\-sievebits " b
+When generating certificates,
+require that the verifier can check numbers smaller than
+.RI 2\*(ss b \*(se
+without assistance.
+Larger values lead to sightly shorter proofs,
+but spend more time at startup constructing a sieve;
+smaller values lead to somewhat longer proofs,
+but spend less time constructing the sieve.
+The default is 32,
+which seems to work well in practice.
+.
+.SS "gen"
+The
+.B gen
+command generates a prime
+.I p
+which is
+.I nbits
+long (i.e.,
+.RI 2\*(ss nbits \-1\*(se
+<
+.I p
+<
+.RI 2\*(ss nbits \*(se,
+and writes a certificate to standard output.
+By default, mysterious runes are written to standard error
+to keep the user amused and informed about the operation's progress;
+the
+.B \-q
+option suppresses the runes;
+the
+.B \-v
+option produces more detailed runes.
+.
+.SS "ll"
+The
+.B ll
+command generates a Lim\(enLee prime
+.I p
+=
+2
+.IR q \*(us0\*(ue
+.IR q \*(us1\*(ue
+\*(..
+.IR q \*(us k \*(ue
++
+1
+which is
+.I nbits
+long (i.e.,
+.RI 2\*(ss nbits \-1\*(se
+<
+.I p
+<
+.RI 2\*(ss nbits \*(se,
+such that each
+.IR q \*(us i \*(ue
+is an
+.IR nsubbits -bit
+prime, for
+0 \(<=
+.I i
+<
+.IR k ,
+and
+.IR q \*(us k \*(ue
+is an
+.IR nsubbits -bit
+prime,
+and writes a certificate to standard output.
+By default, mysterious runes are written to standard error
+to keep the user amused and informed about the operation's progress;
+the
+.B \-q
+option suppresses the runes;
+the
+.B \-v
+option produces more detailed runes.
+.
+.SS "check"
+The
+.B check
+command reads a primality certificate from the named
+.I file
+or from standard input,
+and checks it.
+By default,
+each
+.B check
+line in the certificate causes a line
+.IP
+.B ;;
+.I label
+.B =
+.I n
+.BI [ b ]
+.PP
+to be printed to standard output,
+listing the prime's
+.IR label ,
+value
+.IR n ,
+and length
+.I b
+in bits;
+this behaviour is inhibited by the
+.B \-q
+option.
+.
+.SS Runes
+The following mysterious runes are printed during prime searches.
+This information is provided for diagnostic purposes
+and to satisfy idle curiosity:
+later versions may print different runes,
+or use the same rune characters to mean different things.
+.TP
+.B !
+Started to generate a large prime.
+The next step is to generate a number of smaller primes.
+Usually, this will only need to be done once.
+.TP
+.B .
+Candidate failed a Fermat test.
+.TP
+.B *
+Candidate passed a Fermat test.
+This is usually the last rune for a prime search.
+.TP
+.B @
+A candidate generator failed to generate the necessary subgroup.
+This is unusual.
+.TP
+.B <
+For Lim\(enLee primes,
+discarding the large prime
+because it produces results which are too small.
+.TP
+.B >
+For Lim\(enLee primes,
+discarding the large prime
+because it produces results which are too large.
+.TP
+.B [
+Starting a subsidiary prime search.
+.TP
+.B ]
+Finished a subsidiary prime search.
+.
+.\"--------------------------------------------------------------------------
+.SH CERTIFICATE FORMAT
+.
+A certificate consists of a number of lines.
+Blank lines,
+and lines beginning with a
+.RB ` ; ',
+are ignored.
+Other lines are as follows.
+.TP
+.BI "sievebits " b
+Declares that the verifier is expected to be able to check
+primes smaller than
+.RI 2\*(ss b \*(se
+for primality for itself.
+A
+.B sievebits
+line must appear before the first
+.B small
+line.
+.TP
+.BI "small " label " = " p
+Asserts that
+.I p
+is a small prime,
+and associates it with the
+.IR label .
+It is an error if the label has been assigned by a previous line.
+It is required that
+1 <
+.I p
+<
+.RI 2\*(ss b \*(se
+and that
+.I p
+is prime.
+Such small primes constitute the leaves of a proof tree.
+.TP
+.BI "pock " label " = " a ", " R ", [" i ", " j ", \fR\*(..\fB]"
+Asserts that a number
+.I n
+(defined below) is prime by Pocklington's criterion,
+and associates it with the
+.IR label .
+It is an error if the label has been assigned by a previous line.
+.RS
+.PP
+The strings
+.IR i ,
+.IR j ,
+\*(..
+must be labels assigned by earlier lines.
+For each such label
+.IR i ,
+let
+.IR q \*(us i \*(ue
+be the associated prime.
+Let
+.I Q
+=
+.IR q \*(us i \*(ue
+.IR q \*(us j \*(ue
+\*(..
+be the product of these primes.
+Let
+.I n
+=
+.RI 2 QR
++
+1.
+It is required that:
+.hP 1.
+The
+.IR q \*(us i \*(ue
+are distinct.
+.hP 2.
+.IR Q \*(ss2\*(se
+\(>=
+.IR n .
+.hP 3.
+.IR a \*(ss n \-1\*(se
+\*(==
+1
+(mod
+.IR n ).
+.hP 4.
+.RI gcd( a \*(ss( n \-1)/ q \*(se
+\-
+1,
+.IR n )
+=
+1
+for each prime
+.IR q
+dividing
+.IR Q .
+.PP
+To see why this works, let
+.I p
+be the smallest prime factor of
+.IR n .
+From
+.B 3
+we see that
+the order of
+.I a
+in
+.RB ( Z /\fIp Z )\*(ss\(**\*(se
+divides
+.I n
+\-
+1.
+Consider some prime
+.I q
+dividing
+.I Q
+and let
+.I t
+=
+.IR a \*(ss( n \-1)/ q \*(se;
+then
+.I t
+has order dividing
+.IR q .
+From
+.BR 4 ,
+we have
+.I t
+\*(/=
+1
+(mod
+.IR p ),
+and hence
+.I t
+has order precisely
+.I q
+in
+.RB ( Z /\fIp Z )\*(ss\(**\*(se.
+This implies that
+.I q
+divides
+.I p
+\-
+1.
+Since this holds for each prime
+.I q
+dividing
+.IR Q ,
+and,
+from
+.BR 1 ,
+these primes are distinct,
+we deduce that
+.I Q
+divides
+.I p
+\-
+1
+and hence that
+.I p
+>
+.IR Q .
+Let
+.IR p \(fm
+be any prime factor of
+.IR n / p .
+Then
+.IR p \(fm
+\(>=
+.I p
+>
+.I Q
+so,
+from
+.BR 2 ,
+.IR pp \(fm
+>
+.IR Q \*(ss2\*(se
+\(>=
+.IR n ;
+but
+.IR pp \(fm
+divides
+.I n
+so this is a contradiction.
+We must conclude that
+.IR p \(fm
+does not exist,
+and
+.I n
+must be prime.
+.RE
+.TP
+.BI "ecpp " label " = " n ", " A ", " B ", " x ", " y ", [" i ", " j ", \fR\*(..\fB]"
+Asserts that the number
+.I n
+is prime by Goldwasser and Kilian's ECPP method,
+and associates it with the
+.IR label .
+It is an error if the label has been assigned by a previous line.
+.RS
+.PP
+The strings
+.IR i ,
+.IR j ,
+\*..
+must be labels assigned by earlier lines.
+For each such label
+.IR i ,
+let
+.IR q \*(us i \*(ue
+be the associated prime.
+Let
+.I Q
+=
+.IR q \*(us i \*(ue
+.IR q \*(us j \*(ue
+\*(..
+be the product of these primes.
+Define
+.I E\*(usn\*(ue
+= {
+.RI ( x ", " y )
+\*(mo
+.RB ( Z /\fIn Z )\*(ss2\*(se
+|
+.IR y \*(ss2\*(se
+=
+.IR x \*(ss3\*(se
++
+.I Ax
++
+.I B
+}
+\*(cu
+{ \*(iy };
+if
+.I n
+is prime and the curve is not singular
+then this is the elliptic curve over
+.RI GF( n )
+with short-Weierstrass coefficients
+.I A
+and
+.IR B .
+Let
+.I R
+=
+.RI ( x ,
+.IR y ).
+It is required that:
+.hP 1.
+.I g
+=
+.RI gcd(4 a \*(ss3\*(se
++
+.RI 27 b \*(ss2\*(se,
+.IR n )
+= 1.
+.hP 2.
+.I R
+\*(mo
+.IR E\*(usn\*(ue ;
+i.e.,
+.IR y \*(ss2\*(se
+\*(==
+.IR x \*(ss3\*(se
++
+.I Ax
++
+.I B
+(mod
+.IR n ).
+.hP 3. The
+.I q\*(usi\*(ue
+are distinct.
+.hP 4.
+.IR QR ,
+the elliptic-curve scalar product of the point
+.I R
+by the integer
+.IR Q ,
+calculated as if
+.I E
+is a true elliptic curve,
+is the point at infinity.
+.hP 5.
+.RI ( Q / q ) R
+is finite for each prime
+.I q
+dividing
+.IR Q .
+.hP 6.
+.I Q
+>
+.RI ( n \*(ss1/4\*(se
++ 1)\*(ss2\*(se.
+.PP
+To see why this test works, let
+.I p
+be the smallest prime factor of
+.IR n ,
+and let
+.I E\*(usp\*(ue
+= {
+.RI ( x ", " y )
+\*(mo
+.RI GF( p )\*(ss2\*(se
+|
+.IR y \*(ss2\*(se
+=
+.IR x \*(ss3\*(se
++
+.I Ax
++
+.I B
+}
+\*(cu
+{ \*(iy }.
+From
+.BR 1 ,
+.I g
+= 1,
+.I E\*(usp\*(ue
+is an elliptic curve.
+(If 1 <
+.I g
+<
+.I n
+then
+.I g
+is a proper factor of
+.I n
+and
+.I n
+is certainly not prime;
+if
+.I g
+=
+.I n
+then the curve will be singular and the test fails.)
+From
+.BR 2 ,
+.I R
+is a point on
+.IR E\*(usp\*(ue .
+From
+.BR 4 ,
+.I R
+has
+.IR Q -torsion
+in
+.IR E\*(usp\*(ue .
+Consider some prime
+.I q
+dividing
+.I Q
+and let
+.I T
+=
+.RI ( Q/q ) R ;
+then
+.I T
+has torsion dividing
+.IR q .
+From
+.BR 5 ,
+.RI ( Q/q ) R
+\(!= 0,
+and hence
+.I T
+has order precisely
+.I q
+in
+.IR E\*(usp\*(ue .
+This implies that
+.I q
+divides
+.RI # E\*(usp\*(ue .
+Since this holds for each prime
+.I q
+dividing
+.IR Q ,
+and, from
+.BR 3 ,
+the
+.I q
+are distinct,
+we deduce that
+.I Q
+divides
+.RI # E\*(usp\*(ue
+and hence that
+.RI # E\*(usp\*(ue
+\(>=
+.IR Q .
+Hasse's theorem tells us that
+.RI | p
++ 1 \-
+.RI # E\*(usp\*(ue |
+\(<=
+.RI 2\*(sr p ,
+so, in particular,
+.RI # E\*(usp\*(ue
+\-
+.I p
+\- 1
+\(<=
+.RI 2\*(sr p ,
+whence
+.I p
++ 1 +
+.RI 2\*(sr p
+=
+.RI (\*(sr p
++ 1)\*(ss2\*(se
+\(>=
+.RI # E\*(usp\*(ue
+\(>=
+.IR Q
+>
+.RI ( n \*(ss1/4\*(se
++ 1)\*(ss2\*(se
+(from
+.BR 6 );
+so
+.IR p\*(ss2\*(se
+>
+.IR n .
+Let
+.IR p \(fm
+be any prime factor of
+.IR n / p .
+Then
+.IR p \(fm
+\(>=
+.I p
+and
+.IR pp \(fm
+\(>=
+.IR p \*(ss2\*(se
+>
+.IR n ;
+but
+.IR pp \(fm
+divides
+.I n
+so this is a contradiction.
+We must conclude that
+.IR p \(fm
+does not exist,
+and
+.I n
+must be prime.
+.PP
+Note that
+.B pock
+currently cannot generate proofs which use
+.BR ecpp ,
+though it will verify them.
+.RE
+.TP
+.BI "check " label ", " b ", " p
+Verify that the prime associated with the
+.I label
+is equal to
+.I p
+and that it is
+.I b
+bits long;
+i.e., that
+.RI 2\*(ss b \-1\*(se
+\(<=
+.I p
+<
+.RI 2\*(ss b \*(se.
+Unless
+inhibited by
+.BR \-q ,
+the label and value are printed to stdout during verification.
+.
+.\"--------------------------------------------------------------------------
+.SH "SEE ALSO"
+.BR key (1).
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
--- /dev/null
+/* -*-c-*-
+ *
+ * Public-key cryptography
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- DSA and similar ---------------------------------------------------*/
+
+typedef struct dsa_pyobj {
+ PyObject_HEAD
+ PyObject *G, *u, *p, *rng, *hash;
+ gdsa d;
+} dsa_pyobj;
+
+static PyTypeObject *dsapub_pytype, *dsapriv_pytype;
+static PyTypeObject *kcdsapub_pytype, *kcdsapriv_pytype;
+#define DSA_D(o) (&((dsa_pyobj *)(o))->d)
+#define DSA_G(o) (((dsa_pyobj *)(o))->G)
+#define DSA_U(o) (((dsa_pyobj *)(o))->u)
+#define DSA_P(o) (((dsa_pyobj *)(o))->p)
+#define DSA_RNG(o) (((dsa_pyobj *)(o))->rng)
+#define DSA_HASH(o) (((dsa_pyobj *)(o))->hash)
+
+static void dsa_pydealloc(PyObject *me)
+{
+ dsa_pyobj *g = (dsa_pyobj *)me;
+ Py_DECREF(g->G); Py_DECREF(g->u); Py_DECREF(g->p);
+ Py_DECREF(g->rng); Py_DECREF(g->hash);
+ FREEOBJ(me);
+}
+
+static PyObject *dsa_setup(PyTypeObject *ty, PyObject *G, PyObject *u,
+ PyObject *p, PyObject *rng, PyObject *hash,
+ void (*calcpub)(group *, ge *, mp *))
+{
+ dsa_pyobj *g;
+ ge *pp;
+
+ g = PyObject_New(dsa_pyobj, ty);
+ if (p) Py_INCREF(p);
+ if (!u) {
+ g->d.u = 0;
+ u = Py_None;
+ } else {
+ if ((g->d.u = getmp(u)) == 0)
+ goto end;
+ if (MP_PYCHECK(u)) Py_INCREF(u);
+ else u = mp_pywrap(g->d.u);
+ }
+ if (!p) {
+ assert(g->d.u); assert(calcpub);
+ pp = G_CREATE(GROUP_G(G));
+ calcpub(GROUP_G(G), pp, g->d.u);
+ p = ge_pywrap(G, pp);
+ } else if (GROUP_G(G) != GE_G(p) && !group_samep(GROUP_G(G), GE_G(p)))
+ TYERR("public key not from group");
+ g->d.g = GROUP_G(G);
+ g->d.p = GE_X(p);
+ g->d.r = GRAND_R(rng);
+ g->d.h = GCHASH_CH(hash);
+ g->G = G; Py_INCREF(G); g->u = u; g->p = p;
+ g->rng = rng; Py_INCREF(rng); g->hash = hash; Py_INCREF(hash);
+ return ((PyObject *)g);
+end:
+ Py_XDECREF(p); FREEOBJ(g);
+ return (0);
+}
+
+static PyObject *dsapub_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *G, *p, *rng = rand_pyobj, *hash = sha_pyobj;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "G", "p", "hash", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", KWLIST,
+ group_pytype, &G,
+ ge_pytype, &p,
+ gchash_pytype, &hash,
+ grand_pytype, &rng) ||
+ (rc = dsa_setup(dsapub_pytype, G, 0, p, rng, hash, 0)) == 0)
+ goto end;
+end:
+ return (rc);
+}
+
+static PyObject *dsameth_beginhash(PyObject *me)
+ { return (ghash_pywrap(DSA_HASH(me), gdsa_beginhash(DSA_D(me)))); }
+
+static PyObject *dsameth_endhash(PyObject *me, PyObject *arg)
+{
+ ghash *h;
+ PyObject *rc;
+ if (!PyArg_ParseTuple(arg, "O&:endhash", convghash, &h)) return (0);
+ gdsa_endhash(DSA_D(me), h);
+ h = GH_COPY(h);
+ rc = bytestring_pywrap(0, GH_CLASS(h)->hashsz);
+ GH_DONE(h, BIN_PTR(rc));
+ GH_DESTROY(h);
+ return (rc);
+}
+
+static PyObject *dsameth_sign(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ gdsa_sig s = GDSA_SIG_INIT;
+ struct bin h;
+ mp *k = 0;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "msg", "k", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:sign", KWLIST,
+ convbin, &h, convmp, &k))
+ goto end;
+ if (h.sz != DSA_D(me)->h->hashsz)
+ VALERR("bad message length (doesn't match hash size)");
+ gdsa_sign(DSA_D(me), &s, h.p, k);
+ rc = Py_BuildValue("(NN)", mp_pywrap(s.r), mp_pywrap(s.s));
+end:
+ mp_drop(k);
+ return (rc);
+}
+
+static PyObject *dsameth_verify(PyObject *me, PyObject *arg)
+{
+ struct bin h;
+ gdsa_sig s = GDSA_SIG_INIT;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&(O&O&):verify",
+ convbin, &h, convmp, &s.r, convmp, &s.s))
+ goto end;
+ if (h.sz != DSA_D(me)->h->hashsz)
+ VALERR("bad message length (doesn't match hash size)");
+ rc = getbool(!gdsa_verify(DSA_D(me), &s, h.p));
+end:
+ mp_drop(s.r);
+ mp_drop(s.s);
+ return (rc);
+}
+
+static void dsa_calcpub(group *g, ge *p, mp *u) { G_EXP(g, p, g->g, u); }
+
+static PyObject *dsapriv_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *G, *p = 0, *u, *rng = rand_pyobj, *hash = sha_pyobj;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", KWLIST,
+ group_pytype, &G,
+ &u,
+ ge_pytype, &p,
+ gchash_pytype, &hash,
+ grand_pytype, &rng) ||
+ (rc = dsa_setup(dsapriv_pytype, G, u, p, rng, hash, dsa_calcpub)) == 0)
+ goto end;
+end:
+ return (rc);
+}
+
+static const PyMethodDef dsapub_pymethods[] = {
+#define METHNAME(name) dsameth_##name
+ NAMETH(beginhash, "D.beginhash() -> hash object")
+ METH (endhash, "D.endhash(H) -> BYTES")
+ METH (verify, "D.verify(MSG, (R, S)) -> true/false")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyMethodDef dsapriv_pymethods[] = {
+#define METHNAME(name) dsameth_##name
+ KWMETH(sign, "D.sign(MSG, [k = K]) -> R, S")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyMemberDef dsapub_pymembers[] = {
+#define MEMBERSTRUCT dsa_pyobj
+ MEMBER(G, T_OBJECT, READONLY, "D.G -> group to work in")
+ MEMBER(p, T_OBJECT, READONLY, "D.p -> public key (group element")
+ MEMBER(rng, T_OBJECT, READONLY, "D.rng -> random number generator")
+ MEMBER(hash, T_OBJECT, READONLY, "D.hash -> hash class")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyMemberDef dsapriv_pymembers[] = {
+#define MEMBERSTRUCT dsa_pyobj
+ MEMBER(u, T_OBJECT, READONLY, "D.u -> private key (exponent)")
+#undef MEMBERSTRUCT
+ { 0 }
+};
+
+static const PyTypeObject dsapub_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "DSAPub", /* @tp_name@ */
+ sizeof(dsa_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ dsa_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "DSAPub(GROUP, P, [hash = sha], [rng = rand]): DSA public key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(dsapub), /* @tp_methods@ */
+ PYMEMBERS(dsapub), /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ dsapub_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject dsapriv_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "DSAPriv", /* @tp_name@ */
+ sizeof(dsa_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "DSAPriv(GROUP, U, [p = u G], [hash = sha], [rng = rand]): "
+ "DSA private key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(dsapriv), /* @tp_methods@ */
+ PYMEMBERS(dsapriv), /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ dsapriv_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *kcdsapub_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *G, *p, *rng = rand_pyobj, *hash = has160_pyobj;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "G", "p", "hash", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", KWLIST,
+ group_pytype, &G,
+ ge_pytype, &p,
+ gchash_pytype, &hash,
+ grand_pytype, &rng) ||
+ (rc = dsa_setup(kcdsapub_pytype, G, 0, p, rng, hash, 0)) == 0)
+ goto end;
+end:
+ return (rc);
+}
+
+static void kcdsa_calcpub(group *g, ge *p, mp *u)
+{
+ mp *uinv = mp_modinv(MP_NEW, u, g->r);
+ G_EXP(g, p, g->g, uinv);
+ mp_drop(uinv);
+}
+
+static PyObject *kcdsapriv_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ PyObject *G, *u, *p = 0, *rng = rand_pyobj, *hash = has160_pyobj;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", KWLIST,
+ group_pytype, &G,
+ &u,
+ ge_pytype, &p,
+ gchash_pytype, &hash,
+ grand_pytype, &rng) ||
+ (rc = dsa_setup(kcdsapriv_pytype, G, u, p,
+ rng, hash, kcdsa_calcpub)) == 0)
+ goto end;
+end:
+ return (rc);
+}
+
+static PyObject *kcdsameth_beginhash(PyObject *me)
+ { return (ghash_pywrap(DSA_HASH(me), gkcdsa_beginhash(DSA_D(me)))); }
+
+static PyObject *kcdsameth_endhash(PyObject *me, PyObject *arg)
+{
+ ghash *h;
+ PyObject *rc;
+ if (!PyArg_ParseTuple(arg, "O&:endhash", convghash, &h)) return (0);
+ gkcdsa_endhash(DSA_D(me), h);
+ h = GH_COPY(h);
+ rc = bytestring_pywrap(0, GH_CLASS(h)->hashsz);
+ GH_DONE(h, BIN_PTR(rc));
+ GH_DESTROY(h);
+ return (rc);
+}
+
+static PyObject *kcdsameth_sign(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ gkcdsa_sig s = GKCDSA_SIG_INIT;
+ struct bin h;
+ mp *k = 0;
+ PyObject *r = 0, *rc = 0;
+ static const char *const kwlist[] = { "msg", "k", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:sign", KWLIST,
+ convbin, &h, convmp, &k))
+ goto end;
+ if (h.sz != DSA_D(me)->h->hashsz)
+ VALERR("bad message length (doesn't match hash size)");
+ r = bytestring_pywrap(0, DSA_D(me)->h->hashsz);
+ s.r = (octet *)BIN_PTR(r);
+ gkcdsa_sign(DSA_D(me), &s, h.p, k);
+ rc = Py_BuildValue("(ON)", r, mp_pywrap(s.s));
+end:
+ Py_XDECREF(r);
+ mp_drop(k);
+ return (rc);
+}
+
+static PyObject *kcdsameth_verify(PyObject *me, PyObject *arg)
+{
+ struct bin h, sr;
+ gkcdsa_sig s = GKCDSA_SIG_INIT;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&(O&O&):verify",
+ convbin, &h, convbin, &sr, convmp, &s.s))
+ goto end;
+ if (h.sz != DSA_D(me)->h->hashsz)
+ VALERR("bad message length (doesn't match hash size)");
+ if (sr.sz != DSA_D(me)->h->hashsz)
+ VALERR("bad signature `r' length (doesn't match hash size)");
+ s.r = (/*unconst*/ octet *)sr.p;
+ rc = getbool(!gkcdsa_verify(DSA_D(me), &s, h.p));
+end:
+ mp_drop(s.s);
+ return (rc);
+}
+
+static const PyMethodDef kcdsapub_pymethods[] = {
+#define METHNAME(name) kcdsameth_##name
+ NAMETH(beginhash, "D.beginhash() -> hash object")
+ METH (endhash, "D.endhash(H) -> BYTES")
+ METH (verify, "D.verify(MSG, (R, S)) -> true/false")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyMethodDef kcdsapriv_pymethods[] = {
+#define METHNAME(name) kcdsameth_##name
+ KWMETH(sign, "D.sign(MSG, [k = K]) -> R, S")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject kcdsapub_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KCDSAPub", /* @tp_name@ */
+ sizeof(dsa_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ dsa_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KCDSAPub(GROUP, P, [hash = sha], [rng = rand]): KCDSA public key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(kcdsapub), /* @tp_methods@ */
+ PYMEMBERS(dsapub), /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ kcdsapub_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject kcdsapriv_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "KCDSAPriv", /* @tp_name@ */
+ sizeof(dsa_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "KCDSAPriv(GROUP, U, [p = u G], [hash = sha], [rng = rand]): "
+ "KCDSA private key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(kcdsapriv), /* @tp_methods@ */
+ PYMEMBERS(dsapriv), /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ kcdsapriv_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- RSA ---------------------------------------------------------------*/
+
+typedef struct rsapub_pyobj {
+ PyObject_HEAD
+ rsa_pub pub;
+ rsa_pubctx pubctx;
+} rsapub_pyobj;
+
+#define RSA_PUB(o) (&((rsapub_pyobj *)(o))->pub)
+#define RSA_PUBCTX(o) (&((rsapub_pyobj *)(o))->pubctx)
+
+typedef struct rsapriv_pyobj {
+ PyObject_HEAD
+ rsa_pub pub;
+ rsa_pubctx pubctx;
+ rsa_priv priv;
+ rsa_privctx privctx;
+ PyObject *rng;
+} rsapriv_pyobj;
+
+#define RSA_PRIV(o) (&((rsapriv_pyobj *)(o))->priv)
+#define RSA_PRIVCTX(o) (&((rsapriv_pyobj *)(o))->privctx)
+#define RSA_RNG(o) (((rsapriv_pyobj *)(o))->rng)
+
+static PyTypeObject *rsapub_pytype, *rsapriv_pytype;
+
+static PyObject *rsapub_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ rsa_pub rp = { 0 };
+ rsapub_pyobj *o;
+ static const char *const kwlist[] = { "n", "e", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
+ convmp, &rp.n, convmp, &rp.e))
+ goto end;
+ if (!MP_ODDP(rp.n)) VALERR("RSA modulus must be even");
+ o = (rsapub_pyobj *)ty->tp_alloc(ty, 0);
+ o->pub = rp;
+ rsa_pubcreate(&o->pubctx, &o->pub);
+ return ((PyObject *)o);
+end:
+ rsa_pubfree(&rp);
+ return (0);
+}
+
+static void rsapub_pydealloc(PyObject *me)
+{
+ rsa_pubdestroy(RSA_PUBCTX(me));
+ rsa_pubfree(RSA_PUB(me));
+ FREEOBJ(me);
+}
+
+static PyObject *rsaget_n(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PUB(me)->n)); }
+
+static PyObject *rsaget_e(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PUB(me)->e)); }
+
+static PyObject *rsameth_pubop(PyObject *me, PyObject *arg)
+{
+ mp *x = 0;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:pubop", convmp, &x)) goto end;
+ rc = mp_pywrap(rsa_pubop(RSA_PUBCTX(me), MP_NEW, x));
+end:
+ mp_drop(x);
+ return (rc);
+}
+
+static PyObject *rsapriv_dopywrap(PyTypeObject *ty,
+ rsa_priv *rp, PyObject *rng)
+{
+ rsapriv_pyobj *o;
+
+ o = (rsapriv_pyobj *)ty->tp_alloc(ty, 0);
+ o->priv = *rp;
+ o->pub.n = rp->n;
+ o->pub.e = rp->e;
+ rsa_privcreate(&o->privctx, &o->priv, &rand_global);
+ rsa_pubcreate(&o->pubctx, &o->pub);
+ if (!rng) {
+ rng = Py_None;
+ Py_INCREF(rng);
+ }
+ o->rng = rng;
+ return ((PyObject *)o);
+}
+
+PyObject *rsapriv_pywrap(rsa_priv *rp)
+ { return rsapriv_dopywrap(rsapriv_pytype, rp, 0); }
+
+static PyObject *rsapriv_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ rsa_priv rp = { 0 };
+ PyObject *rng = Py_None;
+ static const char *const kwlist[] =
+ { "n", "e", "d", "p", "q", "dp", "dq", "q_inv", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&O&O&O&O&O:new", KWLIST,
+ convmp, &rp.n, convmp, &rp.e,
+ convmp, &rp.d,
+ convmp, &rp.p, convmp, &rp.q,
+ convmp, &rp.dp, convmp, &rp.dq,
+ convmp, &rp.q_inv,
+ &rng))
+ goto end;
+ if ((rp.n && !MP_ODDP(rp.n)) ||
+ (rp.p && !MP_ODDP(rp.p)) ||
+ (rp.q && !MP_ODDP(rp.q)))
+ VALERR("RSA modulus and factors must be odd");
+ if (rsa_recover(&rp)) VALERR("couldn't construct private key");
+ if (rng != Py_None && !GRAND_PYCHECK(rng))
+ TYERR("not a random number source");
+ Py_INCREF(rng);
+ return (rsapriv_dopywrap(ty, &rp, rng));
+end:
+ rsa_privfree(&rp);
+ return (0);
+}
+
+static void rsapriv_pydealloc(PyObject *me)
+{
+ RSA_PRIVCTX(me)->r = &rand_global;
+ rsa_privdestroy(RSA_PRIVCTX(me));
+ rsa_privfree(RSA_PRIV(me));
+ Py_DECREF(RSA_RNG(me));
+ FREEOBJ(me);
+}
+
+static PyObject *rsaget_d(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PRIV(me)->d)); }
+
+static PyObject *rsaget_p(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PRIV(me)->p)); }
+
+static PyObject *rsaget_q(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PRIV(me)->q)); }
+
+static PyObject *rsaget_dp(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PRIV(me)->dp)); }
+
+static PyObject *rsaget_dq(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PRIV(me)->dq)); }
+
+static PyObject *rsaget_q_inv(PyObject *me, void *hunoz)
+ { return mp_pywrap(MP_COPY(RSA_PRIV(me)->q_inv)); }
+
+static PyObject *rsaget_rng(PyObject *me, void *hunoz)
+ { RETURN_OBJ(RSA_RNG(me)); }
+
+static int rsaset_rng(PyObject *me, PyObject *val, void *hunoz)
+{
+ int rc = -1;
+ if (!val)
+ val = Py_None;
+ else if (val != Py_None && !GRAND_PYCHECK(val))
+ TYERR("expected grand or None");
+ Py_DECREF(RSA_RNG(me));
+ RSA_RNG(me) = val;
+ Py_INCREF(val);
+ rc = 0;
+end:
+ return (rc);
+}
+
+static PyObject *rsameth_privop(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *rng = RSA_RNG(me);
+ mp *x = 0;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "x", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:privop", KWLIST,
+ convmp, &x, &rng))
+ goto end;
+ if (rng != Py_None && !GRAND_PYCHECK(rng))
+ TYERR("not a random number source");
+ RSA_PRIVCTX(me)->r = (rng == Py_None) ? 0 : GRAND_R(rng);
+ rc = mp_pywrap(rsa_privop(RSA_PRIVCTX(me), MP_NEW, x));
+end:
+ mp_drop(x);
+ return (rc);
+}
+
+static PyObject *rsameth_generate(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ grand *r = &rand_global;
+ unsigned nbits;
+ unsigned n = 0;
+ rsa_priv rp;
+ mp *e = 0;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ static const char *const kwlist[] =
+ { "nbits", "event", "rng", "nsteps", "e", 0 };
+ PyObject *rc = 0;
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&O&O&O&:generate", KWLIST,
+ convuint, &nbits, convpgev, &evt,
+ convgrand, &r, convuint, &n,
+ convmp, &e))
+ goto end;
+ if (e) MP_COPY(e);
+ else e = mp_fromulong(MP_NEW, 65537);
+ if (rsa_gen_e(&rp, nbits, e, r, n, evt.ev.proc, evt.ev.ctx))
+ PGENERR(&exc);
+ rc = rsapriv_pywrap(&rp);
+end:
+ droppgev(&evt);
+ mp_drop(e);
+ return (rc);
+}
+
+static const PyGetSetDef rsapub_pygetset[] = {
+#define GETSETNAME(op, name) rsa##op##_##name
+ GET (n, "R.n -> N")
+ GET (e, "R.e -> E")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef rsapub_pymethods[] = {
+#define METHNAME(name) rsameth_##name
+ METH (pubop, "R.pubop(X) -> X^E (mod N)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef rsapriv_pygetset[] = {
+#define GETSETNAME(op, name) rsa##op##_##name
+ GET (d, "R.d -> D")
+ GET (p, "R.p -> P")
+ GET (q, "R.q -> Q")
+ GET (dp, "R.dp -> D mod (P - 1)")
+ GET (dq, "R.dq -> D mod (Q - 1)")
+ GET (q_inv, "R.q_inv -> Q^{-1} mod P")
+ GETSET(rng, "R.rng -> random number source for blinding")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef rsapriv_pymethods[] = {
+#define METHNAME(name) rsameth_##name
+ KWMETH(privop, "R.privop(X, [rng = None]) -> X^D (mod N)")
+ KWSMTH(generate, "generate(NBITS, [event = pgen_nullev], [rng = rand], "
+ "[nsteps = 0]) -> R")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject rsapub_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "RSAPub", /* @tp_name@ */
+ sizeof(rsapub_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ rsapub_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "RSAPub(N, E): RSA public key.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(rsapub), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(rsapub), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ rsapub_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject rsapriv_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "RSAPriv", /* @tp_name@ */
+ sizeof(rsapriv_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ rsapriv_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "RSAPriv(..., [rng = rand]): RSA private key.\n"
+ " Keywords: n, e, d, p, q, dp, dq, q_inv; must provide enough",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(rsapriv), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(rsapriv), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ rsapriv_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- RSA padding schemes -----------------------------------------------*/
+
+static PyObject *meth__p1crypt_encode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ pkcs1 p1;
+ struct bin m, ep = { 0, 0 };
+ unsigned long nbits;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x;
+ static const char *const kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
+
+ p1.r = &rand_global;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:encode", KWLIST,
+ convbin, &m, convulong, &nbits,
+ convbin, &ep, convgrand, &p1.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ p1.ep = ep.p; p1.epsz = ep.sz;
+ if (ep.sz + m.sz + 11 > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ x = pkcs1_cryptencode(MP_NEW, m.p, m.sz, b, sz, nbits, &p1);
+ rc = mp_pywrap(x);
+end:
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__p1crypt_decode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ pkcs1 p1;
+ struct bin ep = { 0, 0 };
+ unsigned long nbits;
+ int n;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x = 0;
+ static const char *const kwlist[] = { "ct", "nbits", "ep", "rng", 0 };
+
+ p1.r = &rand_global;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:decode", KWLIST,
+ convmp, &x, convulong, &nbits,
+ convbin, &ep, convgrand, &p1.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ p1.ep = ep.p; p1.epsz = ep.sz;
+ if (ep.sz + 11 > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ if ((n = pkcs1_cryptdecode(x, b, sz, nbits, &p1)) < 0)
+ VALERR("decryption failed");
+ rc = bytestring_pywrap(b, n);
+end:
+ mp_drop(x);
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__p1sig_encode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ pkcs1 p1;
+ struct bin m, ep = { 0, 0 };
+ unsigned long nbits;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x;
+ static const char *const kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
+
+ p1.r = &rand_global;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:encode", KWLIST,
+ convbin, &m, convulong, &nbits,
+ convbin, &ep, convgrand, &p1.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ p1.ep = ep.p; p1.epsz = ep.sz;
+ if (ep.sz + m.sz + 11 > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ x = pkcs1_sigencode(MP_NEW, m.p, m.sz, b, sz, nbits, &p1);
+ rc = mp_pywrap(x);
+end:
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__p1sig_decode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ pkcs1 p1;
+ struct bin ep = { 0, 0 };
+ unsigned long nbits;
+ int n;
+ PyObject *hukairz;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x = 0;
+ static const char *const kwlist[] =
+ { "msg", "sig", "nbits", "ep", "rng", 0 };
+
+ p1.r = &rand_global;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|O&O&:decode", KWLIST,
+ &hukairz, convmp, &x, convulong, &nbits,
+ convbin, &ep, convgrand, &p1.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ p1.ep = ep.p; p1.epsz = ep.sz;
+ if (ep.sz + 10 > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ if ((n = pkcs1_sigdecode(x, 0, 0, b, sz, nbits, &p1)) < 0)
+ VALERR("verification failed");
+ rc = bytestring_pywrap(b, n);
+end:
+ mp_drop(x);
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__oaep_encode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ oaep o;
+ struct bin m, ep = { 0, 0 };
+ unsigned long nbits;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x;
+ static const char *const kwlist[] =
+ { "msg", "nbits", "mgf", "hash", "ep", "rng", 0 };
+
+ o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&O&O&:encode",
+ KWLIST,
+ convbin, &m, convulong, &nbits,
+ convgccipher, &o.cc,
+ convgchash, &o.ch,
+ convbin, &ep,
+ convgrand, &o.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ o.ep = ep.p; o.epsz = ep.sz;
+ if (2 * o.ch->hashsz + 2 + m.sz > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ x = oaep_encode(MP_NEW, m.p, m.sz, b, sz, nbits, &o);
+ rc = mp_pywrap(x);
+end:
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__oaep_decode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ oaep o;
+ struct bin ep = { 0, 0 };
+ unsigned long nbits;
+ int n;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x = 0;
+ static const char *const kwlist[] =
+ { "ct", "nbits", "mgf", "hash", "ep", "rng", 0 };
+
+ o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&O&O&:decode", KWLIST,
+ convmp, &x, convulong, &nbits,
+ convgccipher, &o.cc,
+ convgchash, &o.ch,
+ convbin, &ep,
+ convgrand, &o.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ o.ep = ep.p; o.epsz = ep.sz;
+ if (2 * o.ch->hashsz > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ if ((n = oaep_decode(x, b, sz, nbits, &o)) < 0)
+ VALERR("decryption failed");
+ rc = bytestring_pywrap(b, n);
+end:
+ mp_drop(x);
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__pss_encode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ pss p;
+ struct bin m;
+ unsigned long nbits;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ mp *x = 0;
+ static const char *const kwlist[] =
+ { "msg", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
+
+ p.cc = &sha_mgf; p.ch = &sha; p.r = &rand_global; p.ssz = (size_t)-1;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&O&O&:encode", KWLIST,
+ convbin, &m, convulong, &nbits,
+ convgccipher, &p.cc,
+ convgchash, &p.ch,
+ convszt, &p.ssz,
+ convgrand, &p.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ if (p.ssz == (size_t)-1) p.ssz = p.ch->hashsz;
+ if (p.ch->hashsz + p.ssz + 2 > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ x = pss_encode(MP_NEW, m.p, m.sz, b, sz, nbits, &p);
+ rc = mp_pywrap(x);
+end:
+ xfree(b);
+ return (rc);
+}
+
+static PyObject *meth__pss_decode(PyObject *me,
+ PyObject *arg, PyObject *kw)
+{
+ pss p;
+ struct bin m;
+ unsigned long nbits;
+ PyObject *rc = 0;
+ octet *b = 0;
+ size_t sz;
+ int n;
+ mp *x = 0;
+ static const char *const kwlist[] =
+ { "msg", "sig", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
+
+ p.cc = &sha_mgf; p.ch = &sha; p.r = &rand_global; p.ssz = (size_t)-1;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&|O&O&O&O&:decode",
+ KWLIST,
+ convbin, &m, convmp, &x,
+ convulong, &nbits,
+ convgccipher, &p.cc,
+ convgchash, &p.ch,
+ convszt, &p.ssz,
+ convgrand, &p.r))
+ goto end;
+ sz = (nbits + 7)/8;
+ if (p.ssz == (size_t)-1) p.ssz = p.ch->hashsz;
+ if (p.ch->hashsz + p.ssz + 2 > sz) VALERR("buffer underflow");
+ b = xmalloc(sz);
+ if ((n = pss_decode(x, m.p, m.sz, b, sz, nbits, &p)) < 0)
+ VALERR("verification failed");
+ rc = Py_None; Py_INCREF(rc);
+end:
+ mp_drop(x);
+ xfree(b);
+ return (rc);
+}
+
+/*----- X25519 and related algorithms -------------------------------------*/
+
+#define XDHS(_) \
+ _(X25519, x25519) \
+ _(X448, x448)
+
+#define DEFXDH(X, x) \
+ static PyObject *meth_##x(PyObject *me, PyObject *arg) \
+ { \
+ struct bin k, p; \
+ PyObject *rc = 0; \
+ if (!PyArg_ParseTuple(arg, "O&O&:" #x, convbin, &k, convbin, &p)) \
+ goto end; \
+ if (k.sz != X##_KEYSZ) VALERR("bad key length"); \
+ if (p.sz != X##_PUBSZ) VALERR("bad public length"); \
+ rc = bytestring_pywrap(0, X##_OUTSZ); \
+ x((octet *)BIN_PTR(rc), k.p, p.p); \
+ return (rc); \
+ end: \
+ return (0); \
+ }
+XDHS(DEFXDH)
+#undef DEFXDH
+
+/*----- Ed25519 and related algorithms ------------------------------------*/
+
+#define EDDSAS(_) \
+ _(ED25519, ed25519, -1, ctx) \
+ _(ED448, ed448, 0, )
+
+#define DEFEDDSA(ED, ed, phdflt, sigver) \
+ \
+ static PyObject *meth_##ed##_pubkey(PyObject *me, PyObject *arg) \
+ { \
+ struct bin k; \
+ PyObject *rc = 0; \
+ if (!PyArg_ParseTuple(arg, "O&:" #ed "_pubkey", convbin, &k)) \
+ goto end; \
+ rc = bytestring_pywrap(0, ED##_PUBSZ); \
+ ed##_pubkey((octet *)BIN_PTR(rc), k.p, k.sz); \
+ return (rc); \
+ end: \
+ return (0); \
+ } \
+ \
+ static PyObject *meth_##ed##_sign(PyObject *me, PyObject *arg, \
+ PyObject *kw) \
+ { \
+ struct bin k, p = { 0, 0}, c = { 0, 0 }, m; \
+ int ph = phdflt; \
+ PyObject *rc = 0; \
+ octet pp[ED##_PUBSZ]; \
+ static const char *const kwlist[] = \
+ { "key", "msg", "pub", "perso", "phflag", 0 }; \
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, \
+ "O&O&|O&O&O&:" #ed "_sign", \
+ KWLIST, \
+ convbin, &k, convbin, &m, \
+ convbin, &p, convbin, &c, \
+ convbool, &ph)) \
+ goto end; \
+ if (p.p && p.sz != ED##_PUBSZ) VALERR("bad public length"); \
+ if (c.p && c.sz > ED##_MAXPERSOSZ) \
+ VALERR("personalization string too long"); \
+ if (c.p && ph == -1) ph = 0; \
+ if (!p.p) { p.p = pp; ed##_pubkey(pp, k.p, k.sz); } \
+ rc = bytestring_pywrap(0, ED##_SIGSZ); \
+ ed##sigver##_sign((octet *)BIN_PTR(rc), k.p, k.sz, \
+ p.p, ph, c.p, c.sz, m.p, m.sz); \
+ return (rc); \
+ end: \
+ return (0); \
+ } \
+ \
+ static PyObject *meth_##ed##_verify(PyObject *me, \
+ PyObject *arg, PyObject *kw) \
+ { \
+ struct bin p, c = { 0, 0 }, m, s; \
+ int ph = phdflt; \
+ PyObject *rc = 0; \
+ static const char *const kwlist[] = \
+ { "pub", "msg", "sig", "perso", "phflag", 0 }; \
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, \
+ "O&O&O&|O&O&:" #ed "_verify", \
+ KWLIST, \
+ convbin, &p, convbin, &m, \
+ convbin, &s, \
+ convbin, &c, convbool, &ph)) \
+ goto end; \
+ if (p.sz != ED##_PUBSZ) VALERR("bad public length"); \
+ if (s.sz != ED##_SIGSZ) VALERR("bad signature length"); \
+ if (c.p && c.sz > ED##_MAXPERSOSZ) \
+ VALERR("personalization string too long"); \
+ if (c.p && ph == -1) ph = 0; \
+ rc = getbool(!ed##sigver##_verify(p.p, ph, c.p, c.sz, \
+ m.p, m.sz, s.p)); \
+ return (rc); \
+ end: \
+ return (0); \
+ }
+EDDSAS(DEFEDDSA)
+#undef DEFEDDSA
+
+/*----- Global stuff ------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(X25519_KEYSZ), CONST(X25519_PUBSZ), CONST(X25519_OUTSZ),
+ CONST(X448_KEYSZ), CONST(X448_PUBSZ), CONST(X448_OUTSZ),
+ CONST(ED25519_KEYSZ), CONST(ED25519_PUBSZ), CONST(ED25519_SIGSZ),
+ CONST(ED25519_MAXPERSOSZ),
+ CONST(ED448_KEYSZ), CONST(ED448_PUBSZ), CONST(ED448_SIGSZ),
+ CONST(ED448_MAXPERSOSZ),
+ { 0 }
+};
+
+static const PyMethodDef methods[] = {
+#define METHNAME(name) meth_##name
+ KWMETH(_p1crypt_encode, 0)
+ KWMETH(_p1crypt_decode, 0)
+ KWMETH(_p1sig_encode, 0)
+ KWMETH(_p1sig_decode, 0)
+ KWMETH(_oaep_encode, 0)
+ KWMETH(_oaep_decode, 0)
+ KWMETH(_pss_encode, 0)
+ KWMETH(_pss_decode, 0)
+#define DEFMETH(X, x) \
+ METH (x, "" #x "(KEY, PUBLIC) -> SHARED")
+ XDHS(DEFMETH)
+#undef DEFMETH
+#define DEFMETH(ED, ed, phdflt, sigver) \
+ METH (ed##_pubkey, "" #ed "_pubkey(KEY) -> PUBLIC") \
+ KWMETH(ed##_sign, "" #ed "_sign(KEY, MSG, [pub = PUBLIC], " \
+ "[perso = STRING], [phflag = BOOL]) -> SIG") \
+ KWMETH(ed##_verify, "" #ed "_verify(PUBLIC, MSG, SIG, " \
+ "[perso = STRING], [phflag = BOOL]) -> BOOL")
+ EDDSAS(DEFMETH)
+#undef DEFMETH
+#undef METHNAME
+ { 0 }
+};
+
+void pubkey_pyinit(void)
+{
+ INITTYPE(dsapub, root);
+ INITTYPE(dsapriv, dsapub);
+ INITTYPE(kcdsapub, root);
+ INITTYPE(kcdsapriv, kcdsapub);
+ INITTYPE(rsapub, root);
+ INITTYPE(rsapriv, rsapub);
+ addmethods(methods);
+}
+
+void pubkey_pyinsert(PyObject *mod)
+{
+ INSERT("DSAPub", dsapub_pytype);
+ INSERT("DSAPriv", dsapriv_pytype);
+ INSERT("KCDSAPub", kcdsapub_pytype);
+ INSERT("KCDSAPriv", kcdsapriv_pytype);
+ INSERT("RSAPub", rsapub_pytype);
+ INSERT("RSAPriv", rsapriv_pytype);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+#! /usr/bin/python
+### -*-python-*-
+###
+### Tool for maintaining a secure-ish password database
+###
+### (c) 2005 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.
+
+from __future__ import with_statement
+
+from os import environ
+import sys as SYS; from sys import argv, exit, stdin, stdout, stderr
+from getopt import getopt, GetoptError
+from fnmatch import fnmatch
+import re
+
+import catacomb as C
+from catacomb.pwsafe import *
+
+###--------------------------------------------------------------------------
+### Python version portability.
+
+if SYS.version_info >= (3,):
+ import io as IO
+ def hack_stream(stream):
+ _enc = stream.encoding
+ _lbuf = stream.line_buffering
+ _nl = stream.newlines
+ return IO.TextIOWrapper(stream.detach(),
+ encoding = _enc,
+ line_buffering = _lbuf,
+ newline = _nl,
+ errors = "surrogateescape")
+ SYS.stdout = stdout = hack_stream(stdout)
+ def _text(bin): return bin.decode(errors = "surrogateescape")
+ def _bin(text): return text.encode(errors = "surrogateescape")
+else:
+ def _text(bin): return bin
+ def _bin(text): return text
+
+def _excval(): return SYS.exc_info()[1]
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+## The program name.
+prog = re.sub(r'^.*[/\\]', '', argv[0])
+
+def moan(msg):
+ """Issue a warning message MSG."""
+ stderr.write('%s: %s\n' % (prog, msg))
+
+def die(msg):
+ """Report MSG as a fatal error, and exit."""
+ moan(msg)
+ exit(1)
+
+###--------------------------------------------------------------------------
+### Subcommand implementations.
+
+def cmd_create(av):
+
+ ## Default crypto-primitive selections.
+ cipher = 'rijndael-cbc'
+ hash = 'sha256'
+ mac = None
+
+ ## Parse the options.
+ try:
+ opts, args = getopt(av, 'c:d:h:m:',
+ ['cipher=', 'database=', 'mac=', 'hash='])
+ except GetoptError:
+ return 1
+ dbty = 'flat'
+ for o, a in opts:
+ if o in ('-c', '--cipher'): cipher = a
+ elif o in ('-m', '--mac'): mac = a
+ elif o in ('-h', '--hash'): hash = a
+ elif o in ('-d', '--database'): dbty = a
+ else: raise Exception("barf")
+ if len(args) > 2: return 1
+ if len(args) >= 1: tag = args[0]
+ else: tag = 'pwsafe'
+
+ ## Set up the database.
+ if mac is None: mac = hash + '-hmac'
+ try: dbcls = StorageBackend.byname(dbty)
+ except KeyError: die("Unknown database backend `%s'" % dbty)
+ PW.create(dbcls, file, tag,
+ C.gcciphers[cipher], C.gchashes[hash], C.gcmacs[mac])
+
+def cmd_changepp(av):
+ if len(av) != 0: return 1
+ with PW(file, writep = True) as pw: pw.changepp()
+
+def cmd_find(av):
+ if len(av) != 1: return 1
+ with PW(file) as pw:
+ try: print(_text(pw[_bin(av[0])]))
+ except KeyError: die("Password `%s' not found" % _excval().args[0])
+
+def cmd_store(av):
+ if len(av) < 1 or len(av) > 2: return 1
+ tag = av[0]
+ with PW(file, writep = True) as pw:
+ if len(av) < 2:
+ pp = C.getpass("Enter passphrase `%s': " % tag)
+ vpp = C.getpass("Confirm passphrase `%s': " % tag)
+ if pp != vpp: die("passphrases don't match")
+ elif av[1] == '-':
+ pp = _bin(stdin.readline().rstrip('\n'))
+ else:
+ pp = _bin(av[1])
+ pw[_bin(av[0])] = pp
+
+def cmd_copy(av):
+ if len(av) < 1 or len(av) > 2: return 1
+ with PW(file) as pw_in:
+ with PW(av[0], writep = True) as pw_out:
+ if len(av) >= 3: pat = av[1]
+ else: pat = None
+ for k in pw_in:
+ ktext = _text(k)
+ if pat is None or fnmatch(ktext, pat): pw_out[k] = pw_in[k]
+
+def cmd_list(av):
+ if len(av) > 1: return 1
+ with PW(file) as pw:
+ if len(av) >= 1: pat = av[0]
+ else: pat = None
+ for k in pw:
+ ktext = _text(k)
+ if pat is None or fnmatch(ktext, pat): print(ktext)
+
+def cmd_topixie(av):
+ if len(av) > 2: return 1
+ with PW(file) as pw:
+ pix = C.Pixie()
+ if len(av) == 0:
+ for tag in pw: pix.set(tag, pw[_bin(tag)])
+ else:
+ tag = _bin(av[0])
+ if len(av) >= 2: pptag = av[1]
+ else: pptag = av[0]
+ pix.set(pptag, pw[tag])
+
+def cmd_del(av):
+ if len(av) != 1: return 1
+ with PW(file, writep = True) as pw:
+ tag = _bin(av[0])
+ try: del pw[tag]
+ except KeyError: die("Password `%s' not found" % _excval().args[0])
+
+def cmd_xfer(av):
+
+ ## Parse the command line.
+ try: opts, args = getopt(av, 'd:', ['database='])
+ except GetoptError: return 1
+ dbty = 'flat'
+ for o, a in opts:
+ if o in ('-d', '--database'): dbty = a
+ else: raise Exception("barf")
+ if len(args) != 1: return 1
+ try: dbcls = StorageBackend.byname(dbty)
+ except KeyError: die("Unknown database backend `%s'" % dbty)
+
+ ## Create the target database.
+ with StorageBackend.open(file) as db_in:
+ with dbcls.create(args[0]) as db_out:
+ for k, v in db_in.iter_meta(): db_out.put_meta(k, v)
+ for k, v in db_in.iter_passwds(): db_out.put_passwd(k, v)
+
+commands = { 'create': [cmd_create,
+ '[-c CIPHER] [-d DBTYPE] [-h HASH] [-m MAC] [PP-TAG]'],
+ 'find' : [cmd_find, 'LABEL'],
+ 'store' : [cmd_store, 'LABEL [VALUE]'],
+ 'list' : [cmd_list, '[GLOB-PATTERN]'],
+ 'changepp' : [cmd_changepp, ''],
+ 'copy' : [cmd_copy, 'DEST-FILE [GLOB-PATTERN]'],
+ 'to-pixie' : [cmd_topixie, '[TAG [PIXIE-TAG]]'],
+ 'delete' : [cmd_del, 'TAG'],
+ 'xfer': [cmd_xfer, '[-d DBTYPE] DEST-FILE'] }
+
+###--------------------------------------------------------------------------
+### Command-line handling and dispatch.
+
+def version():
+ print('%s 1.0.0' % prog)
+ print('Backend types: %s' %
+ ' '.join([c.NAME for c in StorageBackend.classes()]))
+
+def usage(fp):
+ fp.write('Usage: %s COMMAND [ARGS...]\n' % prog)
+
+def help():
+ version()
+ print('')
+ usage(stdout)
+ print('''
+Maintains passwords or other short secrets securely.
+
+Options:
+
+-h, --help Show this help text.
+-v, --version Show program version number.
+-u, --usage Show short usage message.
+
+-f, --file=FILE Where to find the password-safe file.
+
+Commands provided:
+''')
+ for c in sorted(commands):
+ print('%s %s' % (c, commands[c][1]))
+
+## Choose a default database file.
+if 'PWSAFE' in environ:
+ file = environ['PWSAFE']
+else:
+ file = '%s/.pwsafe' % environ['HOME']
+
+## Parse the command-line options.
+try:
+ opts, argv = getopt(argv[1:], 'hvuf:',
+ ['help', 'version', 'usage', 'file='])
+except GetoptError:
+ usage(stderr)
+ exit(1)
+for o, a in opts:
+ if o in ('-h', '--help'):
+ help()
+ exit(0)
+ elif o in ('-v', '--version'):
+ version()
+ exit(0)
+ elif o in ('-u', '--usage'):
+ usage(stdout)
+ exit(0)
+ elif o in ('-f', '--file'):
+ file = a
+ else:
+ raise Exception("barf")
+if len(argv) < 1:
+ usage(stderr)
+ exit(1)
+
+## Dispatch to a command handler.
+if argv[0] in commands:
+ c = argv[0]
+ argv = argv[1:]
+else:
+ c = 'find'
+try:
+ if commands[c][0](argv):
+ stderr.write('Usage: %s %s %s\n' % (prog, c, commands[c][1]))
+ exit(1)
+except DecryptError:
+ die("decryption failure")
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+.\" -*-nroff-*-
+.\"
+.\" Describe the password safe
+.\"
+.\" (c) 2016 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.
+.
+.ie t \{\
+. if \n(.g \{\
+. fam P
+. \}
+. ds o \(bu
+.\}
+.el \{\
+. ds o o
+.\}
+.
+.de hP
+.IP
+\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c
+..
+.
+.TH pwsafe 1 "13 May 2016" "Straylight/Edgeware" "Catacomb cryptographic library"
+.SH NAME
+pwsafe \- store passwords somewhat securely
+.
+.\"--------------------------------------------------------------------------
+.SH SYNOPSIS
+.
+.B pwsafe
+.RB [ \-f
+.IR file ]
+.I command
+.RI [ arguments
+\&...]
+.PP
+Subcommands:
+'RS
+.B changepp
+.br
+.B copy
+.I dest-file
+.RI [ glob-pattern ]
+.br
+.B create
+.RB [ \-c
+.IR cipher ]
+.RB [ \-d
+.IR db-type ]
+.RB [ \-h
+.IR hash ]
+.RB [ \-m
+.IR mac ]
+.RI [ pp-tag ]
+.br
+.B delete
+.I tag
+.br
+.RB [ find ]
+.I label
+.br
+.B list
+.RI [ glob-pattern ]
+.br
+.B store
+.I label
+.RI [ value ]
+.br
+.B to-pixie
+.RI [ label
+.RI [ pixie-tag ]]
+.br
+.B xfer
+.RB [ \-d
+.IR db-type ]
+.I dest-file
+.RE
+.
+.\"--------------------------------------------------------------------------
+.SH DESCRIPTION
+.
+The
+.B pwsafe
+command maintains a database of passwords
+or other short-ish textual secrets.
+Each password is identified by a
+.IR label .
+The database is ultimately protected by a master password.
+It should quite difficult for an adversary
+who has the database file(s)
+but not your master password
+to find out what any of the stored passwords are,
+or even what the labels are.
+.PP
+The
+.B pwsafe
+program will prompt you for the password as necessary.
+If you are running the Catacomb
+.BR pixie (1)
+then it will first ask the pixie for any necessary passwords,
+and use the pixie to remember the master password for a short while.
+.PP
+A number of database backends are available.
+.TP
+.B gdbm
+Uses the GDBM database library to store a database as a single file.
+Provided for compatibility;
+not recommended for new databases.
+.TP
+.B sqlite
+Uses the SQLite3 library to store a database as a single file.
+SQLite3 has good performance and integrity properties.
+.TP
+.B flat
+Stores the password database as a flat text file.
+Not recommended for large databases because performance will be bad,
+but the simple format admits easy hacking.
+.TP
+.B dir
+Stores the password database as a directory structure.
+This uses much more disk space than the alternatives,
+and enumerating passwords is slow,
+but the directory structure can easily be managed by
+a version control system such as Git.
+.PP
+The following command-line options are available.
+.TP
+.B "\-h, \-\-help"
+Print a help summary to standard output,
+and exit with status zero.
+.TP
+.B "\-v, \-\-version"
+Print the program's version number to standard output,
+and exit with status zero.
+.TP
+.B "\-u, \-\-usage"
+Print a very terse usage summary to standard output,
+and exit with status zero.
+.TP
+.BI "\-f, \-\-file=" file
+Use
+.I file
+as the password database file or directory.
+If this is not specified,
+then the value of the
+.B PWSAFE
+environment variable is used;
+if that too is unset, then the default
+.IB home /.pwsafe \fR,
+is used, where
+.I home
+is the value of the
+.B HOME
+environment variable.
+It is a fatal error if
+.B HOME
+is unset.
+.PP
+The provided commands are described in their own sections below.
+.
+.SS "create"
+Create a new, empty password database.
+As a safety check,
+the file/directory named by the top-level
+.B \-f
+option (or default value)
+must not exist.
+.PP
+You will be prompted (twice) for a master password for the database.
+.PP
+The optional positional argument is the tag
+by which the database's master password
+will be known to the passphrase pixie.
+The default tag is
+.BR pwsafe .
+.PP
+The following options are accepted.
+.TP
+.BI "\-c, \-\-cipher=" cipher
+Use the Catacomb
+.I cipher
+to encrypt blobs.
+Run
+.B catcrypt show cipher
+for a list.
+The default is
+.BR rijndael-cbc .
+.TP
+.BI "\-d, \-\-database=" db-type
+Use the
+.I db-type
+database backend.
+See above for a list of the supported backends.
+Note that
+.B gdbm
+and
+.B sqlite
+depend on external modules, and may not be available.
+The default is
+.BR flat .
+.TP
+.BI "\-h, \-\-hash=" hash
+Use the Catacomb
+.I hash
+for key derivation and password-label mangling.
+Run
+.B catcrypt show hash
+for a list.
+The default is
+.BR sha256 .
+.TP
+.BI "\-m, \-\-mac=" mac
+Use the Catacomb
+.I mac
+to protect the integrity of blobs.
+Run
+.B catcrypt show mac
+for a list.
+The default is
+.IR hash -hmac
+where
+.I hash
+is the hash function chosen by the
+.B \-h
+option.
+.
+.SS "changepp"
+Change the master password for a database.
+This will
+.I not
+re-encrypt all of the records,
+so its utility is somewhat limited.
+See also the
+.B copy
+command.
+The program will prompt you for
+the existing master password (if it's not known by the pixie)
+and the new one (twice, always).
+.
+.SS "copy"
+Copy password records from the
+.I file
+to the
+.I dest-file
+which must be an existing password database.
+If a
+.B glob-pattern
+is given,
+then only records whose
+.I label
+matches the pattern are copied;
+otherwise all password records are copied.
+Any existing passwords in the destination database with the same labels
+will be overwritten.
+.PP
+The destination need not use the same database backend
+or cryptographic parameters as the source.
+.PP
+You will be prompted for the necessary master passwords.
+.
+.SS "delete"
+Delete the password with the given
+.I label
+from the database.
+An error is reported if there is no such password.
+.PP
+You will be prompted for master password if necessary.
+.
+.SS "find"
+Write the password with the given label to standard output,
+followed by a newline.
+.PP
+You will be prompted for master password if necessary.
+.PP
+This is the default operation:
+as a convenience,
+you can write
+.IP
+.B pwsafe
+.I label
+.PP
+rather than
+.IP
+.B pwsafe
+.B find
+.I label
+.PP
+if the
+.I label
+isn't the same as any of
+.BR pwsafe 's
+command names.
+.
+.SS "list"
+Write the labels of the passwords in the database,
+one per line,
+to standard output.
+(If labels contain newline characters,
+you will end up with a mess.)
+If a
+.I glob-pattern
+is supplied,
+then only labels which match the pattern are written.
+.PP
+You will be prompted for master password if necessary.
+.
+.SS "store"
+Store a password, associating it with the given
+.IR label .
+.PP
+If a
+.I value
+is supplied on the command line,
+then it is used as the password value.
+(Note that command-line arguments can be seen
+by other users of the system,
+and may be recorded by the shell.
+This is usually a bad idea.)
+.PP
+As a special case, if the
+.I value
+is
+.BR \- ,
+then the password is read from the first line of standard input;
+the trailing newline is removed.
+The author commonly writes
+.IP
+.BI "gorp -fbase64 | pwsafe store " label " \-"
+.PP
+to set random passwords.
+.PP
+Finally, if no
+.I value
+is given,
+then
+.B pwsafe
+will prompt twice for the password.
+.PP
+You will be prompted for the master password if necessary
+.I before
+the new password value is fetched.
+.PP
+If there is an existing password with the same
+.I label
+then it is overwritten.
+.
+.SS "topixie"
+With no arguments,
+store
+.I all
+of the passwords in the database in the pixie,
+with correspondingly named tags.
+This is probably a bad idea.
+.PP
+With a
+.IR label ,
+store only the labelled password in the pixie.
+With a
+.IR pixie-tag ,
+use that as the tag;
+otherwise use the
+.IR label .
+.PP
+You will be prompted for the master password if necessary.
+.
+.SS "xfer"
+Create a new database containing all of the records of an existing one.
+.PP
+This works at the storage-backend level.
+The new database contains exactly the same metadata and passwords
+as the original.
+It is
+.I not
+necessary to enter a password:
+the blobs are simply copied over without being decrypted.
+.PP
+The following options are accepted.
+.TP
+.BI "\-d, \-\-database=" db-type
+Use the
+.I db-type
+database backend.
+See above for a list of the supported backends.
+Note that
+.B gdbm
+and
+.B sqlite
+depend on external modules, and may not be available.
+The default is
+.BR flat .
+.
+.\"--------------------------------------------------------------------------
+.SH TECHNICAL DETAILS
+.
+The password database contains two kinds of records:
+.I metadata records
+hold important information about the database itself,
+and particularly the various cryptographic options
+chosen when the database was created,
+and the various internal keys used to secure the database;
+while
+.I password records
+actually store your encrypted passwords.
+The various backends store these kinds records in different ways;
+see below for the gory details.
+.
+.SS Metadata
+The metadata records are as follows.
+.TP
+.B cipher
+The symmetric cipher used to encrypt data.
+This names a Catacomb
+.B cipher
+class.
+.TP
+.B hash
+The hash function used in various places.
+This names a Catacomb
+.B hash
+class.
+.TP
+.B key
+A blob,
+protected by the
+.I derived
+keys (see below),
+containing the
+.I main
+secrecy and integrity keys.
+The blob payload consists of the main secrecy and integrity keys,
+each prefixed by its 16-bit length (in network byte order)
+and concatenated.
+.TP
+.B mac
+The message authentication code used to protect integrity.
+This names a Catacomb
+.B mac
+class.
+.TP
+.B magic
+A blob containing a string
+used to construct the database keys for password records;
+see below.
+The magic string is chosen at random
+when the database is created,
+and never changes;
+it is the same length as the chosen hash function's output.
+The blob is protected by the
+.I main
+keys.
+.TP
+.B salt
+A random string
+mixed into the key derivation process.
+.TP
+.B tag
+The passphrase tag,
+used to identify the master password
+to the passphrase
+.BR pixie (1).
+.
+.SS Keys
+The following keys are used.
+.TP
+The \fImaster password\fP
+Remembered (hopefully) by the user;
+used to unlock the
+.I main
+keys.
+.TP
+The \fIderived\fP keys
+A secrecy and integrity pair,
+derived from the
+.I master password
+and
+salt using a hash function.
+.TP
+The \fImain\fP keys
+A secrecy and integrity pair,
+kept in a blob in the database
+(the
+.B key
+metadata item)
+protected by the
+.I derived
+keys.
+The main keys are generated at random
+when the database is created
+and they never change;
+the Catacomb default key lengths are used.
+.
+.SS Deriving keys from the master password
+The keys used for protecting the
+.I main
+secrecy and integrity keys
+are derived by hashing strings of the form
+.IB purpose : password \e0 salt \fR,
+where
+.I purpose
+is
+.B cipher
+or
+.B mac
+to derive the secrecy and integrity keys, respectively.
+The
+.I salt
+string is the value of the
+.B salt
+metadata item described below.
+.PP
+No attempt is made to make the key derivation slow;
+.B pwsafe
+takes the view that you are have been specifically targetted for attack
+by a well-resourced adversary,
+and that you
+.I will
+lose if your password is guessable.
+.
+.SS Making a blob
+A
+.I blob
+contains a
+.IR payload ,
+protecting its secrecy and integrity.
+A blob is constructed using a pair of secrecy and integrity keys;
+most blobs are protected with the
+.I main
+keys;
+the main keys themselves are protected with the
+.I derived
+keys.
+.PP
+The steps to construct a blob are as follows.
+.hP 1.
+Choose a random IV of the appropriate length for the chosen
+.BR cipher .
+.hP 2.
+Encrypt the blob payload
+using the chosen
+.B cipher
+with the secrecy key
+and the IV from step 1,
+to form a ciphertext.
+Prefix the ciphertext with the IV
+to form an augmented ciphertext.
+.hP 3.
+Compute a tag over the augmented ciphertext from step 2
+using the chosen
+.B mac
+with the integrity key.
+Prefix the augmented ciphertext with the tag
+to form the blob.
+.PP
+(It seems more usual to put the tag on the end of the ciphertext,
+but that turned out to be pointlessly harder to implement.)
+.
+.SS Password records
+Conceptually,
+password records are indexed with a textual
+.I label
+chosen by the user.
+But users may want to not only keep their passwords secret,
+but also information about
+.I which
+passwords they have.
+The
+.B pwsafe
+program attempts to maintain the privacy of password record labels,
+but it isn't completely successful, as we shall see.
+Most critically,
+the database backends tend to leak information about
+the
+.I order
+in which records were added into the database.
+.PP
+At the database backend,
+the key used for looking up a password record is a hash,
+in binary:
+specifically, it's a hash of
+the record label
+prefixed by the
+.I magic
+string which is the payload of the blob stored in the
+.B magic
+metadata record.
+.PP
+The value of the password record is a blob,
+protected by the
+.I main
+keys,
+whose payload consists of
+.hP \*o
+the 16-bit network-byte-order length of the record label;
+.hP \*o
+the record label itself;
+.hP \*o
+the 16-bit network-byte-order length of the password;
+.hP \*o
+the password itself; and
+.hP \*o
+zero or more zero-valued octets,
+so as to make the payload a multiple of 256 octets long.
+.PP
+The padding serves to conceal the length of the label and password
+from an adversary who has obtained a copy of the database.
+.
+.SS Backend formats
+The various password-database backends
+represent the records described above as follows.
+.TP
+.B gdbm
+A GDBM-backed database is stored in a single file.
+A metadata record with key
+.I r
+and value
+.I v
+is stored in a GDBM record also named
+.IR r ,
+and with value
+.I v ;
+a password record with hash
+.I h
+and blob
+.I b
+is stored in a GDBM record named
+.BI $ h
+with value
+.IR b ,
+both in raw binary.
+.TP
+.B sqlite
+A SQLite-backed database is stored in a single file.
+It contains two tables,
+named
+.B meta
+and
+.BR passwd .
+The
+.B meta
+table has a primary key
+.B name
+and a further column
+.BR value ;
+a metadata record with key
+.I r
+and value
+.I v
+is held in a
+.B meta
+record
+with
+.B name
+.I r
+and
+.B value
+.IR v ;
+additionally, there is a record with
+.B name
+.B $version
+whose
+.B value
+is the schema version;
+this is currently always 0.
+The
+.B passwd
+table has a primary key
+.B label
+and a further column
+.BR payload ;
+a password record with hash
+.I h
+and blob
+.I b
+is stored in a
+.B passwd
+record with
+.B label
+.IR h
+and
+.B payload
+.IR b ,
+both in raw binary.
+.TP
+.B flat
+A flat-file-backed database is stored in a single file,
+with one record per line.
+The first line must be exactly
+.RS
+.IP
+.B "### pwsafe password database"
+.PP
+Blank lines and lines beginning with a
+.RB ` # '
+are ignored.
+.PP
+A metadata record named
+.I r
+with value
+.I v
+is stored as a line of the form
+.IB r\fR\(fm = v\fR\(fm
+where
+.IR r \fR\(fm
+and
+.IR v \fR\(fm
+are encodings of the strings
+.I r
+and
+.I v
+respectively.
+If
+.I r
+consists only of letters, digits,
+and the punctuation characters
+.RB ` \- ',
+.RB ` _ ',
+.RB ` : ',
+.RB ` . ',
+and
+.RB ` / '
+then
+.IR r \fR\(fm
+is simply
+.IR r ;
+otherwise
+.IR r \fR\(fm
+is formed by (simultaneously) replacing
+each space character in
+.I r
+with
+.RB ` + '
+and each other character
+which is not a letter, digit, or
+one of the punctuation characters listed above
+with
+.RB ` % '
+followed by that character's ASCII code in hex,
+and prefixing the whole lot by
+.RB ` ! '.
+Similarly,
+if
+.I v
+consists of letters, digits,
+and the punctuation characters listed above,
+then
+.IR v \fR\(fm
+is simply
+.IR v ;
+otherwise
+.IR v \fR\(fm
+consists of a
+.RB ` ? '
+followed by the base64 encoding of
+.IR v ,
+without any trailing
+.RB ` = '
+characters.
+.PP
+A password record with hash
+.I h
+and blob
+.I b
+is stored as a line of the form
+.BI $ h\fR\(fm = b\fR\(fm
+where
+.IR h \(fm
+and
+.IR b \(fm
+are the base64 encodings of
+.I h
+and
+.I b
+respectively,
+without trailing
+.RB ` = '
+characters.
+.PP
+The records may appear in any order.
+The file is completely rewritten when any change is made;
+if the file is named
+.I f
+then this is done by writing the new contents to
+.IB f .new
+and then renaming
+.IB f .new
+to
+.IR f .
+.RE
+.TP
+.B dir
+A directory-backed database is stored as a directory,
+named
+.I d
+in the sequel.
+The directory must contain a file
+.IB d /meta
+whose first line is
+.RS
+.IP
+.B "### pwsafe password directory metadata"
+.PP
+and directories
+.IB d /pw
+and
+.IB d /tmp \fR.
+.PP
+Metadata records are stored in file
+.IB d /meta
+with one record per line,
+exactly as for the
+.B file
+backend described above.
+.PP
+A password record with hash
+.I h
+and blob
+.I b
+is stored as file named
+.IB d /pw/ h \fR\(fm
+where
+.IR h \(fm
+is the base64 encodings of
+.I h
+without trailing
+.RB ` = '
+characters,
+and with all
+.RB ` / '
+characters
+replaced by
+.RB ` . 's,
+whose content is
+.IR b .
+.PP
+The directory
+.IB d /tmp/
+is used in an unspecified manner
+when creating new password-record files.
+The
+.IB d /meta
+and
+.IB d /pw/ h \fR\(fm
+files are updated by creating a new temporary file and renaming.
+.RE
+.
+.\"--------------------------------------------------------------------------
+.
+.SH BUGS
+This is quite an old program,
+though the manpage is new.
+It provides more footguns than is ideal.
+.
+.SH SEE ALSO
+.BR catcrypt (1),
+.BR pixie (1).
+.
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
--- /dev/null
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+ (append
+ '((author . "Straylight/Edgeware")
+ (licence-text . "[[gpl]]")
+ (full-title . "Pyke: the Python Kit for Extensions")
+ (program . "Pyke"))
+ skel-alist))
--- /dev/null
+/* -*-c-*-
+ *
+ * Random-number generators
+ *
+ * (c) 2004 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+PUBLIC_SYMBOLS;
+#include "algorithms.h"
+PRIVATE_SYMBOLS;
+
+/*----- Main code ---------------------------------------------------------*/
+
+PyTypeObject *grand_pytype;
+static PyTypeObject *truerand_pytype;
+static PyTypeObject *lcrand_pytype, *fibrand_pytype;
+static PyTypeObject *dsarand_pytype, *bbs_pytype, *bbspriv_pytype;
+static PyTypeObject *sslprf_pytype, *tlsdx_pytype, *tlsprf_pytype;
+PyObject *rand_pyobj;
+
+static PyObject *gccrands_dict;
+
+static PyObject *grand_dopywrap(PyTypeObject *ty, grand *r, unsigned f)
+{
+ grand_pyobj *g;
+
+ g = (grand_pyobj *)ty->tp_alloc(ty, 0);
+ g->r = r;
+ g->f = f;
+ return ((PyObject *)g);
+}
+
+PyObject *grand_pywrap(grand *r, unsigned f)
+{
+ PyTypeObject *ty = grand_pytype;
+ PyObject *ob;
+
+ if (STRCMP(r->ops->name, ==, "rand")) ty = truerand_pytype;
+ else if (STRCMP(r->ops->name, ==, "lcrand")) ty = lcrand_pytype;
+ else if (STRCMP(r->ops->name, ==, "fibrand")) ty = fibrand_pytype;
+ else if (STRCMP(r->ops->name, ==, "dsarand")) ty = dsarand_pytype;
+ else if (STRCMP(r->ops->name, ==, "bbs")) ty = bbs_pytype;
+ else if (STRCMP(r->ops->name, ==, "sslprf")) ty = sslprf_pytype;
+ else if (STRCMP(r->ops->name, ==, "tlsdx")) ty = tlsdx_pytype;
+ else if (STRCMP(r->ops->name, ==, "tlsprf")) ty = tlsprf_pytype;
+ else if ((ob = PyMapping_GetItemString
+ (gccrands_dict, (/*unconst*/ char *)r->ops->name)) != 0)
+ ty = (PyTypeObject *)ob;
+ return (grand_dopywrap(ty, r, f));
+}
+
+CONVFUNC(grand, grand *, GRAND_R)
+
+static int grand_check(PyObject *me)
+{
+ if (!GRAND_R(me)) VALERR("random generator object is no longer valid");
+ return (0);
+end:
+ return (-1);
+}
+
+static PyObject *grmeth_byte(PyObject *me)
+{
+ if (grand_check(me)) return (0);
+ return (PyInt_FromLong(grand_byte(GRAND_R(me))));
+}
+
+static PyObject *grmeth_word(PyObject *me)
+{
+ if (grand_check(me)) return (0);
+ return (getulong(grand_word(GRAND_R(me))));
+}
+
+static PyObject *grmeth_range(PyObject *me, PyObject *arg)
+{
+ PyObject *m;
+ mp *x = 0;
+ mp *y = 0;
+
+ if (!PyArg_ParseTuple(arg, "O:range", &m)) return (0);
+ if (grand_check(me)) return (0);
+ if (PyInt_Check(m)) {
+ long mm = PyInt_AsLong(m);
+ if (mm == -1 && PyErr_Occurred()) PyErr_Clear();
+ else if (mm <= 0) goto notpos;
+ else if (mm <= 0xffffffff)
+ return (PyInt_FromLong(grand_range(GRAND_R(me), mm)));
+ }
+ if ((x = getmp(m)) == 0) goto end;
+ if (!MP_POSP(x)) goto notpos;
+ y = mprand_range(MP_NEW, x, GRAND_R(me), 0);
+ MP_DROP(x);
+ return (mp_pywrap(y));
+notpos:
+ VALERR("range must be strictly positive");
+end:
+ if (x) MP_DROP(x);
+ return (0);
+}
+
+static PyObject *grmeth_mp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ size_t l;
+ mpw o = 0;
+ static const char *const kwlist[] = { "bits", "or", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", KWLIST,
+ convszt, &l, convmpw, &o))
+ goto end;
+ if (grand_check(me)) return (0);
+ if (l < MPW_BITS && (o >> l)) VALERR("or mask too large");
+ return (mp_pywrap(mprand(MP_NEW, l, GRAND_R(me), o)));
+end:
+ return (0);
+}
+
+static PyObject *grmeth_block(PyObject *me, PyObject *arg)
+{
+ unsigned long n;
+ PyObject *rc = 0;
+
+ if (!PyArg_ParseTuple(arg, "O&:block", convulong, &n)) goto end;
+ if (grand_check(me)) return (0);
+ rc = bytestring_pywrap(0, n);
+ grand_fill(GRAND_R(me), BIN_PTR(rc), n);
+end:
+ return (rc);
+}
+
+static int checkop(grand *r, unsigned op, const char *what)
+{
+ if (r->ops->misc(r, GRAND_CHECK, op)) return (0);
+ PyErr_Format(PyExc_TypeError, "operation %s not supported", what);
+ return (-1);
+}
+
+static PyObject *grmeth_seedint(PyObject *me, PyObject *arg)
+{
+ int i;
+ grand *r = GRAND_R(me);
+ if (!PyArg_ParseTuple(arg, "i:seedint", &i) ||
+ grand_check(me) || checkop(r, GRAND_SEEDINT, "seedint"))
+ goto end;
+ r->ops->misc(r, GRAND_SEEDINT, i);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *grmeth_seedword(PyObject *me, PyObject *arg)
+{
+ uint32 u;
+ grand *r = GRAND_R(me);
+ if (!PyArg_ParseTuple(arg, "O&:seedword", convu32, &u) ||
+ grand_check(me) || checkop(r, GRAND_SEEDUINT32, "seedword"))
+ goto end;
+ r->ops->misc(r, GRAND_SEEDUINT32, u);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *grmeth_seedblock(PyObject *me, PyObject *arg)
+{
+ struct bin in;
+ grand *r = GRAND_R(me);
+ if (!PyArg_ParseTuple(arg, "O&:seedblock", convbin, &in) ||
+ grand_check(me) || checkop(r, GRAND_SEEDBLOCK, "seedblock"))
+ goto end;
+ r->ops->misc(r, GRAND_SEEDBLOCK, in.p, (size_t)in.sz);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *grmeth_seedmp(PyObject *me, PyObject *arg)
+{
+ PyObject *x;
+ mp *xx;
+ grand *r = GRAND_R(me);
+ if (!PyArg_ParseTuple(arg, "O:seedmp", &x) ||
+ grand_check(me) || checkop(r, GRAND_SEEDMP, "seedmp") ||
+ (xx = getmp(x)) == 0)
+ goto end;
+ r->ops->misc(r, GRAND_SEEDMP, xx);
+ MP_DROP(xx);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *grmeth_seedrand(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { "rng", 0 };
+ grand *r = GRAND_R(me);
+ grand *rr = &rand_global;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", KWLIST,
+ convgrand, &rr) ||
+ grand_check(me) || checkop(r, GRAND_SEEDRAND, "seedrand"))
+ goto end;
+ r->ops->misc(r, GRAND_SEEDRAND, rr);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *grmeth_mask(PyObject *me, PyObject *arg)
+{
+ grand *r = GRAND_R(me);
+ struct bin in;
+ const octet *p; size_t n;
+ octet *q;
+ PyObject *rc;
+
+ if (!PyArg_ParseTuple(arg, "O&:mask", convbin, &in)) return (0);
+ if (grand_check(me)) return (0);
+ rc = bytestring_pywrap(0, in.sz);
+ q = (octet *)BIN_PTR(rc);
+ GR_FILL(r, q, in.sz);
+ p = in.p; n = in.sz; while (n--) *q++ ^= *p++;
+ return (rc);
+}
+
+static void grand_pydealloc(PyObject *me)
+{
+ grand_pyobj *g = (grand_pyobj *)me;
+ if ((g->f & f_freeme) && g->r) GR_DESTROY(g->r);
+ FREEOBJ(me);
+}
+
+static PyObject *grget_name(PyObject *me, void *hunoz)
+ { return (grand_check(me) ? 0 : TEXT_FROMSTR(GRAND_R(me)->ops->name)); }
+
+static PyObject *grget_cryptop(PyObject *me, void *hunoz)
+ { return (grand_check(me) ? 0 : getbool(GRAND_R(me)->ops->f & GRAND_CRYPTO)); }
+
+static const PyGetSetDef grand_pygetset[] = {
+#define GETSETNAME(op, name) gr##op##_##name
+ GET (name, "R.name -> name of this kind of generator")
+ GET (cryptop, "R.cryptop -> flag: cryptographically strong?")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef grand_pymethods[] = {
+#define METHNAME(name) grmeth_##name
+ NAMETH(byte, "R.byte() -> BYTE")
+ NAMETH(word, "R.word() -> WORD")
+ METH (block, "R.block(N) -> STRING")
+ KWMETH(mp, "R.mp(bits, [or = 0]) -> MP")
+ METH (range, "R.range(MAX) -> INT")
+ METH (mask, "R.mask(STR) -> STR")
+ METH (seedint, "R.seedint(I)")
+ METH (seedword, "R.seedword(I)")
+ METH (seedblock, "R.seedblock(BYTES)")
+ METH (seedmp, "R.seedmp(X)")
+ KWMETH(seedrand, "R.seedrand(RR)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject grand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Generic random number source.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(grand), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(grand), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *lcrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw)
+{
+ uint32 n = 0;
+ static const char *const kwlist[] = { "seed", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convu32, &n))
+ return (0);
+ return (grand_dopywrap(lcrand_pytype, lcrand_create(n), f_freeme));
+}
+
+static const PyTypeObject lcrand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "LCRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "LCRand([seed = 0]): linear congruential generator.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ lcrand_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *fibrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw)
+{
+ uint32 n = 0;
+ static const char *const kwlist[] = { "seed", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convu32, &n))
+ return (0);
+ return (grand_dopywrap(fibrand_pytype, fibrand_create(n), f_freeme));
+}
+
+static const PyTypeObject fibrand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "FibRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "FibRand([seed = 0]): Fibonacci generator.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ fibrand_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- True random generator ---------------------------------------------*/
+
+static PyObject *trmeth_gate(PyObject *me)
+ { grand *r = GRAND_R(me); r->ops->misc(GRAND_R(me), RAND_GATE); RETURN_ME; }
+
+static PyObject *trmeth_stretch(PyObject *me)
+ { grand *r = GRAND_R(me); r->ops->misc(r, RAND_STRETCH); RETURN_ME; }
+
+static PyObject *trmeth_add(PyObject *me, PyObject *arg)
+{
+ grand *r = GRAND_R(me);
+ struct bin in; unsigned goodbits;
+ if (!PyArg_ParseTuple(arg, "O&O&:add", convbin, &in, convuint, &goodbits))
+ return (0);
+ r->ops->misc(r, RAND_ADD, in.p, (size_t)in.sz, goodbits);
+ RETURN_ME;
+}
+
+static PyObject *trmeth_key(PyObject *me, PyObject *arg)
+{
+ grand *r = GRAND_R(me);
+ struct bin k;
+ if (!PyArg_ParseTuple(arg, "O&:key", convbin, &k)) return (0);
+ r->ops->misc(r, RAND_KEY, k.p, (size_t)k.sz);
+ RETURN_ME;
+}
+
+static PyObject *trmeth_seed(PyObject *me, PyObject *arg)
+{
+ grand *r = GRAND_R(me);
+ unsigned u;
+ if (!PyArg_ParseTuple(arg, "O&:seed", convuint, &u)) return (0);
+ if (u > RAND_IBITS) VALERR("pointlessly large");
+ r->ops->misc(r, RAND_SEED, u);
+ RETURN_ME;
+end:
+ return (0);
+}
+
+static PyObject *trmeth_timer(PyObject *me)
+ { grand *r = GRAND_R(me); r->ops->misc(r, RAND_TIMER); RETURN_ME; }
+
+static PyObject *truerand_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ static const char *const kwlist[] = { 0 };
+ grand *r;
+ PyObject *rc = 0;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
+ r = rand_create();
+ r->ops->misc(r, RAND_NOISESRC, &noise_source);
+ r->ops->misc(r, RAND_SEED, 160);
+ rc = grand_dopywrap(ty, r, f_freeme);
+end:
+ return (rc);
+}
+
+static const PyMethodDef truerand_pymethods[] = {
+#define METHNAME(name) trmeth_##name
+ NAMETH(gate, "R.gate()")
+ NAMETH(stretch, "R.stretch()")
+ METH (key, "R.key(BYTES)")
+ METH (seed, "R.seed(NBITS)")
+ METH (add, "R.add(BYTES, GOODBITS")
+ NAMETH(timer, "R.timer()")
+#undef METHNAME
+ { 0 }
+};
+
+static PyObject *trget_goodbits(PyObject *me, void *hunoz)
+{
+ grand *r = GRAND_R(me);
+ return (PyInt_FromLong(r->ops->misc(r, RAND_GOODBITS)));
+}
+
+static const PyGetSetDef truerand_pygetset[] = {
+#define GETSETNAME(op, name) tr##op##_##name
+ GET (goodbits, "R.goodbits -> good bits of entropy remaining")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject truerand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "TrueRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "TrueRand(): true random number source.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(truerand), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(truerand), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ truerand_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Generators from symmetric encryption algorithms -------------------*/
+
+static PyTypeObject *gccrand_pytype, *gcrand_pytype, *gclatinrand_pytype;
+
+typedef grand *gcrand_func(const void *, size_t sz);
+typedef grand *gcirand_func(const void *, size_t sz, uint32);
+typedef grand *gcnrand_func(const void *, size_t sz, const void *);
+typedef grand *gcshakerand_func(const void *, size_t,
+ const void *, size_t,
+ const void *, size_t);
+typedef grand *gcshafuncrand_func(const void *, size_t,
+ const void *, size_t);
+typedef grand *gckmacrand_func(const void *, size_t, const void *, size_t);
+typedef struct gccrand_info {
+ const char *name;
+ const octet *keysz;
+ unsigned f;
+ size_t noncesz;
+ gcrand_func *func;
+} gccrand_info;
+
+#define RNGF_MASK 255u
+
+enum {
+ RNG_PLAIN = 0,
+ RNG_SEAL,
+ RNG_LATIN,
+ RNG_SHAKE,
+ RNG_KMAC
+};
+
+typedef struct gccrand_pyobj {
+ PyHeapTypeObject ty;
+ const gccrand_info *info;
+} gccrand_pyobj;
+#define GCCRAND_INFO(o) (((gccrand_pyobj *)(o))->info)
+
+#define GCCRAND_DEF(name, ksz, func, f, nsz) \
+ static const gccrand_info func##_info = \
+ { name, ksz, f, nsz, (gcrand_func *)func };
+RNGS(GCCRAND_DEF)
+
+static const gccrand_info *const gcrandtab[] = {
+#define GCCRAND_ENTRY(name, ksz, func, f, nsz) &func##_info,
+ RNGS(GCCRAND_ENTRY)
+ 0
+};
+
+static PyObject *gcrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ const gccrand_info *info = GCCRAND_INFO(ty);
+ static const char *const kwlist[] = { "key", 0 };
+ struct bin k;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &k))
+ goto end;
+ if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+ return (grand_dopywrap(ty, info->func(k.p, k.sz), f_freeme));
+end:
+ return (0);
+}
+
+static PyObject *gcirand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ const gccrand_info *info = GCCRAND_INFO(ty);
+ uint32 i = 0;
+ static const char *const kwlist[] = { "key", "i", 0 };
+ struct bin k;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convbin, &k, convu32, &i))
+ goto end;
+ if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+ return (grand_dopywrap(ty,
+ ((gcirand_func *)info->func)(k.p, k.sz, i),
+ f_freeme));
+end:
+ return (0);
+}
+
+static PyObject *gcnrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ const gccrand_info *info = GCCRAND_INFO(ty);
+ static const char *const kwlist[] = { "key", "nonce", 0 };
+ static const octet zn[24] = { 0 };
+ struct bin k, n;
+
+ n.p = zn; n.sz = info->noncesz; assert(info->noncesz <= sizeof(zn));
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convbin, &k, convbin, &n))
+ goto end;
+ if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+ if (n.sz != info->noncesz) VALERR("bad nonce length");
+ return (grand_dopywrap(ty,
+ ((gcnrand_func *)info->func)(k.p, k.sz, n.p),
+ f_freeme));
+end:
+ return (0);
+}
+
+static PyObject *gcshakyrand_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ const gccrand_info *info = GCCRAND_INFO(ty);
+ static const char
+ *const kwlist_shake[] = { "key", "func", "perso", 0 },
+ *const kwlist_func[] = { "key", "perso", 0 };
+ struct bin k, f = { 0, 0 }, p = { 0, 0 };
+
+ if ((info->f&RNGF_MASK) == RNG_SHAKE
+ ? !PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&O&:new",
+ (/*unconst*/ char **)kwlist_shake,
+ convbin, &k,
+ convbin, &f, convbin, &p)
+ : !PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new",
+ (/*unconst*/ char **)kwlist_func,
+ convbin, &k, convbin, &p))
+ goto end;
+ if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+ return (grand_dopywrap(ty,
+ (info->f&RNGF_MASK) == RNG_SHAKE
+ ? ((gcshakerand_func *)info->func)(f.p, f.sz,
+ p.p, p.sz,
+ k.p, k.sz)
+ : ((gcshafuncrand_func *)info->func)(p.p, p.sz,
+ k.p, k.sz),
+ f_freeme));
+end:
+ return (0);
+}
+
+static PyObject *gccrand_pywrap(const gccrand_info *info)
+{
+ gccrand_pyobj *g = newtype(gccrand_pytype, 0, info->name);
+ g->info = info;
+ g->ty.ht_type.tp_basicsize = sizeof(grand_pyobj);
+ switch (info->f&RNGF_MASK) {
+ case RNG_LATIN: g->ty.ht_type.tp_base = gclatinrand_pytype; break;
+ default: g->ty.ht_type.tp_base = gcrand_pytype; break;
+ }
+ Py_INCREF(g->ty.ht_type.tp_base);
+ g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HEAPTYPE);
+ g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+ g->ty.ht_type.tp_free = 0;
+ switch (info->f&RNGF_MASK) {
+ case RNG_LATIN: g->ty.ht_type.tp_new = gcnrand_pynew; break;
+ case RNG_SEAL: g->ty.ht_type.tp_new = gcirand_pynew; break;
+ case RNG_SHAKE: case RNG_KMAC:
+ g->ty.ht_type.tp_new = gcshakyrand_pynew; break;
+ default: g->ty.ht_type.tp_new = gcrand_pynew; break;
+ }
+ typeready(&g->ty.ht_type);
+ return ((PyObject *)g);
+}
+
+static PyObject *gccrget_name(PyObject *me, void *hunoz)
+ { return (TEXT_FROMSTR(GCCRAND_INFO(me)->name)); }
+static PyObject *gccrget_keysz(PyObject *me, void *hunoz)
+ { return (keysz_pywrap(GCCRAND_INFO(me)->keysz)); }
+
+static PyObject *gclrmeth_tell(PyObject *me)
+{
+ grand *r = GRAND_R(me);
+ PyObject *rc = 0;
+ kludge64 off;
+
+ r->ops->misc(r, SALSA20_TELLU64, &off);
+ rc = getk64(off);
+ return (rc);
+}
+
+static PyObject *gclrmeth_seek(PyObject *me, PyObject *arg)
+{
+ grand *r = GRAND_R(me);
+ kludge64 off;
+
+ if (!PyArg_ParseTuple(arg, "O&:seek", convk64, &off)) return (0);
+ r->ops->misc(r, SALSA20_SEEKU64, off);
+ RETURN_ME;
+}
+
+static const PyGetSetDef gccrand_pygetset[] = {
+#define GETSETNAME(op, name) gccr##op##_##name
+ GET (keysz, "CR.keysz -> acceptable key sizes")
+ GET (name, "CR.name -> name of this kind of generator")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyMethodDef gclatinrand_pymethods[] = {
+#define METHNAME(name) gclrmeth_##name
+ NAMETH(tell, "R.tell() -> OFF")
+ METH (seek, "R.seek(OFF)")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gccrand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCCRand", /* @tp_name@ */
+ sizeof(gccrand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ 0, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Metaclass for symmetric crypto-based generators.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gccrand), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gcrand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Abstract base class for symmetric crypto-based generators.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject gclatinrand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GCLatinRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Abstract base class for symmetric crypto-based generators.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gclatinrand), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- SSL and TLS generators --------------------------------------------*/
+
+static PyObject *sslprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ struct bin k, s;
+ const gchash *hco = &md5, *hci = &sha;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "key", "seed", "ohash", "ihash", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", KWLIST,
+ convbin, &k, convbin, &s,
+ convgchash, &hco, convgchash, &hci))
+ goto end;
+ rc = grand_dopywrap(ty, sslprf_rand(hco, hci, k.p, k.sz, s.p, s.sz),
+ f_freeme);
+end:
+ return (rc);
+}
+
+static PyObject *tlsdx_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ struct bin k, s;
+ const gcmac *mc = &sha_hmac;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "key", "seed", "mac", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&:new", KWLIST,
+ convbin, &k, convbin, &s,
+ convgcmac, &mc))
+ goto end;
+ rc = grand_dopywrap(ty, tlsdx_rand(mc, k.p, k.sz, s.p, s.sz), f_freeme);
+end:
+ return (rc);
+}
+
+static PyObject *tlsprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ struct bin k, s;
+ const gcmac *mcl = &md5_hmac, *mcr = &sha_hmac;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "key", "seed", "lmac", "rmac", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", KWLIST,
+ convbin, &k, convbin, &s,
+ convgcmac, &mcl, convgcmac, &mcr))
+ goto end;
+ rc = grand_dopywrap(ty, tlsprf_rand(mcl, mcr, k.p, k.sz, s.p, s.sz),
+ f_freeme);
+end:
+ return (rc);
+}
+
+static const PyTypeObject sslprf_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "SSLRand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "SSLRand(KEY, SEED, [ohash = md5], [ihash = sha]):\n"
+ " RNG for SSL master secret.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ sslprf_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject tlsdx_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "TLSDataExpansion", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "TLSDataExpansion(KEY, SEED, [mac = sha_hmac]):\n"
+ " TLS data expansion function.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ tlsdx_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static const PyTypeObject tlsprf_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "TLSPRF", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "TLSPRF(KEY, SEED, [lmac = md5_hmac], [rmac = sha_hmac]):\n"
+ " TLS pseudorandom function.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ tlsprf_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- DSA generator -----------------------------------------------------*/
+
+static PyObject *dsarand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ unsigned passes = 1;
+ grand *r;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "seed", "passes", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convbin, &in, convuint, &passes))
+ goto end;
+ if (!passes) VALERR("must be positive");
+ r = dsarand_create(in.p, in.sz);
+ if (passes != 1) r->ops->misc(r, DSARAND_PASSES, passes);
+ rc = grand_dopywrap(ty, r, f_freeme);
+end:
+ return (rc);
+}
+
+static PyObject *drget_seed(PyObject *me, void *hunoz)
+{
+ grand *r = GRAND_R(me);
+ int n = r->ops->misc(r, DSARAND_SEEDSZ);
+ PyObject *rc = bytestring_pywrap(0, n);
+ r->ops->misc(r, DSARAND_GETSEED, BIN_PTR(rc));
+ return (rc);
+}
+
+static PyObject *drget_passes(PyObject *me, void *hunoz)
+{
+ grand *r = GRAND_R(me);
+ return (PyInt_FromLong(r->ops->misc(r, DSARAND_PASSES, 0)));
+}
+
+static int drset_passes(PyObject *me, PyObject *val, void *hunoz)
+{
+ grand *r = GRAND_R(me);
+ long n;
+ int rc = -1;
+
+ if (!val) NIERR("__del__");
+ n = PyInt_AsLong(val); if (n == -1 && PyErr_Occurred()) goto end;
+ if (n <= 0) VALERR("must be positive");
+ if (n > ULONG_MAX) VALERR("out of range");
+ r->ops->misc(r, DSARAND_PASSES, (unsigned)n);
+ rc = 0;
+end:
+ return (rc);
+}
+
+static const PyGetSetDef dsarand_pygetset[] = {
+#define GETSETNAME(op, name) dr##op##_##name
+ GET (seed, "R.seed -> current generator seed")
+ GETSET(passes, "R.passes -> number of passes to create output")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject dsarand_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "DSARand", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "DSARand(SEED): pseudorandom number generator for DSA parameters.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(dsarand), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ dsarand_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Blum-Blum-Shub generator ------------------------------------------*/
+
+static PyObject *bbs_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+ mp *n = 0, *x = MP_TWO;
+ PyObject *rc = 0;
+ static const char *const kwlist[] = { "n", "x", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+ convmp, &n, convmp, &x))
+ goto end;
+ rc = grand_dopywrap(ty, bbs_rand(n, x), f_freeme);
+end:
+ mp_drop(n);
+ mp_drop(x);
+ return (rc);
+}
+
+static PyObject *bbsmeth_step(PyObject *me)
+ { grand *r = GRAND_R(me); r->ops->misc(r, BBS_STEP); RETURN_ME; }
+
+static PyObject *bbsmeth_bits(PyObject *me, PyObject *arg)
+{
+ grand *r = GRAND_R(me); unsigned n; uint32 w;
+ if (!PyArg_ParseTuple(arg, "O&:bits", convuint, &n)) goto end;
+ if (n > 32) VALERR("can't get more than 32 bits");
+ r->ops->misc(r, BBS_BITS, n, &w); return (getulong(w));
+end:
+ return (0);
+}
+
+static PyObject *bbsmeth_wrap(PyObject *me)
+ { grand *r = GRAND_R(me); r->ops->misc(r, BBS_WRAP); RETURN_ME; }
+
+static PyObject *bbsget_n(PyObject *me, void *hunoz)
+{
+ mp *x = MP_NEW; grand *r = GRAND_R(me);
+ r->ops->misc(r, BBS_MOD, &x); return (mp_pywrap(x));
+}
+
+static PyObject *bbsget_x(PyObject *me, void *hunoz)
+{
+ mp *x = MP_NEW; grand *r = GRAND_R(me);
+ r->ops->misc(r, BBS_STATE, &x); return (mp_pywrap(x));
+}
+
+static int bbsset_x(PyObject *me, PyObject *val, void *hunoz)
+{
+ mp *x = 0; grand *r = GRAND_R(me); int rc = -1; if (!val) NIERR("__del__");
+ if ((x = getmp(val)) == 0) goto end;
+ r->ops->misc(r, BBS_SET, x); rc = 0;
+ end: mp_drop(x); return (rc);
+}
+
+static PyObject *bbsget_stepsz(PyObject *me, void *hunoz)
+{
+ grand *r = GRAND_R(me);
+ return (PyInt_FromLong(r->ops->misc(r, BBS_STEPSZ)));
+}
+
+static const PyMethodDef bbs_pymethods[] = {
+#define METHNAME(name) bbsmeth_##name
+ NAMETH(step, "R.step(): steps the generator (not useful)")
+ METH (bits, "R.bits(N) -> W: returns N bits (<= 32) from the generator")
+ NAMETH(wrap, "R.wrap(): flushes unused bits in internal buffer")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef bbs_pygetset[] = {
+#define GETSETNAME(op, name) bbs##op##_##name
+ GET (n, "R.n -> Blum modulus")
+ GETSET(x, "R.x -> current seed value")
+ GET (stepsz, "R.stepsz -> number of bits generated per step")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject bbs_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BlumBlumShub", /* @tp_name@ */
+ sizeof(grand_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ grand_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "BlumBlumShub(N, [x = 2]): Blum-Blum-Shub pseudorandom number generator.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(bbs), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(bbs), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ bbs_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+typedef struct bbspriv_pyobj {
+ grand_pyobj gr;
+ bbs_priv bp;
+} bbspriv_pyobj;
+
+#define BBSPRIV_BP(o) (&((bbspriv_pyobj *)(o))->bp)
+
+static PyObject *bbspriv_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *p = 0, *q = 0, *n = 0, *x = MP_TWO;
+ bbspriv_pyobj *rc = 0;
+ static const char *const kwlist[] = { "n", "p", "q", "seed", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&:new", KWLIST,
+ convmp, &n, convmp, &p, convmp, &q,
+ convmp, &x))
+ goto end;
+ if (!n + !p + !q > 1) VALERR("must specify at least two of n, p, q");
+ if (!n) n = mp_mul(MP_NEW, p, q);
+ else if (!p) mp_div(&p, 0, n, q);
+ else if (!q) mp_div(&q, 0, n, p);
+ rc = (bbspriv_pyobj *)ty->tp_alloc(ty, 0);
+ rc->gr.r = bbs_rand(n, x);
+ rc->gr.f = f_freeme;
+ rc->bp.p = MP_COPY(p);
+ rc->bp.q = MP_COPY(q);
+ rc->bp.n = MP_COPY(n);
+end:
+ mp_drop(p); mp_drop(q); mp_drop(n); mp_drop(x);
+ return ((PyObject *)rc);
+}
+
+static PyObject *bpmeth_generate(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ bbs_priv bp = { 0 };
+ mp *x = MP_TWO;
+ struct excinfo exc = EXCINFO_INIT;
+ pypgev evt = { { 0 } };
+ unsigned nbits, n = 0;
+ grand *r = &rand_global;
+ static const char *const kwlist[] =
+ { "nbits", "event", "rng", "nsteps", "seed", 0 };
+ bbspriv_pyobj *rc = 0;
+
+ evt.exc = &exc;
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&O&O&O&:generate", KWLIST,
+ convuint, &nbits, convpgev, &evt,
+ convgrand, &r, convuint, &n, convmp, &x))
+ goto end;
+ if (bbs_gen(&bp, nbits, r, n, evt.ev.proc, evt.ev.ctx))
+ PGENERR(&exc);
+ rc = PyObject_New(bbspriv_pyobj, bbspriv_pytype);
+ rc->gr.r = bbs_rand(bp.n, x);
+ rc->gr.f = f_freeme;
+ rc->bp.p = MP_COPY(bp.p);
+ rc->bp.q = MP_COPY(bp.q);
+ rc->bp.n = MP_COPY(bp.n);
+end:
+ mp_drop(bp.p); mp_drop(bp.q); mp_drop(bp.n); mp_drop(x);
+ return ((PyObject *)rc);
+}
+
+static void bbspriv_pydealloc(PyObject *me)
+{
+ bbs_priv *bp = BBSPRIV_BP(me);
+ mp_drop(bp->n);
+ mp_drop(bp->p);
+ mp_drop(bp->q);
+ grand_pydealloc(me);
+}
+
+static PyObject *bpmeth_ff(PyObject *me, PyObject *arg)
+{
+ mp *n = 0; grand *r = GRAND_R(me); bbs_priv *bp = BBSPRIV_BP(me);
+ if (!PyArg_ParseTuple(arg, "O&:ff", convmp, &n)) return (0);
+ r->ops->misc(r, BBS_FF, bp, n); RETURN_ME;
+}
+
+static PyObject *bpmeth_rew(PyObject *me, PyObject *arg)
+{
+ mp *n = 0; grand *r = GRAND_R(me); bbs_priv *bp = BBSPRIV_BP(me);
+ if (!PyArg_ParseTuple(arg, "O&:rew", convmp, &n)) return (0);
+ r->ops->misc(r, BBS_REW, bp, n); RETURN_ME;
+}
+
+static PyObject *bpget_n(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(BBSPRIV_BP(me)->n))); }
+
+static PyObject *bpget_p(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(BBSPRIV_BP(me)->p))); }
+
+static PyObject *bpget_q(PyObject *me, void *hunoz)
+ { return (mp_pywrap(MP_COPY(BBSPRIV_BP(me)->q))); }
+
+static const PyMethodDef bbspriv_pymethods[] = {
+#define METHNAME(name) bpmeth_##name
+ METH (ff, "R.ff(N): fast-forward N places")
+ METH (rew, "R.rew(N): rewind N places")
+ KWSMTH(generate, "generate(NBITS, [event = pgen_nullev], "
+ "[rng = rand], [nsteps = 0], [seed = 2]) -> R")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyGetSetDef bbspriv_pygetset[] = {
+#define GETSETNAME(op, name) bp##op##_##name
+ GET (n, "R.n -> Blum modulus")
+ GET (p, "R.p -> one of the factors of the modulus")
+ GET (q, "R.q -> one of the factors of the modulus")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject bbspriv_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "BBSPriv", /* @tp_name@ */
+ sizeof(bbspriv_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ bbspriv_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "BBSPriv(..., [seed = 2]): Blum-Blum-Shub, with private key.\n"
+ " Keywords: n, p, q; must provide at least two",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(bbspriv), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(bbspriv), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ bbspriv_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Global stuff ------------------------------------------------------*/
+
+static const struct nameval consts[] = {
+ CONST(RAND_IBITS),
+ { 0 }
+};
+
+void rand_pyinit(void)
+{
+ INITTYPE(grand, root);
+ INITTYPE(truerand, grand);
+ INITTYPE(fibrand, grand);
+ INITTYPE(lcrand, grand);
+ INITTYPE(dsarand, grand);
+ INITTYPE(bbs, grand);
+ INITTYPE(bbspriv, bbs);
+ INITTYPE(sslprf, grand);
+ INITTYPE(tlsdx, grand);
+ INITTYPE(tlsprf, grand);
+ INITTYPE(gccrand, type);
+ INITTYPE(gcrand, grand);
+ INITTYPE(gclatinrand, gcrand);
+ rand_noisesrc(RAND_GLOBAL, &noise_source);
+ rand_seed(RAND_GLOBAL, 160);
+}
+
+static const char *crand_namefn(const void *p)
+ { const gccrand_info *const *cls = p; return (*cls ? (*cls)->name : 0); }
+static PyObject *crand_valfn(const void *p)
+ { const gccrand_info *const *cls = p; return (gccrand_pywrap(*cls)); }
+
+void rand_pyinsert(PyObject *mod)
+{
+ INSERT("GRand", grand_pytype);
+ INSERT("TrueRand", truerand_pytype);
+ INSERT("LCRand", lcrand_pytype);
+ INSERT("FibRand", fibrand_pytype);
+ INSERT("SSLRand", sslprf_pytype);
+ INSERT("TLSDataExpansion", tlsdx_pytype);
+ INSERT("TLSPRF", tlsprf_pytype);
+ INSERT("DSARand", dsarand_pytype);
+ INSERT("BlumBlumShub", bbs_pytype);
+ INSERT("BBSPriv", bbspriv_pytype);
+ INSERT("GCCRand", gccrand_pytype);
+ INSERT("GCRand", gcrand_pytype);
+ INSERT("GCLatinRand", gclatinrand_pytype);
+ rand_pyobj = grand_pywrap(&rand_global, 0); Py_INCREF(rand_pyobj);
+ gccrands_dict = make_algtab(gcrandtab, sizeof(gccrand_info *),
+ crand_namefn, crand_valfn);
+ INSERT("gccrands", gccrands_dict); Py_INCREF(gccrands_dict);
+ INSERT("rand", rand_pyobj);
+ setconstants(mod, consts);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+#! /usr/bin/python
+
+import distutils.core as DC
+import mdwsetup as MS
+
+MS.pkg_config('catacomb', '2.5.99~')
+MS.pkg_config('mLib', '2.4.99~')
+
+cat = DC.Extension('catacomb._base',
+ ['catacomb.c', 'bytestring.c', 'buffer.c',
+ 'rand.c', 'algorithms.c', 'pubkey.c', 'pgen.c',
+ 'mp.c', 'field.c', 'ec.c', 'group.c', 'passphrase.c',
+ 'share.c', 'key.c',
+ 'pyke/pyke.c', 'pyke/pyke-mLib.c',
+ 'pyke/mapping.c', 'pyke/mapping-mLib.c'],
+ ##extra_compile_args = ['-O0'],
+ include_dirs = MS.uniquify(MS.INCLUDEDIRS),
+ library_dirs = MS.uniquify(MS.LIBDIRS),
+ libraries = MS.uniquify(MS.LIBS))
+
+MS.setup(name = 'catacomb-python',
+ description = 'Interface to Catacomb cryptographic library',
+ url = 'https://git.distorted.org.uk/~mdw/catacomb-python/',
+ author = 'Straylight/Edgeware',
+ author_email = 'mdw@distorted.org.uk',
+ license = 'GNU General Public License',
+ packages = ['catacomb'],
+ scripts = ['pock', 'pwsafe'],
+ data_files = [('share/man/man1', ['pock.1', 'pwsafe.1'])],
+ genfiles = [MS.Generate('algorithms.h')],
+ unittest_dir = "t",
+ unittests = ["t-misc", "t-algorithms", "t-bytes", "t-buffer",
+ "t-convert", "t-ec", "t-field", "t-group", "t-key",
+ "t-mp", "t-passphrase", "t-pgen", "t-pubkey",
+ "t-rand", "t-rat", "t-share"],
+ ext_modules = [cat])
--- /dev/null
+/* -*-c-*-
+ *
+ * Secret sharing
+ *
+ * (c) 2005 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- GF(2^8)-based secret-sharing --------------------------------------*/
+
+typedef struct gfshare_pyobj {
+ PyObject_HEAD
+ gfshare s;
+} gfshare_pyobj;
+
+static PyTypeObject
+ *gfshare_pytype, *gfsharesplit_pytype, *gfsharejoin_pytype;
+#define GFSHARE_PYCHECK(o) PyObject_TypeCheck((o), gfshare_pytype)
+#define GFSHARESPLIT_PYCHECK(o) PyObject_TypeCheck((o), gfsharesplit_pytype)
+#define GFSHAREJOIN_PYCHECK(o) PyObject_TypeCheck((o), gfsharejoin_pytype)
+#define GFSHARE_S(o) (&((gfshare_pyobj *)(o))->s)
+
+static void gfshare_pydealloc(PyObject *me)
+{
+ gfshare_destroy(GFSHARE_S(me));
+ FREEOBJ(me);
+}
+
+static PyObject *gfsget_threshold(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GFSHARE_S(me)->t)); }
+static PyObject *gfsget_size(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GFSHARE_S(me)->sz)); }
+
+static const PyGetSetDef gfshare_pygetset[]= {
+#define GETSETNAME(op, name) gfs##op##_##name
+ GET (threshold, "S.threshold -> THRESHOLD")
+ GET (size, "S.size -> SECRETSZ")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject gfshare_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GFShare", /* @tp_name@ */
+ sizeof(gfshare_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gfshare_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Binary-field secret sharing base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gfshare), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *gfsharesplit_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ struct bin in;
+ unsigned t;
+ grand *r = &rand_global;
+ gfshare_pyobj *s;
+ static const char *const kwlist[] = { "threshold", "secret", "rng", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&:new", KWLIST,
+ convuint, &t, convbin, &in,
+ convgrand, &r))
+ goto end;
+ if (!t || t > 255) VALERR("threshold must be nonzero and < 256");
+ s = (gfshare_pyobj *)ty->tp_alloc(ty, 0);
+ gfshare_create(&s->s, t, in.sz);
+ gfshare_mkshares(&s->s, r, in.p);
+ return ((PyObject *)s);
+end:
+ return (0);
+}
+
+static PyObject *gfsmeth_get(PyObject *me, PyObject *arg)
+{
+ unsigned i;
+ PyObject *rc = 0;
+ if (!PyArg_ParseTuple(arg, "O&:get", convuint, &i)) goto end;
+ if (i >= 255) VALERR("index must be < 255");
+ rc = bytestring_pywrap(0, GFSHARE_S(me)->sz);
+ gfshare_get(GFSHARE_S(me), i, BIN_PTR(rc));
+end:
+ return (rc);
+}
+
+static const PyMethodDef gfsharesplit_pymethods[] = {
+#define METHNAME(name) gfsmeth_##name
+ METH (get, "S.get(I) -> SHARE")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject gfsharesplit_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GFShareSplit", /* @tp_name@ */
+ sizeof(gfshare_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gfshare_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "GFShareSplit(THRESHOLD, SECRET, [rng = rand]): binary-field sharing:\n"
+ " split secret into shares.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gfsharesplit), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ gfsharesplit_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *gfsharejoin_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ unsigned t, sz;
+ gfshare_pyobj *s;
+ static const char *const kwlist[] = { "threshold", "size", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
+ convuint, &t, convuint, &sz))
+ goto end;
+ if (!t || t > 255) VALERR("threshold must be nonzero and < 256");
+ s = (gfshare_pyobj *)ty->tp_alloc(ty, 0);
+ gfshare_create(&s->s, t, sz);
+ return ((PyObject *)s);
+end:
+ return (0);
+}
+
+static PyObject *gfsmeth_addedp(PyObject *me, PyObject *arg)
+{
+ unsigned i;
+ if (!PyArg_ParseTuple(arg, "O&:addedp", convuint, &i)) goto end;
+ if (i > 254) VALERR("index must be < 255");
+ return (getbool(gfshare_addedp(GFSHARE_S(me), i)));
+end:
+ return (0);
+}
+
+static PyObject *gfsmeth_add(PyObject *me, PyObject *arg)
+{
+ unsigned i;
+ struct bin s;
+ if (!PyArg_ParseTuple(arg, "O&O&:add", convuint, &i, convbin, &s))
+ goto end;
+ if (i > 254) VALERR("index must be < 255");
+ if (s.sz != GFSHARE_S(me)->sz) VALERR("bad share size");
+ if (gfshare_addedp(GFSHARE_S(me), i)) VALERR("this share already added");
+ if (GFSHARE_S(me)->i >= GFSHARE_S(me)->t) VALERR("enough shares already");
+ gfshare_add(GFSHARE_S(me), i, s.p);
+ return (PyInt_FromLong(GFSHARE_S(me)->t - GFSHARE_S(me)->i));
+end:
+ return (0);
+}
+
+static PyObject *gfsmeth_combine(PyObject *me)
+{
+ PyObject *rc = 0;
+ if (GFSHARE_S(me)->i < GFSHARE_S(me)->t) VALERR("not enough shares yet");
+ rc = bytestring_pywrap(0, GFSHARE_S(me)->sz);
+ gfshare_combine(GFSHARE_S(me), BIN_PTR(rc));
+end:
+ return (rc);
+}
+
+static const PyMethodDef gfsharejoin_pymethods[] = {
+#define METHNAME(name) gfsmeth_##name
+ METH (addedp, "S.addedp(I) -> BOOL")
+ METH (add, "S.add(I, SHARE) -> REMAIN")
+ NAMETH(combine, "S.combine() -> SECRET")
+#undef METHNAME
+ { 0 }
+};
+
+static PyObject *gfsget_remain(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(GFSHARE_S(me)->t - GFSHARE_S(me)->i)); }
+
+static const PyGetSetDef gfsharejoin_pygetset[]= {
+#define GETSETNAME(op, name) gfs##op##_##name
+ GET (remain, "S.remain -> REMAIN")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject gfsharejoin_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "GFShareJoin", /* @tp_name@ */
+ sizeof(gfshare_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ gfshare_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "GFShareJoin(THRESHOLD, SIZE): binary field sharing:\n"
+ " join shares to recover secret.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(gfsharejoin), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(gfsharejoin), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ gfsharejoin_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Prime-field secret-sharing ----------------------------------------*/
+
+typedef struct share_pyobj {
+ PyObject_HEAD
+ share s;
+} share_pyobj;
+
+static PyTypeObject
+ *share_pytype, *sharesplit_pytype, *sharejoin_pytype;
+#define SHARE_PYCHECK(o) PyObject_TypeCheck((o), share_pytype)
+#define SHARESPLIT_PYCHECK(o) PyObject_TypeCheck((o), sharesplit_pytype)
+#define SHAREJOIN_PYCHECK(o) PyObject_TypeCheck((o), sharejoin_pytype)
+#define SHARE_S(o) (&((share_pyobj *)(o))->s)
+
+static void share_pydealloc(PyObject *me)
+{
+ share_destroy(SHARE_S(me));
+ FREEOBJ(me);
+}
+
+static PyObject *sget_threshold(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(SHARE_S(me)->t)); }
+static PyObject *sget_modulus(PyObject *me, void *hunoz)
+ { return (mp_pywrap(SHARE_S(me)->p)); }
+
+static const PyGetSetDef share_pygetset[]= {
+#define GETSETNAME(op, name) s##op##_##name
+ GET (threshold, "S.threshold -> THRESHOLD")
+ GET (modulus, "S.modulus -> MODULUS")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject share_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "Share", /* @tp_name@ */
+ sizeof(share_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ share_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "Prime-field secret sharing base class.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ 0, /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(share), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ abstract_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *sharesplit_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ mp *sec = 0;
+ unsigned t;
+ grand *r = &rand_global;
+ mp *m = 0;
+ share_pyobj *s;
+ static const char *const kwlist[] =
+ { "threshold", "secret", "modulus", "rng", 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", KWLIST,
+ convuint, &t, convmp, &sec,
+ convmp, &m, convgrand, &r))
+ goto end;
+ if (!t) VALERR("threshold must be nonzero");
+ s = (share_pyobj *)ty->tp_alloc(ty, 0);
+ share_create(&s->s, t);
+ s->s.p = m;
+ share_mkshares(&s->s, r, sec);
+ mp_drop(sec);
+ return ((PyObject *)s);
+end:
+ mp_drop(m);
+ mp_drop(sec);
+ return (0);
+}
+
+static PyObject *smeth_get(PyObject *me, PyObject *arg)
+{
+ unsigned i;
+ PyObject *rc = 0;
+ if (!PyArg_ParseTuple(arg, "O&:get", convuint, &i)) goto end;
+ rc = mp_pywrap(share_get(SHARE_S(me), MP_NEW, i));
+end:
+ return (rc);
+}
+
+static const PyMethodDef sharesplit_pymethods[] = {
+#define METHNAME(name) smeth_##name
+ METH (get, "S.get(I) -> SHARE")
+#undef METHNAME
+ { 0 }
+};
+
+static const PyTypeObject sharesplit_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ShareSplit", /* @tp_name@ */
+ sizeof(share_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ share_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ShareSplit(THRESHOLD, SECRET, [modulus = ?], [rng = rand]):\n"
+ " prime field secret sharing: split secret into shares.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(sharesplit), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ 0, /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ sharesplit_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+static PyObject *sharejoin_pynew(PyTypeObject *ty,
+ PyObject *arg, PyObject *kw)
+{
+ unsigned t;
+ mp *m = 0;
+ share_pyobj *s;
+ static const char *const kwlist[] = { "threshold", "modulus", 0 };
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
+ convuint, &t, convmp, &m))
+ goto end;
+ if (!t) VALERR("threshold must be nonzero");
+ s = (share_pyobj *)ty->tp_alloc(ty, 0);
+ share_create(&s->s, t);
+ s->s.p = m;
+ return ((PyObject *)s);
+end:
+ mp_drop(m);
+ return (0);
+}
+
+static PyObject *smeth_addedp(PyObject *me, PyObject *arg)
+{
+ unsigned i;
+ if (!PyArg_ParseTuple(arg, "O&:addedp", convuint, &i)) goto end;
+ return (getbool(share_addedp(SHARE_S(me), i)));
+end:
+ return (0);
+}
+
+static PyObject *smeth_add(PyObject *me, PyObject *arg)
+{
+ unsigned i;
+ mp *s = 0;
+ PyObject *rc = 0;
+ if (!PyArg_ParseTuple(arg, "O&O&:add", convuint, &i, convmp, &s)) goto end;
+ if (MP_NEGP(s) || MP_CMP(s, >=, SHARE_S(me)->p))
+ VALERR("share out of range");
+ if (share_addedp(SHARE_S(me), i)) VALERR("this share already added");
+ if (SHARE_S(me)->i >= SHARE_S(me)->t) VALERR("enough shares already");
+ share_add(SHARE_S(me), i, s);
+ rc = PyInt_FromLong(SHARE_S(me)->t - SHARE_S(me)->i);
+end:
+ mp_drop(s);
+ return (rc);
+}
+
+static PyObject *smeth_combine(PyObject *me)
+{
+ PyObject *rc = 0;
+ if (SHARE_S(me)->i < SHARE_S(me)->t) VALERR("not enough shares yet");
+ rc = mp_pywrap(share_combine(SHARE_S(me)));
+end:
+ return (rc);
+}
+
+static const PyMethodDef sharejoin_pymethods[] = {
+#define METHNAME(name) smeth_##name
+ METH (addedp, "S.addedp(I) -> BOOL")
+ METH (add, "S.add(I, SHARE) -> REMAIN")
+ NAMETH(combine, "S.combine() -> SECRET")
+#undef METHNAME
+ { 0 }
+};
+
+static PyObject *sget_remain(PyObject *me, void *hunoz)
+ { return (PyInt_FromLong(SHARE_S(me)->t - SHARE_S(me)->i)); }
+
+static const PyGetSetDef sharejoin_pygetset[]= {
+#define GETSETNAME(op, name) s##op##_##name
+ GET (remain, "S.remain -> REMAIN")
+#undef GETSETNAME
+ { 0 }
+};
+
+static const PyTypeObject sharejoin_pytype_skel = {
+ PyVarObject_HEAD_INIT(0, 0) /* Header */
+ "ShareJoin", /* @tp_name@ */
+ sizeof(share_pyobj), /* @tp_basicsize@ */
+ 0, /* @tp_itemsize@ */
+
+ share_pydealloc, /* @tp_dealloc@ */
+ 0, /* @tp_print@ */
+ 0, /* @tp_getattr@ */
+ 0, /* @tp_setattr@ */
+ 0, /* @tp_compare@ */
+ 0, /* @tp_repr@ */
+ 0, /* @tp_as_number@ */
+ 0, /* @tp_as_sequence@ */
+ 0, /* @tp_as_mapping@ */
+ 0, /* @tp_hash@ */
+ 0, /* @tp_call@ */
+ 0, /* @tp_str@ */
+ 0, /* @tp_getattro@ */
+ 0, /* @tp_setattro@ */
+ 0, /* @tp_as_buffer@ */
+ Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
+ Py_TPFLAGS_BASETYPE,
+
+ /* @tp_doc@ */
+ "ShareJoin(THRESHOLD, MODULUS): prime field secret sharing:\n"
+ " join shares to recover secret.",
+
+ 0, /* @tp_traverse@ */
+ 0, /* @tp_clear@ */
+ 0, /* @tp_richcompare@ */
+ 0, /* @tp_weaklistoffset@ */
+ 0, /* @tp_iter@ */
+ 0, /* @tp_iternext@ */
+ PYMETHODS(sharejoin), /* @tp_methods@ */
+ 0, /* @tp_members@ */
+ PYGETSET(sharejoin), /* @tp_getset@ */
+ 0, /* @tp_base@ */
+ 0, /* @tp_dict@ */
+ 0, /* @tp_descr_get@ */
+ 0, /* @tp_descr_set@ */
+ 0, /* @tp_dictoffset@ */
+ 0, /* @tp_init@ */
+ PyType_GenericAlloc, /* @tp_alloc@ */
+ sharejoin_pynew, /* @tp_new@ */
+ 0, /* @tp_free@ */
+ 0 /* @tp_is_gc@ */
+};
+
+/*----- Global stuff ------------------------------------------------------*/
+
+void share_pyinit(void)
+{
+ INITTYPE(gfshare, root);
+ INITTYPE(gfsharesplit, gfshare);
+ INITTYPE(gfsharejoin, gfshare);
+ INITTYPE(share, root);
+ INITTYPE(sharesplit, share);
+ INITTYPE(sharejoin, share);
+}
+
+void share_pyinsert(PyObject *mod)
+{
+ INSERT("GFShare", gfshare_pytype);
+ INSERT("GFShareSplit", gfsharesplit_pytype);
+ INSERT("GFShareJoin", gfsharejoin_pytype);
+ INSERT("Share", share_pytype);
+ INSERT("ShareSplit", sharesplit_pytype);
+ INSERT("ShareJoin", sharejoin_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+60090be2:twofish encrypt,secret:w6Qmlm8zVc0Y6O4PwXfOrl4ru7+DTCrDD9pkbnh5xhsZc2CSRb3NKu+ihi9hykSkyXdSgn9G7E52xQUA/wyp4Nsp7X+Jg2X6bdvLag== forever forever -
+bd761d35:ec struct:[p=ec,public:0x42d95404921b2d8f19c761211753196901d7695049a96db2f0d7e00bd1dbf8ec,0x8a959136f7ca7e75718b9bc2d2ac3b0680daf4030288324816c8028cf8ebc8c4,private=encrypt,secret:/ddlDkc0+W5cfukYUHBxQoNNwxLmYnjRWYW+GnUwxmqdgv+8NTNIWN/GdahdsMT1GSnWKKGObd0Abz+tEiyJJa/hElI6JaxX/UirHahChpWbKTr/,curve=string,shared:nist%2dp256] forever forever -
+4a4e1ee7:ec-param struct:[curve=string,shared:nist%2dp256] forever forever -
+8599dbab:rsa:ron struct:[private=encrypt,secret:JER0qvxt53DtgAl03m+Rf4KggCkzT3YZ0JkrhJsuuvFVMYXK1gBjp4bk/zGMhWYjRKPVtNujiox3oSL933HwJLL7r0MVhUtZl+3zaS9DQ3c/lpGOMS8/VYDV84Ny1oU7IJtZakdTbRNd7YJapMq44SX5xamERBNEe1dslxVdLNVLn/Cc54fDNbhMVx/S3MrkuQaxF/wNFiL5nOezlbCVal5ln0RF7AXulIwnYkIeeC2P0h4oUfmhXWuiRAvk9bPFc5fMES1JruBk3YaQ7FzErIYV4vTMxotclwcAJvQDo5reUHQzAaaLXY80b3VlxhPQaKCcnZNcAJTDJRYh/OcSDt4AfVcTDqGToTXDfw/PL89KNI4R0vxF9Vqtyes4iPCwr6WJh4ZDBbT9r4WdIoAhO+2gFO1yHii3YbdAwPM4WrFE6caIkmgZHmkbc4v2rMz4DRGNOj1cegfz0xGTBFjvy/haTHy/tzV9qPb8N4VFgTvEroN7OwgS+l6GExlPH/JOff+hsOwcwfTUC0vJ/Cm8tOgL7guivADPL3ThGnqdt0/9S11MIoiYF51+nFH4JvcyJ7kjOI4EQbc50YcoGYVEhatUeUzOwMV4zxymlMGVUVZH509JC5ABCJLM4+qmklRT4G6K2kMHo9w32kJwBcAg5u2RR/2v0ALYWZ7FqAwjP40u3t2LrMeXStiAKs2YzkEqB/20kqlv1D+pasGVUUE3MvfHLm8JyrXjYG2erHs8BZRuXwz+DfZhmC9rZ6v7WaJFu2joIBcLMWouIU3WL5vpaNklDzBXfynZyPSwsgfmUUsi1xXWJPvUJH71IY5UAcHiWaaiBLIuJ3eCpqxR33LA6S8tJmnAC+VgwT9ATutVVW1CS++Q7mGdkgPWAJ3AkJL2lPMfousldvvknoS9Ybx1jJtpRyInFlgdjHoYpfe4DWXjoRDLQbkDemQNHHudog3n9GQ/kM1N/3xRcIxIBp0hzTFg+axMJU9pz+105/ymYQXIRkT9R3kglIa0l7G+uy760+kvh5VZp6wA4qmdqbvEQOl//pJ2hl52bntHd9w0Q61je8ByXSFxZeKqSkJkl1KSX8Z/JMiXWq1ea5enwWk5d9UX4aBgEtQdv2NCkirVSofrXIFvR3+VWfLwlPd2VcS3FAXU4LyjATiNZRp/0t0X81H/ozY6XzSXMmU8xs/FPnbVWL4fmXhspNbfK+uuib/Xd9lpzrxrNu+YbaNbtG97OIUwbtSakD+TbS3QTWvFwGNyIiuBm2t4R/M+vN1/qB+eTupGFmB/bFRv3H0h8fl4SaNSXqsZ6LTjWUQ+oQZ6Hu0Ri/qLtM62Dm3cou0yOASSMINDhScMOlxCcCONwpANefmMmx+HevmdGxSY8oKPMXysU7ZPFNYYPnqPhDg/4YW8Rga305ZwoWWV3kFcfeS5CxjOOtlsLDDsmDT5OYWCZczzseXS8Q2B5a8ZKJRmw7eAKW14yJmdk3HlLnBqGUAdtYlqXuEKR3c9QeIDJJsijEOxOyaTEL/Zp+mKQ9YRfmj/mPHbFJ1kJI9mkgosGdc9LUqCQpE5vsBCgmFljlD3Br7V651TtvnCeuOZGmlhuXz/79+0aEYfJ9/vjXA7S/W+AEdovbSGemATuuXIC792x6Mmy8hWt/U8YTB2kakbMWlJK/zWWFOn5gggc0c7n5D/BagZc6MwD6z8KmlLszgiBhBD904V66nzKQKoc5QJypK52SALhqfBQik8S5Ss4K/XEf7hgR5HNiZUNe3+Y07ru+zp3hejqgT8qJZg9YfZ6H/rkJ+41YdOf0UknyY8+Oeub0BQJquk3kLXWywCMtOYMaN391n/PP6T3GUVJlkhNk7LbrbjadwzLmB12CIgfI4AYDrCcR+/sLPTHOUXHne4F1xqEe1jb8/FWhxBOk+WqbBdgzr0nviHWWI=,n=integer,public:4588518152824847065809864017017980752300172722911691501294534688567191677622293877055275853619101138154946767929897875236974063360161364832318034181230762058282548891215880925776054212542819092223319882319288851559780722078087931927390589564076952023872334935403192026988691452623591714907960325366105508662452732895343767851573374920807255520857672360953324211494673508138823790217244758900285029386826393365768875455330392381958999387168679887837434942105961538360802941784841026202770984642852469173489052850527654973593434682838716079442275717555051916006906119658853435209224439445648915415615440379170113919377940230580258468751352164223236121841848896011526918868701912075848851443130268164520732142338177464314742042248866304723629029407667423868594096042267348555827683577338882853478253550493572887955844539064781262100314281062281504561302389580828642599153244753523052134755591791992951760425215644414834705106809,e=integer,public:65537] forever forever attr=value
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Test symmetric algorithms
+###
+### (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
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+def bad_key_size(ksz):
+ if isinstance(ksz, C.KeySZAny): return None
+ elif isinstance(ksz, C.KeySZRange):
+ if ksz.mod != 1: return ksz.min + 1
+ elif ksz.max is not None: return ksz.max + 1
+ elif ksz.min != 0: return ksz.min - 1
+ else: return None
+ elif isinstance(ksz, C.KeySZSet):
+ for sz in sorted(ksz.set):
+ if sz + 1 not in ksz.set: return sz + 1
+ assert False, "That should have worked."
+ else:
+ return None
+
+def different_key_size(ksz, sz):
+ if isinstance(ksz, C.KeySZAny): return sz + 1
+ elif isinstance(ksz, C.KeySZRange):
+ if sz > ksz.min: return sz - ksz.mod
+ elif ksz.max is None or sz < ksz.max: return sz + ksz.mod
+ else: return None
+ elif isinstance(ksz, C.KeySZSet):
+ for sz1 in sorted(ksz.set):
+ if sz != sz1: return sz1
+ return None
+ else:
+ return None
+
+class HashBufferTestMixin (U.TestCase):
+ """Mixin class for testing all of the various `hash...' methods."""
+
+ def check_hashbuffer_hashn(me, w, bigendp, makefn, hashfn):
+ """Check `hashuN'."""
+
+ ## Check encoding an integer.
+ h0, donefn0 = makefn(w + 2)
+ hashfn(h0.hashu8(0x00), T.bytes_as_int(w, bigendp)).hashu8(w + 1)
+ h1, donefn1 = makefn(w + 2)
+ h1.hash(T.span(w + 2))
+ me.assertEqual(donefn0(), donefn1())
+
+ ## Check overflow detection.
+ h0, _ = makefn(w)
+ me.assertRaises(OverflowError, hashfn, h0, 1 << 8*w)
+
+ def check_hashbuffer_bufn(me, w, bigendp, makefn, hashfn):
+ """Check `hashbufN'."""
+
+ ## Go through a number of different sizes.
+ for n in [0, 1, 7, 8, 19, 255, 12345, 65535, 123456]:
+ if n >= 1 << 8*w: continue
+ h0, donefn0 = makefn(2 + w + n)
+ hashfn(h0.hashu8(0x00), T.span(n)).hashu8(0xff)
+ h1, donefn1 = makefn(2 + w + n)
+ h1.hash(T.prep_lenseq(w, n, bigendp, True))
+ me.assertEqual(donefn0(), donefn1())
+
+ ## Check blocks which are too large for the length prefix.
+ if w <= 3:
+ n = 1 << 8*w
+ h0, _ = makefn(w + n)
+ me.assertRaises(ValueError, hashfn, h0, C.ByteString.zero(n))
+
+ def check_hashbuffer(me, makefn):
+ """Test the various `hash...' methods."""
+
+ ## Check `hashuN'.
+ me.check_hashbuffer_hashn(1, True, makefn, lambda h, n: h.hashu8(n))
+ me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16(n))
+ me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16b(n))
+ me.check_hashbuffer_hashn(2, False, makefn, lambda h, n: h.hashu16l(n))
+ me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24(n))
+ me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24b(n))
+ me.check_hashbuffer_hashn(3, False, makefn, lambda h, n: h.hashu24l(n))
+ me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32(n))
+ me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32b(n))
+ me.check_hashbuffer_hashn(4, False, makefn, lambda h, n: h.hashu32l(n))
+ if hasattr(makefn(0)[0], "hashu64"):
+ me.check_hashbuffer_hashn(8, True, makefn, lambda h, n: h.hashu64(n))
+ me.check_hashbuffer_hashn(8, True, makefn, lambda h, n: h.hashu64b(n))
+ me.check_hashbuffer_hashn(8, False, makefn, lambda h, n: h.hashu64l(n))
+
+ ## Check `hashbufN'.
+ me.check_hashbuffer_bufn(1, True, makefn, lambda h, x: h.hashbuf8(x))
+ me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16(x))
+ me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16b(x))
+ me.check_hashbuffer_bufn(2, False, makefn, lambda h, x: h.hashbuf16l(x))
+ me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24(x))
+ me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24b(x))
+ me.check_hashbuffer_bufn(3, False, makefn, lambda h, x: h.hashbuf24l(x))
+ me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32(x))
+ me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32b(x))
+ me.check_hashbuffer_bufn(4, False, makefn, lambda h, x: h.hashbuf32l(x))
+ if hasattr(makefn(0)[0], "hashbuf64"):
+ me.check_hashbuffer_bufn(8, True, makefn, lambda h, x: h.hashbuf64(x))
+ me.check_hashbuffer_bufn(8, True, makefn, lambda h, x: h.hashbuf64b(x))
+ me.check_hashbuffer_bufn(8, False, makefn, lambda h, x: h.hashbuf64l(x))
+
+###--------------------------------------------------------------------------
+class TestKeysize (U.TestCase):
+
+ def test_any(me):
+
+ ## A typical one-byte spec.
+ ksz = C.seal.keysz
+ me.assertEqual(type(ksz), C.KeySZAny)
+ me.assertEqual(ksz.default, 20)
+ me.assertEqual(ksz.min, 0)
+ me.assertEqual(ksz.max, None)
+ for n in [0, 12, 20, 5000]:
+ me.assertTrue(ksz.check(n))
+ me.assertEqual(ksz.best(n), n)
+ me.assertEqual(ksz.pad(n), n)
+
+ ## A typical two-byte spec. (No published algorithms actually /need/ a
+ ## two-byte key-size spec, but all of the HMAC variants use one anyway.)
+ ksz = C.sha256_hmac.keysz
+ me.assertEqual(type(ksz), C.KeySZAny)
+ me.assertEqual(ksz.default, 32)
+ me.assertEqual(ksz.min, 0)
+ me.assertEqual(ksz.max, None)
+ for n in [0, 12, 20, 5000]:
+ me.assertTrue(ksz.check(n))
+ me.assertEqual(ksz.best(n), n)
+ me.assertEqual(ksz.pad(n), n)
+
+ ## Check construction.
+ ksz = C.KeySZAny(15)
+ me.assertEqual(ksz.default, 15)
+ me.assertEqual(ksz.min, 0)
+ me.assertEqual(ksz.max, None)
+ me.assertRaises(ValueError, lambda: C.KeySZAny(-8))
+ me.assertEqual(C.KeySZAny(0).default, 0)
+
+ def test_set(me):
+ ## Note that no published algorithm uses a 16-bit `set' spec.
+
+ ## A typical spec.
+ ksz = C.salsa20.keysz
+ me.assertEqual(type(ksz), C.KeySZSet)
+ me.assertEqual(ksz.default, 32)
+ me.assertEqual(ksz.min, 10)
+ me.assertEqual(ksz.max, 32)
+ me.assertEqual(ksz.set, set([10, 16, 32]))
+ for x, best, pad in [(9, None, 10), (10, 10, 10), (11, 10, 16),
+ (15, 10, 16), (16, 16, 16), (17, 16, 32),
+ (31, 16, 32), (32, 32, 32), (33, 32, None)]:
+ if x == best == pad: me.assertTrue(ksz.check(x))
+ else: me.assertFalse(ksz.check(x))
+ if best is None: me.assertRaises(ValueError, ksz.best, x)
+ else: me.assertEqual(ksz.best(x), best)
+ if pad is None: me.assertRaises(ValueError, ksz.pad, x)
+ else: me.assertEqual(ksz.pad(x), pad)
+
+ ## Check construction.
+ ksz = C.KeySZSet(7)
+ me.assertEqual(ksz.default, 7)
+ me.assertEqual(ksz.set, set([7]))
+ me.assertEqual(ksz.min, 7)
+ me.assertEqual(ksz.max, 7)
+ ksz = C.KeySZSet(7, iter([3, 6, 9]))
+ me.assertEqual(ksz.default, 7)
+ me.assertEqual(ksz.set, set([3, 6, 7, 9]))
+ me.assertEqual(ksz.min, 3)
+ me.assertEqual(ksz.max, 9)
+
+ def test_range(me):
+ ## Note that no published algorithm uses a 16-bit `range' spec, or an
+ ## unbounded `range'.
+
+ ## A typical spec.
+ ksz = C.rijndael.keysz
+ me.assertEqual(type(ksz), C.KeySZRange)
+ me.assertEqual(ksz.default, 32)
+ me.assertEqual(ksz.min, 4)
+ me.assertEqual(ksz.max, 32)
+ me.assertEqual(ksz.mod, 4)
+ for x, best, pad in [(3, None, 4), (4, 4, 4), (5, 4, 8),
+ (15, 12, 16), (16, 16, 16), (17, 16, 20),
+ (31, 28, 32), (32, 32, 32), (33, 32, None)]:
+ if x == best == pad: me.assertTrue(ksz.check(x))
+ else: me.assertFalse(ksz.check(x))
+ if best is None: me.assertRaises(ValueError, ksz.best, x)
+ else: me.assertEqual(ksz.best(x), best)
+ if pad is None: me.assertRaises(ValueError, ksz.pad, x)
+ else: me.assertEqual(ksz.pad(x), pad)
+
+ ## Check construction.
+ ksz = C.KeySZRange(28, 21, 35, 7)
+ me.assertEqual(ksz.default, 28)
+ me.assertEqual(ksz.min, 21)
+ me.assertEqual(ksz.max, 35)
+ me.assertEqual(ksz.mod, 7)
+ ksz = C.KeySZRange(28, 21, None, 7)
+ me.assertEqual(ksz.min, 21)
+ me.assertEqual(ksz.max, None)
+ me.assertEqual(ksz.mod, 7)
+ me.assertEqual(ksz.pad(36), 42)
+ me.assertRaises(ValueError, C.KeySZRange, 29, 21, 35, 7)
+ me.assertRaises(ValueError, C.KeySZRange, 28, 20, 35, 7)
+ me.assertRaises(ValueError, C.KeySZRange, 28, 21, 34, 7)
+ me.assertRaises(ValueError, C.KeySZRange, 28, -7, 35, 7)
+ me.assertRaises(ValueError, C.KeySZRange, 28, 35, 21, 7)
+ me.assertRaises(ValueError, C.KeySZRange, 35, 21, 28, 7)
+ me.assertRaises(ValueError, C.KeySZRange, 21, 28, 35, 7)
+
+ def test_conversions(me):
+ me.assertEqual(C.KeySZ.fromec(256), 128)
+ me.assertEqual(C.KeySZ.fromschnorr(256), 128)
+ me.assertEqual(round(C.KeySZ.fromdl(2958.6875)), 128)
+ me.assertEqual(round(C.KeySZ.fromif(2958.6875)), 128)
+ me.assertEqual(C.KeySZ.toec(128), 256)
+ me.assertEqual(C.KeySZ.toschnorr(128), 256)
+ me.assertEqual(C.KeySZ.todl(128), 2958.6875)
+ me.assertEqual(C.KeySZ.toif(128), 2958.6875)
+
+###--------------------------------------------------------------------------
+class TestCipher (T.GenericTestMixin):
+ """Test basic symmetric ciphers."""
+
+ def _test_cipher(me, ccls):
+
+ ## Check the class properties.
+ me.assertEqual(type(ccls.name), str)
+ me.assertTrue(isinstance(ccls.keysz, C.KeySZ))
+ me.assertEqual(type(ccls.blksz), int)
+
+ ## Check round-tripping.
+ k = T.span(ccls.keysz.default)
+ iv = T.span(ccls.blksz)
+ m = T.span(253)
+ enc = ccls(k)
+ dec = ccls(k)
+ try: enc.setiv(iv)
+ except ValueError: can_setiv = False
+ else:
+ can_setiv = True
+ dec.setiv(iv)
+ c0 = enc.encrypt(m[0:57])
+ m0 = dec.decrypt(c0)
+ c1 = enc.encrypt(m[57:189])
+ m1 = dec.decrypt(c1)
+ try: enc.bdry()
+ except ValueError: can_bdry = False
+ else:
+ dec.bdry()
+ can_bdry = True
+ c2 = enc.encrypt(m[189:253])
+ m2 = dec.decrypt(c2)
+ me.assertEqual(len(c0) + len(c1) + len(c2), len(m))
+ me.assertEqual(m0, m[0:57])
+ me.assertEqual(m1, m[57:189])
+ me.assertEqual(m2, m[189:253])
+
+ ## Check the `enczero' and `deczero' methods.
+ c3 = enc.enczero(32)
+ me.assertEqual(dec.decrypt(c3), C.ByteString.zero(32))
+ m4 = dec.deczero(32)
+ me.assertEqual(enc.encrypt(m4), C.ByteString.zero(32))
+
+ ## Check that ciphers which support a `boundary' operation actually
+ ## need it.
+ if can_bdry:
+ dec = ccls(k)
+ if can_setiv: dec.setiv(iv)
+ m01 = dec.decrypt(c0 + c1)
+ me.assertEqual(m01, m[0:189])
+
+ ## Check that the boundary actually does something.
+ if can_bdry:
+ dec = ccls(k)
+ if can_setiv: dec.setiv(iv)
+ m012 = dec.decrypt(c0 + c1 + c2)
+ me.assertNotEqual(m012, m)
+
+ ## Check that bad key lengths are rejected.
+ badlen = bad_key_size(ccls.keysz)
+ if badlen is not None: me.assertRaises(ValueError, ccls, T.span(badlen))
+
+TestCipher.generate_testcases((name, C.gcciphers[name]) for name in
+ ["des-ecb", "rijndael-cbc", "twofish-cfb", "serpent-ofb",
+ "blowfish-counter", "rc4", "seal", "salsa20/8", "shake128-xof"])
+
+###--------------------------------------------------------------------------
+class TestAuthenticatedEncryption \
+ (HashBufferTestMixin, T.GenericTestMixin):
+ """Test authenticated encryption schemes."""
+
+ def _test_aead(me, aecls):
+
+ ## Check the class properties.
+ me.assertEqual(type(aecls.name), str)
+ me.assertTrue(isinstance(aecls.keysz, C.KeySZ))
+ me.assertTrue(isinstance(aecls.noncesz, C.KeySZ))
+ me.assertTrue(isinstance(aecls.tagsz, C.KeySZ))
+ me.assertEqual(type(aecls.blksz), int)
+ me.assertEqual(type(aecls.bufsz), int)
+ me.assertEqual(type(aecls.ohd), int)
+ me.assertEqual(type(aecls.flags), int)
+
+ ## Check round-tripping, with full precommitment. First, select some
+ ## parameters. (It's conceivable that some AEAD schemes are more
+ ## restrictive than advertised by the various properties, but this works
+ ## out OK in practice.)
+ k = T.span(aecls.keysz.default)
+ n = T.span(aecls.noncesz.default)
+ if aecls.flags&C.AEADF_NOAAD: h = T.span(0)
+ else: h = T.span(131)
+ m = T.span(253)
+ tsz = aecls.tagsz.default
+ key = aecls(k)
+
+ ## Next, encrypt a message, checking that things are proper as we go.
+ enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+ me.assertEqual(enc.hsz, len(h))
+ me.assertEqual(enc.msz, len(m))
+ me.assertEqual(enc.mlen, 0)
+ me.assertEqual(enc.tsz, tsz)
+ aad = enc.aad()
+ if aecls.flags&C.AEADF_AADNDEP: me.assertEqual(aad.hsz, len(h))
+ else: me.assertEqual(aad.hsz, None)
+ me.assertEqual(aad.hlen, 0)
+ if not aecls.flags&C.AEADF_NOAAD:
+ aad.hash(h[0:83])
+ me.assertEqual(aad.hlen, 83)
+ aad.hash(h[83:131])
+ me.assertEqual(aad.hlen, 131)
+ c0 = enc.encrypt(m[0:57])
+ me.assertEqual(enc.mlen, 57)
+ me.assertTrue(57 - aecls.bufsz <= len(c0) <= 57 + aecls.ohd)
+ c1 = enc.encrypt(m[57:189])
+ me.assertEqual(enc.mlen, 189)
+ me.assertTrue(132 - aecls.bufsz <= len(c1) <=
+ 132 + aecls.bufsz + aecls.ohd)
+ c2 = enc.encrypt(m[189:253])
+ me.assertEqual(enc.mlen, 253)
+ me.assertTrue(64 - aecls.bufsz <= len(c2) <=
+ 64 + aecls.bufsz + aecls.ohd)
+ c3, t = enc.done(aad = aad)
+ me.assertTrue(len(c3) <= aecls.bufsz + aecls.ohd)
+ c = c0 + c1 + c2 + c3
+ me.assertTrue(len(m) <= len(c) <= len(m) + aecls.ohd)
+ me.assertEqual(len(t), tsz)
+
+ ## And now decrypt it again, with different record boundaries.
+ dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+ me.assertEqual(dec.hsz, len(h))
+ me.assertEqual(dec.csz, len(c))
+ me.assertEqual(dec.clen, 0)
+ me.assertEqual(dec.tsz, tsz)
+ aad = dec.aad()
+ if aecls.flags&C.AEADF_AADNDEP: me.assertEqual(aad.hsz, len(h))
+ else: me.assertEqual(aad.hsz, None)
+ me.assertEqual(aad.hlen, 0)
+ aad.hash(h)
+ m0 = dec.decrypt(c[0:156])
+ me.assertTrue(156 - aecls.bufsz <= len(m0) <= 156)
+ m1 = dec.decrypt(c[156:])
+ me.assertTrue(len(c) - 156 - aecls.bufsz <= len(m1) <=
+ len(c) - 156 + aecls.bufsz)
+ m2 = dec.done(tag = t, aad = aad)
+ me.assertEqual(m0 + m1 + m2, m)
+
+ ## And again, with the wrong tag.
+ dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+ aad = dec.aad(); aad.hash(h)
+ _ = dec.decrypt(c)
+ me.assertRaises(ValueError, dec.done, tag = t ^ tsz*C.bytes("55"))
+
+ ## Check that the all-in-one methods work.
+ me.assertEqual((c, t),
+ key.encrypt(n = n, h = h, m = m, tsz = tsz))
+ me.assertEqual(m,
+ key.decrypt(n = n, h = h, c = c, t = t))
+
+ ## Check that bad key, nonce, and tag lengths are rejected.
+ badlen = bad_key_size(aecls.keysz)
+ if badlen is not None: me.assertRaises(ValueError, aecls, T.span(badlen))
+ badlen = bad_key_size(aecls.noncesz)
+ if badlen is not None:
+ me.assertRaises(ValueError, key.enc, nonce = T.span(badlen),
+ hsz = len(h), msz = len(m), tsz = tsz)
+ me.assertRaises(ValueError, key.dec, nonce = T.span(badlen),
+ hsz = len(h), csz = len(c), tsz = tsz)
+ if not aecls.flags&C.AEADF_PCTSZ:
+ enc = key.enc(nonce = n, hsz = 0, msz = len(m))
+ _ = enc.encrypt(m)
+ me.assertRaises(ValueError, enc.done, tsz = badlen)
+ badlen = bad_key_size(aecls.tagsz)
+ if badlen is not None:
+ me.assertRaises(ValueError, key.enc, nonce = n,
+ hsz = len(h), msz = len(m), tsz = badlen)
+ me.assertRaises(ValueError, key.dec, nonce = n,
+ hsz = len(h), csz = len(c), tsz = badlen)
+
+ ## Check that we can't get a loose `aad' object from a scheme which has
+ ## nonce-dependent AAD processing.
+ if aecls.flags&C.AEADF_AADNDEP: me.assertRaises(ValueError, key.aad)
+
+ ## Check the menagerie of AAD hashing methods.
+ if not aecls.flags&C.AEADF_NOAAD:
+ def mkhash(hsz):
+ enc = key.enc(nonce = n, hsz = hsz, msz = 0, tsz = tsz)
+ aad = enc.aad()
+ return aad, lambda: enc.done(aad = aad)[1]
+ me.check_hashbuffer(mkhash)
+
+ ## Check that encryption/decryption works with the given precommitments.
+ def quick_enc_check(**kw):
+ enc = key.enc(**kw)
+ aad = enc.aad().hash(h)
+ c0 = enc.encrypt(m); c1, tt = enc.done(aad = aad, tsz = tsz)
+ me.assertEqual((c, t), (c0 + c1, tt))
+ def quick_dec_check(**kw):
+ dec = key.dec(**kw)
+ aad = dec.aad().hash(h)
+ m0 = dec.decrypt(c); m1 = dec.done(aad = aad, tag = t)
+ me.assertEqual(m, m0 + m1)
+
+ ## Check that we can get away without precommitting to the header length
+ ## if and only if the AEAD scheme says it will let us.
+ if aecls.flags&C.AEADF_PCHSZ:
+ me.assertRaises(ValueError, key.enc, nonce = n,
+ msz = len(m), tsz = tsz)
+ me.assertRaises(ValueError, key.dec, nonce = n,
+ csz = len(c), tsz = tsz)
+ else:
+ quick_enc_check(nonce = n, msz = len(m), tsz = tsz)
+ quick_dec_check(nonce = n, csz = len(c), tsz = tsz)
+
+ ## Check that we can get away without precommitting to the message/
+ ## ciphertext length if and only if the AEAD scheme says it will let us.
+ if aecls.flags&C.AEADF_PCMSZ:
+ me.assertRaises(ValueError, key.enc, nonce = n,
+ hsz = len(h), tsz = tsz)
+ me.assertRaises(ValueError, key.dec, nonce = n,
+ hsz = len(h), tsz = tsz)
+ else:
+ quick_enc_check(nonce = n, hsz = len(h), tsz = tsz)
+ quick_dec_check(nonce = n, hsz = len(h), tsz = tsz)
+
+ ## Check that we can get away without precommitting to the tag length if
+ ## and only if the AEAD scheme says it will let us.
+ if aecls.flags&C.AEADF_PCTSZ:
+ me.assertRaises(ValueError, key.enc, nonce = n,
+ hsz = len(h), msz = len(m))
+ me.assertRaises(ValueError, key.dec, nonce = n,
+ hsz = len(h), csz = len(c))
+ else:
+ quick_enc_check(nonce = n, hsz = len(h), msz = len(m))
+ quick_dec_check(nonce = n, hsz = len(h), csz = len(c))
+
+ ## Check that if we precommit to the header length, we're properly held
+ ## to the commitment.
+ if not aecls.flags&C.AEADF_NOAAD:
+
+ ## First, check encryption with underrun. If we must supply AAD first,
+ ## then the underrun will be reported when we start trying to encrypt;
+ ## otherwise, checking is delayed until `done'.
+ enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+ aad = enc.aad().hash(h[0:83])
+ if aecls.flags&C.AEADF_AADFIRST:
+ me.assertRaises(ValueError, enc.encrypt, m)
+ else:
+ _ = enc.encrypt(m)
+ me.assertRaises(ValueError, enc.done, aad = aad)
+
+ ## Next, check decryption with underrun. If we must supply AAD first,
+ ## then the underrun will be reported when we start trying to encrypt;
+ ## otherwise, checking is delayed until `done'.
+ dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+ aad = dec.aad().hash(h[0:83])
+ if aecls.flags&C.AEADF_AADFIRST:
+ me.assertRaises(ValueError, dec.decrypt, c)
+ else:
+ _ = dec.decrypt(c)
+ me.assertRaises(ValueError, dec.done, tag = t, aad = aad)
+
+ ## If AAD processing is nonce-dependent then an overrun will be
+ ## detected imediately.
+ if aecls.flags&C.AEADF_AADNDEP:
+ enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+ aad = enc.aad().hash(h[0:83])
+ me.assertRaises(ValueError, aad.hash, h[82:131])
+ dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+ aad = dec.aad().hash(h[0:83])
+ me.assertRaises(ValueError, aad.hash, h[82:131])
+
+ ## Some additional tests for nonce-dependent `aad' objects.
+ if aecls.flags&C.AEADF_AADNDEP:
+
+ ## Check that `aad' objects can't be used once their parents are gone.
+ enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+ aad = enc.aad()
+ del enc
+ me.assertRaises(ValueError, aad.hash, h)
+
+ ## Check that they can't be crossed over.
+ enc0 = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+ enc1 = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+ enc0.aad().hash(h)
+ aad1 = enc1.aad().hash(h)
+ _ = enc0.encrypt(m)
+ me.assertRaises(ValueError, enc0.done, tsz = tsz, aad = aad1)
+
+ ## Test copying AAD.
+ if not aecls.flags&C.AEADF_AADNDEP and not aecls.flags&C.AEADF_NOAAD:
+ aad0 = key.aad()
+ aad0.hash(h[0:83])
+ aad1 = aad0.copy()
+ aad2 = aad1.copy()
+ aad0.hash(h[83:131])
+ aad1.hash(h[83:131])
+ aad2.hash(h[83:131] ^ 48*C.bytes("ff"))
+ me.assertEqual(key.enc(nonce = n, hsz = len(h),
+ msz = 0, tsz = tsz).done(aad = aad0),
+ key.enc(nonce = n, hsz = len(h),
+ msz = 0, tsz = tsz).done(aad = aad1))
+ me.assertNotEqual(key.enc(nonce = n, hsz = len(h),
+ msz = 0, tsz = tsz).done(aad = aad0),
+ key.enc(nonce = n, hsz = len(h),
+ msz = 0, tsz = tsz).done(aad = aad2))
+
+ ## Check that if we precommit to the message length, we're properly held
+ ## to the commitment. (Fortunately, this is way simpler than the AAD
+ ## case above.) First, try an underrun.
+ enc = key.enc(nonce = n, hsz = 0, msz = len(m), tsz = tsz)
+ _ = enc.encrypt(m[0:183])
+ me.assertRaises(ValueError, enc.done, tsz = tsz)
+ dec = key.dec(nonce = n, hsz = 0, csz = len(c), tsz = tsz)
+ _ = dec.decrypt(c[0:183])
+ me.assertRaises(ValueError, dec.done, tag = t)
+
+ ## And now an overrun.
+ enc = key.enc(nonce = n, hsz = 0, msz = 183, tsz = tsz)
+ me.assertRaises(ValueError, enc.encrypt, m)
+ dec = key.dec(nonce = n, hsz = 0, csz = 183, tsz = tsz)
+ me.assertRaises(ValueError, dec.decrypt, c)
+
+ ## Finally, check that if we precommit to a tag length, we're properly
+ ## held to the commitment. This depends on being able to find a tag size
+ ## which isn't the default.
+ tsz1 = different_key_size(aecls.tagsz, tsz)
+ if tsz1 is not None:
+ enc = key.enc(nonce = n, hsz = 0, msz = len(m), tsz = tsz1)
+ _ = enc.encrypt(m)
+ me.assertRaises(ValueError, enc.done, tsz = tsz)
+ dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz1)
+ aad = dec.aad().hash(h)
+ _ = dec.decrypt(c)
+ me.assertRaises(ValueError, enc.done, tsz = tsz, aad = aad)
+
+TestAuthenticatedEncryption.generate_testcases \
+ ((name, C.gcaeads[name]) for name in
+ ["des3-ccm", "blowfish-ocb1", "square-ocb3", "rijndael-gcm",
+ "serpent-eax", "salsa20-naclbox", "chacha20-poly1305"])
+
+###--------------------------------------------------------------------------
+class BaseTestHash (HashBufferTestMixin):
+ """Base class for testing hash functions."""
+
+ def check_hash(me, hcls, need_bufsz = True):
+ """
+ Check hash class HCLS.
+
+ If NEED_BUFSZ is false, then don't insist that HCLS has a working `bufsz'
+ attribute. This test is mostly reused for MACs, which don't have this
+ attribute.
+ """
+ ## Check the class properties.
+ me.assertEqual(type(hcls.name), str)
+ if need_bufsz: me.assertEqual(type(hcls.bufsz), int)
+ me.assertEqual(type(hcls.hashsz), int)
+
+ ## Set some initial values.
+ m = T.span(131)
+ h = hcls().hash(m).done()
+
+ ## Check that hash length comes out right.
+ me.assertEqual(len(h), hcls.hashsz)
+
+ ## Check that we get the same answer if we split the message up.
+ me.assertEqual(h, hcls().hash(m[0:73]).hash(m[73:131]).done())
+
+ ## Check the `check' method.
+ me.assertTrue(hcls().hash(m).check(h))
+ me.assertFalse(hcls().hash(m).check(h ^ hcls.hashsz*C.bytes("aa")))
+
+ ## Check the menagerie of random hashing methods.
+ def mkhash(_):
+ h = hcls()
+ return h, h.done
+ me.check_hashbuffer(mkhash)
+
+class TestHash (BaseTestHash, T.GenericTestMixin):
+ """Test hash functions."""
+ def _test_hash(me, hcls): me.check_hash(hcls, need_bufsz = True)
+
+TestHash.generate_testcases((name, C.gchashes[name]) for name in
+ ["md5", "sha", "whirlpool", "sha256", "sha512/224", "sha3-384", "shake256",
+ "crc32"])
+
+###--------------------------------------------------------------------------
+class TestMessageAuthentication (BaseTestHash, T.GenericTestMixin):
+ """Test message authentication codes."""
+
+ def _test_mac(me, mcls):
+
+ ## Check the MAC properties.
+ me.assertEqual(type(mcls.name), str)
+ me.assertTrue(isinstance(mcls.keysz, C.KeySZ))
+ me.assertEqual(type(mcls.tagsz), int)
+
+ ## Test hashing.
+ k = T.span(mcls.keysz.default)
+ key = mcls(k)
+ me.assertEqual(key.hashsz, key.tagsz)
+ me.check_hash(key, need_bufsz = False)
+
+ ## Check that bad key lengths are rejected.
+ badlen = bad_key_size(mcls.keysz)
+ if badlen is not None: me.assertRaises(ValueError, mcls, T.span(badlen))
+
+TestMessageAuthentication.generate_testcases \
+ ((name, C.gcmacs[name]) for name in
+ ["sha-hmac", "rijndael-cmac", "twofish-pmac1", "kmac128"])
+
+class TestPoly1305 (HashBufferTestMixin):
+ """Check the Poly1305 one-time message authentication function."""
+
+ def test_poly1305(me):
+
+ ## Check the MAC properties.
+ me.assertEqual(C.poly1305.name, "poly1305")
+ me.assertEqual(type(C.poly1305.keysz), C.KeySZSet)
+ me.assertEqual(C.poly1305.keysz.default, 16)
+ me.assertEqual(C.poly1305.keysz.set, set([16]))
+ me.assertEqual(C.poly1305.tagsz, 16)
+ me.assertEqual(C.poly1305.masksz, 16)
+
+ ## Set some initial values.
+ k = T.span(16)
+ u = T.span(64)[-16:]
+ m = T.span(149)
+ key = C.poly1305(k)
+ t = key(u).hash(m).done()
+
+ ## Check the key properties.
+ me.assertEqual(key.name, "poly1305")
+ me.assertEqual(key.tagsz, 16)
+ me.assertEqual(key.tagsz, 16)
+ me.assertEqual(len(t), 16)
+
+ ## Check that we get the same answer if we split the message up.
+ me.assertEqual(t, key(u).hash(m[0:86]).hash(m[86:149]).done())
+
+ ## Check the `check' method.
+ me.assertTrue(key(u).hash(m).check(t))
+ me.assertFalse(key(u).hash(m).check(t ^ 16*C.bytes("cc")))
+
+ ## Check the menagerie of random hashing methods.
+ def mkhash(_):
+ h = key(u)
+ return h, h.done
+ me.check_hashbuffer(mkhash)
+
+ ## Check that we can't complete hashing without a mask.
+ me.assertRaises(ValueError, key().hash(m).done)
+
+ ## Check `concat'.
+ h0 = key().hash(m[0:96])
+ h1 = key().hash(m[96:117])
+ me.assertEqual(t, key(u).concat(h0, h1).hash(m[117:149]).done())
+ key1 = C.poly1305(k)
+ me.assertRaises(TypeError, key().concat, key1().hash(m[0:96]), h1)
+ me.assertRaises(TypeError, key().concat, h0, key1().hash(m[96:117]))
+ me.assertRaises(ValueError, key().concat, key().hash(m[0:93]), h1)
+
+###--------------------------------------------------------------------------
+class TestHLatin (U.TestCase):
+ """Test the `hsalsa20' and `hchacha20' functions."""
+
+ def test_hlatin(me):
+ kk = [T.span(sz) for sz in [10, 16, 32]]
+ n = T.span(16)
+ bad_k = T.span(18)
+ bad_n = T.span(13)
+ for fn in [C.hsalsa208_prf, C.hsalsa2012_prf, C.hsalsa20_prf,
+ C.hchacha8_prf, C.hchacha12_prf, C.hchacha20_prf]:
+ for k in kk:
+ h = fn(k, n)
+ me.assertEqual(len(h), 32)
+ me.assertRaises(ValueError, fn, bad_k, n)
+ me.assertRaises(ValueError, fn, k, bad_n)
+
+###--------------------------------------------------------------------------
+class TestKeccak (HashBufferTestMixin):
+ """Test the Keccak-p[1600, n] sponge function."""
+
+ def test_keccak(me):
+
+ ## Make a state and feed some stuff into it.
+ m0 = T.bin("some initial string")
+ m1 = T.bin("awesome follow-up string")
+ st0 = C.Keccak1600()
+ me.assertEqual(st0.nround, 24)
+ st0.mix(m0).step()
+
+ ## Make another step with a different round count.
+ st1 = C.Keccak1600(23)
+ st1.mix(m0).step()
+ me.assertNotEqual(st0.extract(32), st1.extract(32))
+
+ ## Check state copying and `mix' vs `set'.
+ st1 = st0.copy()
+ mask = st1.extract(len(m1))
+ st0.mix(m1)
+ st1.set(m1 ^ mask)
+ me.assertEqual(st0.extract(32), st1.extract(32))
+
+ ## Check error conditions.
+ _ = st0.extract(200)
+ me.assertRaises(ValueError, st0.extract, 201)
+ st0.mix(T.span(200))
+ me.assertRaises(ValueError, st0.mix, T.span(201))
+ st0.set(T.span(200))
+ me.assertRaises(ValueError, st0.set, T.span(201))
+ me.assertRaises(ValueError, st0.set, T.span(199))
+
+ def check_shake(me, xcls, c, done_matches_xof = True):
+ """
+ Test the SHAKE and cSHAKE XOFs.
+
+ This is also used for testing KMAC, but that sets DONE_MATCHES_XOF false
+ to indicate that the XOF output is range-separated from the fixed-length
+ outputs (unlike the basic SHAKE functions).
+ """
+
+ ## Check the hash attributes.
+ x = xcls()
+ me.assertEqual(x.rate, 200 - c)
+ me.assertEqual(x.buffered, 0)
+ me.assertEqual(x.state, "absorb")
+
+ ## Set some initial values.
+ func = T.bin("TESTXOF")
+ perso = T.bin("catacomb-python test")
+ m = T.span(167)
+ h0 = xcls().hash(m).done(193)
+ me.assertEqual(len(h0), 193)
+ h1 = xcls(func = func, perso = perso).hash(m).done(193)
+ me.assertEqual(len(h1), 193)
+ me.assertNotEqual(h0, h1)
+
+ ## Check input and output in pieces, and the state machine.
+ if done_matches_xof: h = h0
+ else: h = xcls().hash(m).xof().get(len(h0))
+ x = xcls().hash(m[0:76]).hash(m[76:167]).xof()
+ me.assertEqual(h, x.get(98) + x.get(95))
+
+ ## Check masking.
+ x = xcls().hash(m).xof()
+ me.assertEqual(x.mask(m), m ^ h[0:len(m)])
+
+ ## Check the `check' method.
+ me.assertTrue(xcls().hash(m).check(h0))
+ me.assertFalse(xcls().hash(m).check(h1))
+
+ ## Check the menagerie of random hashing methods.
+ def mkhash(_):
+ x = xcls(func = func, perso = perso)
+ return x, x.done
+ me.check_hashbuffer(mkhash)
+
+ ## Check the state machine tracking.
+ x = xcls(); me.assertEqual(x.state, "absorb")
+ x.hash(m); me.assertEqual(x.state, "absorb")
+ xx = x.copy()
+ h = xx.done(); me.assertEqual(len(h), 100 - x.rate//2)
+ me.assertEqual(xx.state, "dead")
+ me.assertRaises(ValueError, xx.done, 1)
+ me.assertRaises(ValueError, xx.get, 1)
+ me.assertEqual(x.state, "absorb")
+ me.assertRaises(ValueError, x.get, 1)
+ x.xof(); me.assertEqual(x.state, "squeeze")
+ me.assertRaises(ValueError, x.done, 1)
+ _ = x.get(1)
+ yy = x.copy(); me.assertEqual(yy.state, "squeeze")
+
+ def test_shake128(me): me.check_shake(C.Shake128, 32)
+ def test_shake256(me): me.check_shake(C.Shake256, 64)
+
+ def check_kmac(me, mcls, c):
+ k = T.span(32)
+ me.check_shake(lambda func = None, perso = None:
+ mcls(k, perso = perso),
+ c, done_matches_xof = False)
+
+ def test_kmac128(me): me.check_kmac(C.KMAC128, 32)
+ def test_kmac256(me): me.check_kmac(C.KMAC256, 64)
+
+###--------------------------------------------------------------------------
+class TestPRP (T.GenericTestMixin):
+ """Test pseudorandom permutations (PRPs)."""
+
+ def _test_prp(me, pcls):
+
+ ## Check the PRP properties.
+ me.assertEqual(type(pcls.name), str)
+ me.assertTrue(isinstance(pcls.keysz, C.KeySZ))
+ me.assertEqual(type(pcls.blksz), int)
+
+ ## Check round-tripping.
+ k = T.span(pcls.keysz.default)
+ key = pcls(k)
+ m = T.span(pcls.blksz)
+ c = key.encrypt(m)
+ me.assertEqual(len(c), pcls.blksz)
+ me.assertEqual(m, key.decrypt(c))
+
+ ## Check that bad key lengths are rejected.
+ badlen = bad_key_size(pcls.keysz)
+ if badlen is not None: me.assertRaises(ValueError, pcls, T.span(badlen))
+
+ ## Check that bad blocks are rejected.
+ badblk = T.span(pcls.blksz + 1)
+ me.assertRaises(ValueError, key.encrypt, badblk)
+ me.assertRaises(ValueError, key.decrypt, badblk)
+
+TestPRP.generate_testcases((name, C.gcprps[name]) for name in
+ ["desx", "blowfish", "rijndael"])
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Test read and write buffers
+###
+### (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 TestReadBuffer (U.TestCase):
+
+ def check_getn(me, w, bigendp, getfn):
+ """Check that `getuN' works."""
+ buf = C.ReadBuffer(T.span(w + 2))
+ me.assertEqual(buf.getu8(), 0x00)
+ me.assertEqual(getfn(buf), T.bytes_as_int(w, bigendp))
+ me.assertEqual(buf.getu8(), w + 1)
+ me.assertTrue(buf.endp)
+ me.assertRaises(C.BufferError, getfn, C.ReadBuffer(T.span(w - 1)))
+ me.assertEqual(getfn(C.ReadBuffer(w*C.bytes("00"))), 0)
+ me.assertEqual(getfn(C.ReadBuffer(w*C.bytes("ff"))), (1 << 8*w) - 1)
+
+ def check_getbufn(me, w, bigendp, blkfn, buffn):
+ """Check that `getblkN' and `getbufN' work."""
+
+ ## Run tests for several different data sizes.
+ for n in [0, 1, 7, 8, 19, 255, 12345, 65535, 123456]:
+
+ ## Make a sequence to parse. If it's too large, then skip.
+ if n >= 1 << 8*w: continue
+ seq = T.prep_lenseq(w, n, bigendp, True)
+
+ ## Check `getblkN'.
+ buf = C.ReadBuffer(seq)
+ me.assertEqual(buf.getu8(), 0)
+ me.assertEqual(blkfn(buf), T.span(n))
+ me.assertEqual(buf.getu8(), 0xff)
+ me.assertTrue(buf.endp)
+
+ ## Check `getbufN'. Delete the outside buffer early, to make sure that
+ ## the subbuffer keeps it alive.
+ buf = C.ReadBuffer(seq)
+ me.assertEqual(buf.getu8(), 0)
+ b = buffn(buf)
+ me.assertEqual(buf.getu8(), 0xff)
+ me.assertTrue(buf.endp)
+ del buf
+ me.assertEqual(b.offset, 0)
+ me.assertEqual(b.size, n)
+ if n > 0:
+ me.assertEqual(b.getu8(), 0x00)
+ b.offset = n - 1
+ me.assertEqual(b.getu8(), (n - 1)&0xff)
+ me.assertTrue(b.endp)
+
+ ## Test invalid lengths. This is going to work by setting the top bit
+ ## of the length, so if it's already set, then that won't be any good.
+ if n >= 1 << 8*w - 1: continue
+ seq = T.prep_lenseq(w, n, bigendp, False)
+
+ ## Check `getblkN'.
+ me.assertRaises(C.BufferError, blkfn, C.ReadBuffer(T.span(w - 1)))
+ b = C.ReadBuffer(seq)
+ me.assertEqual(b.getu8(), 0)
+ me.assertRaises(C.BufferError, blkfn, b)
+
+ ## Check `getbufN'.
+ me.assertRaises(C.BufferError, buffn, C.ReadBuffer(T.span(w - 1)))
+ b = C.ReadBuffer(seq)
+ me.assertEqual(b.getu8(), 0)
+ me.assertRaises(C.BufferError, buffn, b)
+
+ def test_readbuffer(me):
+
+ ## Check `getuN'.
+ me.check_getn(1, True, lambda buf: buf.getu8())
+ me.check_getn(2, True, lambda buf: buf.getu16())
+ me.check_getn(2, True, lambda buf: buf.getu16b())
+ me.check_getn(2, False, lambda buf: buf.getu16l())
+ me.check_getn(3, True, lambda buf: buf.getu24())
+ me.check_getn(3, True, lambda buf: buf.getu24b())
+ me.check_getn(3, False, lambda buf: buf.getu24l())
+ me.check_getn(4, True, lambda buf: buf.getu32())
+ me.check_getn(4, True, lambda buf: buf.getu32b())
+ me.check_getn(4, False, lambda buf: buf.getu32l())
+ if hasattr(C.ReadBuffer, "getu64"):
+ me.check_getn(8, True, lambda buf: buf.getu64())
+ me.check_getn(8, True, lambda buf: buf.getu64b())
+ me.check_getn(8, False, lambda buf: buf.getu64l())
+
+ ## Check `getblkN' and `getbufN'.
+ me.check_getbufn(1, True,
+ lambda buf: buf.getblk8(),
+ lambda buf: buf.getbuf8())
+ me.check_getbufn(2, True,
+ lambda buf: buf.getblk16(),
+ lambda buf: buf.getbuf16())
+ me.check_getbufn(2, True,
+ lambda buf: buf.getblk16b(),
+ lambda buf: buf.getbuf16b())
+ me.check_getbufn(2, False,
+ lambda buf: buf.getblk16l(),
+ lambda buf: buf.getbuf16l())
+ me.check_getbufn(3, True,
+ lambda buf: buf.getblk24(),
+ lambda buf: buf.getbuf24())
+ me.check_getbufn(3, True,
+ lambda buf: buf.getblk24b(),
+ lambda buf: buf.getbuf24b())
+ me.check_getbufn(3, False,
+ lambda buf: buf.getblk24l(),
+ lambda buf: buf.getbuf24l())
+ me.check_getbufn(4, True,
+ lambda buf: buf.getblk32(),
+ lambda buf: buf.getbuf32())
+ me.check_getbufn(4, True,
+ lambda buf: buf.getblk32b(),
+ lambda buf: buf.getbuf32b())
+ me.check_getbufn(4, False,
+ lambda buf: buf.getblk32l(),
+ lambda buf: buf.getbuf32l())
+ if hasattr(C.ReadBuffer, "getu64"):
+ me.check_getbufn(8, True,
+ lambda buf: buf.getblk64(),
+ lambda buf: buf.getbuf64())
+ me.check_getbufn(8, True,
+ lambda buf: buf.getblk64b(),
+ lambda buf: buf.getbuf64b())
+ me.check_getbufn(8, False,
+ lambda buf: buf.getblk64l(),
+ lambda buf: buf.getbuf64l())
+
+ ## Check other `ReadBuffer' methods and properties.
+ buf = C.ReadBuffer(T.span(256))
+ me.assertEqual(buf.size, 256)
+ me.assertEqual(buf.left, 256)
+ me.assertEqual(buf.offset, 0)
+ buf.offset = 52
+ me.assertEqual(buf.left, 204)
+ buf.skip(7)
+ me.assertEqual(buf.offset, 59)
+ me.assertEqual(buf.left, 197)
+ me.assertRaises(C.BufferError, C.ReadBuffer(T.span(6)).skip, 7)
+ me.assertEqual(buf.get(5), C.bytes("3b3c3d3e3f"))
+ me.assertRaises(C.BufferError, C.ReadBuffer(T.span(4)).get, 5)
+
+###--------------------------------------------------------------------------
+class TestWriteBuffer (U.TestCase):
+
+ def check_putn(me, w, bigendp, putfn):
+ """Check `putuN'."""
+
+ ## Check encoding an integer.
+ buf = C.WriteBuffer()
+ buf.putu8(0x00)
+ putfn(buf, T.bytes_as_int(w, bigendp))
+ buf.putu8(w + 1)
+ me.assertEqual(buf.contents, T.span(w + 2))
+ me.assertEqual(putfn(C.WriteBuffer(), (1 << 8*w) - 1).contents,
+ w*C.bytes("ff"))
+ me.assertEqual(putfn(C.WriteBuffer(), C.MP(0)).contents, w*C.bytes("00"))
+
+ ## Check overflow detection.
+ me.assertRaises(OverflowError, putfn, C.WriteBuffer(), 1 << 8*w)
+
+ def check_putbufn(me, w, bigendp, putfn):
+ """Check `putblkN'."""
+
+ ## Go through a number of different sizes.
+ for n in [0, 1, 7, 8, 19, 255, 12345, 65535, 123456]:
+ if n >= 1 << 8*w: continue
+ me.assertEqual(putfn(C.WriteBuffer().putu8(0x00),
+ T.span(n)).putu8(0xff).contents,
+ T.prep_lenseq(w, n, bigendp, True))
+
+ ## Check blocks which are too large for the length prefix.
+ if w <= 3:
+ me.assertRaises(ValueError, putfn,
+ C.WriteBuffer(), C.ByteString.zero(1 << 8*w))
+
+ def test_writebuffer(me):
+
+ ## Check `putuN'.
+ me.check_putn(1, True, lambda buf, n: buf.putu8(n))
+ me.check_putn(2, True, lambda buf, n: buf.putu16(n))
+ me.check_putn(2, True, lambda buf, n: buf.putu16b(n))
+ me.check_putn(2, False, lambda buf, n: buf.putu16l(n))
+ me.check_putn(3, True, lambda buf, n: buf.putu24(n))
+ me.check_putn(3, True, lambda buf, n: buf.putu24b(n))
+ me.check_putn(3, False, lambda buf, n: buf.putu24l(n))
+ me.check_putn(4, True, lambda buf, n: buf.putu32(n))
+ me.check_putn(4, True, lambda buf, n: buf.putu32b(n))
+ me.check_putn(4, False, lambda buf, n: buf.putu32l(n))
+ if hasattr(C.WriteBuffer, "putu64"):
+ me.check_putn(8, True, lambda buf, n: buf.putu64(n))
+ me.check_putn(8, True, lambda buf, n: buf.putu64b(n))
+ me.check_putn(8, False, lambda buf, n: buf.putu64l(n))
+
+ ## Check `putblkN".
+ me.check_putbufn(1, True, lambda buf, x: buf.putblk8(x))
+ me.check_putbufn(2, True, lambda buf, x: buf.putblk16(x))
+ me.check_putbufn(2, True, lambda buf, x: buf.putblk16b(x))
+ me.check_putbufn(2, False, lambda buf, x: buf.putblk16l(x))
+ me.check_putbufn(3, True, lambda buf, x: buf.putblk24(x))
+ me.check_putbufn(3, True, lambda buf, x: buf.putblk24b(x))
+ me.check_putbufn(3, False, lambda buf, x: buf.putblk24l(x))
+ me.check_putbufn(4, True, lambda buf, x: buf.putblk32(x))
+ me.check_putbufn(4, True, lambda buf, x: buf.putblk32b(x))
+ me.check_putbufn(4, False, lambda buf, x: buf.putblk32l(x))
+ if hasattr(C.WriteBuffer, "putu64"):
+ me.check_putbufn(8, True, lambda buf, x: buf.putblk64(x))
+ me.check_putbufn(8, True, lambda buf, x: buf.putblk64b(x))
+ me.check_putbufn(8, False, lambda buf, x: buf.putblk64l(x))
+
+ ## Check other methods and properties.
+ buf = C.WriteBuffer()
+ buf.zero(17)
+ buf.put(T.span(23))
+ me.assertEqual(buf.size, 40)
+ me.assertEqual(buf, C.ByteString.zero(17) + T.span(23))
+ me.assertEqual(buf.contents, C.ByteString.zero(17) + T.span(23))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Test `ByteString'
+###
+### (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 sys as SYS
+import catacomb as C
+import unittest as U
+import testutils as T
+
+###--------------------------------------------------------------------------
+class TestByteString (U.TestCase):
+
+ def test_create(me):
+
+ ## Create a string and make sure it looks right.
+ x = C.ByteString(T.bin("abcde"))
+ me.assertEqual(x, T.bin("abcde"))
+ me.assertEqual(x, C.bytes("6162636465"))
+ me.assertEqual(len(x), 5)
+
+ def test_index(me):
+
+ x = C.ByteString(T.bin("once upon a time there was a string"))
+
+ ## Check that simple indexing works. Alas the behaviour differs between
+ ## Python major versions.
+ if T.PY3:
+ me.assertEqual(type(x[3]), int)
+ me.assertEqual(x[3], 101)
+ me.assertEqual(x[-5], 116)
+ else:
+ me.assertEqual(type(x[3]), C.ByteString)
+ me.assertEqual(x[3], 'e')
+ me.assertEqual(x[-5], 't')
+
+ ## Check out-of-range detection.
+ x[34]; me.assertRaises(IndexError, lambda: x[35])
+ x[-35]; me.assertRaises(IndexError, lambda: x[-36])
+
+ ## Check slicing. This should always give us bytes.
+ me.assertEqual(type(x[7:17]), C.ByteString)
+ me.assertEqual(x[7:17], T.bin("on a time "))
+
+ ## Complex slicing is also supported.
+ me.assertEqual(x[5:23:3], C.bytes("756e206d7472"))
+
+ def test_compare(me):
+ """
+ Test byte string comparison.
+
+ This is rather important, since we override it and many of the other
+ tests assume that comparison works.
+ """
+
+ def check(big, small):
+ """Check comparisons between BIG and SMALL strings."""
+
+ ## Equality.
+ me.assertTrue(big == big)
+ me.assertFalse(big == small)
+
+ ## Inequality.
+ me.assertFalse(big != big)
+ me.assertTrue(big != small)
+
+ ## Strict less-than.
+ me.assertFalse(big < big)
+ me.assertFalse(big < small)
+ me.assertTrue(small < big)
+
+ ## Non-strict less-than.
+ me.assertTrue(big <= big)
+ me.assertFalse(big <= small)
+ me.assertTrue(small <= big)
+
+ ## Non-strict greater-than.
+ me.assertTrue(big >= big)
+ me.assertTrue(big >= small)
+ me.assertFalse(small >= big)
+
+ ## Strict greater-than.
+ me.assertFalse(big > big)
+ me.assertTrue(big > small)
+ me.assertFalse(small > big)
+
+ ## Strings with equal length.
+ check(C.ByteString(T.bin("a string which is unlike the second")),
+ C.ByteString(T.bin("a string that is not like the first")))
+
+ ## A string and a prefix of it.
+ check(C.ByteString(T.bin("short strings order before longer ones")),
+ C.ByteString(T.bin("short string")))
+
+ ## The `ctstreq' function.
+ x = T.bin("special test string")
+ y = T.bin("my different string")
+ me.assertTrue(C.ctstreq(x, x))
+ me.assertFalse(C.ctstreq(x, y))
+
+ def test_operators(me):
+
+ ## Some example strings.
+ x = C.bytes("03a5fc")
+ y = C.bytes("5fac30")
+ z = C.bytes("00000000")
+
+ ## Operands of a binary operator must have equal lengths.
+ me.assertRaises(ValueError, lambda: x&z)
+ me.assertRaises(ValueError, lambda: x|z)
+ me.assertRaises(ValueError, lambda: x^z)
+
+ ## Bitwise AND.
+ me.assertEqual(type(x&y), C.ByteString)
+ me.assertEqual(x&y, C.bytes("03a430"))
+
+ ## Bitwise OR.
+ me.assertEqual(type(x | y), C.ByteString)
+ me.assertEqual(x | y, C.bytes("5fadfc"))
+
+ # Bitwise XOR.
+ me.assertEqual(type(x ^ y), C.ByteString)
+ me.assertEqual(x ^ y, C.bytes("5c09cc"))
+
+ # Bitwise NOT.
+ me.assertEqual(type(~x), C.ByteString)
+ me.assertEqual(~x, C.bytes("fc5a03"))
+
+ ## Concatenation.
+ me.assertEqual(type(x + y), C.ByteString)
+ me.assertEqual(x + y, C.bytes("03a5fc5fac30"))
+
+ ## Replication (asymmetric but commutative).
+ me.assertEqual(type(3*x), C.ByteString)
+ me.assertEqual(type(x*3), C.ByteString)
+ me.assertEqual(3*x, C.bytes("03a5fc03a5fc03a5fc"))
+ me.assertEqual(x*3, C.bytes("03a5fc03a5fc03a5fc"))
+
+ ## Replication by zero (regression test).
+ me.assertEqual(type(0*x), C.ByteString)
+ me.assertEqual(type(x*0), C.ByteString)
+ me.assertEqual(0*x, C.ByteString(T.bin("")))
+ me.assertEqual(x*0, C.ByteString(T.bin("")))
+
+ def test_zero(me):
+ me.assertEqual(C.ByteString.zero(7), T.bin(7*"\0"))
+ me.assertEqual(C.ByteString.zero(0), T.bin(""))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Testing implicit conversions
+###
+### (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 sys as SYS
+import unittest as U
+import testutils as T
+
+###--------------------------------------------------------------------------
+class TestConvert (U.TestCase):
+ """Test implicit conversions between various mathematical types."""
+
+ def test(me):
+
+ k = C.PrimeField(17)
+ E = k.ec(-3, 4)
+ P = E(1) # order 15
+ kk = C.BinPolyField(0x13)
+ EE = kk.ec(1, 2)
+ PP = E(9) # order 16
+
+ ## `MP' asymmetric operations.
+ me.assertEqual(pow(C.MP(5), 2), 25)
+ me.assertEqual(pow(5, C.MP(2)), 25)
+ me.assertEqual(pow(C.MP(5), 2, 7), 4)
+ me.assertEqual(pow(5, C.MP(2), 7), 4)
+ me.assertEqual(pow(5, 2, C.MP(7)), 4)
+ for bad in [C.GF, k, kk, str, float, lambda x: [x]]:
+ me.assertRaises(TypeError, pow, C.MP(5), bad(2))
+ me.assertRaises(TypeError, pow, C.MP(5), bad(2), 7)
+ if not (T.PY2 and T.DEBUGP):
+ ## Debug builds of Python 2 crash here, and it's not our fault. Run
+ ##
+ ## $ python2.7-dbg -c 'pow(long(5), 2, [7])'
+ ##
+ ## to confirm. The `[7]' causes coercion to occur. The first and
+ ## second operands are coerced first, and successfully replaced by
+ ## the results: the first operand (in this case) is unchanged, but
+ ## has its refcount bumped, and the second operand is replaced by the
+ ## result of coercion, which now has a refcount of 1. Then the first
+ ## and third operands are coerced, which fails. Python decrements
+ ## the refcounts of the results of the first coercion, so the second
+ ## operand is now freed and, in debug builds, clobbered. Python then
+ ## tries to format an error message, quoting the types of the
+ ## operands, but one of them has been lost.
+ me.assertRaises(TypeError, pow, C.MP(5), 2, bad(7))
+ me.assertRaises(TypeError, T.lsl, C.MP(5), bad(2))
+
+ ## `GF' asymmetric operations.
+ me.assertEqual(pow(C.GF(0x5), 2), C.GF(0x11))
+ me.assertEqual(pow(C.GF(0x5), C.MP(2)), C.GF(0x11))
+ me.assertEqual(pow(C.GF(5), 2, C.GF(0x13)), C.GF(0x2))
+ for bad in [k, kk, str, float, lambda x: [x]]:
+ me.assertRaises(TypeError, pow, C.GF(5), bad(2))
+ me.assertRaises(TypeError, T.lsl, C.GF(5), bad(2))
+ for bad in [int, C.MP, k, kk, str, float, lambda x: [x]]:
+ me.assertRaises(TypeError, pow, bad(5), C.GF(2))
+ me.assertRaises(TypeError, pow, bad(5), C.GF(2), bad(7))
+ me.assertRaises(TypeError, pow, bad(5), bad(2), C.GF(7))
+ me.assertRaises(TypeError, pow, C.GF(5), bad(2), bad(7))
+ if not (T.PY2 and T.DEBUGP):
+ ## Python bug: see above.
+ me.assertRaises(TypeError, pow, C.GF(5), 2, bad(7))
+
+ ## `MP' and `GF'.
+ me.assertEqual(C.MP(5), 5)
+ me.assertEqual(5, C.MP(5))
+ me.assertNotEqual(C.GF(5), 5)
+ me.assertNotEqual(5, C.GF(5))
+ me.assertNotEqual(C.MP(5), C.GF(5))
+ me.assertNotEqual(C.GF(5), C.MP(5))
+
+ me.assertEqual(C.MP(5) + 3, 8)
+ me.assertEqual(3 + C.MP(5), 8)
+ me.assertRaises(TypeError, T.add, C.GF(5), 3)
+ me.assertRaises(TypeError, T.add, 3, C.GF(5))
+ me.assertRaises(TypeError, T.add, C.MP(5), C.GF(3))
+ me.assertRaises(TypeError, T.add, C.GF(3), C.MP(5))
+ if T.PY3: me.assertRaises(TypeError, T.le, C.MP(4), C.GF(5))
+
+ ## Field elements.
+ me.assertEqual(k(3) + 4, 7)
+ me.assertEqual(4 + k(3), 7)
+ me.assertEqual(k(3) + C.MP(4), 7)
+ me.assertEqual(C.MP(4) + k(3), 7)
+ me.assertEqual(k(3) + 4, 7)
+ me.assertEqual(C.GF(7) + kk(3), C.GF(4))
+ me.assertEqual(kk(3) + C.GF(7), C.GF(4))
+
+ me.assertRaises(TypeError, T.add, k(3), 3.0)
+ me.assertRaises(TypeError, T.add, k(3), "3")
+ me.assertRaises(TypeError, T.add, k(3), kk(3))
+ me.assertRaises(TypeError, T.add, kk(3), k(3))
+ me.assertRaises(TypeError, T.add, k(3), C.GF(7))
+ me.assertRaises(TypeError, T.add, C.GF(7), k(3))
+ me.assertRaises(TypeError, T.add, kk(3), 7)
+ me.assertRaises(TypeError, T.add, kk(3), 7.0)
+ me.assertRaises(TypeError, T.add, kk(3), "7")
+ me.assertRaises(TypeError, T.add, 7, kk(3))
+ me.assertRaises(TypeError, T.add, kk(3), C.MP(7))
+ me.assertRaises(TypeError, T.add, C.MP(7), kk(3))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Testing elliptic curve 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
+
+k = C.PrimeField(19)
+E = k.ec(-3, 6)
+P = E(0) # order 26
+
+kk = C.BinPolyField(0x13)
+EE = kk.ec(1, 8)
+PP = EE(8) # order 20
+
+###--------------------------------------------------------------------------
+class TestCurvelessPoints (U.TestCase):
+ """Test handling of points without an explicit curve."""
+
+ def test(me):
+
+ ## Construction.
+ O = C.ECPt(); me.assertFalse(O)
+ P = C.ECPt(12345, 67890); me.assertTrue(P)
+ Q = C.ECPt(23456, 78901); me.assertTrue(Q); me.assertNotEqual(P, Q)
+ R = C.ECPt(O); me.assertFalse(R); me.assertEqual(O, R); me.assertNotEqual(P, R)
+ R = C.ECPt(None); me.assertFalse(R); me.assertEqual(O, R)
+ me.assertEqual(C.ECPt("12345, 67890"), P)
+ me.assertEqual(C.ECPt((12345, 67890)), P)
+ me.assertRaises(TypeError, C.ECPt, ())
+ me.assertRaises(TypeError, C.ECPt, (1234,))
+ me.assertRaises(TypeError, C.ECPt, (1, 2, 3, 4))
+ me.assertRaises(ValueError, C.ECPt, "12345")
+ me.assertRaises(ValueError, C.ECPt, "12345,")
+ me.assertRaises(ValueError, C.ECPt, "12345, xyzzy")
+ me.assertRaises(ValueError, C.ECPt, "12345, 67890!??")
+ me.assertRaises(ValueError, C.ECPt, (1, 2, 3))
+ me.assertRaises(TypeError, C.ECPt, 1, 2, 3)
+ me.assertRaises(TypeError, C.ECPt, 1234)
+ me.assertRaises(TypeError, C.ECPt, object())
+ me.assertRaises(TypeError, C.ECPt, 1, None)
+ #me.assertRaises(TypeError, C.ECPt, (1, None))
+
+ ## Arithmetic shouldn't work.
+ me.assertRaises(TypeError, T.neg, P)
+ me.assertRaises(TypeError, T.add, P, Q)
+
+ ## Attributes. We only have raw integer access.
+ me.assertTrue(P.point is P)
+ me.assertEqual(P.ix, 12345)
+ me.assertEqual(P.iy, 67890)
+ me.assertEqual(P.tobuf(), C.bytes("000230390003010932"))
+ me.assertRaises(AttributeError, lambda: P.curve)
+ me.assertRaises(AttributeError, lambda: P.x)
+ me.assertRaises(AttributeError, lambda: P.y)
+ me.assertRaises(AttributeError, lambda: P._x)
+ me.assertRaises(AttributeError, lambda: P._y)
+ me.assertRaises(AttributeError, lambda: P._z)
+
+ ## Encoding and decoding.
+ P = C.ECPt(254, 291)
+ me.assertEqual(O.tobuf(), C.bytes("0000"))
+ me.assertEqual(C.ECPt(0, 0).tobuf(), C.bytes("000100000100"))
+ me.assertEqual(P.tobuf(), C.bytes("0001fe00020123"))
+ me.assertEqual(C.ECPt.frombuf(C.bytes("0001fe000201233f")),
+ (P, C.bytes("3f")))
+ me.assertRaises(ValueError, C.ECPt.frombuf, C.bytes("0001fe000201"))
+
+ ## String conversion and parsing.
+ me.assertEqual(str(P), "(254, 291)")
+ me.assertEqual(C.ECPt.parse("254, 291)"), (P, ")"))
+ me.assertEqual(C.ECPt.parse("(254, 291)"), (P, ""))
+ me.assertRaises(ValueError, C.ECPt.parse, "(254, 291")
+
+###--------------------------------------------------------------------------
+class TestCurves (T.GenericTestMixin):
+ """Test elliptic curves."""
+
+ def test_compare(me):
+ me.assertEqual(E, E)
+ E1 = k.ec(-3, 6)
+ me.assertFalse(E is E1)
+ me.assertEqual(E, E1)
+ me.assertNotEqual(E, EE)
+ me.assertNotEqual(E, [])
+
+ def _test_curve(me, einfo, checkfail = False):
+
+ ## Some useful values.
+ E = einfo.curve
+ P = einfo.G
+ O = E()
+ n = einfo.r
+ h = einfo.h
+ k = E.field
+ me.assertTrue(n.primep()); l = C.NicePrimeField(n)
+
+ ## Check that things are basically sane.
+ me.assertFalse(O)
+ me.assertTrue(P)
+ me.assertTrue(n)
+ nP = n*P; me.assertFalse(nP); me.assertEqual(nP, O)
+
+ ## Check point construction.
+ me.assertEqual(type(P.ix), C.MP)
+ me.assertEqual(type(P.iy), C.MP)
+ me.assertTrue(isinstance(P.x, C.FE))
+ me.assertTrue(isinstance(P.y, C.FE))
+ me.assertTrue(isinstance(P._x, C.FE))
+ me.assertTrue(isinstance(P._y, C.FE))
+ if isinstance(E, C.ECPrimeProjCurve) or isinstance(E, C.ECBinProjCurve):
+ me.assertTrue(isinstance(P._z, C.FE))
+ else:
+ me.assertEqual(P._z, None)
+ me.assertEqual(E(None), O)
+ me.assertEqual(E(P.x, P.y), P)
+ me.assertEqual(E((P.x, P.y)), P)
+ me.assertEqual(E(P._x, P._y, P._z), P)
+ me.assertEqual(E((P._x, P._y, P._z)), P)
+ Q = E(P.point); me.assertEqual(type(Q), E); me.assertEqual(Q, P)
+ me.assertEqual(E("%s, %s" % (P.ix, P.iy)), P)
+ me.assertRaises(ValueError, E, "1234")
+ me.assertRaises(ValueError, E, "1234,")
+ me.assertRaises(ValueError, E, "1234, 5678?")
+ me.assertRaises(TypeError, E, 1, None)
+ Q = E(P.ix); me.assertTrue(Q == P or Q == -P)
+ for i in T.range(128):
+ x = P.ix + i
+ try: E(x)
+ except ValueError: badx = x; break
+ else:
+ me.fail("no off-curve point found")
+
+ ## Attributes.
+ me.assertEqual(P.ix, P.point.ix)
+ me.assertEqual(P.iy, P.point.iy)
+ me.assertEqual(P.x, k(P.point.ix))
+ me.assertEqual(P.y, k(P.point.iy))
+ R = 6*P
+ if isinstance(E, C.ECPrimeProjCurve) or isinstance(E, C.ECBinProjCurve):
+ me.assertEqual(P._z, k.one)
+ me.assertEqual(R._x, R.x*R._z**2)
+ me.assertEqual(R._y, R.y*R._z**3)
+ me.assertNotEqual(R._z, k.one)
+ else:
+ me.assertEqual(P._z, None)
+ me.assertEqual(R._x, R.x)
+ me.assertEqual(R._y, R.y)
+ me.assertEqual(R._z, None)
+ me.assertEqual(R.curve, E)
+
+ ## Arithmetic.
+ Q = 17*P
+ me.assertEqual(Q, P*17)
+ me.assertEqual(-Q, (n - 17)*P)
+ me.assertEqual(Q + R, 23*P)
+ me.assertEqual(Q + R.point, 23*P)
+ me.assertRaises(TypeError, T.add, Q.point, R.point)
+ me.assertRaises(TypeError, T.mul, kk(1), Q)
+ me.assertEqual(Q - R, 11*P)
+ me.assertEqual(l(17)*P, Q)
+ me.assertEqual(P*l(17), Q)
+
+ ## Ordering.
+ me.assertTrue(P == P)
+ me.assertTrue(P != Q)
+ me.assertRaises(TypeError, T.lt, P, Q)
+ me.assertRaises(TypeError, T.le, P, Q)
+ me.assertRaises(TypeError, T.ge, P, Q)
+ me.assertRaises(TypeError, T.gt, P, Q)
+
+ ## Encoding.
+ Z0 = C.ByteString.zero(0)
+ Z1 = C.ByteString.zero(1)
+ me.assertEqual(O.ec2osp(), Z1)
+ me.assertEqual(E.os2ecp(Z1), (O, Z0))
+ t = C.WriteBuffer() \
+ .putu8(0x04) \
+ .put(P.ix.storeb(k.noctets)) \
+ .put(P.iy.storeb(k.noctets)) \
+ .contents
+ me.assertEqual(P.ec2osp(), t)
+ me.assertEqual(C.WriteBuffer().putecptraw(P).contents, t)
+ me.assertEqual(C.WriteBuffer().ec2osp(P).contents, t)
+ me.assertEqual(E.os2ecp(t), (P, Z0))
+ me.assertEqual(C.ReadBuffer(t).getecptraw(E), P)
+ me.assertEqual(C.ReadBuffer(t).os2ecp(E), P)
+ if isinstance(k, C.PrimeField): ybit = int(P.iy&1)
+ else:
+ try: ybit = int((P.y/P.x).value&C.GF(1))
+ except ZeroDivisionError: ybit = 0
+ t = C.WriteBuffer() \
+ .putu8(0x02 | ybit) \
+ .put(P.ix.storeb(k.noctets)) \
+ .contents
+ me.assertEqual(P.ec2osp(C.EC_LSB), t)
+ me.assertEqual(C.WriteBuffer().ec2osp(P, C.EC_LSB).contents, t)
+ me.assertEqual(E.os2ecp(t, C.EC_LSB), (P, Z0))
+ me.assertEqual(C.ReadBuffer(t).os2ecp(E, C.EC_LSB), P)
+
+ ## Curve methods.
+ Q = E.find(P.x); me.assertTrue(Q == P or Q == -P)
+ Q = E.find(P.ix); me.assertTrue(Q == P or Q == -P)
+ me.assertRaises(ValueError, E.find, badx)
+ for i in T.range(128):
+ if E.rand() != P: break
+ else:
+ me.fail("random point always gives me P")
+ for i in T.range(128):
+ R = E.rand(C.LCRand(i))
+ if R != P: break
+ else:
+ me.fail("random point always gives me P")
+ me.assertEqual(R, E.rand(C.LCRand(i)))
+ me.assertEqual(E.parse("%s, %s!xyzzy" % (P.ix, P.iy)), (P, "!xyzzy"))
+
+ ## Simultaneous multiplication.
+ Q, R, S = 5*P, 7*P, 11*P
+ me.assertEqual(E.mmul(set([(Q, 9), (R, 8), (S, 5)])), 156*P)
+ me.assertEqual(E.mmul([Q, 9, R, 8, S, 5]), 156*P)
+ me.assertEqual(E.mmul(Q, 9, R, 8, S, 5), 156*P)
+
+ ## Test other curve info things while we're here.
+ if not checkfail: einfo.check()
+ else: me.assertRaises(ValueError, einfo.check)
+
+ def test_tinycurves(me):
+ me._test_curve(C.ECInfo(E, 2*P, 13, 2), checkfail = True)
+ ei = C.ECInfo.fromstring("binpoly: 0x13; bin: 0x01, 0x08; 0x02, 0x0c: 5*4")
+ me._test_curve(ei, checkfail = True)
+
+TestCurves.generate_testcases((name, C.eccurves[name]) for name in
+ ["nist-p256", "nist-k233", "nist-b163", "nist-b283n"])
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing finite-field 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 itertools as I
+
+import catacomb as C
+import unittest as U
+import testutils as T
+
+###--------------------------------------------------------------------------
+class TestFields (T.GenericTestMixin):
+ """Test finite fields."""
+
+ def _test_field(me, k):
+
+ ## Some initial values.
+ zero = k.zero
+ one = k.one
+ x = k(2760820866)
+ y = k(3757175895)
+ z = k(920571320)
+
+ ## Check that they're all different.
+ v = [zero, one, x, y, z]
+ 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 - y, x); me.assertEqual(t - x, y)
+ t = x - y; me.assertEqual(t + y, x)
+ t = x*y; me.assertEqual(t/x, y); me.assertEqual(t/y, x)
+ me.assertEqual(+x, x)
+ me.assertEqual(x + -x, zero)
+ me.assertEqual(x - x, zero)
+ me.assertEqual(x*x.inv(), one)
+ me.assertEqual(x/x, one)
+ me.assertRaises(ZeroDivisionError, k.inv, zero)
+
+ ## Exponentiation. At least we know the group exponent.
+ me.assertEqual(x**(k.q - 1), one)
+
+ ## 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)
+
+ ## Conversion back to integer.
+ me.assertEqual(int(x), 2760820866)
+
+ ## Square and square-root. In a prime field, we may need to search
+ ## around to find a quadratic residue. In binary fields, squaring is
+ ## linear, and every element has a unique square root.
+ me.assertEqual(x*x, x.sqr())
+ for i in T.range(128):
+ t = k(int(x) + i)
+ q = t.sqrt()
+ if q is not None: break
+ else:
+ me.fail("no quadratic residue found")
+ me.assertEqual(q.sqr(), t)
+
+ ## Hex and octal conversions.
+ me.assertEqual(hex(x), hex(T.long(x.value)).rstrip("L"))
+ me.assertEqual(oct(x), oct(T.long(x.value)).rstrip("L"))
+
+ if isinstance(k, C.PrimeField):
+
+ ## Representation.
+ me.assertEqual(type(x.value), C.MP)
+ me.assertEqual(k.type, C.FTY_PRIME)
+
+ ## Properties.
+ me.assertEqual(k.p, k.q)
+
+ ## Operations.
+ me.assertEqual(x.dbl(), 2*x)
+ me.assertEqual(x.tpl(), 3*x)
+ me.assertEqual(x.qdl(), 4*x)
+ me.assertEqual(x.hlv(), x/2)
+
+ else:
+
+ ## Representation.
+ me.assertEqual(type(x.value), C.GF)
+ me.assertEqual(k.type, C.FTY_BINARY)
+
+ ## Properties.
+ me.assertEqual(k.m, k.nbits)
+ me.assertEqual(k.p.degree, k.m)
+ if isinstance(k, C.BinNormField):
+ l = C.BinPolyField(k.p)
+ a, b = l.zero, l(k.beta)
+ for i in T.range(k.m):
+ a += b
+ b = b.sqr()
+ me.assertEqual(a, l.one)
+
+ ## Operations.
+ for i in T.range(128):
+ t = k(int(x) + i)
+ u = t.quadsolve()
+ if u is not None: break
+ else:
+ me.fail("no quadratic solution found")
+ me.assertEqual(u*u + u, t)
+
+ ## Encoding.
+ me.assertEqual(k.nbits, (k.q - 1).nbits)
+ me.assertEqual(k.noctets, (k.q - 1).noctets)
+
+TestFields.generate_testcases \
+ (("%s-%s" % (name, suffix), getfn(C.eccurves[name]))
+ for suffix, getfn in [("coords", lambda einfo: einfo.curve.field),
+ ("scalars", lambda einfo: C.PrimeField(einfo.r))]
+ for name in ["nist-p256", "nist-k233", "nist-b163", "nist-b283n"])
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /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.WriteBuffer().putmp(i).contents)
+ 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.WriteBuffer().zero(G.noctets - 1).putu8(1).contents)
+
+ ## 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()
--- /dev/null
+### -*-python-*-
+###
+### Testing key-management 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 sys as SYS
+import unittest as U
+import testutils as T
+import time as TM
+
+###--------------------------------------------------------------------------
+class TestKeyError (U.TestCase):
+
+ def test_keyerror(me):
+
+ try: C.KeyFile("notexist", C.KOPEN_NOFILE).newkey(1, "foo")
+ except C.KeyError: e = SYS.exc_info()[1]
+ else: me.fail("expected `catacomb.KeyError'")
+ me.assertEqual(e.err, C.KERR_READONLY)
+ me.assertEqual(e.errstring, "Key file is read-only")
+ me.assertEqual(e.args, (C.KERR_READONLY,))
+ me.assertEqual(str(e),
+ "KERR_READONLY (%d): Key file is read-only" %
+ C.KERR_READONLY)
+
+ me.assertRaises(TypeError, C.KeyError)
+ token = ["TOKEN"]
+ e = C.KeyError(C.KERR_DUPID, token)
+ me.assertEqual(e.err, C.KERR_DUPID)
+ me.assertEqual(e.errstring, "Key id already exists")
+ me.assertEqual(e.args, (C.KERR_DUPID, token))
+
+###--------------------------------------------------------------------------
+class TestKeyFile (U.TestCase):
+
+ def test_keyring(me):
+
+ kf = C.KeyFile("t/keyring")
+
+ ## Check basic attributes.
+ me.assertEqual(kf.name, "t/keyring")
+ me.assertEqual(kf.modifiedp, False)
+ me.assertEqual(kf.writep, False)
+ me.assertEqual(kf.filep, False)
+
+ ## Check enumeration.
+ me.assertEqual(set(k.type for k in T.itervalues(kf)),
+ set(["rsa", "ec", "ec-param", "twofish"]))
+ me.assertEqual(len(kf), 4)
+
+ ## Start with `rsa'.
+ k = kf.bytag("ron")
+ me.assertEqual(k.type, "rsa")
+ me.assertEqual(k.id, 0x8599dbab)
+ me.assertEqual(type(k.data), C.KeyDataStructured)
+ me.assertEqual(set(k.data), set(["e", "n", "private"]))
+ priv = k.data["private"]
+ me.assertEqual(type(priv), C.KeyDataEncrypted)
+ me.assertRaises(C.KeyError, priv.unlock, T.bin("wrong secret"))
+ priv = priv.unlock(T.bin("very secret"))
+ me.assertEqual(type(priv), C.KeyDataStructured)
+ me.assertEqual(set(priv),
+ set(["p", "q", "d", "d-mod-p", "d-mod-q", "q-inv"]))
+ me.assertEqual(k.data["n"].mp, priv["p"].mp*priv["q"].mp)
+
+ ## This key has an attribute. Poke about at them.
+ a = k.attr
+ me.assertEqual(len(a), 1)
+ me.assertEqual(set(a), set(["attr"]))
+ me.assertEqual(a["attr"], "value")
+ me.assertRaises(KeyError, lambda: a["notexist"])
+ me.assertEqual(a.get("attr"), "value")
+ me.assertEqual(a.get("notexist"), None)
+
+ ## Check fingerprinting while we're here.
+ for filter in ["-secret", "none"]:
+ h = C.sha256(); me.assertTrue(k.fingerprint(h, filter)); fp0 = h.done()
+ h = C.sha256()
+ h.hash(T.bin("catacomb-key-fingerprint:")) \
+ .hashu32(k.id) \
+ .hashbuf8(T.bin(k.type))
+ h.hash(k.data.encode(filter))
+ for a in sorted(T.iterkeys(k.attr)):
+ h.hashbuf8(T.bin(a)).hashbuf16(T.bin(k.attr[a]))
+ fp1 = h.done()
+ me.assertEqual(fp0, fp1)
+
+ ## Try `ec-param'. This should be fairly easy.
+ k = kf["ec-param"]
+ me.assertEqual(k.tag, None)
+ me.assertEqual(k.id, 0x4a4e1ee7)
+ me.assertEqual(type(k.data), C.KeyDataStructured)
+ me.assertEqual(set(k.data), set(["curve"]))
+ curve = k.data["curve"]
+ me.assertEqual(type(curve), C.KeyDataString)
+ me.assertEqual(curve.str, "nist-p256")
+
+ ## Check qualified-tag lookups.
+ me.assertRaises(C.KeyError, kf.qtag, "notexist.curve")
+ me.assertRaises(C.KeyError, kf.qtag, "ec-param.notexist")
+ t, k, kd = kf.qtag("ec-param.curve")
+ me.assertEqual(t, "4a4e1ee7:ec-param.curve")
+ me.assertEqual(k.type, "ec-param")
+ me.assertEqual(type(kd), C.KeyDataString)
+ me.assertEqual(kd.str, "nist-p256")
+
+ ## Try `ec'. A little trickier.
+ k = kf.bytype("ec")
+ me.assertEqual(k.tag, None)
+ me.assertEqual(k.id, 0xbd761d35)
+ me.assertEqual(type(k.data), C.KeyDataStructured)
+ me.assertEqual(set(k.data), set(["curve", "p", "private"]))
+ curve = k.data["curve"]
+ me.assertEqual(type(curve), C.KeyDataString)
+ me.assertEqual(curve.str, "nist-p256")
+ einfo = C.eccurves[curve.str]
+ me.assertEqual(type(k.data["p"]), C.KeyDataECPt)
+ X = k.data["p"].ecpt
+ priv = k.data["private"]
+ me.assertEqual(type(priv), C.KeyDataEncrypted)
+ me.assertRaises(C.KeyError, priv.unlock, T.bin("wrong secret"))
+ priv = priv.unlock(T.bin("super secret"))
+ me.assertEqual(type(priv), C.KeyDataStructured)
+ me.assertEqual(set(priv), set(["x"]))
+ x = priv["x"].mp
+ me.assertEqual(x*einfo.G, X)
+
+ ## Finish with `twofish'.
+ k = kf.byid(0x60090be2)
+ me.assertEqual(k.tag, None)
+ me.assertEqual(k.type, "twofish")
+ me.assertEqual(type(k.data), C.KeyDataEncrypted)
+ me.assertRaises(C.KeyError, k.data.unlock, T.bin("wrong secret"))
+ kd = k.data.unlock(T.bin("not secret"))
+ me.assertEqual(type(kd), C.KeyDataBinary)
+ me.assertEqual(kd.bin, C.bytes("d337b98eea24425826df202a6a3d1ef8"
+ "377b71923fe1179451564776da29bb84"))
+
+ ## Check unsuccessful searches.
+ me.assertRaises(KeyError, lambda: kf["notexist"])
+ me.assertRaises(C.KeyError, kf.bytag, "notexist")
+ me.assertRaises(C.KeyError, kf.bytag, 12345)
+ me.assertEqual(kf.bytag("notexist", fail = False), None)
+ me.assertRaises(C.KeyError, kf.bytype, "notexist")
+ me.assertRaises(TypeError, kf.bytype, 12345)
+ me.assertEqual(kf.bytype("notexist", fail = False), None)
+ me.assertRaises(C.KeyError, kf.byid, 0x12345678)
+ me.assertEqual(kf.byid(0x12345678, fail = False), None)
+
+ me.assertRaises(C.KeyError, kf.mergeline, "nowhere", 2, "")
+
+ ## The keyring should be readonly.
+ me.assertRaises(C.KeyError, kf.newkey, 0x12345678, "fail")
+ me.assertRaises(C.KeyError, setattr, k, "tag", "foo")
+ me.assertRaises(C.KeyError, delattr, k, "tag")
+ me.assertRaises(C.KeyError, kf.mergeline, "notexist", 1,
+ "22222222:test integer,public:32519164 forever forever -")
+ me.assertRaises(C.KeyError, setattr, k, "data", C.KeyDataString("foo"))
+
+ def test_keywrite(me):
+ kf = C.KeyFile("test", C.KOPEN_WRITE | C.KOPEN_NOFILE)
+ me.assertEqual(kf.modifiedp, False)
+ now = int(TM.time())
+ exp = now + 86400
+
+ k = kf.newkey(0x11111111, "first", exp)
+ me.assertEqual(kf.modifiedp, True)
+
+ me.assertEqual(k, kf[0x11111111])
+ me.assertEqual(k.exptime, exp)
+ me.assertEqual(k.deltime, exp)
+ me.assertRaises(ValueError, setattr, k, "deltime", C.KEXP_FOREVER)
+ k.exptime = exp + 5
+ me.assertEqual(k.data.str, "<unset>")
+ n = 9876543210
+ k.data = C.KeyDataMP(n)
+ me.assertEqual(k.data.mp, n)
+ me.assertEqual(k.comment, None)
+ c = ";; just a test"
+ k.comment = c
+ me.assertEqual(k.comment, c)
+ k.comment = None
+ me.assertEqual(k.comment, None)
+ k.comment = c
+ me.assertEqual(k.comment, c)
+ del k.comment
+ me.assertEqual(k.comment, None)
+
+ kf.mergeline("notexist", 1,
+ "22222222:test integer,public:32519164 forever forever -")
+
+###--------------------------------------------------------------------------
+class TestKeyData (U.TestCase):
+
+ def test_flags(me):
+ me.assertEqual(C.KeyData.readflags("none"), (0, 0, ""))
+ me.assertEqual(C.KeyData.readflags("ec,public:..."),
+ (C.KENC_EC | C.KCAT_PUB,
+ C.KF_ENCMASK | C.KF_CATMASK,
+ ":..."))
+ me.assertEqual(C.KeyData.readflags("int,burn"),
+ (C.KENC_MP | C.KF_BURN, C.KF_ENCMASK | C.KF_BURN, ""))
+ me.assertRaises(C.KeyError, C.KeyData.readflags, "int,burn?")
+ me.assertRaises(C.KeyError, C.KeyData.readflags, "int,ec")
+ me.assertRaises(C.KeyError, C.KeyData.readflags, "snork")
+ me.assertEqual(C.KeyData.writeflags(0), "binary,symmetric")
+ me.assertEqual(C.KeyData.writeflags(C.KENC_EC | C.KCAT_PUB), "ec,public")
+
+ def test_misc(me):
+ kd = C.KeyDataStructured({ "a": C.KeyDataString("foo", "public"),
+ "b": C.KeyDataMP(12345, "private"),
+ "c": C.KeyDataString("bar", "public") })
+
+ kd2 = kd.copy()
+ me.assertEqual(type(kd2), C.KeyDataStructured)
+ me.assertEqual(set(T.iterkeys(kd2)), set(["a", "b", "c"]))
+
+ kd2 = C.KeyDataMP(12345, C.KCAT_PRIV).copy("private")
+
+ kd2 = kd.copy("-secret")
+ me.assertEqual(type(kd2), C.KeyDataStructured)
+ me.assertEqual(set(T.iterkeys(kd2)), set(["a", "c"]))
+
+ kd2 = kd.copy((0, C.KF_NONSECRET))
+ me.assertEqual(type(kd2), C.KeyDataStructured)
+ me.assertEqual(set(T.iterkeys(kd2)), set(["b"]))
+
+ def check_encode(me, kd):
+ me.assertEqual(C.KeyData.decode(kd.encode()), kd)
+ me.assertEqual(C.KeyData.read(kd.write()), (kd, ""))
+
+ def test_bin(me):
+ rng = T.detrand("kd-bin")
+ by = rng.block(16)
+ kd = C.KeyDataBinary(by, "symm,burn")
+ me.assertEqual(kd.bin, by)
+ me.check_encode(kd)
+
+ def test_mp(me):
+ rng = T.detrand("kd-mp")
+ x = rng.mp(128)
+ kd = C.KeyDataMP(x, "symm,burn")
+ me.assertEqual(kd.mp, x)
+ me.check_encode(kd)
+
+ def test_string(me):
+ s = "some random string"
+ kd = C.KeyDataString(s, "symm,burn")
+ me.assertEqual(kd.str, s)
+ me.check_encode(kd)
+
+ def test_enc(me):
+ rng = T.detrand("kd-enc")
+ ct = rng.block(16)
+ kd = C.KeyDataEncrypted(ct, "symm")
+ me.assertEqual(kd.ct, ct)
+ me.check_encode(kd)
+
+ def test_ecpt(me):
+ rng = T.detrand("kd-ec")
+ Q = C.ECPt(rng.mp(128), rng.mp(128))
+ kd = C.KeyDataECPt(Q, "symm,burn")
+ me.assertEqual(kd.ecpt, Q)
+ me.check_encode(kd)
+
+ def test_struct(me):
+ rng = T.detrand("kd-struct")
+ kd = C.KeyDataStructured({ "a": C.KeyDataString("a"),
+ "b": C.KeyDataString("b") },
+ c = C.KeyDataString("c"),
+ d = C.KeyDataString("d"))
+ for i in ["a", "b", "c", "d"]: me.assertEqual(kd[i].str, i)
+ me.assertEqual(len(kd), 4)
+ me.check_encode(kd)
+ me.assertRaises(TypeError, C.KeyDataStructured, { "a": "a" })
+ me.assertRaises(ValueError, C.KeyDataStructured,
+ { "a": C.KeyDataString("a") },
+ a = C.KeyDataString("b"))
+
+###--------------------------------------------------------------------------
+### Mappings.
+
+class TestKeyFileMapping (T.ImmutableMappingTextMixin):
+ def _mkkey(me, i): return i
+ def _getkey(me, k): return k
+ def _getvalue(me, v): return v.data.mp
+
+ def test_keyfile(me):
+ kf = C.KeyFile("test", C.KOPEN_WRITE | C.KOPEN_NOFILE)
+ model = {}
+ for i in [1, 2, 3]:
+ model[i] = 100 + i
+ kf.newkey(i, "k#%d" % i).data = C.KeyDataMP(100 + i)
+
+ me.assertEqual(kf[1], kf[1])
+
+ me.check_immutable_mapping(kf, model)
+
+class TestKeyStructMapping (T.MutableMappingTestMixin):
+ def _mkvalue(me, i): return C.KeyDataMP(i)
+ def _getvalue(me, v): return v.mp
+
+ def test_keystructmap(me):
+ me.check_mapping(C.KeyDataStructured)
+
+class TestKeyAttrMapping (T.MutableMappingTestMixin):
+
+ def test_attrmap(me):
+ def mkmap():
+ kf = C.KeyFile("test", C.KOPEN_WRITE | C.KOPEN_NOFILE)
+ k = kf.newkey(0x12345678, "test-key")
+ return k.attr
+ me.check_mapping(mkmap)
+
+ a = mkmap()
+ me.assertRaises(TypeError, a.update, { 3: 3, 4: 5 })
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Miscellaneous tests
+###
+### (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
+
+###--------------------------------------------------------------------------
+class Tests (U.TestCase):
+
+ def test_path(me):
+ me.assertTrue(C.__file__.startswith("build"))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing multiprecision integer (and related) 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 TestMP (U.TestCase):
+
+ def test_make(me):
+ x = C.MP(5)
+ k = C.PrimeField(17)
+ kk = C.BinPolyField(C.GF(0x13))
+ E = k.ec(-3, 1)
+ me.assertEqual(x, 5)
+ me.assertTrue(C.MP(x) is x)
+ me.assertEqual(C.MP(k(8)), 8)
+ me.assertEqual(C.MP(kk(8)), 8)
+ me.assertEqual(C.MP(E(1, 4)), 1)
+ me.assertRaises(TypeError, C.MP, E())
+
+ me.assertEqual(int(x), 5)
+ big = 6556380541834372447694561492436749633
+ me.assertEqual(type(big), T.long)
+ y = C.MP(big)
+ me.assertEqual(y, big)
+ me.assertEqual(int(y), big)
+
+ me.assertEqual(C.MP(str(big)), big)
+ me.assertEqual(C.MP('0x4eeb684a0954ec4ceb255e3e9778d41'), big)
+ me.assertEqual(C.MP('4eeb684a0954ec4ceb255e3e9778d41', 16), big)
+ me.assertEqual(C.MP('0x4eeb684a0954ec4ceb255e3e9778d41', 16), big)
+ me.assertEqual(C.MP('0b0', 16), 176) # not 0
+
+ me.assertEqual(C.MP('047353320450112516611472622536175135706501'), big)
+ me.assertEqual(C.MP('0o47353320450112516611472622536175135706501'), big)
+ me.assertEqual(C.MP('047353320450112516611472622536175135706501', 8), big)
+ me.assertEqual(C.MP('0o47353320450112516611472622536175135706501', 8), big)
+ me.assertEqual(C.MP('47353320450112516611472622536175135706501', 8), big)
+
+ me.assertEqual(C.MP('0b100111011011001100000010001011'), 661438603)
+ me.assertEqual(C.MP('0b100111011011001100000010001011', 2), 661438603)
+ me.assertEqual(C.MP('100111011011001100000010001011', 2), 661438603)
+
+ def test_string(me):
+ y = C.MP(6556380541834372447694561492436749633)
+ me.assertEqual(str(y), '6556380541834372447694561492436749633')
+ me.assertEqual(repr(y), 'MP(6556380541834372447694561492436749633)')
+ me.assertEqual(hex(y), '0x4eeb684a0954ec4ceb255e3e9778d41')
+ me.assertEqual(oct(y), T.py23('0', '0o') +
+ '47353320450112516611472622536175135706501')
+ try: bin
+ except NameError: pass
+ else: me.assertEqual(bin(C.MP(661438603)),
+ '0b100111011011001100000010001011')
+
+ def test_number(me):
+ x, y, m, zero = C.MP(169), C.MP(24), C.MP(205), C.MP(0)
+
+ me.assertEqual(-x, -169)
+ me.assertEqual(~x, -170)
+ me.assertEqual(abs(x), 169)
+ me.assertEqual(abs(-x), 169)
+
+ me.assertEqual(x + y, 193)
+ me.assertEqual(x - y, 145)
+ me.assertEqual(x*y, 4056)
+ me.assertEqual(x&y, 8)
+ me.assertEqual(x&-y, 168)
+ me.assertEqual(x | y, 185)
+ me.assertEqual(x | -y, -23)
+ me.assertEqual(x ^ y, 177)
+ me.assertEqual(x ^ -y, -191)
+
+ me.assertEqual(x << 3, 1352)
+ me.assertEqual(x << -2, 42)
+ me.assertEqual(x >> 2, 42)
+ me.assertEqual(x >> -3, 1352)
+ me.assertEqual(-x << 3, -1352)
+ me.assertEqual(-x >> 2, -43)
+
+ u = x/y; me.assertEqual((u.numer, u.denom), (169, 24))
+ me.assertEqual(x//y, 7)
+ me.assertEqual(x%y, 1)
+ me.assertEqual(divmod(x, y), (7, 1))
+ me.assertRaises(ZeroDivisionError, lambda: x/zero)
+ me.assertRaises(ZeroDivisionError, lambda: x//zero)
+ me.assertRaises(ZeroDivisionError, lambda: x%zero)
+ me.assertRaises(ZeroDivisionError, divmod, x, zero)
+
+ me.assertEqual(pow(x, y), 294632676319010105335586872991323185304149065116720321)
+ me.assertEqual(pow(x, y, m), 51)
+ me.assertRaises(ValueError, pow, x, -y)
+ me.assertEqual(pow(x, -y, m), 201)
+ me.assertRaises(ZeroDivisionError, pow, x, -y, 208)
+
+ me.assertTrue(x)
+ me.assertFalse(zero)
+
+ def test_order(me):
+ x, y = C.MP(169), C.MP(24)
+ me.assertTrue(x == x)
+ me.assertFalse(x != x)
+ me.assertFalse(x == y)
+ me.assertTrue(x != y)
+ me.assertTrue(x > y)
+ me.assertFalse(y > x)
+ me.assertFalse(x > x)
+ me.assertTrue(x >= y)
+ me.assertFalse(y >= x)
+ me.assertTrue(x >= x)
+ me.assertFalse(x <= y)
+ me.assertTrue(y <= x)
+ me.assertTrue(x <= x)
+ me.assertFalse(x < y)
+ me.assertTrue(y < x)
+ me.assertFalse(x < x)
+
+ def test_float(me):
+ x, y = C.MP(169), 24.0
+ for fn in [T.add, T.sub, T.mul, T.div]:
+ me.assertEqual(type(fn(x, y)), float)
+ me.assertEqual(type(fn(y, x)), float)
+ me.assertEqual(x, 169.0)
+ me.assertNotEqual(x, 169.1)
+ me.assertNotEqual(x, 168.9)
+ me.assertTrue(x > 168.9)
+ me.assertTrue(x < 169.1)
+ z = 1.0
+ while z == z + 1: z *= 2.0
+ me.assertNotEqual(C.MP(int(z)) + 1, z)
+
+ def test_strconv(me):
+ x, y = C.MP(169), "24"
+ for fn in [T.add, T.sub, T.div]:
+ me.assertRaises(TypeError, fn, x, y)
+ me.assertRaises(TypeError, fn, y, x)
+ me.assertEqual(x*y, 169*"24")
+ me.assertEqual(y*x, 169*"24")
+
+ def test_bits(me):
+ x, y, zero = C.MP(169), C.MP(-24), C.MP(0)
+ me.assertTrue(x.testbit(0))
+ me.assertFalse(x.testbit(1))
+ me.assertFalse(x.testbit(1000))
+ me.assertFalse(y.testbit(0))
+ me.assertTrue(y.testbit(3))
+ me.assertTrue(y.testbit(1000))
+
+ me.assertEqual(x.setbit(0), x)
+ me.assertEqual(x.clearbit(0), 168)
+ me.assertEqual(x.setbit(1), 171)
+ me.assertEqual(x.clearbit(1), x)
+ me.assertEqual(y.setbit(0), -23)
+ me.assertEqual(y.clearbit(0), y)
+ me.assertEqual(y.setbit(3), y)
+ me.assertEqual(y.clearbit(3), -32)
+ me.assertEqual(y.setbit(1000), y)
+
+ me.assertEqual(x.nbits, 8)
+ me.assertEqual(y.nbits, 5)
+ me.assertEqual(zero.nbits, 0)
+
+ def test_loadstore(me):
+ x = C.MP(0x0123456789ab)
+ y = -x
+ u = C.MP(0xfedcba9876)
+
+ me.assertEqual(x.noctets, 6)
+ me.assertEqual(x.noctets2c, 6)
+ me.assertEqual(y.noctets, 6)
+ me.assertEqual(y.noctets2c, 6)
+ me.assertEqual(u.noctets, 5)
+ me.assertEqual(u.noctets2c, 6)
+
+ me.assertEqual(x, C.MP.loadb(C.bytes("0123456789ab")))
+ me.assertEqual(x, C.MP.loadb2c(C.bytes("0123456789ab")))
+ me.assertEqual(y, C.MP.loadb2c(C.bytes("fedcba987655")))
+
+ me.assertEqual(x.storeb(), C.bytes("0123456789ab"))
+ me.assertEqual(x.storeb(3), C.bytes("6789ab"))
+ me.assertEqual(x.storeb(8), C.bytes("00000123456789ab"))
+ me.assertEqual(x.storeb2c(), C.bytes("0123456789ab"))
+ me.assertEqual(x.storeb2c(3), C.bytes("6789ab"))
+ me.assertEqual(x.storeb2c(8), C.bytes("00000123456789ab"))
+ me.assertEqual(u.storeb2c(), C.bytes("00fedcba9876"))
+ me.assertEqual(y.storeb2c(), C.bytes("fedcba987655"))
+ me.assertEqual(y.storeb2c(3), C.bytes("987655"))
+ me.assertEqual(y.storeb2c(8), C.bytes("fffffedcba987655"))
+
+ me.assertEqual(x, C.MP.loadl(C.bytes("ab8967452301")))
+ me.assertEqual(x, C.MP.loadl2c(C.bytes("ab8967452301")))
+ me.assertEqual(y, C.MP.loadl2c(C.bytes("557698badcfe")))
+
+ me.assertEqual(x.storel(), C.bytes("ab8967452301"))
+ me.assertEqual(x.storel(3), C.bytes("ab8967"))
+ me.assertEqual(x.storel(8), C.bytes("ab89674523010000"))
+ me.assertEqual(x.storel2c(), C.bytes("ab8967452301"))
+ me.assertEqual(x.storel2c(3), C.bytes("ab8967"))
+ me.assertEqual(x.storel2c(8), C.bytes("ab89674523010000"))
+ me.assertEqual(u.storel2c(), C.bytes("7698badcfe00"))
+ me.assertEqual(y.storel2c(), C.bytes("557698badcfe"))
+ me.assertEqual(y.storel2c(3), C.bytes("557698"))
+ me.assertEqual(y.storel2c(8), C.bytes("557698badcfeffff"))
+
+ me.assertEqual(x.tobuf(), C.bytes("00060123456789ab"))
+ me.assertEqual((x, T.bin("abcd")),
+ C.MP.frombuf(C.bytes("00060123456789ab61626364")))
+
+ def test_numbertheory(me):
+ p, x, y, z = C.MP(173), C.MP(169), C.MP(24), C.MP(20)
+
+ me.assertEqual(x.odd(), (0, x))
+ me.assertEqual(y.odd(), (3, 3))
+
+ me.assertEqual(x.sqr(), 28561)
+ me.assertEqual(x.sqrt(), 13)
+ me.assertEqual(y.sqrt(), 4)
+
+ me.assertEqual(x.gcd(y), 1)
+ me.assertEqual(x.gcdx(y), (1, -23, 162))
+ me.assertEqual(y.gcdx(x), (1, -7, 1))
+ me.assertEqual(x.modinv(y), 162)
+ me.assertEqual(y.modinv(x), 1)
+
+ me.assertEqual(x.jacobi(y), 1)
+ me.assertEqual(x.jacobi(13), 0)
+ me.assertEqual(y.jacobi(x), 1)
+ me.assertEqual(p.jacobi(y), 1)
+ me.assertEqual(p.jacobi(z), -1)
+ me.assertEqual(p.modsqrt(y), 71)
+ me.assertRaises(ValueError, p.modsqrt, z)
+
+ me.assertEqual(y.leastcongruent(x, 32), 184)
+
+ me.assertTrue(p.primep())
+ me.assertFalse(x.primep())
+
+ def test_bang(me):
+ me.assertEqual(C.MP.factorial(0), 1)
+ me.assertEqual \
+ (C.MP.factorial(50),
+ 30414093201713378043612608166064768844377641568960512000000000000)
+ me.assertRaises((ValueError, OverflowError), C.MP.factorial, -1)
+
+ def test_fib(me):
+ me.assertEqual(C.MP.fibonacci(-2), -1)
+ me.assertEqual(C.MP.fibonacci(-1), +1)
+ me.assertEqual(C.MP.fibonacci( 0), 0)
+ me.assertEqual(C.MP.fibonacci(+1), +1)
+ me.assertEqual(C.MP.fibonacci(+2), +1)
+ me.assertEqual(C.MP.fibonacci(50), 12586269025)
+
+###--------------------------------------------------------------------------
+class TestMPMul (U.TestCase):
+
+ def test(me):
+ m = C.MPMul()
+ me.assertTrue(m.livep)
+ m.factor(1, 2, 3)
+ m.factor([4, 5, 6])
+ me.assertEqual(m.done(), 720)
+ me.assertFalse(m.livep)
+
+ me.assertEqual(C.MPMul(T.range(1, 7)).done(), 720)
+ me.assertEqual(C.MP.factorial(6), 720)
+
+###--------------------------------------------------------------------------
+class TestMPMont (U.TestCase):
+
+ def test(me):
+
+ me.assertRaises(ValueError,
+ C.MPMont, 35315021952044908656941308411353985942)
+ me.assertRaises(ValueError, C.MPMont, -9)
+
+ p = C.MP(269464705320809171350781605680038324101)
+ g = C.MP(2) # lucky chance
+ x = C.MP(211184293914316080585277908844600399612)
+ y = C.MP(154454671298730680774195646814344206562)
+ xy = C.MP(209444562478584646216087606217820187655)
+ me.assertTrue(p.primep())
+ m = C.MPMont(p)
+ me.assertEqual(m.m, p)
+
+ ## The precise values of m.r and m.r2 are dependent on the internal
+ ## bignum representation. But we expect m.r to be congruent to some
+ ## power of two. (It should be 2^128.)
+ t = p.modinv(m.r)
+ for i in T.range(1025):
+ if t == 1: break
+ t *= 2
+ if t >= p: t -= p
+ else:
+ me.fail("m.r is not a small-ish power of 2")
+ me.assertEqual(m.r2, pow(2, 2*i, p))
+ me.assertEqual(m.ext(m.r), 1)
+ me.assertEqual(m.reduce(m.r), 1)
+
+ me.assertEqual(m.ext(m.int(x)), x)
+ me.assertEqual(m.int(x), m.mul(x, m.r2))
+ me.assertEqual(m.mul(m.int(x), y), xy)
+ me.assertEqual(m.ext(m.mul(m.int(x), m.int(y))), xy)
+
+ me.assertEqual(m.exp(2, p - 1), 1)
+ me.assertEqual(m.expr(m.int(2), p - 1), m.r)
+
+ q, r, s, z = 32, 128, 2048, pow(g, 156, p)
+ me.assertEqual(m.mexp(set([(q, 9), (r, 8), (s, 5)])), z)
+ me.assertEqual(m.mexp([(q, 9), (r, 8), (s, 5)]), z)
+ me.assertEqual(m.mexp(q, 9, r, 8, s, 5), z)
+
+ q, r, s, z = T.imap(m.int, [32, 128, 2048, pow(g, 156, p)])
+ me.assertEqual(m.mexpr(set([(q, 9), (r, 8), (s, 5)])), z)
+ me.assertEqual(m.mexpr([(q, 9), (r, 8), (s, 5)]), z)
+ me.assertEqual(m.mexpr(q, 9, r, 8, s, 5), z)
+
+###--------------------------------------------------------------------------
+class TestMPBarrett (U.TestCase):
+
+ def test(me):
+
+ p = C.MP(269464705320809171350781605680038324101)
+ g = C.MP(2) # lucky chance
+ x = C.MP(211184293914316080585277908844600399612)
+ y = C.MP(154454671298730680774195646814344206562)
+ xy = C.MP(209444562478584646216087606217820187655)
+ me.assertTrue(p.primep())
+ m = C.MPBarrett(p)
+ me.assertEqual(m.m, p)
+
+ me.assertEqual(m.reduce(x*y), xy)
+
+ me.assertEqual(m.exp(2, p - 1), 1)
+
+ q, r, s, z = 32, 128, 2048, pow(g, 156, p)
+ me.assertEqual(m.mexp(set([(q, 9), (r, 8), (s, 5)])), z)
+ me.assertEqual(m.mexp([(q, 9), (r, 8), (s, 5)]), z)
+ me.assertEqual(m.mexp(q, 9, r, 8, s, 5), z)
+
+###--------------------------------------------------------------------------
+class TestMPReduce (U.TestCase):
+
+ def test(me):
+
+ p = C.MP(2)**127 - 1
+ g = C.MP(2) # lucky chance
+ x = C.MP(94827182170881245766374991987593163418)
+ y = C.MP(106025009945795266831396608563402138277)
+ xy = C.MP(80027041045616838298103413933629021123)
+ me.assertTrue(p.primep())
+ m = C.MPReduce(p)
+ me.assertEqual(m.m, p)
+
+ me.assertEqual(m.reduce(x*y), xy)
+
+ me.assertEqual(m.exp(2, 127), 1)
+
+###--------------------------------------------------------------------------
+class TestMPCRT (U.TestCase):
+
+ def test(me):
+
+ c = C.MPCRT(5, 7, 11)
+ me.assertEqual(c.moduli, [5, 7, 11])
+ me.assertEqual(c.product, 385)
+ me.assertEqual(c.solve([2, 3, 4]), 367)
+ me.assertEqual(c.solve([2, -4, -7]), 367)
+
+ me.assertRaises(ValueError, C.MPCRT, [6, 15, 35])
+
+###--------------------------------------------------------------------------
+class TestGF (U.TestCase):
+
+ def test_make(me):
+ x = C.GF(5)
+ k = C.PrimeField(17)
+ kk = C.BinPolyField(C.GF(0x13))
+ E = k.ec(-3, 1)
+ me.assertTrue(C.GF(x) is x)
+ me.assertEqual(C.GF(k(8)), C.GF(8))
+ me.assertEqual(C.GF(kk(8)), C.GF(8))
+ me.assertEqual(C.GF(E(1, 4)), C.GF(1))
+ me.assertRaises(TypeError, C.GF, E())
+
+ me.assertNotEqual(x, 5) # no implicit conversion to int
+ me.assertEqual(int(x), 5)
+ y = C.GF(0x4eeb684a0954ec4ceb255e3e9778d41)
+ me.assertEqual(type(int(y)), T.long)
+
+ me.assertEqual(C.GF('0x4eeb684a0954ec4ceb255e3e9778d41'), y)
+ me.assertEqual(C.GF('4eeb684a0954ec4ceb255e3e9778d41', 16), y)
+ me.assertEqual(C.GF('0x4eeb684a0954ec4ceb255e3e9778d41', 16), y)
+ me.assertEqual(C.GF('0b0', 16), C.GF(176)) # not 0
+
+ me.assertEqual(C.GF('047353320450112516611472622536175135706501'), y)
+ me.assertEqual(C.GF('0o47353320450112516611472622536175135706501'), y)
+ me.assertEqual(C.GF('047353320450112516611472622536175135706501', 8), y)
+ me.assertEqual(C.GF('0o47353320450112516611472622536175135706501', 8), y)
+ me.assertEqual(C.GF('47353320450112516611472622536175135706501', 8), y)
+
+ t = C.GF(661438603)
+ me.assertEqual(C.GF('0b100111011011001100000010001011'), t)
+ me.assertEqual(C.GF('0b100111011011001100000010001011', 2), t)
+ me.assertEqual(C.GF('100111011011001100000010001011', 2), t)
+
+ def test_string(me):
+ y = C.GF(0x4eeb684a0954ec4ceb255e3e9778d41)
+ me.assertEqual(str(y), '0x4eeb684a0954ec4ceb255e3e9778d41')
+ me.assertEqual(repr(y), 'GF(0x4eeb684a0954ec4ceb255e3e9778d41)')
+ me.assertEqual(hex(y), '0x4eeb684a0954ec4ceb255e3e9778d41')
+ me.assertEqual(oct(y), T.py23('0', '0o') +
+ '47353320450112516611472622536175135706501')
+ try: bin
+ except NameError: pass
+ else: me.assertEqual(bin(C.GF(661438603)),
+ '0b100111011011001100000010001011')
+
+ def test_number(me):
+ x, y, m, zero = C.GF(0xa9), C.GF(0x18), C.GF(0x11b), C.GF(0)
+
+ me.assertEqual(x, -x)
+ me.assertEqual(abs(x), x)
+
+ me.assertEqual(x + y, C.GF(0xb1))
+ me.assertEqual(x - y, C.GF(0xb1))
+ me.assertEqual(x*y, C.GF(0xfd8))
+ me.assertEqual(x&y, C.GF(0x8))
+ me.assertEqual(x | y, C.GF(0xb9))
+ me.assertEqual(x ^ y, C.GF(0xb1))
+
+ me.assertEqual(x << 3, C.GF(0x548))
+ me.assertEqual(x << -2, C.GF(0x2a))
+ me.assertEqual(x >> 2, C.GF(0x2a))
+ me.assertEqual(x >> -3, C.GF(0x548))
+
+ u = x/y; me.assertEqual((u.numer, u.denom), (C.GF(0x67), C.GF(0x8)))
+ me.assertEqual(x//y, C.GF(0xc))
+ me.assertEqual(x%y, C.GF(0x9))
+ me.assertEqual(divmod(x, y), (C.GF(0xc), C.GF(0x9)))
+ me.assertRaises(ZeroDivisionError, lambda: x/zero)
+ me.assertRaises(ZeroDivisionError, lambda: x//zero)
+ me.assertRaises(ZeroDivisionError, lambda: x%zero)
+ me.assertRaises(ZeroDivisionError, divmod, x, zero)
+
+ me.assertEqual(pow(x, 24),
+ C.GF(0x1000100000001010000010101000101010001000001))
+ me.assertEqual(pow(x, 24, m), C.GF(0x78))
+ me.assertEqual(pow(x, -24, m), C.GF(0xb6))
+ me.assertRaises(ZeroDivisionError, pow, x, -24, C.GF(0x18))
+
+ me.assertTrue(x)
+ me.assertFalse(zero)
+
+ def test_order(me):
+ x, y, z = C.GF(0xa9), C.GF(0x18), C.GF(0xb3)
+ me.assertTrue(x == x)
+ me.assertFalse(x != x)
+ me.assertFalse(x == y)
+ me.assertTrue(x != y)
+ me.assertTrue(x > y)
+ me.assertFalse(y > x)
+ me.assertFalse(x > x)
+ me.assertFalse(x > z)
+ me.assertFalse(z > x)
+ me.assertTrue(x >= y)
+ me.assertFalse(y >= x)
+ me.assertTrue(x >= x)
+ me.assertTrue(x >= z)
+ me.assertTrue(z >= x)
+ me.assertFalse(x <= y)
+ me.assertTrue(y <= x)
+ me.assertTrue(x <= x)
+ me.assertTrue(x <= z)
+ me.assertTrue(z <= x)
+ me.assertFalse(x < y)
+ me.assertTrue(y < x)
+ me.assertFalse(x < x)
+ me.assertFalse(x < z)
+ me.assertFalse(z < x)
+
+ def test_bits(me):
+ x, zero = C.GF(0xa9), C.GF(0)
+ me.assertTrue(x.testbit(0))
+ me.assertFalse(x.testbit(1))
+ me.assertFalse(x.testbit(1000))
+
+ me.assertEqual(x.setbit(0), x)
+ me.assertEqual(x.clearbit(0), C.GF(0xa8))
+ me.assertEqual(x.setbit(1), C.GF(0xab))
+ me.assertEqual(x.clearbit(1), x)
+
+ me.assertEqual(x.nbits, 8)
+ me.assertEqual(x.degree, 7)
+ me.assertEqual(zero.nbits, 0)
+ me.assertEqual(zero.degree, -1)
+
+ def test_loadstore(me):
+ x = C.GF(0x0123456789ab)
+
+ me.assertEqual(x.noctets, 6)
+
+ me.assertEqual(x, C.GF.loadb(C.bytes("0123456789ab")))
+
+ me.assertEqual(x.storeb(), C.bytes("0123456789ab"))
+ me.assertEqual(x.storeb(3), C.bytes("6789ab"))
+ me.assertEqual(x.storeb(8), C.bytes("00000123456789ab"))
+
+ me.assertEqual(x, C.GF.loadl(C.bytes("ab8967452301")))
+
+ me.assertEqual(x.storel(), C.bytes("ab8967452301"))
+ me.assertEqual(x.storel(3), C.bytes("ab8967"))
+ me.assertEqual(x.storel(8), C.bytes("ab89674523010000"))
+
+ me.assertEqual(x.tobuf(), C.bytes("00060123456789ab"))
+ me.assertEqual((x, T.bin("abcd")),
+ C.GF.frombuf(C.bytes("00060123456789ab61626364")))
+
+ def test_numbertheory(me):
+ p, x, y = C.GF(0x11b), C.GF(0xa9), C.GF(0x18)
+
+ me.assertEqual(x.sqr(), C.GF(0x4441))
+
+ me.assertEqual(x.gcd(y), C.GF(0x3))
+ me.assertEqual(x.gcdx(y), (C.GF(0x3), C.GF(0x3), C.GF(0x15)))
+ me.assertEqual(p.modinv(x), C.GF(0xc8))
+
+ me.assertTrue(p.irreduciblep())
+ me.assertFalse(x.irreduciblep())
+
+###--------------------------------------------------------------------------
+class TestGFReduce (U.TestCase):
+
+ def test(me):
+ p = C.GF(0x87).setbit(128)
+ me.assertTrue(p.irreduciblep())
+ m = C.GFReduce(p)
+
+ x = C.GF(0xce46b4c1d3a1523520b1bb6eb5c61883)
+ y = C.GF(0xb5b0b3566b8e03f4b4a2b1ac413f8566)
+ xy = C.GF(0x28e5b895c11b08edc2fe7e1be5694c64)
+
+ me.assertEqual(m.reduce(x*y), xy)
+ me.assertEqual(m.trace(x), 0)
+ me.assertEqual(m.trace(y), 1)
+ me.assertEqual(m.sqrt(x), C.GF(0xa277ee4bf770e5974cf1e31b1ccb54a1))
+ me.assertEqual(m.halftrace(y), C.GF(0x9cea73e79ffd190dd3c81d33e58d8e6f))
+ me.assertEqual(m.quadsolve(x), C.GF(0x9664c09d23d168147a438de6a813c784))
+
+###--------------------------------------------------------------------------
+class TestGFN (U.TestCase):
+
+ def test(me):
+ p = C.GF(0x87).setbit(128)
+ beta = C.GF(0xc50f387e37194d4a4b41e157a3e2b5e1)
+ y = C.GF(0xdaca76dc2578a63c788a2ce0fc7878f6)
+ yy = C.GF(0x298a98f955100f054fcee3433f96b00e)
+ zero, one, fff = C.GF(0), C.GF(1), C.GF(T.long(2)**128 - 1)
+ me.assertTrue(p.irreduciblep())
+
+ gfn = C.GFN(p, beta)
+ me.assertEqual(gfn.p, p)
+ me.assertEqual(gfn.beta, beta)
+ me.assertEqual(gfn.pton(zero), zero)
+ me.assertEqual(gfn.ntop(zero), zero)
+ me.assertEqual(gfn.pton(one), fff)
+ me.assertEqual(gfn.ntop(fff), one)
+ me.assertEqual(gfn.pton(y), yy)
+ me.assertEqual(gfn.ntop(yy), y)
+
+ ## Doesn't generate a normal basis.
+ me.assertRaises(ValueError, C.GFN, p, y)
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing (some) passsword management 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.
+
+from __future__ import with_statement
+
+###--------------------------------------------------------------------------
+### Imported modules.
+
+import catacomb as C
+import errno as E
+import unittest as U
+import os as OS
+import subprocess as SUB
+import sys as SYS
+import testutils as T
+import time as TM
+
+###--------------------------------------------------------------------------
+### Running the pixie.
+
+class PixieTestCase (U.TestCase):
+ def setUp(me):
+ pix = "pixie-py%d.%d%s" % (SYS.version_info[0],
+ SYS.version_info[1],
+ T.DEBUGP and "dbg" or "")
+ me.token = C.rand.block(8).hex()
+ try: OS.mkdir(OS.path.join("build", pix), int("700", 8))
+ except OSError:
+ if SYS.exc_info()[1].errno == E.EEXIST: pass
+ else: raise
+ me.sock = OS.path.join("build", pix, "sock")
+ OS.environ["CATACOMB_PIXIE_SOCKET"] = me.sock
+ with open(OS.path.join("build", pix, "log"), "a") as logf:
+ SUB.check_call(["pixie", "-dr", "-s" + me.sock,
+ "-c", "echo 'pp.%%t.%s'" % me.token], stderr = logf)
+ def tearDown(me):
+ SUB.check_call(["pixie", "-s" + me.sock, "-C", "quit"])
+
+###--------------------------------------------------------------------------
+class TestPixie (PixieTestCase):
+
+ def test(me):
+ px = C.Pixie(socket = me.sock)
+
+ pp = T.bin("super secret")
+ pp1 = T.bin("pp.test1.%s" % me.token)
+ pp2 = T.bin("pp.test2.%s" % me.token)
+ px.set("test1", pp)
+ me.assertEqual(px.read("test1"), pp)
+ me.assertEqual(px.read("test1", C.PMODE_READ), pp)
+ me.assertEqual(px.read("test1", C.PMODE_VERIFY), pp1)
+ me.assertEqual(px.read("test1", C.PMODE_READ), pp1)
+ px.set("test1", pp)
+ me.assertEqual(px.read("test1", C.PMODE_READ), pp)
+ me.assertEqual(px.read("test2"), pp2)
+ px.cancel("test1")
+ me.assertEqual(px.read("test1"), pp1)
+
+ px.set("test1", pp)
+ me.assertEqual(C.ppread("test1"), pp)
+ C.ppcancel("test1")
+ me.assertEqual(px.read("test1"), pp1)
+ C.ppcancel("test1")
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing prime-generation 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 itertools as I
+import unittest as U
+import testutils as T
+
+###--------------------------------------------------------------------------
+class TestPrimeFilter (U.TestCase):
+
+ def test(me):
+ n0 = 55614384877957400296344428606761687241
+ pf = C.PrimeFilter(n0)
+ me.assertEqual(pf.status, C.PGEN_FAIL)
+ me.assertFalse(pf)
+ me.assertEqual(pf.x, n0)
+ me.assertEqual(int(pf), n0)
+ while not pf: pf.step(2)
+ me.assertEqual(pf.status, C.PGEN_TRY)
+ me.assertEqual(pf.x, n0 + 6)
+
+ n1 = 111228769755914800592688857213523374495
+ pf1 = pf.muladd(2, 1)
+ me.assertEqual(pf1.x, n1)
+ me.assertEqual(pf1.status, C.PGEN_FAIL)
+
+ step = 2275063338
+ pf0 = C.PrimeFilter(step)
+ me.assertEqual(pf0.status, C.PGEN_FAIL)
+ while not pf1: pf1.jump(pf0)
+ me.assertEqual(pf1.x, n1 + 6*step)
+
+###--------------------------------------------------------------------------
+class TestRabinMiller (U.TestCase):
+
+ def test(me):
+ ## See `Prime and Prejudice' by Martin R. Albrecht, Jake Massimo,
+ ## Kenneth G. Paterson, and Juraj Somorovsky.
+ p1 = C.MP(142445387161415482404826365418175962266689133006163)
+ p2 = C.MP(5840260873618034778597880982145214452934254453252643)
+ p3 = C.MP(14386984103302963722887462907235772188935602433622363)
+ n = p1*p2*p3
+
+ rm = C.RabinMiller(n)
+ me.assertEqual(rm.test(2), C.PGEN_PASS)
+ me.assertEqual(rm.test(41), C.PGEN_FAIL)
+ me.assertEqual(rm.niters, 6)
+
+ def test_iters(me):
+ me.assertEqual(C.RabinMiller.iters(50), 27)
+ me.assertEqual(C.RabinMiller.iters(100), 27)
+ me.assertEqual(C.RabinMiller.iters(500), 6)
+ me.assertEqual(C.RabinMiller.iters(1000), 3)
+ me.assertEqual(C.RabinMiller.iters(2000), 2)
+
+###--------------------------------------------------------------------------
+
+class FailingHandler (object):
+ def pg_begin(me, ev): return C.PGEN_TRY
+ def pg_try(me, ev): raise T.Explosion
+ def pg_done(me, ev): raise ValueError("pg_done")
+
+class TestPGen (U.TestCase):
+
+ def test_pgen(me):
+ ev = T.EventRecorder()
+ p0 = T.detrand("pgen").mp(256, 1)
+ p = C.pgen(p0, name = "p", event = ev)
+ me.assertEqual(p, p0 + 54)
+ me.assertTrue(p.primep())
+ me.assertEqual(ev.events, "[p:F4/P11/D]")
+
+ def test_pgen_exc(me):
+ rng = T.detrand("pgen_exc")
+ exc = [None]
+ def hook(why, ty, val, tb): exc[0] = val
+ C.lostexchook = hook
+ me.assertRaises(T.Explosion, C.pgen, rng.mp(256, 1), name = "p",
+ event = T.EventRecorder(explode_after = 19))
+ me.assertEqual(exc[0], None)
+ me.assertRaises(T.Explosion, C.pgen, rng.mp(256, 1), name = "p",
+ event = FailingHandler(), stepper = FailingHandler())
+ me.assertEqual(exc[0].args[0], "pg_done")
+ exc = [None]
+ me.assertRaises(T.Explosion, C.limlee, 512, 96, name = "p", rng = rng,
+ event = T.EventRecorder(explode_after = 8))
+ ev = T.EventRecorder(explode_after = 19)
+ me.assertRaises(T.Explosion, C.limlee, 512, 96, name = "p", rng = rng,
+ ievent = ev)
+ me.assertRaises(ValueError, ev.rng.byte)
+ me.assertEqual(exc[0], None)
+ C.lostexchook = C.default_lostexchook
+
+ def test_strongprime_setup(me):
+ ev = T.EventRecorder()
+ p0, delta = C.strongprime_setup(256, name = "p", event = ev,
+ rng = T.detrand("strongprime_setup"))
+ p = C.pgen(p0, name = "p", event = ev,
+ stepper = C.PrimeGenJumper(delta))
+ me.assertTrue(p.primep())
+ me.assertEqual((p - p0)%delta, 0)
+ me.assertEqual(p.nbits, 256)
+ me.assertEqual(ev.events,
+ "[p [s]:F3/P26/D]"
+ "[p [t]:F6/P26/D]"
+ "[p [r]:F5/P26/D]"
+ "[p:F7/P11/D]")
+
+ def test_strongprime(me):
+ ev = T.EventRecorder()
+ p = C.strongprime(256, name = "p", event = ev,
+ rng = T.detrand("strongprime"))
+ me.assertTrue(p.primep())
+ me.assertEqual(p.nbits, 256)
+ me.assertEqual(ev.events,
+ "[p [s]:F5/P26/D]"
+ "[p [t]:F13/P26/D]"
+ "[p [r]:F7/P26/D]"
+ "[p:F13/P11/D]")
+
+ def test_limlee(me):
+ ev = T.EventRecorder()
+ p, qq = C.limlee(512, 96, name = "p", event = ev, ievent = ev,
+ rng = T.detrand("limlee"))
+ me.assertTrue(p.primep())
+ me.assertEqual(p.nbits, 512)
+ qbig = qq.pop()
+ me.assertTrue(qbig.primep())
+ me.assertTrue(qbig.nbits >= 96)
+ for q in qq:
+ me.assertTrue(q.primep())
+ me.assertEqual(q.nbits, 96)
+ me.assertEqual(p, C.MPMul(qq).factor(qbig).factor(2).done() + 1)
+ me.assertEqual(ev.events,
+ "[p:"
+ "[p_0:P26/D]"
+ "[p_1:P26/D]"
+ "[p_2:P26/D]"
+ "[p_3:F5/P26/D]"
+ "[p*_4:P26/D]"
+ "[p_5:F6/P26/D]"
+ "[p_6:F3/P26/D]"
+ "[p*_7:P26/D]"
+ "[p_8:F5/P26/D]"
+ "[p*_9:F3/P26/D]"
+ "[p_10:F3/P26/D]"
+ "[p_11:F1/P26/D]"
+ "[p_12:F2/P26/D]"
+ "[p_13:F4/P26/D]"
+ "[p_14:F15/P26/D]"
+ "[p_15:P26/D]"
+ "[p_16:F3/P26/D]"
+ "[p_17:P26/D]"
+ "[p_18:F1/P26/D]"
+ "[p_19:F8/P26/D]"
+ "[p_20:F12/P26/D]"
+ "[p_21:F2/P26/D]"
+ "[p_22:F2/P26/D]"
+ "[p_23:F2/P26/D]"
+ "[p_24:F1/P26/D]"
+ "[p_25:F9/P26/D]"
+ "[p_26:P26/D]"
+ "[p_27:F2/P26/D]"
+ "[p*_28:F8/P26/D]"
+ "[p*_29:F14/P26/D]"
+ "[p*_30:F4/P26/D]"
+ "[p*_31:F6/P26/D]"
+ "[p*_32:P26/D]"
+ "[p*_33:F1/P26/D]"
+ "[p*_34:F6/P26/D]"
+ "[p*_35:F5/P26/D]"
+ "[p*_36:F6/P26/D]"
+ "[p*_37:P26/D]"
+ "[p*_38:F3/P26/D]"
+ "[p*_39:P26/D]"
+ "[p*_40:P26/D]"
+ "F41/P5/D]")
+
+ def test_sgprime(me):
+ ev = T.EventRecorder()
+ q0 = T.detrand("sgprime").mp(255, 1)
+ q = C.sgprime(q0, event = ev)
+ me.assertTrue(q.primep())
+ p = 2*q + 1
+ me.assertEqual(p.nbits, 256)
+ me.assertTrue(p.primep())
+
+ def test_kcdsa(me):
+ ev = T.EventRecorder()
+ p, q, h = C.kcdsaprime(512, 96, event = ev, rng = T.detrand("kcdsa"))
+ me.assertTrue(q.primep())
+ me.assertEqual(q.nbits, 96)
+ me.assertTrue(h.primep())
+ me.assertEqual(p, 2*q*h + 1)
+ me.assertTrue(p.primep())
+ me.assertEqual(p.nbits, 512)
+ me.assertEqual(ev.events, "[p [h]:F53/P6/D][p:F32/P26/D]")
+
+###--------------------------------------------------------------------------
+class TestPrimeIter (U.TestCase):
+
+ def test(me):
+ me.assertEqual(list(I.islice(C.PrimeIter(0), 5)), [2, 3, 5, 7, 11])
+ me.assertEqual(list(I.islice(C.PrimeIter(1000), 5)),
+ [1009, 1013, 1019, 1021, 1031])
+ me.assertEqual(list(I.islice(C.PrimeIter(1000000), 5)),
+ [1000003, 1000033, 1000037, 1000039, 1000081])
+ me.assertEqual(list(I.islice(C.PrimeIter(1000000000000000000000000000000000000), 5)),
+ [1000000000000000000000000000000000067,
+ 1000000000000000000000000000000000123,
+ 1000000000000000000000000000000000141,
+ 1000000000000000000000000000000000159,
+ 1000000000000000000000000000000000163])
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing public-key crypto 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 TestDSA (U.TestCase):
+
+ def check(me, privcls, pubcls, rng, H, G):
+ msg = T.bin("A simple test message")
+ wrong = T.bin("This is not the message you're looking for")
+ a = privcls(G, rng.range(G.r), hash = H, rng = rng)
+ me.assertEqual(a.hash, H)
+ me.assertEqual(a.rng, rng)
+ A = pubcls(G, a.p, hash = H, rng = rng)
+ h = a.endhash(a.beginhash().hash(msg))
+ me.assertEqual(h, A.endhash(a.beginhash().hash(msg)))
+ sig = a.sign(h)
+ me.assertTrue(A.verify(h, sig))
+ me.assertFalse(A.verify(A.endhash(A.beginhash().hash(wrong)), sig))
+
+ me.assertRaises(ValueError, a.sign, h + C.bytes("00"))
+ me.assertRaises(ValueError, a.verify, h + C.bytes("00"), sig)
+
+ def test_dsa(me):
+ me.check(C.DSAPriv, C.DSAPub, T.detrand("dsa"), C.sha256,
+ C.primegroups["catacomb-ll-256-3072"].group())
+ def test_ecdsa(me):
+ me.check(C.DSAPriv, C.DSAPub, T.detrand("ecdsa"), C.sha256,
+ C.eccurves["nist-p256"].group())
+ def test_kcdsa(me):
+ me.check(C.KCDSAPriv, C.KCDSAPub, T.detrand("kcdsa"), C.has160,
+ C.primegroups["catacomb-ll-160-1024"].group())
+ def test_eckcdsa(me):
+ me.check(C.KCDSAPriv, C.KCDSAPub, T.detrand("eckcdsa"), C.sha256,
+ C.eccurves["nist-p256"].group())
+
+###--------------------------------------------------------------------------
+class TestRSA (U.TestCase):
+
+ def test(me):
+ rng = T.detrand("rsa")
+ ev = T.EventRecorder()
+ msg = T.bin("A simple test message")
+ h = C.sha256().hash(msg).done()
+ wrong = T.bin("This is not the message you're looking for")
+ hh = C.sha256().hash(wrong).done()
+
+ a = C.RSAPriv.generate(1536, event = ev, rng = rng)
+ A = C.RSAPub(n = a.n, e = a.e)
+ me.assertEqual(a.n.nbits, 1536)
+ me.assertEqual(a.rng, None)
+ a.rng = rng
+ me.assertEqual(a.rng, rng)
+ me.assertEqual(ev.events,
+ "[p [s]:F6/P7/D]"
+ "[p [t]:F68/P7/D]"
+ "[p [r]:F20/P7/D]"
+ "[p:F35/P3/D]"
+ "[q [s]:F47/P7/D]"
+ "[q [t]:F4/P7/D]"
+ "[q [r]:F8/P7/D]"
+ "[q:F22/P3/D]")
+
+ C.RSAPriv(n = a.n, e = a.e, d = a.d)
+ C.RSAPriv(n = a.n, p = a.p, d = a.d)
+ C.RSAPriv(n = a.n, q = a.q, e = a.e)
+ C.RSAPriv(p = a.p, q = a.q, e = a.e)
+ me.assertRaises(ValueError, C.RSAPriv, p = a.p, q = a.q, dp = a.dp)
+
+ me.assertTrue(a.p.primep())
+ me.assertTrue(a.q.primep())
+ me.assertEqual(a.n, a.p*a.q)
+ me.assertEqual(a.dp, a.d%(a.p - 1))
+ me.assertEqual(a.dq, a.d%(a.q - 1))
+ me.assertEqual((a.e*a.dp)%(a.p - 1), 1)
+ me.assertEqual((a.e*a.dq)%(a.q - 1), 1)
+ me.assertEqual((a.q*a.q_inv)%a.p, 1)
+
+ x = rng.range(a.n)
+ y = a.privop(x)
+ me.assertEqual(x, a.pubop(y))
+ me.assertEqual(x, A.pubop(y))
+ z = a.pubop(x)
+ me.assertEqual(z, A.pubop(x))
+ me.assertEqual(x, a.privop(z))
+
+ pad = C.PKCS1Crypt(rng = rng)
+ ct = A.encrypt(msg, pad)
+ me.assertEqual(a.decrypt(ct, pad), msg)
+ me.assertRaises(ValueError, a.decrypt, ct ^ 1, pad)
+
+ pad = C.PKCS1Sig(ep = C.bytes("3031300d060960864801650304020105000420"))
+ sig = a.sign(h, pad)
+ me.assertTrue(A.verify(h, sig, pad))
+ me.assertFalse(A.verify(hh, sig, pad))
+
+ pad = C.OAEP(mgf = C.sha256_mgf, hash = C.sha256, rng = rng)
+ ct = A.encrypt(msg, pad)
+ me.assertEqual(a.decrypt(ct, pad), msg)
+ me.assertRaises(ValueError, a.decrypt, ct ^ 1, pad)
+
+ pad = C.PSS(mgf = C.sha256_mgf, hash = C.sha256, rng = rng)
+ sig = a.sign(h, pad)
+ me.assertTrue(A.verify(h, sig, pad))
+ me.assertFalse(A.verify(hh, sig, pad))
+
+###--------------------------------------------------------------------------
+class TestXDH (U.TestCase):
+
+ def check(me, privcls, pubcls, rng):
+ msg = T.bin("A simple test message")
+ n = rng.block(24)
+ n1 = rng.block(24)
+
+ a = privcls.generate(rng)
+ A = pubcls(a.pub)
+ b = privcls.generate(rng)
+ B = pubcls(b.pub)
+ Z = a.agree(B)
+ me.assertEqual(b.agree(A), Z)
+ me.assertEqual(b.agree(a), Z)
+
+ ct = a.box(B, n, msg)
+ me.assertEqual(b.unbox(A, n, ct), msg)
+ me.assertRaises(ValueError, b.unbox, A, n1, ct)
+
+ def test_x25519(me):
+ me.check(C.X25519Priv, C.X25519Pub, T.detrand("x25519"))
+ def test_x448(me):
+ me.check(C.X448Priv, C.X448Pub, T.detrand("x448"))
+
+###--------------------------------------------------------------------------
+class TestEdDSA (U.TestCase):
+
+ def check(me, privcls, pubcls, rng):
+ msg = T.bin("A simple test message")
+ wrong = T.bin("This is not the message you're looking for")
+ perso = T.bin("Catacomb/Python test")
+ a = privcls.generate(rng)
+ A = pubcls(a.pub)
+
+ sig = a.sign(msg)
+ me.assertTrue(a.verify(msg, sig))
+ me.assertTrue(A.verify(msg, sig))
+ me.assertFalse(A.verify(wrong, sig))
+
+ sig = a.sign(msg, perso = perso)
+ me.assertTrue(a.verify(msg, sig, perso = perso))
+ me.assertFalse(A.verify(msg, sig))
+
+ h = a.endhash(a.beginhash().hash(msg))
+ sig = a.sign(h, phflag = True)
+ me.assertTrue(a.verify(h, sig, phflag = True))
+ me.assertFalse(A.verify(h, sig))
+
+ def test_ed25519(me):
+ me.check(C.Ed25519Priv, C.Ed25519Pub, T.detrand("ed25519"))
+ def test_ed448(me):
+ me.check(C.Ed448Priv, C.Ed448Pub, T.detrand("ed448"))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing random-generator 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 TestRandomGenerator (U.TestCase):
+
+ def check_rand(me, rng):
+
+ for i in T.range(50):
+ x = rng.byte()
+ me.assertEqual(type(x), int)
+ me.assertTrue(0 <= x < 256)
+
+ x = rng.word()
+ me.assertTrue(0 <= x <= 0xffffffff)
+
+ for i in T.range(50):
+ x = rng.range(10)
+ me.assertEqual(type(x), int)
+ me.assertTrue(0 <= x < 10)
+ x = rng.range(T.MAXFIXNUM + 1)
+ me.assertEqual(type(x), C.MP)
+
+ for i in T.range(50):
+ x = rng.mp(123, 7)
+ me.assertEqual(type(x), C.MP)
+ me.assertEqual(x.nbits, 123)
+ me.assertEqual(x&7, 7)
+ me.assertRaises((OverflowError, ValueError), rng.mp, 128, -1)
+ me.assertRaises((OverflowError, ValueError), rng.mp, 128, C.MPW_MAX + 1)
+
+ x = rng.block(17)
+ me.assertEqual(type(x), C.ByteString)
+ me.assertEqual(len(x), 17)
+
+ def test_lcrand(me):
+ rng = C.LCRand(0)
+ me.assertFalse(rng.cryptop)
+ w0 = rng.word()
+ rng.seedint(0)
+ me.assertEqual(rng.word(), w0)
+
+ def test_firand(me):
+ rng = C.FibRand(0)
+ me.assertFalse(rng.cryptop)
+ w0 = rng.word()
+ rng.seedint(0)
+ me.assertEqual(rng.word(), w0)
+
+ def test_truerand(me):
+ rng = C.TrueRand()
+ me.assertTrue(rng.cryptop)
+ me.check_rand(rng)
+ rng.key(T.span(23))
+ rng.seed(256)
+ me.assertRaises(ValueError, rng.seed, C.RAND_IBITS + 1)
+ rng.seedint(-314)
+ rng.seedword(0x12345678)
+ rng.seedrand(T.detrand("seed-truerand"))
+ rng.seedblock(T.span(123))
+ rng.add(T.span(123), 978)
+ rng.gate()
+ rng.stretch()
+ rng.timer()
+ me.check_rand(rng)
+
+ def test_cryptorand(me):
+ for r, kw in [("rijndael-counter", {}),
+ ("rc4", {}),
+ ("xchacha20", { "nonce": T.span(24) }),
+ ("seal", { "i": 12345678 }),
+ ("shake128", { "func": T.bin("TEST"),
+ "perso": T.bin("Catacomb/Python test") }),
+ ("kmac256", { "perso": T.bin("Catacomb/Python test") })]:
+ rcls = C.gccrands[r]
+ rng = rcls(T.span(rcls.keysz.default), **kw)
+ me.assertTrue(rng.cryptop)
+ if kw:
+ rng = rcls(T.span(rcls.keysz.default))
+ me.check_rand(rng)
+
+ def test_sslrand(me):
+ rng = C.SSLRand(T.span(16), T.span(32), C.md5, C.sha)
+ me.check_rand(rng)
+ def test_tlsdx(me):
+ rng = C.TLSDataExpansion(T.span(16), T.span(32), C.sha256_hmac)
+ me.check_rand(rng)
+ def test_tlsprf(me):
+ rng = C.TLSPRF(T.span(16), T.span(32), C.md5_hmac, C.sha_hmac)
+ me.check_rand(rng)
+
+ def test_dsarand(me):
+ seed = T.span(16)
+ n = C.MP.loadb(seed)
+ rng = C.DSARand(seed, passes = 2)
+ me.check_rand(rng)
+ me.assertEqual(rng.seed, (n + 2*153).storeb(16))
+ me.assertEqual(rng.passes, 2);
+ rng.passes = 1
+ me.check_rand(rng)
+ me.assertEqual(rng.seed, (n + 3*153 + 1).storeb(16))
+
+ def test_bbs(me):
+ ev = T.EventRecorder()
+ drng = T.detrand("bbs")
+ rngpriv = C.BBSPriv.generate(1536, event = ev, rng = drng)
+ me.assertEqual(rngpriv.n.nbits, 1536)
+ me.assertEqual(rngpriv.p&3, 3)
+ me.assertEqual(rngpriv.q&3, 3)
+ me.assertTrue(rngpriv.p.primep())
+ me.assertTrue(rngpriv.q.primep())
+ me.assertEqual(rngpriv.n, rngpriv.p*rngpriv.q)
+ me.assertEqual(ev.events,
+ "[p [s]:F10/P7/D]"
+ "[p [t]:F2/P7/D]"
+ "[p [r]:F7/P7/D]"
+ "[p:F6/P3/D]"
+ "[q [s]:P7/D]"
+ "[q [t]:F33/P7/D]"
+ "[q [r]:F33/P7/D]"
+ "[q:F55/P3/D]")
+
+ x0 = drng.range(rngpriv.n)
+ rngpriv.seedmp(x0)
+ rng = C.BlumBlumShub(rngpriv.n, x0)
+ me.check_rand(rngpriv)
+ me.check_rand(rng)
+ me.assertEqual(rngpriv.x, rng.x)
+
+ msg = T.span(123)
+ rng.wrap()
+ ct = rng.mask(msg)
+ rng.wrap()
+
+ rngpriv.x = rng.x
+ nsteps = (123*8 + 7)//(rng.stepsz)
+ rngpriv.rew(nsteps + 1)
+ me.assertEqual(rngpriv.mask(ct), msg)
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing rational arithmetic 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 TestRat (U.TestCase):
+
+ def check_rat(me, k):
+ R = k.RING
+
+ ## Check that exact true division in the base ring gives base-ring
+ ## elements.
+ a, b, c = R(19), R(5), R(8)
+ m = a*b
+ q = m/b
+ me.assertEqual(type(q), R)
+ me.assertEqual(q, a)
+
+ ## Check that inexact division gives a fraction.
+ f = (m + c)/b
+ me.assertNotEqual(type(f), R)
+ me.assertNotEqual(f, a)
+ r = f - q
+ me.assertEqual(b*r, c)
+
+ ## More complicated arithmetic.
+ u, v = a/b, a/c
+ me.assertEqual((u + v)*(b*c), a*(b + c))
+ me.assertEqual((u - v)*(b*c), a*(c - b))
+
+ ## Ordering.
+ me.assertTrue(b < c)
+ me.assertTrue(b/a < c/a)
+ me.assertFalse(c/a < b/a)
+ me.assertTrue(b/a <= c/a)
+ me.assertFalse(c/a <= b/a)
+ me.assertFalse(b/a >= c/a)
+ me.assertTrue(c/a >= b/a)
+ me.assertFalse(b/a > c/a)
+ me.assertTrue(c/a > b/a)
+
+ ## Conversions from string.
+ me.assertRaises(TypeError, T.add, f, "3")
+
+ def test_intrat(me):
+ me.check_rat(C.IntRat)
+
+ ## Check interaction with floating point.
+ u = C.MP(5)/C.MP(4)
+ me.assertEqual(type(u + 0.0), float)
+ me.assertEqual(u, 1.25)
+ me.assertTrue(u < 1.26)
+ me.assertTrue(u > 1.24)
+
+ ## Check string conversions.
+ me.assertEqual(str(u), "5/4")
+ me.assertEqual(repr(u), "IntRat(5, 4)")
+
+ def test_gfrat(me):
+ me.check_rat(C.GFRat)
+
+ ## Check string conversions.
+ u = C.GF(5)/C.GF(4)
+ me.assertRaises(TypeError, T.add, u, 0.0)
+ me.assertEqual(str(u), "0x5/0x4")
+ me.assertEqual(repr(u), "GFRat(0x5, 0x4)")
+
+ def test_cross(me):
+ u = C.MP(5)/C.MP(3)
+ v = C.GF(5)/C.GF(3)
+ me.assertRaises(TypeError, T.add, u, v)
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*-python-*-
+###
+### Testing (some) passsword management 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 TestShare (U.TestCase):
+
+ def check_share(me, splitcls, splitargs, joincls, joinargs, secret):
+
+ split = splitcls(3, secret, *splitargs)
+ me.assertEqual(split.threshold, 3)
+ shares = [split.get(i) for i in T.range(5)]
+
+ join = joincls(3, *joinargs)
+ me.assertEqual(join.threshold, 3)
+ me.assertFalse(join.addedp(1))
+ me.assertEqual(join.remain, 3)
+ join.add(1, shares[1])
+ me.assertTrue(join.addedp(1))
+ me.assertEqual(join.remain, 2)
+ me.assertRaises(ValueError, join.combine)
+ join.add(0, shares[0])
+ join.add(3, shares[3])
+ me.assertEqual(join.remain, 0)
+ me.assertEqual(join.combine(), secret)
+
+ def test_share(me):
+ rng = T.detrand("share")
+ p = C.MP(2)**127 - 1
+ me.check_share(C.ShareSplit, [p, rng], C.ShareJoin, [p], rng.range(p))
+
+ def test_gfshare(me):
+ rng = T.detrand("gfshare")
+ me.check_share(C.GFShareSplit, [rng], C.GFShareJoin, [123],
+ rng.block(123))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()
--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Test utilities
+###
+### (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 sys as SYS
+if SYS.version_info >= (3,): import builtins as B
+else: import __builtin__ as B
+import unittest as U
+
+###--------------------------------------------------------------------------
+### Main code.
+
+## Some compatibility hacks.
+if SYS.version_info >= (3,):
+ PY2, PY3 = False, True
+ def bin(x): return x.encode('iso8859-1')
+ def py23(x, y): return y
+ range = range
+ byteseq = bytes
+ long = int
+ imap = map
+ def iterkeys(m): return m.keys()
+ def itervalues(m): return m.values()
+ def iteritems(m): return m.items()
+ from io import StringIO
+ MAXFIXNUM = SYS.maxsize
+else:
+ import itertools as I
+ PY2, PY3 = True, False
+ def bin(x): return x
+ def py23(x, y): return x
+ range = xrange
+ long = long
+ imap = I.imap
+ def byteseq(seq): return "".join(map(chr, seq))
+ def iterkeys(m): return m.iterkeys()
+ def itervalues(m): return m.itervalues()
+ def iteritems(m): return m.iteritems()
+ from cStringIO import StringIO
+ MAXFIXNUM = SYS.maxint
+
+DEBUGP = hasattr(SYS, "gettotalrefcount")
+
+FULLSPAN = byteseq(range(256))
+def span(n):
+ """A string `00 01 .. NN'."""
+ return (n >> 8)*FULLSPAN + FULLSPAN[:n&255]
+
+def bytes_as_int(w, bigendp):
+ """Convert the byte-sequence `01 02 ... WW' to an integer."""
+ x = 0
+ if bigendp:
+ for i in range(w): x = x << 8 | i + 1
+ else:
+ for i in range(w): x |= i + 1 << 8*i
+ return x
+
+def prep_lenseq(w, n, bigendp, goodp):
+ """
+ Return a reference buffer containing `00 LL .. LL 00 01 02 .. NN ff'.
+
+ Here, LL .. LL is the length of following sequence, not including the final
+ `ff', as a W-byte integer. If GOODP is false, then the most significant
+ bit of LL .. LL is set, to provoke an overflow.
+ """
+ if goodp: l = n
+ else: l = n + (1 << 8*w - 1)
+ lenbyte = bigendp \
+ and (lambda i: (l >> 8*(w - i - 1))&0xff) \
+ or (lambda i: (l >> 8*i)&0xff)
+ return byteseq([0x00]) + \
+ byteseq([lenbyte(i) for i in range(w)]) + \
+ span(n) + \
+ byteseq([0xff])
+
+def detrand(seed):
+ """Return a fast deterministic random generator with the given SEED."""
+ return C.chacha8rand(C.sha256().hash(bin(seed)).done())
+
+class GenericTestMixin (U.TestCase):
+ """
+ A mixin class to generate test-case functions for all similar things.
+ """
+
+ @classmethod
+ def generate_testcases(cls, things):
+ testfns = dict()
+ checkfns = []
+ for k, v in iteritems(cls.__dict__):
+ if k.startswith("_test_"): checkfns.append((k[6:], v))
+ for name, thing in things:
+ for test, checkfn in checkfns:
+ testfn = lambda me, thing = thing: checkfn(me, thing)
+ doc = getattr(checkfn, "__doc__", None)
+ if doc is not None: testfn.__doc__ = doc % name
+ testfns["test_%s%%%s" % (test, name)] = testfn
+ tmpcls = type("_tmp", (cls,), testfns)
+ for k, v in iteritems(tmpcls.__dict__):
+ if k.startswith("test_"): setattr(cls, k, v)
+
+class ImmutableMappingTextMixin (U.TestCase):
+
+ ## Subclass stubs.
+ def _mkkey(me, i): return "k#%d" % i
+ def _getkey(me, k): return int(k[2:])
+ def _getvalue(me, v): return int(v[2:])
+ def _getitem(me, it): k, v = it; return me._getkey(k), me._getvalue(v)
+
+ def check_immutable_mapping(me, map, model):
+
+ ## Lookup.
+ limk = 0
+ any = False
+ me.assertEqual(len(map), len(model))
+ for k, v in iteritems(model):
+ any = True
+ if k >= limk: limk = k + 1
+ me.assertTrue(me._mkkey(k) in map)
+ if PY2: me.assertTrue(map.has_key(me._mkkey(k)))
+ me.assertEqual(me._getvalue(map[me._mkkey(k)]), v)
+ me.assertEqual(me._getvalue(map.get(me._mkkey(k))), v)
+ if any: me.assertTrue(me._mkkey(k) in map)
+ if PY2: me.assertFalse(map.has_key(me._mkkey(limk)))
+ me.assertRaises(KeyError, lambda: map[me._mkkey(limk)])
+ me.assertEqual(map.get(me._mkkey(limk)), None)
+
+ if PY3:
+ empty = set()
+
+ for k, v in iteritems(map):
+ me.assertTrue(k in map.keys())
+ me.assertTrue((k, v) in map.items())
+ me.assertFalse(me._mkkey(limk) in map.keys())
+
+ for viewfn, getfn in [(lambda x: x.keys(), me._getkey),
+ (lambda x: x.items(), me._getitem)]:
+ rview, rview2, mview = viewfn(map), viewfn(map), viewfn(model)
+ me.assertEqual(set(imap(getfn, rview)), set(mview))
+ me.assertEqual(rview, rview2)
+ me.assertEqual(rview, set(rview2))
+ me.assertEqual(rview | empty, set(rview))
+ me.assertEqual(rview | rview2, set(rview))
+ me.assertEqual(rview ^ empty, set(rview))
+ me.assertEqual(rview ^ rview, empty)
+ me.assertEqual(rview & empty, empty)
+ me.assertEqual(len(rview), len(model))
+
+ if any: subset = set(rview2); subset.pop()
+ superset = set(rview2); superset.add(object())
+
+ me.assertFalse(rview < rview2)
+ me.assertTrue(rview < superset)
+ me.assertFalse(superset < rview)
+ me.assertFalse(rview < empty)
+ if any:
+ me.assertTrue(empty < rview)
+ me.assertTrue(subset < rview)
+ me.assertFalse(rview < subset)
+
+ me.assertTrue(rview <= rview2)
+ me.assertTrue(rview <= superset)
+ me.assertFalse(superset <= rview)
+ if any:
+ me.assertTrue(empty <= rview)
+ me.assertFalse(rview <= empty)
+ me.assertTrue(subset <= rview)
+ me.assertFalse(rview <= subset)
+
+ me.assertTrue(rview >= rview2)
+ me.assertTrue(superset >= rview)
+ me.assertFalse(rview >= superset)
+ if any:
+ me.assertTrue(rview >= empty)
+ me.assertFalse(empty >= rview)
+ me.assertTrue(rview >= subset)
+ me.assertFalse(subset >= rview)
+
+ me.assertFalse(rview > rview2)
+ me.assertTrue(superset > rview)
+ me.assertFalse(rview > superset)
+ me.assertFalse(empty > rview)
+ if any:
+ me.assertTrue(rview > empty)
+ me.assertTrue(rview > subset)
+ me.assertFalse(subset > rview)
+
+ else:
+ for listfn, getfn in [(lambda x: x.keys(), me._getkey),
+ (lambda x: x.values(), me._getvalue),
+ (lambda x: x.items(), me._getitem)]:
+ rlist, mlist = listfn(map), listfn(model)
+ me.assertEqual(type(rlist), list)
+ rlist = B.map(getfn, rlist)
+ rlist.sort(); mlist.sort(); me.assertEqual(rlist, mlist)
+ for iterfn, getfn in [(lambda x: x.iterkeys(), me._getkey),
+ (lambda x: x.itervalues(), me._getvalue),
+ (lambda x: x.iteritems(), me._getitem)]:
+ me.assertEqual(set(imap(getfn, iterfn(map))), set(iterfn(model)))
+
+class MutableMappingTestMixin (ImmutableMappingTextMixin):
+
+ ## Subclass stubs.
+ def _mkvalue(me, i): return "v#%d" % i
+
+ def check_mapping(me, emptymapfn):
+
+ map = emptymapfn()
+ me.assertEqual(len(map), 0)
+
+ if not PY3:
+ def check_views():
+ me.check_immutable_mapping(map, model)
+ else:
+ kview, iview, vview = map.keys(), map.items(), map.values()
+ def check_views():
+ me.check_immutable_mapping(map, model)
+ me.assertEqual(set(imap(me._getkey, kview)), model.keys())
+ me.assertEqual(set(imap(me._getitem, iview)), model.items())
+ me.assertEqual(set(imap(me._getvalue, vview)), set(model.values()))
+
+ model = { 1: 101, 2: 202, 4: 404 }
+ for k, v in iteritems(model): map[me._mkkey(k)] = me._mkvalue(v)
+ check_views()
+
+ model.update({ 2: 212, 6: 606, 7: 707 })
+ map.update({ me._mkkey(2): me._mkvalue(212),
+ me._mkkey(6): me._mkvalue(606) },
+ **{ me._mkkey(7): me._mkvalue(707) })
+ check_views()
+
+ model[9] = 909
+ map[me._mkkey(9)] = me._mkvalue(909)
+ check_views()
+
+ model[9] = 919
+ map[me._mkkey(9)] = me._mkvalue(919)
+ check_views()
+
+ map.setdefault(me._mkkey(9), me._mkvalue(929))
+ check_views()
+
+ model[8] = 808
+ map.setdefault(me._mkkey(8), me._mkvalue(808))
+ check_views()
+
+ me.assertRaises(KeyError, map.pop, me._mkkey(5))
+ obj = object()
+ me.assertEqual(map.pop(me._mkkey(5), obj), obj)
+ me.assertEqual(me._getvalue(map.pop(me._mkkey(8))), 808)
+ del model[8]
+ check_views()
+
+ del model[9]
+ del map[me._mkkey(9)]
+ check_views()
+
+ k, v = map.popitem()
+ mk, mv = me._getkey(k), me._getvalue(v)
+ me.assertEqual(model[mk], mv)
+ del model[mk]
+ check_views()
+
+ map.clear()
+ model = {}
+ check_views()
+
+class Explosion (Exception): pass
+
+class EventRecorder (C.PrimeGenEventHandler):
+ def __init__(me, parent = None, explode_after = None, *args, **kw):
+ super(EventRecorder, me).__init__(*args, **kw)
+ me._streak = 0
+ me._op = None
+ me._parent = parent
+ me._countdown = explode_after
+ me.rng = None
+ if parent is None: me._buf = StringIO()
+ else: me._buf = parent._buf
+ def _event_common(me, ev):
+ if me.rng is None: me.rng = ev.rng
+ if me._countdown is None: pass
+ elif me._countdown == 0: raise Explosion()
+ else: me._countdown -= 1
+ def _put(me, op):
+ if op == me._op:
+ me._streak += 1
+ else:
+ if me._op is not None: me._buf.write("%s%d/" % (me._op, me._streak))
+ me._op = op
+ me._streak = 1
+ def pg_begin(me, ev):
+ me._event_common(ev)
+ me._buf.write("[%s:" % ev.name)
+ def pg_try(me, ev):
+ me._event_common(ev)
+ def pg_fail(me, ev):
+ me._event_common(ev)
+ me._put("F")
+ def pg_pass(me, ev):
+ me._event_common(ev)
+ me._put("P")
+ def pg_done(me, ev):
+ me._event_common(ev)
+ me._put(None); me._buf.write("D]")
+ def pg_abort(me, ev):
+ me._event_common(ev)
+ me._put(None); me._buf.write("A]")
+ @property
+ def events(me):
+ return me._buf.getvalue()
+
+## Functions for operators.
+neg = lambda x: -x
+pos = lambda x: +x
+add = lambda x, y: x + y
+sub = lambda x, y: x - y
+mul = lambda x, y: x*y
+div = lambda x, y: x/y
+mod = lambda x, y: x%y
+floordiv = lambda x, y: x//y
+bitand = lambda x, y: x&y
+bitor = lambda x, y: x | y
+bitxor = lambda x, y: x ^ y
+bitnot = lambda x: ~x
+lsl = lambda x, y: x << y
+lsr = lambda x, y: x >> y
+eq = lambda x, y: x == y
+ne = lambda x, y: x != y
+lt = lambda x, y: x < y
+le = lambda x, y: x <= y
+ge = lambda x, y: x >= y
+gt = lambda x, y: x > y
+
+###----- That's all, folks --------------------------------------------------