From: mdw Date: Sun, 10 Apr 2005 18:03:11 +0000 (+0000) Subject: Initial check-in of catacomb-python. X-Git-Tag: 1.0.1~54 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/catacomb-python/commitdiff_plain/d7ab1bab81155baa763449d5afa81e16df98dbe7?hp=5a18d659e4e4ba741416528a5de24ea943f3a21c Initial check-in of catacomb-python. --- diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..49672f3 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,4 @@ +file py/python2.2-2.2.1/debian/python2.2/usr/bin/python2.2 +dir py/python2.2-2.2.1/builddir +cd build/lib.linux-i686-2.2 +set env LD_LIBRARY_PATH=/home/mdw/src/catacomb/deb-build/.libs diff --git a/.skelrc b/.skelrc new file mode 100644 index 0000000..f72cc74 --- /dev/null +++ b/.skelrc @@ -0,0 +1,9 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Straylight/Edgeware") + (licence-text . "[[gpl]]") + (full-title . "the Python interface to Catacomb") + (program . "Catacomb/Python")) + skel-alist)) diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..a00416c --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README Makefile MANIFEST MANIFEST.in +include *.c *.h +recursive-include catacomb *.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96c081f --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +## Makefile + +PYTHON = python2.2 +prefix=/usr/local + +all: setup.py + $(PYTHON) setup.py build + +clean: setup.py + $(PYTHON) setup.py clean + rm -rf build + +dist: setup.py + $(PYTHON) setup.py sdist + +install: setup.py + $(PYTHON) setup.py install --prefix $(prefix) + +.PHONY: all clean dist diff --git a/README b/README new file mode 100644 index 0000000..699d922 --- /dev/null +++ b/README @@ -0,0 +1,69 @@ +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) \ No newline at end of file diff --git a/algorithms.c b/algorithms.c new file mode 100644 index 0000000..6eda802 --- /dev/null +++ b/algorithms.c @@ -0,0 +1,1192 @@ +/* -*-c-*- + * + * $Id$ + * + * 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" +#include "algorithms.h" + +/*----- Key sizes ---------------------------------------------------------*/ + +PyTypeObject *keysz_pytype; +PyTypeObject *keyszany_pytype, *keyszrange_pytype, *keyszset_pytype; +PyObject *sha_pyobj, *has160_pyobj; + +PyObject *keysz_pywrap(const octet *k) +{ + switch (k[0]) { + case KSZ_ANY: { + keysz_pyobj *o = PyObject_New(keysz_pyobj, keyszany_pytype); + o->dfl = k[1]; + return ((PyObject *)o); + } break; + case KSZ_RANGE: { + keyszrange_pyobj *o = + PyObject_New(keyszrange_pyobj, keyszrange_pytype); + o->dfl = k[1]; + o->min = k[2]; + o->max = k[3]; + o->mod = k[4]; + if (!o->mod) o->mod = 1; + return ((PyObject *)o); + } break; + case KSZ_SET: { + keyszset_pyobj *o = + PyObject_New(keyszset_pyobj, keyszset_pytype); + int i, n; + o->dfl = k[1]; + for (i = 0; k[i + 1]; i++) ; + n = i; o->set = PyTuple_New(n); + for (i = 0; i < n; i++) + PyTuple_SET_ITEM(o->set, i, PyInt_FromLong(k[i + 1])); + return ((PyObject *)o); + } break; + default: + abort(); + } +} + +static PyObject *keyszany_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + char *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) +{ + char *kwlist[] = { "default", "min", "max", "mod", 0 }; + int dfl, min = 0, max = 0, mod = 1; + keyszrange_pyobj *o; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|iii:new", kwlist, + &dfl, &min, &max, &mod)) + 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) +{ + char *kwlist[] = { "default", "set", 0 }; + int dfl, i, n, xx; + PyObject *set = 0; + PyObject *x = 0, *l = 0; + keyszset_pyobj *o = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|O:new", kwlist, + &dfl, &set)) + goto end; + if (!set) set = PyTuple_New(0); + else Py_INCREF(set); + if (!PySequence_Check(set)) TYERR("want a sequence"); + n = PySequence_Size(set); + l = PyList_New(0); + if (PyErr_Occurred()) 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 (i = 0; i < n; i++) { + if ((x = PySequence_GetItem(set, i)) == 0) goto end; + xx = PyInt_AsLong(x); + if (PyErr_Occurred()) goto end; + if (xx == dfl) continue; + if (xx < 0) VALERR("key size cannot be negative"); + PyList_Append(l, x); + Py_DECREF(x); + x = 0; + } + Py_DECREF(set); + if ((set = PySequence_Tuple(l)) == 0) goto end; + o = (keyszset_pyobj *)ty->tp_alloc(ty, 0); + o->dfl = dfl; + o->set = set; + Py_INCREF(set); +end: + Py_XDECREF(set); + Py_XDECREF(l); + Py_XDECREF(x); + return ((PyObject *)o); +} + +static PyObject *kaget_min(PyObject *me, void *hunoz) + { return (PyInt_FromLong(0)); } +#define kaget_max kaget_min + +static PyObject *ksget_min(PyObject *me, void *hunoz) +{ + PyObject *set = ((keyszset_pyobj *)me)->set; + int i, n, y, x = -1; + n = PyTuple_Size(set); + for (i = 0; i < n; i++) { + y = PyInt_AsLong(PyTuple_GetItem(set, i)); + if (x == -1 || y < x) x = y; + } + return (PyInt_FromLong(x)); +} + +static PyObject *ksget_max(PyObject *me, void *hunoz) +{ + PyObject *set = ((keyszset_pyobj *)me)->set; + int i, n, y, x = -1; + n = PyTuple_Size(set); + for (i = 0; i < n; i++) { + y = PyInt_AsLong(PyTuple_GetItem(set, i)); + if (y > x) x = y; + } + return (PyInt_FromLong(x)); +} + +static PyMemberDef keysz_pymembers[] = { +#define MEMBERSTRUCT keysz_pyobj +#define default dfl /* ugh! */ + MEMBER(default, T_INT, READONLY, "KSZ.default -> default key size") +#undef default +#undef MEMBERSTRUCT + { 0 } +}; + +static PyGetSetDef keyszany_pygetset[] = { +#define GETSETNAME(op, name) ka##op##_##name + GET (min, "KSZ.min -> smallest allowed key size") + GET (max, "KSZ.min -> largest allowed key size") +#undef GETSETNAME + { 0 } +}; + +static PyMemberDef keyszrange_pymembers[] = { +#define MEMBERSTRUCT keyszrange_pyobj + MEMBER(min, T_INT, READONLY, "KSZ.min -> smallest allowed key size") + MEMBER(max, T_INT, READONLY, "KSZ.min -> largest allowed key size") + MEMBER(mod, T_INT, READONLY, + "KSZ.mod -> key size must be a multiple of this") +#undef MEMBERSTRUCT + { 0 } +}; + +static PyGetSetDef keyszset_pygetset[] = { +#define GETSETNAME(op, name) ks##op##_##name + GET (min, "KSZ.min -> smallest allowed key size") + GET (max, "KSZ.min -> largest allowed key size") +#undef GETSETNAME + { 0 } +}; + +static PyMemberDef keyszset_pymembers[] = { +#define MEMBERSTRUCT keyszset_pyobj + MEMBER(set, T_OBJECT, READONLY, "KSZ.set -> allowed key sizes") +#undef MEMBERSTRUCT + { 0 } +}; + +static PyTypeObject keysz_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.KeySZ", /* @tp_name@ */ + sizeof(keysz_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + _PyObject_Del, /* @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.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + keysz_pymembers, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject keyszany_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.KeySZAny", /* @tp_name@ */ + sizeof(keysz_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + _PyObject_Del, /* @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. This object imposes no constraints on size.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + keyszany_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject keyszrange_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.KeySZRange", /* @tp_name@ */ + sizeof(keyszrange_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + _PyObject_Del, /* @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. This object asserts minimum and maximum (if\n\ +sizes, and requires the key length to be a multiple of some value.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + keyszrange_pymembers, /* @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@ */ + keyszrange_pynew, /* @tp_new@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject keyszset_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.KeySZSet", /* @tp_name@ */ + sizeof(keyszset_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + _PyObject_Del, /* @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. This object requires the key to be one of a\n\ +few listed sizes.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + keyszset_pymembers, /* @tp_members@ */ + keyszset_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Symmetric encryption ----------------------------------------------*/ + +PyTypeObject *gccipher_pytype, *gcipher_pytype; + +CONVFUNC(gccipher, gccipher *, GCCIPHER_CC) +CONVFUNC(gcipher, gcipher *, GCIPHER_C) + +PyObject *gcipher_pywrap(PyObject *cobj, gcipher *c, unsigned f) +{ + 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; + g->f = f; + return ((PyObject *)g); +} + +static PyObject *gcipher_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *kwlist[] = { "k", 0 }; + char *k; + int sz; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz)) + goto end; + if (keysz(sz, GCCIPHER_CC(ty)->keysz) != sz) VALERR("bad key length"); + return (gcipher_pywrap((PyObject *)ty, + GC_INIT(GCCIPHER_CC(ty), k, sz), + f_freeme)); +end: + return (0); +} + +PyObject *gccipher_pywrap(gccipher *cc) +{ + gccipher_pyobj *g = newtype(gccipher_pytype, 0); + g->cc = cc; + g->ty.tp_name = (/*unconst*/ char *)cc->name; + g->ty.tp_basicsize = sizeof(gcipher_pyobj); + g->ty.tp_base = gcipher_pytype; + Py_INCREF(gcipher_pytype); + g->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HEAPTYPE); + g->ty.tp_alloc = PyType_GenericAlloc; + g->ty.tp_free = _PyObject_Del; + g->ty.tp_new = gcipher_pynew; + PyType_Ready(&g->ty); + return ((PyObject *)g); +} + +static void gcipher_pydealloc(PyObject *me) +{ + if (GCIPHER_F(me) & f_freeme) + GC_DESTROY(GCIPHER_C(me)); + Py_DECREF(me->ob_type); + PyObject_DEL(me); +} + +static PyObject *gccget_name(PyObject *me, void *hunoz) + { return (PyString_FromString(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) +{ + char *p; + int sz; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &sz)) return (0); + rc = bytestring_pywrap(0, sz); + GC_ENCRYPT(GCIPHER_C(me), p, PyString_AS_STRING(rc), 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 = PyString_AS_STRING(rc); + memset(p, 0, sz); + GC_ENCRYPT(GCIPHER_C(me), p, p, sz); + return (rc); +} + +static PyObject *gcmeth_decrypt(PyObject *me, PyObject *arg) +{ + char *p; + int sz; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "s#:decrypt", &p, &sz)) return (0); + rc = bytestring_pywrap(0, sz); + GC_DECRYPT(GCIPHER_C(me), p, PyString_AS_STRING(rc), 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 = PyString_AS_STRING(rc); + memset(p, 0, sz); + GC_DECRYPT(GCIPHER_C(me), p, p, sz); + return (rc); +} + +static PyObject *gcmeth_setiv(PyObject *me, PyObject *arg) +{ + char *p; + int sz; + + if (!PyArg_ParseTuple(arg, "s#:setiv", &p, &sz)) goto end; + if (!GC_CLASS(GCIPHER_C(me))->blksz) VALERR("not a block cipher mode"); + if (sz != GC_CLASS(GCIPHER_C(me))->blksz) VALERR("bad IV length"); + GC_SETIV(GCIPHER_C(me), p); + RETURN_ME; +end: + return (0); +} + +static PyObject *gcmeth_bdry(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":bdry")) goto end; + if (!GC_CLASS(GCIPHER_C(me))->blksz) VALERR("not a block cipher mode"); + GC_BDRY(GCIPHER_C(me)); + RETURN_ME; +end: + return (0); +} + +static 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 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)") + METH (bdry, "C.bdry()") +#undef METHNAME + { 0 } +}; + +static PyTypeObject gccipher_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + gccipher_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject gcipher_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + gcipher_pymethods, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Hash functions ----------------------------------------------------*/ + +PyTypeObject *gchash_pytype, *ghash_pytype; + +CONVFUNC(gchash, gchash *, GCHASH_CH) +CONVFUNC(ghash, ghash *, GHASH_H) + +static PyObject *ghash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *kwlist[] = { 0 }; + if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist)) + goto end; + return (ghash_pywrap((PyObject *)ty, GH_INIT(GCHASH_CH(ty)), f_freeme)); +end: + return (0); +} + +PyObject *gchash_pywrap(gchash *ch) +{ + gchash_pyobj *g = newtype(gchash_pytype, 0); + g->ch = ch; + g->ty.tp_name = (/*unconst*/ char *)ch->name; + g->ty.tp_basicsize = sizeof(ghash_pyobj); + g->ty.tp_base = ghash_pytype; + Py_INCREF(ghash_pytype); + g->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HEAPTYPE); + g->ty.tp_alloc = PyType_GenericAlloc; + g->ty.tp_free = _PyObject_Del; + g->ty.tp_new = ghash_pynew; + PyType_Ready(&g->ty); + return ((PyObject *)g); +} + +PyObject *ghash_pywrap(PyObject *cobj, ghash *h, unsigned f) +{ + 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; + g->f = f; + return ((PyObject *)g); +} + +static void ghash_pydealloc(PyObject *me) +{ + if (GHASH_F(me) & f_freeme) + GH_DESTROY(GHASH_H(me)); + Py_DECREF(me->ob_type); + PyObject_DEL(me); +} + +static PyObject *gchget_name(PyObject *me, void *hunoz) + { return (PyString_FromString(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_hash(PyObject *me, PyObject *arg) +{ + char *p; + int sz; + if (!PyArg_ParseTuple(arg, "s#:hash", &p, &sz)) return (0); + GH_HASH(GHASH_H(me), p, sz); + RETURN_ME; +} + +static PyObject *ghmeth_done(PyObject *me, PyObject *arg) +{ + ghash *g; + PyObject *rc; + if (!PyArg_ParseTuple(arg, ":done")) return (0); + g = GH_COPY(GHASH_H(me)); + rc = bytestring_pywrap(0, g->ops->c->hashsz); + GH_DONE(g, PyString_AS_STRING(rc)); + GH_DESTROY(g); + return (rc); +} + +static PyGetSetDef gchash_pygetset[] = { +#define GETSETNAME(op, name) gch##op##_##name + GET (bufsz, "CH.bufsz -> hash buffer size, or zero") + GET (hashsz, "CH.blksz -> hash output size") + GET (name, "CH.name -> name of this kind of hash") +#undef GETSETNAME + { 0 } +}; + +static PyMethodDef ghash_pymethods[] = { +#define METHNAME(name) ghmeth_##name + METH (hash, "H.hash(M)") + METH (done, "H.done() -> HASH") +#undef METHNAME + { 0 } +}; + +static PyTypeObject gchash_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + gchash_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject ghash_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + ghash_pymethods, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Message authentication --------------------------------------------*/ + +PyTypeObject *gcmac_pytype, *gmac_pytype, *gmhash_pytype; + +CONVFUNC(gcmac, gcmac *, GCMAC_CM) +CONVFUNC(gmac, gmac *, GMAC_M) +CONVFUNC(gmhash, ghash *, GHASH_H) + +static PyObject *gmac_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *kwlist[] = { "k", 0 }; + char *k; + int sz; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz)) + goto end; + if (keysz(sz, GCMAC_CM(ty)->keysz) != sz) VALERR("bad key length"); + return (gmac_pywrap((PyObject *)ty, + GM_KEY(GCMAC_CM(ty), k, sz), + f_freeme)); +end: + return (0); +} + +static PyObject *gmhash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *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)); + g->f = f_freeme; + Py_INCREF(ty); + return ((PyObject *)g); +} + +PyObject *gcmac_pywrap(gcmac *cm) +{ + gcmac_pyobj *g = newtype(gcmac_pytype, 0); + g->cm = cm; + g->ty.tp_name = (/*unconst*/ char *)cm->name; + g->ty.tp_basicsize = sizeof(gmac_pyobj); + g->ty.tp_base = gmac_pytype; + Py_INCREF(gmac_pytype); + g->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HEAPTYPE); + g->ty.tp_alloc = PyType_GenericAlloc; + g->ty.tp_free = _PyObject_Del; + g->ty.tp_new = gmac_pynew; + PyType_Ready(&g->ty); + return ((PyObject *)g); +} + +PyObject *gmac_pywrap(PyObject *cobj, gmac *m, unsigned f) +{ + gmac_pyobj *g; + if (!cobj) cobj = gcmac_pywrap((/*unconst*/ gcmac *)GM_CLASS(m)); + else Py_INCREF(cobj); + g = newtype((PyTypeObject *)cobj, 0); + g->nameobj = PyString_FromFormat("%s(keyed)", m->ops->c->name); + g->ty.tp_name = PyString_AS_STRING(g->nameobj); + g->ty.tp_base = gmhash_pytype; + Py_INCREF(gmac_pytype); + g->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HEAPTYPE); + g->ty.tp_alloc = PyType_GenericAlloc; + g->ty.tp_free = _PyObject_Del; + g->ty.tp_new = gmhash_pynew; + PyType_Ready(&g->ty); + g->m = m; + g->f = f; + return ((PyObject *)g); +} + +static void gmac_pydealloc(PyObject *me) +{ + if (GMAC_F(me) & f_freeme) + GM_DESTROY(GMAC_M(me)); + Py_DECREF(me->ob_type); + Py_DECREF(GMAC_NAMEOBJ(me)); + PyType_Type.tp_dealloc(me); +} + +static PyObject *gcmget_name(PyObject *me, void *hunoz) + { return (PyString_FromString(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 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 PyTypeObject gcmac_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + gcmac_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject gmac_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject gmhash_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Main code ---------------------------------------------------------*/ + +void algorithms_pyinit(void) +{ + INITTYPE(keysz, root); + INITTYPE(keyszany, keysz); + INITTYPE(keyszrange, keysz); + INITTYPE(keyszset, keysz); + INITTYPE(gccipher, type); + INITTYPE(gcipher, root); + INITTYPE(gchash, type); + INITTYPE(ghash, root); + INITTYPE(gcmac, type); + INITTYPE(gmac, type); + INITTYPE(gmhash, ghash); +} + +#define GEN(func, base) \ + static PyObject *func(void) \ + { \ + PyObject *d = PyDict_New(); \ + PyObject *o; \ + int i; \ + \ + for (i = 0; g##base##tab[i]; i++) { \ + o = gc##base##_pywrap((/*unconst*/ gc##base *)g##base##tab[i]); \ + PyDict_SetItemString(d, \ + (/*unconst*/ char *)g##base##tab[i]->name, \ + o); \ + Py_DECREF(o); \ + } \ + return (d); \ + } +GEN(gcciphers, cipher) +GEN(gchashes, hash) +GEN(gcmacs, mac) + +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", gcciphers()); + INSERT("GCHash", gchash_pytype); + INSERT("GHash", ghash_pytype); + INSERT("gchashes", d = gchashes()); + sha_pyobj = PyDict_GetItemString(d, "sha"); Py_INCREF(sha_pyobj); + has160_pyobj = PyDict_GetItemString(d, "has160"); Py_INCREF(has160_pyobj); + INSERT("GCMAC", gcmac_pytype); + INSERT("GMAC", gmac_pytype); + INSERT("GMACHash", gmhash_pytype); + INSERT("gcmacs", gcmacs()); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/algorithms.h b/algorithms.h new file mode 100644 index 0000000..26099eb --- /dev/null +++ b/algorithms.h @@ -0,0 +1,276 @@ +/* algorithms.h [generated] */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRPS(DO) \ + DO(DES, des) \ + DO(DESX, desx) \ + DO(DES3, des3) \ + DO(MARS, mars) \ + DO(IDEA, idea) \ + DO(SAFER, safer) \ + DO(SAFERSK, safersk) \ + DO(BLOWFISH, blowfish) \ + DO(TWOFISH, twofish) \ + DO(TEA, tea) \ + DO(XTEA, xtea) \ + DO(RC2, rc2) \ + DO(RC5, rc5) \ + DO(SKIPJACK, skipjack) \ + DO(CAST128, cast128) \ + DO(CAST256, cast256) \ + DO(SQUARE, square) \ + DO(RIJNDAEL, rijndael) \ + DO(RIJNDAEL192, rijndael192) \ + DO(RIJNDAEL256, rijndael256) \ + DO(SERPENT, serpent) \ + DO(NOEKEON, noekeon) \ + /* end */ + +#define RNGS(DO) \ + DO("des-ofb", des_ofbrand) \ + DO("des-counter", des_counterrand) \ + DO("desx-ofb", desx_ofbrand) \ + DO("desx-counter", desx_counterrand) \ + DO("des3-ofb", des3_ofbrand) \ + DO("des3-counter", des3_counterrand) \ + DO("mars-ofb", mars_ofbrand) \ + DO("mars-counter", mars_counterrand) \ + DO("idea-ofb", idea_ofbrand) \ + DO("idea-counter", idea_counterrand) \ + DO("safer-ofb", safer_ofbrand) \ + DO("safer-counter", safer_counterrand) \ + DO("safersk-ofb", safersk_ofbrand) \ + DO("safersk-counter", safersk_counterrand) \ + DO("blowfish-ofb", blowfish_ofbrand) \ + DO("blowfish-counter", blowfish_counterrand) \ + DO("twofish-ofb", twofish_ofbrand) \ + DO("twofish-counter", twofish_counterrand) \ + DO("tea-ofb", tea_ofbrand) \ + DO("tea-counter", tea_counterrand) \ + DO("xtea-ofb", xtea_ofbrand) \ + DO("xtea-counter", xtea_counterrand) \ + DO("rc2-ofb", rc2_ofbrand) \ + DO("rc2-counter", rc2_counterrand) \ + DO("rc5-ofb", rc5_ofbrand) \ + DO("rc5-counter", rc5_counterrand) \ + DO("skipjack-ofb", skipjack_ofbrand) \ + DO("skipjack-counter", skipjack_counterrand) \ + DO("cast128-ofb", cast128_ofbrand) \ + DO("cast128-counter", cast128_counterrand) \ + DO("cast256-ofb", cast256_ofbrand) \ + DO("cast256-counter", cast256_counterrand) \ + DO("square-ofb", square_ofbrand) \ + DO("square-counter", square_counterrand) \ + DO("rijndael-ofb", rijndael_ofbrand) \ + DO("rijndael-counter", rijndael_counterrand) \ + DO("rijndael192-ofb", rijndael192_ofbrand) \ + DO("rijndael192-counter", rijndael192_counterrand) \ + DO("rijndael256-ofb", rijndael256_ofbrand) \ + DO("rijndael256-counter", rijndael256_counterrand) \ + DO("serpent-ofb", serpent_ofbrand) \ + DO("serpent-counter", serpent_counterrand) \ + DO("noekeon-ofb", noekeon_ofbrand) \ + DO("noekeon-counter", noekeon_counterrand) \ + DO("md2-mgf", md2_mgfrand) \ + DO("md4-mgf", md4_mgfrand) \ + DO("md5-mgf", md5_mgfrand) \ + DO("tiger-mgf", tiger_mgfrand) \ + DO("has160-mgf", has160_mgfrand) \ + DO("sha-mgf", sha_mgfrand) \ + DO("sha224-mgf", sha224_mgfrand) \ + DO("sha256-mgf", sha256_mgfrand) \ + DO("sha384-mgf", sha384_mgfrand) \ + DO("sha512-mgf", sha512_mgfrand) \ + DO("rmd128-mgf", rmd128_mgfrand) \ + DO("rmd160-mgf", rmd160_mgfrand) \ + DO("rmd256-mgf", rmd256_mgfrand) \ + DO("rmd320-mgf", rmd320_mgfrand) \ + DO("whirlpool-mgf", whirlpool_mgfrand) \ + DO("whirlpool256-mgf", whirlpool256_mgfrand) \ + DO("rc4", rc4_rand) \ + DO("seal", seal_randkludge) \ + /* end */ + diff --git a/algorithms.py b/algorithms.py new file mode 100644 index 0000000..cc209c6 --- /dev/null +++ b/algorithms.py @@ -0,0 +1,72 @@ +## -*-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 +'''.split() +streamciphers = ''' +rc4 seal +'''.split() +hashes = ''' +md2 md4 md5 tiger has160 +sha sha224 sha256 sha384 sha512 +rmd128 rmd160 rmd256 rmd320 +whirlpool whirlpool256 +'''.split() +hmodes = ''' +mgf hmac +'''.split() + +print '/* algorithms.h [generated] */' +print + +for i in prps: + print '#include ' % i + for j in pmodes: + print '#include ' % (i, j) +for i in streamciphers: + print '#include ' % i +print +for i in hashes: + print '#include ' % i + for j in hmodes: + print '#include ' % (i, j) +print + +print '#define PRPS(DO) \\' +for i in prps: + print ' DO(%s, %s) \\' % (i.upper(), i) +print ' /* end */' +print + +print '#define RNGS(DO) \\' +for i in (cross(prps, ['ofb', 'counter']) + + cross(hashes, 'mgf')): + print ' DO("%(prim)s-%(mode)s", %(prim)s_%(mode)srand) \\' % \ + {'prim': i[0], 'mode': i[1]} +print ' DO("rc4", rc4_rand) \\' +print ' DO("seal", seal_randkludge) \\' +print ' /* end */' +print diff --git a/bytestring.c b/bytestring.c new file mode 100644 index 0000000..c649125 --- /dev/null +++ b/bytestring.c @@ -0,0 +1,183 @@ +/* -*-c-*- + * + * $Id$ + * + * 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 ---------------------------------------------------------*/ + +PyTypeObject *bytestring_pytype; + +PyObject *bytestring_pywrap(const void *p, size_t n) +{ + PyStringObject *x = PyObject_NewVar(PyStringObject, bytestring_pytype, n); + if (p) memcpy(x->ob_sval, p, n); + x->ob_sval[n] = 0; +#ifdef CACHE_HASH + x->ob_shash = -1; +#endif +#ifdef INTERN_STRINGS + x->ob_sinterned = NULL; +#endif + return ((PyObject *)x); +} + +PyObject *bytestring_pywrapbuf(buf *b) + { return bytestring_pywrap(BCUR(b), BLEFT(b)); } + +#define BINOP(name, op) \ + static PyObject *bytestring_py##name(PyObject *x, PyObject *y) { \ + const void *xv, *yv; \ + const unsigned char *xp, *yp; \ + unsigned char *zp; \ + int xsz, ysz; \ + int i; \ + PyObject *rc = 0; \ + if (PyObject_AsReadBuffer(x, &xv, &xsz) || \ + PyObject_AsReadBuffer(y, &yv, &ysz)) \ + goto end; \ + if (xsz != ysz) VALERR("length mismatch"); \ + rc = bytestring_pywrap(0, xsz); \ + xp = xv; yp = yv; zp = (unsigned char *)PyString_AS_STRING(rc); \ + for (i = xsz; 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) { \ + const void *xv; \ + const unsigned char *xp; \ + unsigned char *zp; \ + int xsz; \ + int i; \ + PyObject *rc = 0; \ + if (PyObject_AsReadBuffer(x, &xv, &xsz)) goto end; \ + rc = bytestring_pywrap(0, xsz); \ + xp = xv; zp = (unsigned char *)PyString_AS_STRING(rc); \ + for (i = xsz; i > 0; i--) *zp++ = op *xp++; \ + end: \ + return (rc); \ + } +UNOP(not, ~) + +static PyNumberMethods bytestring_pynumber = { + 0, /* @nb_add@ */ + 0, /* @nb_subtract@ */ + 0, /* @nb_multiply@ */ + 0, /* @nb_divide@ */ + 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 PyBufferProcs bytestring_pybuffer; + +static PyTypeObject bytestring_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &bytestring_pynumber, /* @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@ */ + &bytestring_pybuffer, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ +"Byte string class.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + 0, /* @tp_new@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Initialization ----------------------------------------------------*/ + +#define string_pytype &PyString_Type +void bytestring_pyinit(void) +{ + INITTYPE(bytestring, string); +} + +void bytestring_pyinsert(PyObject *mod) +{ + INSERT("ByteString", bytestring_pytype); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/catacomb-python.h b/catacomb-python.h new file mode 100644 index 0000000..c93ef07 --- /dev/null +++ b/catacomb-python.h @@ -0,0 +1,520 @@ +/* -*-c-*- + * + * $Id$ + * + * 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 +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +/*----- Utility macros ----------------------------------------------------*/ + +#define RETURN_OBJ(obj) do { Py_INCREF(obj); return (obj); } while (0) +#define RETURN_NONE RETURN_OBJ(Py_None) +#define RETURN_NOTIMPL RETURN_OBJ(Py_NotImplemented) +#define RETURN_TRUE RETURN_OBJ(Py_True) +#define RETURN_FALSE RETURN_OBJ(Py_False) +#define RETURN_ME RETURN_OBJ(me) + +#define EXCERR(exc, str) do { \ + PyErr_SetString(exc, str); \ + goto end; \ +} while (0) +#define VALERR(str) EXCERR(PyExc_ValueError, str) +#define TYERR(str) EXCERR(PyExc_TypeError, str) +#define ZDIVERR(str) EXCERR(PyExc_ZeroDivisionError, str) +#define SYNERR(str) EXCERR(PyExc_SyntaxError, str) +#define SYSERR(str) EXCERR(PyExc_SystemError, str) +#define OSERR(name) do { \ + PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); \ + goto end; \ +} while (0) +#define PGENERR do { pgenerr(); goto end; } while (0) + +#define CONVFUNC(ty, cty, ext) \ + int conv##ty(PyObject *o, void *p) \ + { \ + if (!PyObject_TypeCheck(o, ty##_pytype)) \ + TYERR("wanted a " #ty); \ + *(cty *)p = ext(o); \ + return (1); \ + end: \ + return (0); \ + } + +#define root_pytype 0 +#define type_pytype &PyType_Type +#define INITTYPE(ty, base) do { \ + ty##_pytype_skel.tp_base = base##_pytype; \ + ty##_pytype = inittype(&ty##_pytype_skel); \ +} while (0) + +#define INSERT(name, ob) do { \ + PyObject *_o = (PyObject *)(ob); \ + Py_INCREF(_o); \ + PyModule_AddObject(mod, name, _o); \ +} while (0) + +#define METH(func, doc) \ + { #func, METHNAME(func), METH_VARARGS, doc }, +#define KWMETH(func, doc) \ + { #func, (PyCFunction)METHNAME(func), \ + METH_VARARGS | METH_KEYWORDS, doc }, + +#define GET(func, doc) \ + { #func, GETSETNAME(get, func), 0, doc }, +#define GETSET(func, doc) \ + { #func, GETSETNAME(get, func), GETSETNAME(set, func), doc }, + +#define MEMBER(name, ty, f, doc) \ + { #name, ty, offsetof(MEMBERSTRUCT, name), f, doc }, + +#define MODULES(DO) \ + DO(bytestring) \ + DO(rand) DO(algorithms) DO(pubkey) DO(pgen) \ + DO(mp) DO(field) DO(ec) DO(group) \ + DO(passphrase) +#define DOMODINIT(m) m##_pyinit(); +#define DOMODINSERT(m) m##_pyinsert(mod); +#define INIT_MODULES do { MODULES(DOMODINIT) } while (0) +#define INSERT_MODULES do { MODULES(DOMODINSERT) } while (0) + +#define DO(m) \ + extern void m##_pyinit(void); \ + extern void m##_pyinsert(PyObject *); +MODULES(DO) +#undef DO + +/*----- Bytestrings -------------------------------------------------------*/ + +PyTypeObject *bytestring_pyobj; +PyObject *bytestring_pywrap(const void *, size_t); +PyObject *bytestring_pywrapbuf(buf *); + +/*----- 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(PyLongObject *); +extern PyLongObject *mp_topylong(mp *); +extern mp *tomp(PyObject *); +extern mp *getmp(PyObject *); +extern int convmp(PyObject *, void *); +extern mp *getgf(PyObject *); +extern int convgf(PyObject *, void *); +extern PyObject *mp_pywrap(mp *); +extern PyObject *gf_pywrap(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 *); + +/*----- Abstract fields ---------------------------------------------------*/ + +typedef struct field_pyobj { + PyTypeObject ty; + field *f; +} field_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 *)(o)->ob_type) +#define FE_X(o) (((fe_pyobj *)(o))->x) +extern PyObject *fe_pywrap(PyObject *, mp *); +extern mp *getfe(field *, PyObject *); + +typedef struct fe_pyobj { + PyObject_HEAD + field *f; + PyObject *fobj; /* to keep it alive */ + mp *x; +} fe_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 *); + +/*----- Elliptic curves ---------------------------------------------------*/ + +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 *)(o)->ob_type) +#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 convec(PyObject *, void *); + +typedef struct eccurve_pyobj { + PyTypeObject 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 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 fginfo_pyobj { + PyObject_HEAD + gprime_param dp; +} fginfo_pyobj; + +PyTypeObject *fginfo_pytype, *dhinfo_pytype, *bindhinfo_pytype; +#define FGINFO_DP(fg) (&((fginfo_pyobj *)(fg))->dp) +PyObject *fginfo_pywrap(gprime_param *, PyTypeObject *); + +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 *)(group_pyobj *)(o)->ob_type) +extern PyObject *ge_pywrap(PyObject *, ge *); + +typedef struct group_pyobj { + PyTypeObject ty; + group *g; +} group_pyobj; + +extern PyTypeObject *group_pytype; +extern PyTypeObject *primegroup_pytype, *bingroup_pytype, *ecgroup_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, *truerand_pytype; +extern PyTypeObject *lcrand_pytype,* fibrand_pytype; +extern PyTypeObject *dsarand_pytype, *bbs_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 *); + +/*----- Key sizes ---------------------------------------------------------*/ + +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) +extern PyObject *keysz_pywrap(const octet *); + +/*----- Symmetric cryptography --------------------------------------------*/ + +typedef struct gccipher_pyobj { + PyTypeObject ty; + gccipher *cc; +} gccipher_pyobj; + +extern PyTypeObject *gccipher_pytype; +#define GCCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gccipher_pytype) +#define GCCIPHER_CC(o) (((gccipher_pyobj *)(o))->cc) +#define GCCIPHER_F(o) (((gccipher_pyobj *)(o))->f) +extern PyObject *gccipher_pywrap(gccipher *); +extern int convgccipher(PyObject *, void *); +extern int convgcipher(PyObject *, void *); + +typedef struct gcipher_pyobj { + PyObject_HEAD + unsigned f; + gcipher *c; +} gcipher_pyobj; + +extern PyTypeObject *gcipher_pytype; +#define GCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gcipher_pytype) +#define GCIPHER_C(o) (((gcipher_pyobj *)(o))->c) +#define GCIPHER_F(o) (((gcipher_pyobj *)(o))->f) +extern PyObject *gcipher_pywrap(PyObject *, gcipher *, unsigned); +extern int convgcipher(PyObject *, void *); + +typedef struct gchash_pyobj { + PyTypeObject ty; + gchash *ch; +} gchash_pyobj; + +extern PyTypeObject *gchash_pytype; +#define GCHASH_PYCHECK(o) PyObject_TypeCheck((o), gchash_pytype) +#define GCHASH_CH(o) (((gchash_pyobj *)(o))->ch) +#define GCHASH_F(o) (((gchash_pyobj *)(o))->f) +extern PyObject *gchash_pywrap(gchash *); +extern int convgchash(PyObject *, void *); + +typedef struct ghash_pyobj { + PyObject_HEAD + unsigned f; + ghash *h; +} ghash_pyobj; + +extern PyTypeObject *ghash_pytype, *gmhash_pytype; +extern PyObject *sha_pyobj, *has160_pyobj; +#define GHASH_PYCHECK(o) PyObject_TypeCheck((o), ghash_pytype) +#define GHASH_H(o) (((ghash_pyobj *)(o))->h) +#define GHASH_F(o) (((ghash_pyobj *)(o))->f) +extern PyObject *ghash_pywrap(PyObject *, ghash *, unsigned); +extern int convghash(PyObject *, void *); +extern int convgmhash(PyObject *, void *); + +typedef struct gcmac_pyobj { + PyTypeObject ty; + gcmac *cm; +} gcmac_pyobj; + +extern PyTypeObject *gcmac_pytype; +#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) +extern PyObject *gcmac_pywrap(gcmac *); +extern int convgcmac(PyObject *, void *); + +typedef struct gmac_pyobj { + PyTypeObject ty; + unsigned f; + gmac *m; + PyObject *nameobj; +} 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_NAMEOBJ(o) (((gmac_pyobj *)(o))->nameobj) +#define GMAC_F(o) (((gmac_pyobj *)(o))->f) +extern PyObject *gmac_pywrap(PyObject *, gmac *, unsigned); +extern int convgmac(PyObject *, void *); + +/*----- Public key crypto -------------------------------------------------*/ + +/*----- 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) + +extern int convpgev(PyObject *, void *); +extern void droppgev(pgev *); +extern void pgenerr(void); + +/*----- Core utility functions --------------------------------------------*/ + +extern PyObject *mexp_common(PyObject *, PyObject *, size_t, + PyObject *(*id)(PyObject *), + int (*fill)(void *, PyObject *, + PyObject *, PyObject *), + PyObject *(*exp)(PyObject *, void *, int), + void (*drop)(void *)); + +extern int convulong(PyObject *, void *); +extern int convu32(PyObject *, void *); +extern int convmpw(PyObject *, void *); +extern int convuint(PyObject *, void *); +extern int convszt(PyObject *, void *); +extern int convbool(PyObject *, void *); +extern PyObject *abstract_pynew(PyTypeObject *, PyObject *, PyObject *); +extern PyObject *getbool(int); +extern PyObject *getu32(uint32); +extern void *newtype(PyTypeObject *, const PyTypeObject *); +extern PyTypeObject *inittype(PyTypeObject *); +extern void addmethods(const PyMethodDef *); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/catacomb.c b/catacomb.c new file mode 100644 index 0000000..742ce5f --- /dev/null +++ b/catacomb.c @@ -0,0 +1,279 @@ +/* -*-c-*- + * + * $Id$ + * + * 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 ---------------------------------------------------------*/ + +static void setconstants(PyObject *mod) +{ + static const struct { const char *name; unsigned long value; } consts[] = { +#define C(x) { #x, x } + C(FTY_PRIME), C(FTY_BINARY), + C(PGEN_PASS), C(PGEN_FAIL), C(PGEN_BEGIN), C(PGEN_TRY), C(PGEN_DONE), + C(PGEN_ABORT), + C(MPW_MAX), + C(PMODE_READ), C(PMODE_VERIFY), +#undef C + { 0 } + }; + int i; + PyObject *x; + + for (i = 0; consts[i].name; i++) { + if (consts[i].value > LONG_MAX) + x = PyLong_FromUnsignedLong(consts[i].value); + else + x = PyInt_FromLong(consts[i].value); + PyModule_AddObject(mod, (/*unconst*/ char *)consts[i].name, x); + } +} + +PyObject *getu32(uint32 w) +{ + if (w <= 0x7fffffff) + return (PyInt_FromLong(w)); + else + return (PyLong_FromUnsignedLong(w)); +} + +PyObject *getbool(int b) +{ + if (b) RETURN_TRUE; + else RETURN_FALSE; +} + +PyObject *abstract_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + PyErr_SetString(PyExc_TypeError, "can't instantiate this class"); + return (0); +} + +int convulong(PyObject *o, void *pp) +{ + long i; + unsigned long *p = pp; + PyObject *t; + + if (PyInt_Check(o)) { + i = PyInt_AS_LONG(o); + if (i < 0) TYERR("must be nonnegative"); + *p = i; + } else { + if ((t = PyNumber_Long(o)) == 0) goto end; + *p = PyLong_AsUnsignedLong(t); + Py_DECREF(t); + if (PyErr_Occurred()) goto end; + } + return (1); +end: + return (0); +} + +int convu32(PyObject *o, void *pp) +{ + unsigned long u; + uint32 *p = pp; + + if (!convulong(o, &u)) goto end; + if (u > 0xffffffff) TYERR("out of range"); + *p = u; + return (1); +end: + return (0); +} + +int convuint(PyObject *o, void *pp) +{ + unsigned long u; + unsigned *p = pp; + + if (!convulong(o, &u)) goto end; + if (u > UINT_MAX) TYERR("out of range"); + *p = u; + return (1); +end: + return (0); +} + +int convmpw(PyObject *o, void *pp) +{ + unsigned long u; + unsigned *p = pp; + + if (!convulong(o, &u)) goto end; + if (u > MPW_MAX) TYERR("out of range"); + *p = u; + return (1); +end: + return (0); +} + +int convszt(PyObject *o, void *pp) +{ + unsigned long u; + size_t *p = pp; + + if (!convulong(o, &u)) goto end; + if (u > ~(size_t)0) TYERR("out of range"); + *p = u; + return (1); +end: + return (0); +} + +int convbool(PyObject *o, void *pp) +{ + *(int *)pp = PyObject_IsTrue(o); + return (1); +} + +PyObject *mexp_common(PyObject *me, PyObject *arg, + size_t efsz, + PyObject *(*id)(PyObject *), + int (*fill)(void *, PyObject *, + PyObject *, PyObject *), + PyObject *(*exp)(PyObject *, void *, int), + void (*drop)(void *)) +{ + int i = 0, j, n, flat; + PyObject *qq, *x, *y, *z = 0; + char *v = 0, *vv; + + if (PyTuple_Size(arg) == 1) + arg = PyTuple_GetItem(arg, 0); + Py_INCREF(arg); + if (!PySequence_Check(arg)) TYERR("not a sequence"); + n = PySequence_Size(arg); if (!n) { z = id(me); goto end; } + x = PySequence_GetItem(arg, 0); + if (PySequence_Check(x)) + flat = 0; + else { + if (n % 2) VALERR("must have even number of arguments"); + n /= 2; + flat = 1; + } + Py_DECREF(x); + + v = xmalloc(n * efsz); + for (i = j = 0, vv = v; i < n; i++, vv += efsz) { + if (flat) { + x = PySequence_GetItem(arg, j++); + y = PySequence_GetItem(arg, j++); + } else { + qq = PySequence_GetItem(arg, j++); + if (!qq) goto end; + if (!PySequence_Check(qq) || PySequence_Size(qq) != 2) { + Py_DECREF(qq); + TYERR("want a sequence of pairs"); + } + x = PySequence_GetItem(qq, 0); + y = PySequence_GetItem(qq, 1); + Py_DECREF(qq); + } + if (!x || !y) goto end; + if (fill(vv, me, x, y)) { + Py_DECREF(x); + Py_DECREF(y); + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, "type mismatch"); + goto end; + } + Py_DECREF(x); + Py_DECREF(y); + } + z = exp(me, v, n); + +end: + if (v) { + for (j = 0, vv = v; j < i; j++, vv += efsz) + drop(vv); + xfree(v); + } + Py_DECREF(arg); + return (z); +} + +DA_DECL(method_v, PyMethodDef); +static method_v global_pymethods = DA_INIT; +void addmethods(const PyMethodDef *m) +{ + size_t n; + + for (n = 0; m[n].ml_name; n++); + DA_ENSURE(&global_pymethods, n); + memcpy(DA(&global_pymethods) + DA_LEN(&global_pymethods), + m, n * sizeof(*m)); + DA_EXTEND(&global_pymethods, n); +} + +static const PyTypeObject emptytype = { 0 }; + +void *newtype(PyTypeObject *metaty, const PyTypeObject *skel) +{ + PyTypeObject *ty = (PyTypeObject *)_PyObject_GC_Malloc(metaty, 0); + if (!skel) skel = &emptytype; + memcpy(ty, skel, sizeof(*skel)); + if (ty->tp_base) Py_INCREF(ty->tp_base); + PyObject_INIT(ty, metaty); + Py_INCREF(metaty); + return (ty); +} + +static PyObject *smallprimes(void) +{ + PyObject *v = PyList_New(NPRIME); + int i; + + for (i = 0; i < NPRIME; i++) + PyList_SetItem(v, i, PyInt_FromLong(primetab[i])); + return (v); +} + +PyTypeObject *inittype(PyTypeObject *tyskel) +{ + PyTypeObject *ty = newtype(&PyType_Type, tyskel); + ty->tp_flags |= Py_TPFLAGS_HEAPTYPE; + PyType_Ready(ty); + return (ty); +} + +void init_base(void) { + static const PyMethodDef mzero = { 0 }; + PyObject *mod; + INIT_MODULES; + DA_PUSH(&global_pymethods, mzero); + mod = Py_InitModule("catacomb._base", DA(&global_pymethods)); + INSERT_MODULES; + INSERT("smallprimes", smallprimes()); + setconstants(mod); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/catacomb/__init__.py b/catacomb/__init__.py new file mode 100644 index 0000000..b7f40fa --- /dev/null +++ b/catacomb/__init__.py @@ -0,0 +1,472 @@ +# -*-python-*- +# +# $Id$ +# +# 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. + +import _base +import types as _types +from binascii import hexlify as _hexify, unhexlify as _unhexify + +def _init(): + d = globals() + b = _base.__dict__; + for i in b: + if i[0] != '_': + d[i] = b[i]; + for i in ['MP', 'GF', 'Field', + 'ECPt', 'ECPtCurve', 'ECCurve', 'ECInfo', + 'DHInfo', 'BinDHInfo', 'RSAPriv', 'PrimeFilter', 'RabinMiller', + 'Group', 'GE']: + c = d[i] + pre = '_' + i + '_' + plen = len(pre) + for j in b: + if j[:plen] == pre: + setattr(c, j[plen:], classmethod(b[j])) + for i in [gcciphers, gchashes, gcmacs]: + for j in i: + c = i[j] + d[c.name.replace('-', '_')] = c +_init() + +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) + +class _tmp: + def fromhex(x): + return ByteString(_unhexify(x)) + fromhex = staticmethod(fromhex) + def __hex__(me): + return _hexify(me) + def __repr__(me): + return 'bytes(%r)' % hex(me) +_augment(ByteString, _tmp) +bytes = ByteString.fromhex + +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 factorial(x): + 'factorial(X) -> X!' + if x < 0: raise ValueError, 'factorial argument must be > 0' + return MP.product(xrange(1, x + 1)) + factorial = staticmethod(factorial) +_augment(MP, _tmp) + +def _checkend(r): + x, rest = r + if rest != '': + raise SyntaxError, 'junk at end of string' + return x + +class _tmp: + def reduce(x): return GReduce(x) +_augment(GF, _tmp) + +class _tmp: + def fromstring(str): return _checkend(Field.parse(str)) + fromstring = staticmethod(fromstring) +_augment(Field, _tmp) + +class _tmp: + def __repr__(me): return '%s(%sL)' % (type(me).__name__, me.p) + def ec(me, a, b): return ECPrimeProjCurve(me, a, b) +_augment(PrimeField, _tmp) + +class _tmp: + def __repr__(me): return '%s(%sL)' % (type(me).__name__, hex(me.p)) + def ec(me, a, b): return ECBinProjCurve(me, a, b) +_augment(BinField, _tmp) + +class _tmp: + def __str__(me): return str(me.value) + def __repr__(me): return '%s(%s)' % (repr(me.field), repr(me.value)) +_augment(FE, _tmp) + +class _groupmap (object): + def __init__(me, map, nth): + me.map = map + me.nth = nth + me.i = [None] * (max(map.values()) + 1) + def __repr__(me): + return '{%s}' % ', '.join(['%r: %r' % (k, me[k]) for k in me]) + def __contains__(me, k): + return k in me.map + def __getitem__(me, k): + i = me.map[k] + if me.i[i] is None: + me.i[i] = me.nth(i) + return me.i[i] + def __setitem__(me, k, v): + raise TypeError, "immutable object" + def __iter__(me): + return iter(me.map) +eccurves = _groupmap(_base._eccurves, ECInfo._curven) +primegroups = _groupmap(_base._pgroups, DHInfo._groupn) +bingroups = _groupmap(_base._bingroups, BinDHInfo._groupn) + +class _tmp: + def __repr__(me): + return '%s(%r, %s, %s)' % (type(me).__name__, me.field, me.a, me.b) + def frombuf(me, s): + return ecpt.frombuf(me, s) + def fromraw(me, s): + return ecpt.fromraw(me, s) + def pt(me, *args): + return ECPt(me, *args) +_augment(ECCurve, _tmp) + +class _tmp: + def __repr__(me): + if not me: return 'ECPt()' + return 'ECPt(%s, %s)' % (me.ix, me.iy) + def __str__(me): + if not me: return 'inf' + return '(%s, %s)' % (me.ix, me.iy) +_augment(ECPt, _tmp) + +class _tmp: + def __repr__(me): + return 'ECInfo(curve = %r, G = %r, r = %s, h = %s)' % \ + (me.curve, me.G, me.r, me.h) + 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) +_augment(ECPtCurve, _tmp) + +class _tmp: + def __repr__(me): return 'KeySZAny(%d)' % me.default + def check(me, sz): return True + def best(me, sz): return sz +_augment(KeySZAny, _tmp) + +class _tmp: + def __repr__(me): + return 'KeySZRange(%d, %d, %d, %d)' % \ + (me.default, me.min, me.max, me.mod) + 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 sz > me.max: return me.max + else: return sz - (sz % me.mod) +_augment(KeySZRange, _tmp) + +class _tmp: + def __repr__(me): return 'KeySZSet(%d, %s)' % (me.default, me.set) + 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 +_augment(KeySZSet, _tmp) + +class _tmp: + def __repr__(me): + return '%s(p = %s, r = %s, g = %s)' % \ + (type(me).__name__, me.p, me.r, me.g) +_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)' % (type(me).__name__, me.info) +_augment(Group, _tmp) + +class _tmp: + def __repr__(me): + return '%r(%r)' % (me.group, str(me)) +_augment(GE, _tmp) + +class PKCS1Crypt(object): + def __init__(me, ep = '', 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 = '', 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 = '', 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 +_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)) +_augment(RSAPriv, _tmp) + + +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 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 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 + h = pgen(rng.mp(hbits, 1), name + ' [h]', + PrimeGenStepper(2), PrimeGenTester(), + event, nsteps, RabinMiller.iters(hbits)) + q = pgen(rng.mp(qbits, 1), name, SimulStepper(2 * h, 1, 2), + SimulTester(2 * h, 1), event, nsteps, RabinMiller.iters(qbits)) + p = 2 * q * h + 1 + return p, q, h + +#----- That's all, folks ---------------------------------------------------- diff --git a/ec.c b/ec.c new file mode 100644 index 0000000..8fd32ca --- /dev/null +++ b/ec.c @@ -0,0 +1,1561 @@ +/* -*-c-*- + * + * $Id$ + * + * 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") == 0) + c = ec_prime(f, a, b); + else if (strcmp(EC_NAME(c), "primeproj") == 0) + c = ec_primeproj(f, a, b); + else if (strcmp(EC_NAME(c), "bin") == 0) + c = ec_bin(f, a, b); + else if (strcmp(EC_NAME(c), "binproj") == 0) + 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)); + PyObject_DEL(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) || (xx = tomp(x)) == 0) RETURN_NOTIMPL; + ec_imul(ECPT_C(y), &zz, ECPT_P(y), xx); + return (ecpt_pywrap(ECPT_COBJ(y), &zz)); +} + +static long ecpt_pyhash(PyObject *me) +{ + long i; + ec p = EC_INIT; + + EC_OUT(ECPT_C(me), &p, ECPT_P(me)); + i = 0xe0fdd039; /* random perturbance */ + if (p.x) i ^= mp_tolong(p.x); + if (p.y) i ^= mp_tolong(p.y); + if (i == -1) i = -2; + EC_DESTROY(&p); + return (i); +} + +static PyObject *ecpt_pyrichcompare(PyObject *x, PyObject *y, int op) +{ + ec_curve *c; + PyObject *cobj; + ec p = EC_INIT, q = EC_INIT; + int b; + PyObject *rc = 0; + + if (ecbinop(x, y, &c, &cobj, &p, &q)) RETURN_NOTIMPL; + EC_OUT(c, &p, &p); + EC_OUT(c, &q, &q); + 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, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":oncurvep")) return (0); + return (getbool(!ec_check(ECPT_C(me), ECPT_P(me)))); +} + +static PyObject *epmeth_dbl(PyObject *me, PyObject *arg) +{ + ec p = EC_INIT; + if (!PyArg_ParseTuple(arg, ":dbl")) return (0); + EC_DBL(ECPT_C(me), &p, ECPT_P(me)); + return (ecpt_pywrap(ECPT_COBJ(me), &p)); +} + +static PyObject *epmeth_tobuf(PyObject *me, PyObject *arg) +{ + buf b; + ec p = EC_INIT; + PyObject *rc; + size_t n; + + if (!PyArg_ParseTuple(arg, ":tobuf")) return (0); + getecptout(&p, me); + if (EC_ATINF(&p)) + n = 2; + else + n = mp_octets(p.x) + mp_octets(p.y) + 4; + rc = bytestring_pywrap(0, n); + buf_init(&b, PyString_AS_STRING(rc), n); + buf_putec(&b, &p); + assert(BOK(&b)); + _PyString_Resize(&rc, BLEN(&b)); + EC_DESTROY(&p); + return (rc); +} + +static PyObject *epmeth_toraw(PyObject *me, PyObject *arg) +{ + buf b; + PyObject *rc; + char *p; + ec_curve *c = ECPT_C(me); + ec pp = EC_INIT; + int len; + + if (!PyArg_ParseTuple(arg, ":toraw")) return (0); + len = c->f->noctets * 2 + 1; + rc = bytestring_pywrap(0, len); + p = PyString_AS_STRING(rc); + buf_init(&b, p, len); + EC_OUT(c, &pp, ECPT_P(me)); + ec_putraw(c, &b, &pp); + EC_DESTROY(&pp); + _PyString_Resize(&rc, BLEN(&b)); + return (rc); +} + +static PyObject *epget_curve(PyObject *me, void *hunoz) + { RETURN_OBJ(ECPT_COBJ(me)); } + +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) + 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; + mp *xx = 0; + const void *q; + int n; + qd_parse qd; + + Py_XINCREF(x); + if (!x || x == Py_None) + /*cool*/; + else if (ECPT_PYCHECK(x)) { + getecptout(p, x); + goto fix; + } else if (PyString_Check(x)) { + if (PyObject_AsReadBuffer(x, &q, 0)) + goto end; + qd.p = q; + qd.e = 0; + if (!ec_ptparse(&qd, p)) + SYNERR(qd.e); + 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 (PySequence_Check(x)) { + t = x; x = 0; + n = PySequence_Size(t); + if (n != 2 && (n != 3 || !c)) + TYERR("want sequence of two or three items"); + if ((x = PySequence_GetItem(t, 0)) == 0 || + (y = PySequence_GetItem(t, 1)) == 0 || + (n == 3 && (z = PySequence_GetItem(t, 2)) == 0)) + goto end; + rc = (n == 2) ? ecptxl_2(c, p, x, y) : ecptxl_3(c, p, x, y, z); + } else + TYERR("can't convert to curve point"); + goto ok; + +fix: + if (c) EC_IN(c, p, p); +ok: + rc = 0; +end: + Py_XDECREF(x); Py_XDECREF(y); Py_XDECREF(z); Py_XDECREF(t); + 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 int ecpt_fromobjects(PyObject *cc, ec *p, + PyObject *x, PyObject *y, PyObject *z) +{ + ec_curve *c = 0; + + if (cc && PyType_IsSubtype((PyTypeObject *)cc, ecptcurve_pytype)) + c = ECCURVE_C(cc); + return (ecptxl(c, p, x, y, z)); +} + +static PyObject *ecptnc_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + PyObject *x = 0, *y = 0, *z = 0; + ec p = EC_INIT; + char *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: + EC_DESTROY(&p); + 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)) goto end; + rc = PyInt_FromLong(l); +end: + EC_DESTROY(&p); + return (rc); +} + +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 = (PyObject *)mp_topylong(p.x); +end: + EC_DESTROY(&p); + return (rc); +} + +static PyObject *ecpt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + PyObject *x = 0, *y = 0, *z = 0; + ec p = EC_INIT; + char *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: + EC_DESTROY(&p); + return (0); +} + +static 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 PyMethodDef ecptnc_pymethods[] = { +#define METHNAME(func) epmeth_##func + METH (tobuf, "X.tobuf() -> BIN") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods ecpt_pynumber = { + 0, /* @nb_add@ */ + 0, /* @nb_subtract@ */ + 0, /* @nb_multiply@ */ + 0, /* @nb_divide@ */ + 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@ */ + 0, /* @nb_coerce@ */ + ecpt_pyint, /* @nb_int@ */ + ecpt_pylong, /* @nb_long@ */ + 0, /* @nb_float@ */ + 0, /* @nb_oct@ */ + 0, /* @nb_hex@ */ + + 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 PyTypeObject ecpt_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &ecpt_pynumber, /* @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@ */ +"Elliptic curve points, not associated with any curve.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + ecpt_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + ecptnc_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + ecptnc_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyGetSetDef ecpt_pygetset[] = { +#define GETSETNAME(op, name) ep##op##_##name + GET (curve, "P.curve -> elliptic curve containing P") + 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 PyMethodDef ecpt_pymethods[] = { +#define METHNAME(func) epmeth_##func + METH (toraw, "X.toraw() -> BIN") + METH (dbl, "X.dbl() -> X + X") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods ecptcurve_pynumber = { + ecpt_pyadd, /* @nb_add@ */ + ecpt_pysub, /* @nb_subtract@ */ + ecpt_pymul, /* @nb_multiply@ */ + 0, /* @nb_divide@ */ + 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@ */ + 0, /* @nb_coerce@ */ + 0, /* @nb_int@ */ + 0, /* @nb_long@ */ + 0, /* @nb_float@ */ + 0, /* @nb_oct@ */ + 0, /* @nb_hex@ */ + + 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 PyTypeObject ecptcurve_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &ecptcurve_pynumber, /* @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_iternexr@ */ + ecpt_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + ecpt_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Elliptic curves themselves ----------------------------------------*/ + +static PyObject *eccurve_pyrichcompare(PyObject *x, PyObject *y, int op) +{ + int b = ec_samep(ECCURVE_C(x), ECCURVE_C(y)); + switch (op) { + case Py_EQ: break; + case Py_NE: b = !b; + 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; + + if (getecpt(ECCURVE_C(me), &f->base, x) || + (f->exp = getmp(m)) == 0) + return (-1); + f->base = *ECPT_P(x); + return (0); +} + +static PyObject *ecmmul_exp(PyObject *me, void *pp, int 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 PyObject *meth__ECPtCurve_fromraw(PyObject *me, PyObject *arg) +{ + char *p; + int len; + buf b; + PyObject *rc = 0; + ec_curve *cc; + ec pp = EC_INIT; + + if (!PyArg_ParseTuple(arg, "Os#:fromraw", &me, &p, &len)) + return (0); + buf_init(&b, p, len); + cc = ECCURVE_C(me); + if (ec_getraw(cc, &b, &pp)) + SYNERR("bad point"); + EC_IN(cc, &pp, &pp); + rc = Py_BuildValue("(NN)", ecpt_pywrap(me, &pp), bytestring_pywrapbuf(&b)); +end: + return (rc); +} + +static PyObject *meth__ECPt_frombuf(PyObject *me, PyObject *arg) +{ + buf b; + char *p; + int sz; + PyObject *rc = 0; + ec pp = EC_INIT; + + if (!PyArg_ParseTuple(arg, "Os#:frombuf", &me, &p, &sz)) goto end; + buf_init(&b, p, 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 *meth__ECPt_parse(PyObject *me, PyObject *arg) +{ + char *p; + qd_parse qd; + PyObject *rc = 0; + ec pp = EC_INIT; + + if (!PyArg_ParseTuple(arg, "Os:parse", &me, &p)) goto end; + qd.p = p; + qd.e = 0; + if (!ec_ptparse(&qd, &pp)) SYNERR(qd.e); + rc = Py_BuildValue("(Ns)", ecpt_pywrapout(me, &pp), qd.p); +end: + return (rc); +} + +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) +{ + char *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 *eccurve_dopywrap(PyTypeObject *ty, + PyObject *fobj, ec_curve *c) +{ + eccurve_pyobj *cobj = newtype(ty, 0); + cobj->c = c; + cobj->fobj = fobj; + cobj->ty.tp_name = (/*unconst*/ char *)c->ops->name; + cobj->ty.tp_basicsize = sizeof(ecpt_pyobj); + cobj->ty.tp_base = ecptcurve_pytype; + Py_INCREF(ecptcurve_pytype); + cobj->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_HEAPTYPE); + cobj->ty.tp_alloc = PyType_GenericAlloc; + cobj->ty.tp_free =_PyObject_Del; + cobj->ty.tp_new = ecpt_pynew; + PyType_Ready(&cobj->ty); + 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") == 0) + ty = ecprimecurve_pytype; + else if (strcmp(EC_NAME(c), "primeproj") == 0) + ty = ecprimeprojcurve_pytype; + else if (strcmp(EC_NAME(c), "bin") == 0) + ty = ecbincurve_pytype; + else if (strcmp(EC_NAME(c), "binproj") == 0) + 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; + char *kwlist[] = { "field", "a", "b", 0 }; + mp *aa = 0, *bb = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!OO", 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 *meth__ECCurve_parse(PyObject *me, PyObject *arg) +{ + char *p; + qd_parse qd; + ec_curve *c; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "Os", &me, &p)) + goto end; + qd.p = p; + qd.e = 0; + if ((c = ec_curveparse(&qd)) == 0) + SYNERR(qd.e); + rc = eccurve_pywrap(0, c); +end: + return (rc); +} + +static PyObject *ecget_name(PyObject *me, void *hunoz) + { return (PyString_FromString(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 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 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") +#undef METHNAME + { 0 } +}; + +static PyTypeObject eccurve_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + eccurve_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + eccurve_pygetset, /* @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@ */ + _PyObject_Del, /* @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 PyTypeObject ecprimecurve_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "An elliptic curve over a prime field. Use ecprimeprojcurve.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + eccurve_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @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 PyTypeObject ecprimeprojcurve_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "An elliptic curve over a prime field, using projective coordinates.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + eccurve_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @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 PyTypeObject ecbincurve_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "An elliptic curve over a binary field. Use ecbinprojcurve.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + eccurve_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @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 PyTypeObject ecbinprojcurve_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "An elliptic curve over a binary field, using projective coordinates.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + eccurve_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Curve info --------------------------------------------------------*/ + +static int ncurves = -1; + +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)); + PyObject_DEL(me); +} + +static PyObject *ecinfo_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + ec_info ei = { 0 }; + PyObject *e, *g; + char *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_COPY(&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 *meth__ECInfo_parse(PyObject *me, PyObject *arg) +{ + char *p; + qd_parse qd; + ec_info ei; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "Os:parse", &me, &p)) + goto end; + qd.p = p; + qd.e = 0; + if (ec_infoparse(&qd, &ei)) + SYNERR(qd.e); + rc = Py_BuildValue("(Ns)", ecinfo_pywrap(&ei), qd.p); +end: + return (rc); +} + +static PyObject *meth__ECInfo__curven(PyObject *me, PyObject *arg) +{ + int i; + ec_info ei; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "Oi:_curven", &me, &i)) goto end; + if (i < 0 || i >= ncurves) VALERR("curve index out of range"); + ec_infofromdata(&ei, ectab[i].data); + rc = ecinfo_pywrap(&ei); +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) +{ + char *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 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 PyMethodDef ecinfo_pymethods[] = { +#define METHNAME(name) eimeth_##name + KWMETH(check, "I.check() -> None") +#undef METHNAME + { 0 } +}; + +static PyTypeObject ecinfo_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Elliptic curve domain parameters.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + ecinfo_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + ecinfo_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + ecinfo_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Setup -------------------------------------------------------------*/ + +static PyMethodDef methods[] = { +#define METHNAME(func) meth_##func + METH (_ECPt_frombuf, "frombuf(E, STR) -> (P, REST)") + METH (_ECPtCurve_fromraw, "fromraw(E, STR) -> (P, REST)") + METH (_ECPt_parse, "parse(E, STR) -> (P, REST)") + METH (_ECCurve_parse, "parse(STR) -> (E, REST)") + METH (_ECInfo_parse, "parse(STR) -> (I, REST)") + METH (_ECInfo__curven, "_curven(N) -> I") +#undef METHNAME + { 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); + addmethods(methods); +} + +static PyObject *namedcurves(void) +{ + int i, j; + const char *p; + PyObject *d, *c; + + d = PyDict_New(); + for (i = 0; ectab[i].name; i++) { + p = ectab[i].name; + for (j = 0; j < i; j++) { + if (ectab[i].data == ectab[j].data) { + c = PyDict_GetItemString(d, (/*unconst*/ char *)ectab[j].name); + Py_INCREF(c); + goto found; + } + } + c = PyInt_FromLong(i); + found: + PyDict_SetItemString(d, (/*unconst*/ char *)ectab[i].name, c); + Py_DECREF(c); + } + ncurves = i; + return (d); +} + +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", namedcurves()); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/field.c b/field.c new file mode 100644 index 0000000..ea7d6de --- /dev/null +++ b/field.c @@ -0,0 +1,998 @@ +/* -*-c-*- + * + * $Id$ + * + * 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; + char *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); + fobj->f = f; + fobj->ty.tp_name = (/*unconst*/ char *)f->ops->name; + fobj->ty.tp_basicsize = sizeof(fe_pyobj); + fobj->ty.tp_base = fe_pytype; + Py_INCREF(fe_pytype); + fobj->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_HEAPTYPE); + fobj->ty.tp_alloc = PyType_GenericAlloc; + fobj->ty.tp_free = _PyObject_Del; + fobj->ty.tp_new = fe_pynew; + PyType_Ready(&fobj->ty); + return ((PyObject *)fobj); +} + +PyObject *field_pywrap(field *f) +{ + PyTypeObject *ty; + + if (strcmp(F_NAME(f), "prime") == 0) ty = primefield_pytype; + else if (strcmp(F_NAME(f), "niceprime") == 0) ty = niceprimefield_pytype; + else if (strcmp(F_NAME(f), "binpoly") == 0) ty = binpolyfield_pytype; + else if (strcmp(F_NAME(f), "binnorm") == 0) ty = binnormfield_pytype; + else abort(); + return (field_dopywrap(ty, f)); +} + +field *field_copy(field *f) +{ + if (strcmp(F_NAME(f), "prime") == 0) + f = field_prime(f->m); + else if (strcmp(F_NAME(f), "niceprime") == 0) + f = field_niceprime(f->m); + else if (strcmp(F_NAME(f), "binpoly") == 0) + f = field_binpoly(f->m); + else if (strcmp(F_NAME(f), "binnorm") == 0) { + 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 *tofe(field *f, PyObject *o) +{ + mp *x = 0, *y = 0; + + if (FE_PYCHECK(o)) { + if (FE_F(o) != f && !field_samep(FE_F(o), f)) return (0); + y = FE_X(o); + } + if ((x = tomp(o)) != 0) { + if (MP_ZEROP(x)) + y = f->zero; + else if (MP_EQ(x, MP_ONE)) + y = f->one; + } + if (x) MP_DROP(x); + if (y) MP_COPY(y); + return (y); +} + +mp *getfe(field *f, PyObject *o) +{ + mp *x = 0; + if ((x = tofe(f, o)) == 0) { + PyErr_Format(PyExc_TypeError, "can't convert %.100s to fe", + o->ob_type->tp_name); + } + return (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 = tofe(*f, x)) == 0) + return (-1); + if ((*yy = tofe(*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) || (yy = tomp(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 long fe_pyhash(PyObject *me) +{ + long i = mp_tolong(FE_X(me)); + i ^= 0xdcf62d6c; /* random perturbance */ + if (i == -1) + i = -2; + return (i); +} + +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 = tofe(FE_F(*x), *y)) != 0) { + Py_INCREF(*x); + *y = fe_pywrap(FE_FOBJ(*x), z); + return (0); + } + return (1); + +end: + return (-1); +} + +static PyObject *fe_pyint(PyObject *x) +{ + long l; + mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x)); + if (mp_tolong_checked(xx, &l)) { MP_DROP(xx); return (0); } + MP_DROP(xx); + return (PyInt_FromLong(l)); +} + +static PyObject *fe_pylong(PyObject *x) +{ + mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x)); + PyObject *rc = (PyObject *)mp_topylong(xx); + MP_DROP(xx); + return (rc); +} + +#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(FE_X(x), radix, 0, pre, 0); \ + MP_DROP(xx); \ + return (rc); \ + } +BASEOP(oct, 8, "0"); +BASEOP(hex, 16, "0x"); +#undef BASEOP + +static void fe_pydealloc(PyObject *me) +{ + Py_DECREF(FE_FOBJ(me)); + MP_DROP(FE_X(me)); + PyObject_DEL(me); +} + +#define UNOP(name, check) \ + static PyObject *femeth_##name(PyObject *me, PyObject *arg) { \ + field *f = FE_F(me); \ + mp *x = FE_X(me); \ + if (!PyArg_ParseTuple(arg, ":" #name)) return (0); \ + 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_field(PyObject *me, void *hunoz) + { RETURN_OBJ(FE_FOBJ(me)); } + +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 PyGetSetDef fe_pygetset[] = { +#define GETSETNAME(op, name) fe##op##_##name + GET (field, "X.field -> field containing X") + GET (value, "X.value -> `natural' integer representation of X") + GET (_value, "X._value -> internal integer representation of X") +#undef GETSETNAME + { 0 } +}; + +static PyMethodDef fe_pymethods[] = { +#define METHNAME(func) femeth_##func + METH (inv, "X.inv() -> X^{-1}") + METH (sqr, "X.sqr() -> X^2") + METH (sqrt, "X.sqrt() -> sqrt(X)") + METH (quadsolve, "X.quadsolve() -> Y where Y^2 + Y = X (binary only)") + METH (dbl, "X.dbl() -> 2 * X (prime only)") + METH (tpl, "X.tpl() -> 3 * X (prime only)") + METH (qdl, "X.qdl() -> 4 * X (prime only)") + METH (hlv, "X.hlv() -> X/2 (prime only)") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods fe_pynumber = { + fe_pyadd, /* @nb_add@ */ + fe_pysub, /* @nb_subtract@ */ + fe_pymul, /* @nb_multiply@ */ + fe_pydiv, /* @nb_divide@ */ + 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@ */ + fe_pycoerce, /* @nb_coerce@ */ + fe_pyint, /* @nb_int@ */ + fe_pylong, /* @nb_long@ */ + 0 /* meaningless */, /* @nb_float@ */ + fe_pyoct, /* @nb_oct@ */ + fe_pyhex, /* @nb_hex@ */ + + 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@ */ + fe_pydiv, /* @nb_true_divide@ */ + 0, /* @nb_inplace_floor_divide@ */ + 0, /* @nb_inplace_true_divide@ */ +}; + +static PyTypeObject fe_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &fe_pynumber, /* @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_iternexr@ */ + fe_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + fe_pygetset, /* @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@ */ + _PyObject_Del, /* @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) +{ + char *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 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 (PyString_FromString(F_NAME(FIELD_F(me)))); } + +static PyObject *fget_type(PyObject *me, void *hunoz) + { return (PyInt_FromLong(F_TYPE(FIELD_F(me)))); } + +static 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 PyMethodDef field_pymethods[] = { +#define METHNAME(name) fmeth_##name + METH (_adopt, "F._adopt(X) -> FE") + KWMETH(rand, "F.rand(rng = rand) -> FE, uniformly distributed") +#undef METHNAME + { 0 } +}; + +static PyTypeObject field_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + field_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + field_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Prime fields ------------------------------------------------------*/ + +static PyObject *primefield_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + mp *xx = 0; + field *f; + char *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 PyGetSetDef primefield_pygetset[] = { +#define GETSETNAME(op, name) pf##op##_##name + GET (p, "F.p -> prime field characteristic") +#undef GETSETNAME +}; + +static PyTypeObject primefield_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Prime fields.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + field_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + primefield_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *niceprimefield_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + mp *xx = 0; + field *f; + char *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 PyTypeObject niceprimefield_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Nice prime fields.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + field_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Binary fields -----------------------------------------------------*/ + +static PyObject *bfget_m(PyObject *me, void *hunoz) + { return (PyInt_FromLong(FIELD_F(me)->nbits)); } + +static PyGetSetDef binfield_pygetset[] = { +#define GETSETNAME(op, name) bf##op##_##name + GET (m, "F.m -> field polynomial degree") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject binfield_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + field_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + binfield_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *binpolyfield_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + mp *xx = 0; + field *f; + char *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 PyGetSetDef binpolyfield_pygetset[] = { +#define GETSETNAME(op, name) pf##op##_##name + GET (p, "F.p -> field polynomial") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject binpolyfield_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Binary fields with polynomial basis representation.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + field_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + binpolyfield_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *binnormfield_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + mp *xx = 0, *yy = 0; + field *f; + char *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 PyGetSetDef binnormfield_pygetset[] = { +#define GETSETNAME(op, name) pf##op##_##name + GET (p, "F.p -> field polynomial") +#undef GETSETNAME +#define GETSETNAME(op, name) bnf##op##_##name + GET (beta, "F.beta -> conversion factor") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject binnormfield_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Binary fields with normal basis representation.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + field_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + binnormfield_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Setup -------------------------------------------------------------*/ + +static PyObject *meth__Field_parse(PyObject *me, PyObject *arg) +{ + field *f; + char *p; + PyObject *rc = 0; + qd_parse qd; + + if (!PyArg_ParseTuple(arg, "Os:parse", &me, &p)) + goto end; + qd.p = p; + qd.e = 0; + if ((f = field_parse(&qd)) == 0) + SYNERR(qd.e); + rc = Py_BuildValue("(Ns)", field_pywrap(f), qd.p); +end: + return (rc); +} + +static PyMethodDef methods[] = { +#define METHNAME(func) meth_##func + METH (_Field_parse, "parse(STR) -> F, REST") +#undef METHNAME + { 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); + addmethods(methods); +} + +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); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/gf.c b/gf.c new file mode 100644 index 0000000..6933c7e --- /dev/null +++ b/gf.c @@ -0,0 +1,61 @@ +/* -*-c-*- + * + * $Id$ + * + * Binary polynomial 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log$ + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "catacomb-python.h" + +/*----- Main code ---------------------------------------------------------*/ + +PyTypeObject *gf_pytype; + +PyObject *gf_pywrap(mp *x) +{ + mp_pyobj *z = PyObject_New(mp_pyobj, mp_pytype); + z->x = x; + return ((PyObject *)z); +} + +mp *getgf(PyObject *o) +{ + mp *x = 0; + if ((x = tomp(o)) == 0) { + PyErr_Format(PyExc_TypeError, "can't convert %.100s to gf", + o->ob_type->tp_name); + } + return (x); +} + + + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/group.c b/group.c new file mode 100644 index 0000000..9049bba --- /dev/null +++ b/group.c @@ -0,0 +1,1365 @@ +/* -*-c-*- + * + * $Id$ + * + * 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 -----------------------------------------*/ + +PyObject *fginfo_pywrap(gprime_param *dp, PyTypeObject *ty) +{ + fginfo_pyobj *z = PyObject_New(fginfo_pyobj, ty); + z->dp.p = MP_COPY(dp->p); + z->dp.q = MP_COPY(dp->q); + z->dp.g = MP_COPY(dp->g); + return ((PyObject *)z); +} + +static PyObject *fginfo_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + char *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(FGINFO_DP(me)->q); } + +static PyObject *diget_p(PyObject *me, void *hunoz) + { return mp_pywrap(FGINFO_DP(me)->p); } + +static PyObject *diget_g(PyObject *me, void *hunoz) + { return mp_pywrap(FGINFO_DP(me)->g); } + +static PyObject *biget_p(PyObject *me, void *hunoz) + { return gf_pywrap(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(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); + _PyObject_Del(me); +} + +static PyObject *meth__DHInfo_generate(PyObject *me, + PyObject *arg, PyObject *kw) +{ + dh_param dp; + unsigned ql = 0, pl; + unsigned steps = 0; + grand *r = &rand_global; + pgev evt = { 0 }; + char *kwlist[] = + { "class", "pbits", "qbits", "event", "rng", "nsteps", 0 }; + PyObject *rc = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist, + &me, convuint, &pl, convuint, &ql, + convpgev, &evt, convgrand, &r, + convuint, &steps)) + goto end; + if (dh_gen(&dp, ql, pl, steps, r, evt.proc, evt.ctx)) + PGENERR; + rc = fginfo_pywrap(&dp, dhinfo_pytype); +end: + droppgev(&evt); + return (rc); +} + +static PyObject *meth__DHInfo_genlimlee(PyObject *me, + PyObject *arg, PyObject *kw) +{ + dh_param dp; + unsigned ql, pl; + unsigned steps = 0; + grand *r = &rand_global; + pgev oe = { 0 }, ie = { 0 }; + int subgroupp = 1; + unsigned f = 0; + char *kwlist[] = { "class", "pbits", "qbits", "event", "ievent", + "rng", "nsteps", "subgroupp", 0 }; + size_t i, nf; + mp **v = 0; + PyObject *rc = 0, *vec = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, + "OO&O&|O&O&O&O&O&:genlimlee", kwlist, + &me, 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.proc, oe.ctx, ie.proc, ie.ctx, &nf, &v)) + PGENERR; + vec = PyList_New(nf); + for (i = 0; i < nf; i++) + PyList_SetItem(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 *meth__DHInfo_gendsa(PyObject *me, + PyObject *arg, PyObject *kw) +{ + dsa_param dp; + unsigned ql, pl; + unsigned steps = 0; + dsa_seed ds; + char *k; + int ksz; + pgev evt = { 0 }; + char *kwlist[] = + { "class", "pbits", "qbits", "seed", "event", "nsteps", 0 }; + PyObject *rc = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&s#|O&O&:generate", kwlist, + &me, convuint, &pl, convuint, &ql, + &k, &ksz, convpgev, &evt, + convuint, &steps)) + goto end; + if (dsa_gen(&dp, ql, pl, steps, k, ksz, &ds, evt.proc, evt.ctx)) + PGENERR; + 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 int npgroups = -1, nbingroups = -1; + +static PyObject *namedgroups(const pentry *pp, int *ne) +{ + int i, j; + const char *p; + PyObject *d, *c; + + d = PyDict_New(); + for (i = 0; pp[i].name; i++) { + p = pp[i].name; + for (j = 0; j < i; j++) { + if (pp[i].data == pp[j].data) { + c = PyDict_GetItemString(d, (/*unconst*/ char *)pp[j].name); + Py_INCREF(c); + goto found; + } + } + c = PyInt_FromLong(i); + found: + PyDict_SetItemString(d, (/*unconst*/ char *)pp[i].name, c); + Py_DECREF(c); + } + *ne = i; + return (d); +} + +static PyObject *meth__groupn(PyObject *me, PyObject *arg, + PyTypeObject *ty, const pentry *pp, int ne) +{ + int i; + gprime_param gp; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "Oi:_groupn", &me, &i)) goto end; + if (i < 0 || i >= ne) VALERR("group index out of range"); + dh_infofromdata(&gp, pp[i].data); + rc = fginfo_pywrap(&gp, ty); +end: + return (rc); +} + +static PyObject *meth__DHInfo__groupn(PyObject *me, PyObject *arg) + { return (meth__groupn(me, arg, dhinfo_pytype, ptab, npgroups)); } + +static PyObject *meth__BinDHInfo__groupn(PyObject *me, PyObject *arg) + { return (meth__groupn(me, arg, bindhinfo_pytype, bintab, nbingroups)); } + +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, "Os:parse", &me, &p)) goto end; + qd.p = p; + qd.e = 0; + if (parse(&qd, &gp)) SYNERR(qd.e); + rc = fginfo_pywrap(&gp, ty); +end: + return (rc); +} + +static PyObject *meth__DHInfo_parse(PyObject *me, PyObject *arg) + { return (meth__parse(me, arg, dhinfo_pytype, dh_parse)); } + +static PyObject *meth__BinDHInfo_parse(PyObject *me, PyObject *arg) + { return (meth__parse(me, arg, bindhinfo_pytype, dhbin_parse)); } + +static PyGetSetDef fginfo_pygetset[] = { +#define GETSETNAME(op, name) fi##op##_##name + GET (r, "I.r -> group order") +#undef GETSETNAME + { 0 } +}; + +static PyGetSetDef dhinfo_pygetset[] = { +#define GETSETNAME(op, name) di##op##_##name + GET (p, "I.p -> prime") + GET (g, "I.g -> generator") +#undef GETSETNAME + { 0 } +}; + +static 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 PyTypeObject fginfo_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + fginfo_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject dhinfo_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Standard (integer) Diffie-Hellman group information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + dhinfo_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject bindhinfo_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Binary-field Diffie-Hellman group information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + bindhinfo_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- General utilities -------------------------------------------------*/ + +PyTypeObject *ge_pytype, *group_pytype; +PyTypeObject *primegroup_pytype, *bingroup_pytype, *ecgroup_pytype; + +group *group_copy(group *g) +{ + if (strcmp(G_NAME(g), "prime") == 0) { + 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") == 0) { + 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") == 0) { + 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) +{ + char *kwlist[] = { "x", 0 }; + PyObject *x; + group *g; + ec p = EC_INIT; + mp *y = 0; + ge *xx = 0; + 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 (PyString_Check(x)) { + sc.buf = PyString_AS_STRING(x); + sc.lim = sc.buf + PyString_GET_SIZE(x); + if (G_READ(g, xx, &mptext_stringops, &sc) || sc.buf < sc.lim) + SYNERR("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); + gobj->g = g; + gobj->ty.tp_name = (/*unconst*/ char *)g->ops->name; + gobj->ty.tp_basicsize = sizeof(ge_pyobj); + gobj->ty.tp_base = ge_pytype; + Py_INCREF(group_pytype); + gobj->ty.tp_flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_HEAPTYPE); + gobj->ty.tp_alloc = PyType_GenericAlloc; + gobj->ty.tp_free =_PyObject_Del; + gobj->ty.tp_new = ge_pynew; + PyType_Ready(&gobj->ty); + return ((PyObject *)gobj); +} + +PyObject *group_pywrap(group *g) +{ + PyTypeObject *ty; + + if (strcmp(G_NAME(g), "prime") == 0) ty = primegroup_pytype; + else if (strcmp(G_NAME(g), "bin") == 0) ty = bingroup_pytype; + else if (strcmp(G_NAME(g), "ec") == 0) 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, PyObject *arg) \ + { \ + group *g; \ + ge *z; \ + if (!PyArg_ParseTuple(arg, ":" #name)) return (0); \ + 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) || (nn = getmp(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)); + PyObject_DEL(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, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":check")) goto end; + 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 = PyString_FromStringAndSize(d.buf, d.len); + DDESTROY(&d); + return (rc); +} + +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 = (PyObject *)mp_topylong(x); +end: + mp_drop(x); + return (rc); +} + +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)) goto end; + rc = PyInt_FromLong(l); +end: + mp_drop(x); + return (rc); +} + +static PyObject *gemeth_toint(PyObject *me, PyObject *arg) +{ + mp *x; + + if (!PyArg_ParseTuple(arg, ":toint")) goto end; + 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) +{ + char *kwlist[] = { "curve", 0 }; + PyTypeObject *cty = ecpt_pytype; + ec p = EC_INIT; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:toec", kwlist, + &cty)) goto end; + if (!PyType_Check(cty) || !PyType_IsSubtype(cty, ecpt_pytype)) + TYERR("want subtype of catacomb.ECPt"); + if (G_TOEC(GE_G(me), &p, GE_X(me))) + TYERR("can't convert to ec point"); + return (ecpt_pywrapout(cty, &p)); +end: + return (0); +} + +static PyObject *gemeth_tobuf(PyObject *me, PyObject *arg) +{ + buf b; + PyObject *rc; + size_t n; + + if (!PyArg_ParseTuple(arg, ":tobuf")) return (0); + n = GE_G(me)->noctets + 4; + rc = bytestring_pywrap(0, n); + buf_init(&b, PyString_AS_STRING(rc), n); + G_TOBUF(GE_G(me), &b, GE_X(me)); + assert(BOK(&b)); + _PyString_Resize(&rc, BLEN(&b)); + return (rc); +} + +static PyObject *gemeth_toraw(PyObject *me, PyObject *arg) +{ + buf b; + PyObject *rc; + size_t n; + + if (!PyArg_ParseTuple(arg, ":toraw")) return (0); + n = GE_G(me)->noctets; + rc = bytestring_pywrap(0, n); + buf_init(&b, PyString_AS_STRING(rc), n); + G_TORAW(GE_G(me), &b, GE_X(me)); + assert(BOK(&b)); + _PyString_Resize(&rc, BLEN(&b)); + return (rc); +} + +static PyObject *gmexp_exp(PyObject *me, void *pp, int 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_check(PyObject *me, PyObject *arg, PyObject *kw) +{ + char *kwlist[] = { "rng", 0 }; + grand *r = &rand_global; + const char *p; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:check", 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 *meth__GE_frombuf(PyObject *me, PyObject *arg) +{ + buf b; + char *p; + int n; + group *g; + ge *x = 0; + + if (!PyArg_ParseTuple(arg, "Os#:frombuf", &me, &p, &n)) + return (0); + g = GROUP_G(me); + buf_init(&b, p, n); + 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 *meth__GE_fromraw(PyObject *me, PyObject *arg) +{ + buf b; + char *p; + int n; + group *g; + ge *x = 0; + + if (!PyArg_ParseTuple(arg, "Os#:fromraw", &me, &p, &n)) + return (0); + g = GROUP_G(me); + buf_init(&b, p, n); + 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 *meth__GE_fromstring(PyObject *me, PyObject *arg) +{ + mptext_stringctx sc; + char *p; + int n; + group *g; + ge *x = 0; + + if (!PyArg_ParseTuple(arg, "Os#:fromstring", &me, &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)) + SYNERR("bad group element string"); + return (Py_BuildValue("(Ns#)", ge_pywrap(me, x), + sc.buf, (int)(sc.lim - sc.buf))); +end: + if (x) G_DESTROY(g, x); + return (0); +} + +static PyObject *meth__Group_parse(PyObject *me, PyObject *arg) +{ + char *p; + qd_parse qd; + group *g; + + if (!PyArg_ParseTuple(arg, "Os:parse", &me, &p)) + goto end; + qd.p = p; + qd.e = 0; + if ((g = group_parse(&qd)) == 0) + SYNERR(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 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 PyGetSetDef ge_pygetset[] = { +#define GETSETNAME(op, name) ge##op##_##name + GET (group, "X.group -> group containing X") +#undef GETSETNAME + { 0 } +}; + +static PyMethodDef ge_pymethods[] = { +#define METHNAME(name) gemeth_##name + METH (inv, "X.inv() -> inverse element of X") + METH (sqr, "X.sqr() -> X^2 = X * X") + METH (check, "X.check() -> check X really belongs to its group") + METH (toint, "X.toint() -> X converted to an integer") + KWMETH(toec, "\ +X.toec(curve = ecpt) -> X converted to elliptic curve point") + METH (tobuf, "X.tobuf() -> X in buffer representation") + METH (toraw, "X.toraw() -> X in raw representation") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods ge_pynumber = { + 0, /* @nb_add@ */ + 0, /* @nb_subtract@ */ + ge_pymul, /* @nb_multiply@ */ + ge_pydiv, /* @nb_divide@ */ + 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@ */ + 0, /* @nb_coerce@ */ + ge_pyint, /* @nb_int@ */ + ge_pylong, /* @nb_long@ */ + 0 /* meaningless */, /* @nb_float@ */ + 0, /* @nb_oct@ */ + 0, /* @nb_hex@ */ + + 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@ */ + ge_pydiv, /* @nb_true_divide@ */ + 0, /* @nb_inplace_floor_divide@ */ + 0, /* @nb_inplace_true_divide@ */ +}; + +static PyTypeObject ge_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &ge_pynumber, /* @tp_as_number@ */ + 0, /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @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_iternexr@ */ + ge_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + ge_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static 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 PyMethodDef group_pymethods[] = { +#define METHNAME(name) gmeth_##name + METH (mexp, "\ +G.mexp([(X0, N0), (X1, N1), ...]) -> X0^N0 X1^N1 ...") + KWMETH(check, "G.check(rand = random): check group is good") +#undef METHNAME + { 0 } +}; + +static PyTypeObject group_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + group_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + group_pygetset, /* @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@ */ + _PyObject_Del, /* @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); + return (fginfo_pywrap(&dp, dhinfo_pytype)); +} + +static 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; + char *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 PyTypeObject primegroup_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Subgroups of prime fields.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + primegroup_pygetset, /* @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@ */ + _PyObject_Del, /* @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); + return (fginfo_pywrap(&dp, bindhinfo_pytype)); +} + +static 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; + char *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 PyTypeObject bingroup_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Subgroups of binary fields.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + bingroup_pygetset, /* @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@ */ + _PyObject_Del, /* @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 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; + char *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 PyTypeObject ecgroup_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Elliptic curve groups.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + ecgroup_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Global stuff ------------------------------------------------------*/ + +static PyMethodDef methods[] = { +#define METHNAME(name) meth_##name + METH (_GE_frombuf, "frombuf(BUF) -> X, REST") + METH (_GE_fromraw, "fromraw(BUF) -> X, REST") + METH (_GE_fromstring, "fromstring(STR) -> X, REST") + METH (_Group_parse, "parse(STR) -> G, REST") + METH (_DHInfo_parse, "parse(STR) -> D, REST") + METH (_BinDHInfo_parse, "parse(STR) -> D, REST") + METH (_DHInfo__groupn, 0) + METH (_BinDHInfo__groupn, 0) + KWMETH(_DHInfo_generate, "\ +generate(PBITS, [qbits = 0, event = pgen_nullev, + rng = rand, nsteps = 0]) -> D") + KWMETH(_DHInfo_genlimlee, "\ +genlimlee(PBITS, QBITS, [event = pgen_nullev, ievent = pgen_nullev,\n\ + rng = rand, nsteps = 0, subgroupp = True]) -> (D, [Q, ...])") + KWMETH(_DHInfo_gendsa, "\ +gendsa(PBITS, QBITS, SEED, [event = pgen_nullev, nsteps = 0])\n\ + -> (D, SEED, COUNT)") +#undef METHNAME + { 0 } +}; + +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); + addmethods(methods); +} + +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("_pgroups", namedgroups(ptab, &npgroups)); + INSERT("_bingroups", namedgroups(bintab, &nbingroups)); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/key.c b/key.c new file mode 100644 index 0000000..a7d3621 --- /dev/null +++ b/key.c @@ -0,0 +1,41 @@ +/* -*-c-*- + * + * $Id$ + * + * 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" + +/*----- Key files ---------------------------------------------------------*/ + +static PyObject *keyfile_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/mp.c b/mp.c new file mode 100644 index 0000000..a2bd7dc --- /dev/null +++ b/mp.c @@ -0,0 +1,2290 @@ +/* -*-c-*- + * + * $Id$ + * + * 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; + +mp *mp_frompylong(PyLongObject *l) +{ + unsigned long bits; + int sz; + size_t w; + mpd r = 0; + int b = 0; + int i; + mp *x; + mpw *p; + + sz = l->ob_size; + if (sz < 0) sz = -sz; + assert(MPW_BITS >= SHIFT); + bits = (unsigned long)sz * SHIFT; + w = (bits + MPW_BITS - 1)/MPW_BITS; + x = mp_new(w, l->ob_size < 0 ? MP_NEG : 0); + p = x->v; + for (i = 0; i < sz; i++) { + r |= (mpd)l->ob_digit[i] << b; + b += 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); +} + +PyLongObject *mp_topylong(mp *x) +{ + unsigned long bits = mp_bits(x); + int sz = (bits + SHIFT - 1)/SHIFT; + PyLongObject *l = _PyLong_New(sz); + mpd r = 0; + int b = 0; + mpw *p = x->v; + int i = 0; + + assert(MPW_BITS >= SHIFT); + while (i < sz && p < x->vl) { + r |= *p << b; + b += MPW_BITS; + while (i < sz && b >= SHIFT) { + l->ob_digit[i++] = r & MASK; + r >>= SHIFT; + b -= SHIFT; + } + } + while (i < sz && r) { + l->ob_digit[i++] = r & MASK; + r >>= SHIFT; + } + l->ob_size = (x->f & MP_NEG) ? -sz : sz; + return (l); +} + +mp *mp_frompyobject(PyObject *o, int radix) +{ + mp *x; + + if ((x = tomp(o)) != 0) + return (x); + if (PyString_Check(o)) { + mptext_stringctx sc; + mp *x; + sc.buf = PyString_AS_STRING(o); + sc.lim = sc.buf + PyString_GET_SIZE(o); + 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); + } + 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); + o = PyString_FromStringAndSize(0, len + 1 + xprelen + prelen + postlen); + p = PyString_AS_STRING(o); + 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); + _PyString_Resize(&o, sc.buf - p); + return (o); +} + +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) +{ + 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)) + VALERR("mp out of range for int"); + *l = mp_tolong(x); + rc = 0; +end: + return (rc); +} + +/*----- Python interface --------------------------------------------------*/ + +static void mp_pydealloc(PyObject *o) +{ + MP_DROP(MP_X(o)); + PyObject_DEL(o); +} + +static PyObject *mp_pyrepr(PyObject *o) + { return mp_topystring(MP_X(o), 10, "MP(", 0, "L)"); } + +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) + 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; + 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); + } else if (PyInt_Check(o)) + return (mp_fromlong(MP_NEW, PyInt_AS_LONG(o))); + else if ((l = PyNumber_Long(o)) != 0) { + x = mp_frompylong((PyLongObject *)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", + o->ob_type->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", + o->ob_type->tp_name); + } + return (x); +} + +int convgf(PyObject *o, void *p) +{ + mp *x; + if ((x = getgf(o)) == 0) return (0); + *(mp **)p = x; + return (1); +} + +static int mpbinop(PyObject *x, PyObject *y, mp **xx, mp **yy) +{ + if ((*xx = tomp(x)) == 0) + return (-1); + if ((*yy = tomp(y)) == 0) { + MP_DROP(*xx); + return (-1); + } + return (0); +} + +#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 (mpbinop(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, add) +BINOP(mp, sub) +BINOP(mp, mul) +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, name, rname) \ + static PyObject *pre##_py##name(PyObject *x, PyObject *y) { \ + mp *xx, *yy; \ + PyObject *z = 0; \ + long n; \ + if (mpbinop(x, y, &xx, &yy)) RETURN_NOTIMPL; \ + if (mp_tolong_checked(yy, &n)) goto end; \ + if (n < 0) \ + z = pre##_pywrap(mp_##rname(MP_NEW, xx, -n)); \ + else \ + z = pre##_pywrap(mp_##name(MP_NEW, xx, n)); \ + end: \ + MP_DROP(xx); MP_DROP(yy); \ + return (z); \ + } +SHIFTOP(mp, lsl2c, lsr2c) +SHIFTOP(mp, lsr2c, lsl2c) +SHIFTOP(gf, lsl, lsr) +SHIFTOP(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 (mpbinop(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 = tomp(x)) == 0 || (yy = tomp(y)) == 0 || + (z && z != Py_None && (zz = tomp(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); } +BASEOP(oct, 8, "0"); +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)) return (0); + return (PyInt_FromLong(l)); +} +static PyObject *mp_pylong(PyObject *x) + { return (PyObject *)mp_topylong(MP_X(x)); } +static PyObject *mp_pyfloat(PyObject *x) +{ + PyObject *l = (PyObject *)mp_topylong(MP_X(x)); + double f = PyLong_AsDouble(l); + Py_DECREF(l); + return (PyFloat_FromDouble(f)); +} + +#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 = tomp(*y)) != 0) { \ + Py_INCREF(*x); \ + *y = pre##_pywrap(z); \ + return (0); \ + } \ + return (1); \ + } +COERCE(mp, MP) +COERCE(gf, GF) +#undef COERCE + +static int mp_pycompare(PyObject *x, PyObject *y) + { return mp_cmp(MP_X(x), MP_X(y)); } + +static PyObject *mp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + PyObject *x; + mp *z; + mp_pyobj *zz = 0; + int radix = 0; + char *kwlist[] = { "x", "radix", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:mp", kwlist, &x, &radix)) + goto end; + if (MP_PYCHECK(x)) RETURN_OBJ(x); + if (radix < -255 || radix > 62) VALERR("radix out of range"); + if ((z = mp_frompyobject(x, radix)) == 0) { + PyErr_Format(PyExc_TypeError, "can't convert %.100s to mp", + x->ob_type->tp_name); + goto end; + } + zz = (mp_pyobj *)ty->tp_alloc(ty, 0); + zz->x = z; +end: + return ((PyObject *)zz); +} + +static long mp_pyhash(PyObject *me) +{ + long i = mp_tolong(MP_X(me)); + if (i == -1) + i = -2; + return (i); +} + +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) \ + { \ + int i; \ + if (!PyArg_ParseTuple(arg, "i:" #name, &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) +{ + int i; + if (!PyArg_ParseTuple(arg, "i:testbit", &i)) return (0); + return (getbool(mp_testbit2c(MP_X(me), i))); +} + +static PyObject *gfmeth_testbit(PyObject *me, PyObject *arg) +{ + int i; + if (!PyArg_ParseTuple(arg, "i:testbit", &i)) return (0); + return (getbool(mp_testbit(MP_X(me), i))); +} + +static PyObject *mpmeth_odd(PyObject *me, PyObject *arg) +{ + mp *t; + size_t s; + + if (!PyArg_ParseTuple(arg, ":odd")) return (0); + t = mp_odd(MP_NEW, MP_X(me), &s); + return (Py_BuildValue("(lN)", (long)s, mp_pywrap(t))); +} + +static PyObject *mpmeth_sqr(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":sqr")) return (0); + return (mp_pywrap(mp_sqr(MP_NEW, MP_X(me)))); +} + +static PyObject *mpmeth_sqrt(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":sqrt")) return (0); + 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; + char *kwlist[] = { "radix", 0 }; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", kwlist, &radix)) + goto end; + if (radix < -255 || radix > 62 || radix == -1 || radix == 0 || radix == 1) + 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); +} + +#define STOREOP(name, c) \ + static PyObject *mpmeth_##name(PyObject *me, \ + PyObject *arg, PyObject *kw) \ + { \ + long len = -1; \ + char *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), PyString_AS_STRING(rc), len); \ + end: \ + return (rc); \ + } +STOREOP(storel, ) +STOREOP(storeb, ) +STOREOP(storel2c, 2c) +STOREOP(storeb2c, 2c) +#undef STOREOP + +#define BUFOP(ty, pyty) \ + static PyObject *meth__##pyty##_frombuf(PyObject *me, PyObject *arg) \ + { \ + buf b; \ + char *p; \ + int sz; \ + PyObject *rc = 0; \ + mp *x; \ + \ + if (!PyArg_ParseTuple(arg, "Os#:frombuf", &me, &p, &sz)) goto end; \ + buf_init(&b, p, 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, MP) +BUFOP(gf, GF) +#undef BUFOP + +static PyObject *mpmeth_tobuf(PyObject *me, PyObject *arg) +{ + buf b; + PyObject *rc; + mp *x; + size_t n; + + if (!PyArg_ParseTuple(arg, ":tobuf")) return (0); + x = MP_X(me); + n = mp_octets(x) + 3; + rc = bytestring_pywrap(0, n); + buf_init(&b, PyString_AS_STRING(rc), n); + buf_putmp(&b, x); + assert(BOK(&b)); + _PyString_Resize(&rc, BLEN(&b)); + return (rc); +} + +static PyObject *mpmeth_primep(PyObject *me, PyObject *arg, PyObject *kw) +{ + grand *r = &rand_global; + char *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 *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 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 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") + METH (odd, "X.odd() -> S, T where X = 2^S T with T odd") + METH (sqr, "X.sqr() -> X^2") + METH (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") + KWMETH(primep, "X.primep(rng = rand) -> true/false if 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") + METH (tobuf, "X.tobuf() -> buffer format") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods mp_pynumber = { + mp_pyadd, /* @nb_add@ */ + mp_pysub, /* @nb_subtract@ */ + mp_pymul, /* @nb_multiply@ */ + 0, /* @nb_divide@ */ + 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@ */ + mp_pycoerce, /* @nb_coerce@ */ + mp_pyint, /* @nb_int@ */ + mp_pylong, /* @nb_long@ */ + mp_pyfloat, /* @nb_float@ */ + mp_pyoct, /* @nb_oct@ */ + mp_pyhex, /* @nb_hex@ */ + + 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@ */ + + mp_pydiv, /* @nb_floor_divide@ */ + 0, /* @nb_true_divide@ */ + 0, /* @nb_inplace_floor_divide@ */ + 0, /* @nb_inplace_true_divide@ */ +}; + +static PyTypeObject mp_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.MP", /* @tp_name@ */ + sizeof(mp_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + mp_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + mp_pycompare, /* @tp_compare@ */ + mp_pyrepr, /* @tp_repr@ */ + &mp_pynumber, /* @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.\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 ints and longs.\n\ +\n\ +Notes:\n\ +\n\ + * Use `//' for division. MPs don't have `/' division.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + mp_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + mp_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *meth__MP_fromstring(PyObject *me, + PyObject *arg, PyObject *kw) +{ + int r = 0; + char *p; + int len; + PyObject *z = 0; + mp *zz; + mptext_stringctx sc; + char *kwlist[] = { "class", "x", "radix", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring", + kwlist, &me, &p, &len, &r)) + goto end; + if (r < -255 || r > 62) VALERR("radix out of range"); + sc.buf = p; sc.lim = p + len; + if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0) + SYNERR("bad integer"); + z = Py_BuildValue("(Ns#)", mp_pywrap(zz), sc.buf, (int)(sc.lim - sc.buf)); +end: + return (z); +} + +static PyObject *meth__MP_product(PyObject *me, PyObject *arg) +{ + mpmul m; + PyObject *q, *i; + mp *x; + + if (PyTuple_Size(arg) != 2) { + i = PyObject_GetIter(arg); + PyIter_Next(i); + } else { + if ((q = PyTuple_GetItem(arg, 1)) == 0) return (0); + if ((i = PyObject_GetIter(q)) == 0) { + PyErr_Clear(); /* that's ok */ + i = PyObject_GetIter(arg); + } + } + if (!i) return (0); + mpmul_init(&m); + while ((q = PyIter_Next(i)) != 0) { + x = getmp(q); Py_DECREF(q); if (!x) { + MP_DROP(mpmul_done(&m)); + Py_DECREF(i); + return (0); + } + mpmul_add(&m, x); + MP_DROP(x); + } + x = mpmul_done(&m); + Py_DECREF(i); + return (mp_pywrap(x)); +} + +#define LOADOP(pre, py, name) \ + static PyObject *meth__##py##_##name(PyObject *me, PyObject *arg) \ + { \ + char *p; \ + int len; \ + if (!PyArg_ParseTuple(arg, "Os#:" #name, &me, &p, &len)) return (0); \ + return (pre##_pywrap(mp_##name(MP_NEW, p, len))); \ + } +LOADOP(mp, MP, loadl) +LOADOP(mp, MP, loadb) +LOADOP(mp, MP, loadl2c) +LOADOP(mp, MP, loadb2c) +LOADOP(gf, GF, loadl) +LOADOP(gf, GF, loadb) +#undef LOADOP + +/*----- Montgomery reduction ----------------------------------------------*/ + +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, int 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, int 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)); + PyObject_DEL(me); +} + +static PyObject *mpmont_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + mpmont_pyobj *mm = 0; + char *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 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 PyMethodDef mpmont_pymethods[] = { +#define METHNAME(name) mmmeth_##name + METH (int, "M.out(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, "\ +B.mexp([(XR0, N0), (XR1, N1), ...]) = ZR where Z = X0^N0 X1^N1 mod B.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, "\ +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 PyTypeObject *mpmont_pytype, mpmont_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"A Montgomery reduction context.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + mpmont_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + mpmont_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Barrett reduction -------------------------------------------------*/ + +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, int 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)); + PyObject_DEL(me); +} + +static PyObject *mpbarrett_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + mpbarrett_pyobj *mb = 0; + char *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 PyGetSetDef mpbarrett_pygetset[] = { +#define GETSETNAME(op, name) mb##op##_##name + GET (m, "B.m -> modulus for reduction") +#undef GETSETNAME + { 0 } +}; + +static 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 PyTypeObject *mpbarrett_pytype, mpbarrett_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"A Barrett reduction context.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + mpbarrett_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + mpbarrett_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Nice prime reduction ----------------------------------------------*/ + +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)); + PyObject_DEL(me); +} + +static PyObject *mpreduce_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + mpreduce_pyobj *mr = 0; + mpreduce r; + char *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 PyGetSetDef mpreduce_pygetset[] = { +#define GETSETNAME(op, name) mr##op##_##name + GET (m, "R.m -> modulus for reduction") +#undef GETSETNAME + { 0 } +}; + +static 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 PyTypeObject *mpreduce_pytype, mpreduce_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"A reduction context for reduction modulo primes of special form.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + mpreduce_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + mpreduce_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Chinese Remainder Theorem solution --------------------------------*/ + +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, *x, *z = 0; + mp *xx; + mp **v = 0; + int i = 0, n = c->k; + + Py_INCREF(me); + if (PyTuple_Size(arg) == n) + q = arg; + else if (!PyArg_ParseTuple(arg, "O:solve", &q)) + goto end; + Py_INCREF(q); + if (!PySequence_Check(q)) TYERR("want a sequence of residues"); + if (PySequence_Size(q) != n) VALERR("residue count mismatch"); + v = xmalloc(n * sizeof(*v)); + for (i = 0; i < n; i++) { + if ((x = PySequence_GetItem(q, i)) == 0) goto end; + xx = getmp(x); Py_DECREF(x); if (!xx) goto end; + v[i] = xx; + } + z = mp_pywrap(mpcrt_solve(c, MP_NEW, v)); +end: + if (v) { + n = i; + for (i = 0; i < n; i++) + MP_DROP(v[i]); + xfree(v); + } + Py_DECREF(me); + Py_XDECREF(q); + 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; + int n, i = 0; + char *kwlist[] = { "mv", 0 }; + PyObject *q = 0, *x; + mp *xx; + mpcrt_pyobj *c = 0; + + if (PyTuple_Size(arg) > 1) + q = arg; + else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &q)) + goto end; + Py_INCREF(q); + if (!PySequence_Check(q)) TYERR("want a sequence of moduli"); + n = PySequence_Size(q); + if (PyErr_Occurred()) goto end; + if (!n) VALERR("want at least one modulus"); + v = xmalloc(n * sizeof(*v)); + for (i = 0; i < n; i++) { + if ((x = PySequence_GetItem(q, i)) == 0) goto end; + xx = getmp(x); Py_DECREF(x); if (!xx) goto end; + v[i].m = xx; v[i].n = 0; v[i].ni = 0; v[i].nni = 0; + } + c = (mpcrt_pyobj *)ty->tp_alloc(ty, 0); + mpcrt_create(&c->c, v, n, 0); + Py_DECREF(q); + return ((PyObject *)c); + +end: + if (v) { + n = i; + for (i = 0; i < n; i++) + MP_DROP(v[i].m); + xfree(v); + } + Py_XDECREF(q); + 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_SetItem(q, i, mp_pywrap(c->v[i].m)); + return (q); +} + +static 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 PyMethodDef mpcrt_pymethods[] = { +#define METHNAME(name) mcmeth_##name + METH (solve, "C.solve([R0, R1]) -> X mod C.product") +#undef METHNAME + { 0 } +}; + +static PyTypeObject *mpcrt_pytype, mpcrt_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"A context for the solution of Chinese Remainder Theorem problems.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + mpcrt_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + mpcrt_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Binary polynomials ------------------------------------------------*/ + +static PyObject *gf_pyrepr(PyObject *o) + { return mp_topystring(MP_X(o), 16, "GF(", "0x", "L)"); } + +static PyObject *gf_pyrichcompare(PyObject *x, PyObject *y, int op) +{ + mp *xx, *yy; + int xl, yl; + int b; + + if (mpbinop(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; + char *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 (radix < -255 || radix > 62) VALERR("radix out of range"); + if ((z = mp_frompyobject(x, radix)) == 0) { + PyErr_Format(PyExc_TypeError, "can't convert %.100s to gf", + x->ob_type->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 long gf_pyhash(PyObject *me) +{ + long i = mp_tolong(MP_X(me)); + i ^= 0xc7ecd67c; /* random perturbance */ + if (i == -1) + i = -2; + return (i); +} + +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 = tomp(x)) == 0 || (yy = tomp(y)) == 0 || + (z && z != Py_None && (zz = tomp(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, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":sqr")) return (0); + 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_irreduciblep(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":irreduciblep")) return (0); + 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 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 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") + METH (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") + METH (irreduciblep, "X.irreduciblep() -> true/false") +#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") + METH (tobuf, "X.tobuf() -> buffer format") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods gf_pynumber = { + gf_pyadd, /* @nb_add@ */ + gf_pysub, /* @nb_subtract@ */ + gf_pymul, /* @nb_multiply@ */ + 0, /* @nb_divide@ */ + 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@ */ + gf_pycoerce, /* @nb_coerce@ */ + mp_pyint, /* @nb_int@ */ + mp_pylong, /* @nb_long@ */ + 0 /* doesn't make any sense */, /* @nb_float@ */ + mp_pyoct, /* @nb_oct@ */ + mp_pyhex, /* @nb_hex@ */ + + 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@ */ + + gf_pydiv, /* @nb_floor_divide@ */ + 0, /* @nb_true_divide@ */ + 0, /* @nb_inplace_floor_divide@ */ + 0, /* @nb_inplace_true_divide@ */ +}; + +static PyTypeObject gf_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &gf_pynumber, /* @tp_as_number@ */ + 0, /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + gf_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 ints and longs.\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 division. GFs don't have `/' division.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + gf_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + gf_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + gf_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *meth__GF_fromstring(PyObject *me, + PyObject *arg, PyObject *kw) +{ + int r = 0; + char *p; + int len; + PyObject *z = 0; + mp *zz; + mptext_stringctx sc; + char *kwlist[] = { "class", "x", "radix", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring", + kwlist, &me, &p, &len, &r)) + goto end; + if (r < -255 || r > 62) VALERR("radix out of range"); + sc.buf = p; sc.lim = p + len; + if ((zz = mp_read(MP_NEW, r, &mptext_stringops, &sc)) == 0 || MP_NEGP(zz)) + z = Py_BuildValue("(Os#)", Py_None, p, len); + else + z = Py_BuildValue("(Ns#)", gf_pywrap(zz), + sc.buf, (int)(sc.lim - sc.buf)); +end: + return (z); +} + +/*----- Sparse poly reduction ---------------------------------------------*/ + +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_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)); + PyObject_DEL(me); +} + +static PyObject *gfreduce_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + gfreduce_pyobj *mr = 0; + gfreduce r; + char *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 PyGetSetDef gfreduce_pygetset[] = { +#define GETSETNAME(op, name) gr##op##_##name + GET (m, "R.m -> reduction polynomial") +#undef GETSETNAME + { 0 } +}; + +static PyMethodDef gfreduce_pymethods[] = { +#define METHNAME(name) grmeth_##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 PyTypeObject *gfreduce_pytype, gfreduce_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"A reduction context for reduction modulo sparse irreducible polynomials.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + gfreduce_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + gfreduce_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Normal/poly transformation ----------------------------------------*/ + +typedef struct gfn_pyobj { + PyObject_HEAD + mp *p; + gfn ntop, pton; +} gfn_pyobj; + +static PyTypeObject *gfn_pytype, gfn_pytype_skel; + +#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; + char *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); + if (gfn_create(p, beta, &gg->ntop, &gg->pton)) { + PyObject_DEL(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 (mp_pywrap(z)); \ + } +XFORMOP(pton, PTON) +XFORMOP(ntop, NTOP) +#undef XFORMOP + +static void gfn_pydealloc(PyObject *me) +{ + gfn_destroy(GFN_PTON(me)); + gfn_destroy(GFN_NTOP(me)); + PyObject_DEL(me); +} + +static 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 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 PyTypeObject gfn_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"An object for transforming elements of binary fields between polynomial\n\ +and normal basis representations.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + gfn_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + gfn_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Glue --------------------------------------------------------------*/ + +static PyMethodDef methods[] = { +#define METHNAME(func) meth_##func + KWMETH(_MP_fromstring, "\ +fromstring(STR, radix = 0) -> (X, REST)\n\ +\n\ +Parse STR as a large integer, according to radix. If radix is zero,\n\ +read a prefix from STR to decide radix: allow `0' for octal, `0x' for hex\n\ +or `R_' for other radix R.") + KWMETH(_GF_fromstring, "\ +fromstring(STR, radix = 0) -> (X, REST)\n\ +\n\ +Parse STR as a binary polynomial, according to radix. If radix is zero,\n\ +read a prefix from STR to decide radix: allow `0' for octal, `0x' for hex\n\ +or `R_' for other radix R.") + METH (_MP_loadl, "\ +loadl(STR) -> X: read little-endian bytes") + METH (_MP_loadb, "\ +loadb(STR) -> X: read big-endian bytes") + METH (_MP_loadl2c, "\ +loadl2c(STR) -> X: read little-endian bytes, two's complement") + METH (_MP_loadb2c, "\ +loadb2c(STR) -> X: read big-endian bytes, two's complement") + METH (_MP_frombuf, "\ +frombuf(STR) -> (X, REST): read buffer format") + METH (_MP_product, "\ +product(ITER) -> X: product of things iterated over") + METH (_GF_loadl, "\ +loadl(STR) -> X: read little-endian bytes") + METH (_GF_loadb, "\ +loadb(STR) -> X: read big-endian bytes") + METH (_GF_frombuf, "\ +frombuf(STR) -> (X, REST): read buffer format") +#undef METHNAME + { 0 } +}; + +void mp_pyinit(void) +{ + INITTYPE(mp, root); + INITTYPE(gf, root); + INITTYPE(mpmont, root); + INITTYPE(mpbarrett, root); + INITTYPE(mpreduce, root); + INITTYPE(mpcrt, root); + INITTYPE(gfreduce, root); + INITTYPE(gfn, root); + addmethods(methods); +} + +void mp_pyinsert(PyObject *mod) +{ + INSERT("MP", mp_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); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/passphrase.c b/passphrase.c new file mode 100644 index 0000000..a72bd90 --- /dev/null +++ b/passphrase.c @@ -0,0 +1,244 @@ +/* -*-c-*- + * + * $Id$ + * + * 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) + +static int convpixie(PyObject *o, void *p) +{ + if (!PIXIE_PYCHECK(o)) + TYERR("want pixie"); + *(int *)p = PIXIE_FD(o); + return (1); +end: + return (0); +} + +static PyObject *pixie_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + pixie_pyobj *rc = 0; + char *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)); + PyObject_DEL(me); + +} + +static PyObject *pixmeth_read(PyObject *me, PyObject *arg, PyObject *kw) +{ + unsigned mode = PMODE_READ; + char *tag; + char *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 = PyString_FromString(buf); +end: + return (rc); +} + +static PyObject *pixmeth_set(PyObject *me, PyObject *arg) +{ + char *tag; + char *phrase; + + if (!PyArg_ParseTuple(arg, "ss: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 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 PyTypeObject pixie_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Passphrase pixie connection.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + pixie_pymethods, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Main code ---------------------------------------------------------*/ + +static PyObject *meth_ppread(PyObject *me, PyObject *arg, PyObject *kw) +{ + char *tag; + unsigned f = PMODE_READ; + PyObject *rc = 0; + char *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 = PyString_FromString(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 = PyString_FromString(buf); +end: + return (rc); +} + +static 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); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/pgen.c b/pgen.c new file mode 100644 index 0000000..e201b62 --- /dev/null +++ b/pgen.c @@ -0,0 +1,1078 @@ +/* -*-c-*- + * + * $Id$ + * + * 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) +{ + char *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)); PyObject_DEL(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 *meth__PrimeFilter_smallfactor(PyObject *me, PyObject *arg) +{ + mp *x = 0; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "OO&:smallfactor", &me, 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)) goto end; + rc = PyInt_FromLong(l); +end: + return (rc); +} + +static PyObject *pfilt_pylong(PyObject *me) + { return ((PyObject *)mp_topylong(PFILT_F(me)->m)); } + +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 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 PyMethodDef pfilt_pymethods[] = { +#define METHNAME(name) pfmeth_##name + METH (step, "F.step(N)") + METH (muladd, "F.muladd(M, A)") + METH (jump, "F.jump(FF)") +#undef METHNAME + { 0 } +}; + +static PyNumberMethods pfilt_pynumber = { + 0, /* @nb_add@ */ + 0, /* @nb_subtract@ */ + 0, /* @nb_multiply@ */ + 0, /* @nb_divide@ */ + 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@ */ + 0, /* @nb_coerce@ */ + pfilt_pyint, /* @nb_int@ */ + pfilt_pylong, /* @nb_long@ */ + 0, /* @nb_float@ */ + 0, /* @nb_oct@ */ + 0, /* @nb_hex@ */ + + 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 PyTypeObject pfilt_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + &pfilt_pynumber, /* @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@ */ +"Small-primes filter.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + pfilt_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + pfilt_pygetset, /* @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@ */ + _PyObject_Del, /* @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; + char *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)); + PyObject_DEL(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 *meth__RabinMiller_iters(PyObject *me, PyObject *arg) +{ + unsigned n; + + if (!PyArg_ParseTuple(arg, "OO&:iters", &me, convuint, &n)) return (0); + return (PyInt_FromLong(rabin_iters(n))); +} + +static 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 PyMethodDef rabin_pymethods[] = { +#define METHNAME(name) rmeth_##name + METH (test, "R.test(W) -> PGST") + METH (rtest, "R.rtest(W) -> PGST") +#undef METHNAME + { 0 } +}; + +static PyTypeObject rabin_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Rabin-Miller strong primality test.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + rabin_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + rabin_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Events ------------------------------------------------------------*/ + +typedef struct pgevent_pyobj { + PyObject_HEAD + 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; + return ((PyObject *)o); +} + +static CONVFUNC(pgevent, pgen_event *, PGEVENT_EV) + +static void pgevent_kill(PyObject *me) { PGEVENT_EV(me) = 0; } +static void pgevent_pydealloc(PyObject *me) { PyObject_DEL(me); } + +#define PGEVENT_CHECK(me) do { \ + if (!PGEVENT_EV(me)) { \ + PyErr_SetString(PyExc_ValueError, "event object is dead"); \ + return (0); \ + } \ +} while (0) + +static PyObject *peget_name(PyObject *me, void *hunoz) + { PGEVENT_CHECK(me); return (PyString_FromString(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_CHECK(me); return (grand_pywrap(PGEVENT_EV(me)->r, 0)); } + +static int peset_x(PyObject *me, PyObject *xobj, void *hunoz) +{ + mp *x = 0; + pgen_event *ev = PGEVENT_EV(me); + int rc = -1; + 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 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 PyTypeObject pgevent_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + pgevent_pygetset, /* @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@ */ + _PyObject_Del, /* @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) +{ + PyObject *py = p; + PyObject *pyev = 0; + PyObject *rc = 0; + int st = PGEN_ABORT; + long l; + char *meth[] = { + "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass" + }; + + Py_INCREF(py); + rq++; + if (rq > N(meth)) SYSERR("event code out of range"); + pyev = pgevent_pywrap(ev); + if ((rc = PyObject_CallMethod(py, 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 (pyev) { + pgevent_kill(pyev); + Py_DECREF(pyev); + } + Py_XDECREF(rc); + Py_DECREF(py); + 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) +{ + pgev *pg = p; + + if (PGEV_PYCHECK(o)) + *pg = *PGEV_PG(o); + else { + pg->proc = pgev_python; + pg->ctx = o; + Py_INCREF(o); + } + return (1); +} + +void droppgev(pgev *p) +{ + if (p->proc == pgev_python) { + PyObject *py = p->ctx; + Py_DECREF(py); + } +} + +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 PyMethodDef pgev_pymethods[] = { +#define METHNAME(name) pgmeth_##name + METH (pg_abort, "E.pg_abort() -> PGRC -- prime generation aborted") + METH (pg_done, "E.pg_done() -> PGRC -- prime generation finished") + METH (pg_begin, "E.pg_begin() -> PGRC -- commence stepping/testing") + METH (pg_try, "E.pg_try() -> PGRC -- found new candidate") + METH (pg_pass, "E.pg_pass() -> PGRC -- passed primality test") + METH (pg_fail, "E.pg_fail() -> PGRC -- failed primality test") +#undef METHNAME + { 0 } +}; + +static PyTypeObject pgev_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.PrimeGenBuiltinHandler", /* @tp_name@ */ + sizeof(pgev_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + _PyObject_Del, /* @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_iternexr@ */ + pgev_pymethods, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *pgstep_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + mpw s; + pgstep_pyobj *rc = 0; + char *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 PyGetSetDef pgstep_pygetset[] = { +#define GETSETNAME(op, name) ps##op##_##name + GET (step, "S.step -> step size for the stepper") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject pgstep_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ + "Simple prime-number stepper with small-factors filter.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + pgstep_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *pgjump_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + PyObject *o, *fobj; + pgjump_pyobj *rc = 0; + char *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)); + _PyObject_Del(me); +} + +static PyObject *pjget_jump(PyObject *me, void *hunoz) + { RETURN_OBJ(PGJUMP_FOBJ(me)); } + +static PyGetSetDef pgjump_pygetset[] = { +#define GETSETNAME(op, name) pj##op##_##name + GET (jump, "S.jump -> jump size for the stepper") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject pgjump_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"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_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + pgjump_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *pgtest_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + pgtest_pyobj *rc = 0; + char *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 PyTypeObject pgtest_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Rabin-Miller tester.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Prime generation functions ----------------------------------------*/ + +void pgenerr(void) +{ + if (!PyErr_Occurred()) + 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; + pgev step = { 0 }, test = { 0 }, evt = { 0 }; + unsigned nsteps = 0, ntests = 0; + char *kwlist[] = { "start", "name", "stepper", "tester", "event", + "nsteps", "ntests", 0 }; + + step.proc = pgen_filter; step.ctx = &fc; + test.proc = pgen_test; test.ctx = &tc; + 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.proc, evt.ctx, + nsteps, step.proc, step.ctx, + ntests, test.proc, test.ctx)) == 0) + PGENERR; + if (PyErr_Occurred()) goto end; + 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; + pgev evt = { 0 }; + PyObject *rc = 0; + char *kwlist[] = { "nbits", "name", "event", "rng", "nsteps", 0 }; + + 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.proc, evt.ctx)) == 0) + PGENERR; + 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; + pgev evt = { 0 }; + PyObject *rc = 0; + char *kwlist[] = { "nbits", "name", "event", "rng", "nsteps", 0 }; + + 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.proc, evt.ctx)) == 0) + PGENERR; + 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"; + pgev ie = { 0 }, oe = { 0 }; + unsigned ql, pl; + grand *r = &rand_global; + unsigned on = 0; + size_t i, nf = 0; + PyObject *rc = 0, *vec; + char *kwlist[] = { "pbits", "qbits", "name", "event", "ievent", + "rng", "nsteps", 0 }; + mp *x = 0, **v = 0; + + 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.proc, oe.ctx, ie.proc, ie.ctx, &nf, &v)) == 0) + PGENERR; + vec = PyList_New(nf); + for (i = 0; i < nf; i++) + PyList_SetItem(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 PyMethodDef methods[] = { +#define METHNAME(name) meth_##name + METH (_PrimeFilter_smallfactor, "smallfactor(X) -> PGRC") + METH (_RabinMiller_iters, "iters(NBITS) -> NITERS") + 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_setup(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); + 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)); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/pubkey.c b/pubkey.c new file mode 100644 index 0000000..e002972 --- /dev/null +++ b/pubkey.c @@ -0,0 +1,1155 @@ +/* -*-c-*- + * + * $Id$ + * + * 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); + PyObject_DEL(me); +} + +static PyObject *dsa_setup(PyTypeObject *ty, PyObject *G, PyObject *u, + PyObject *p, PyObject *rng, PyObject *hash) +{ + dsa_pyobj *g; + + g = PyObject_New(dsa_pyobj, ty); + if (GROUP_G(G) != GE_G(p) && !group_samep(GROUP_G(G), GE_G(p))) + TYERR("public key not from group"); + if (!u) { + g->d.u = 0; + u = Py_None; + } else if ((g->d.u = getmp(u)) == 0) + goto end; + 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; Py_INCREF(u); g->p = p; Py_INCREF(p); + rng = g->rng; Py_INCREF(rng); g->hash = hash; Py_INCREF(hash); + return ((PyObject *)g); +end: + PyObject_DEL(g); + return (0); +} + +static PyObject *dsapub_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + PyObject *G, *p, *u = 0, *rng = rand_pyobj, *hash = sha_pyobj; + PyObject *rc = 0; + char *kwlist[] = { "G", "p", "u", "hash", "rng", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O!|OO!:new", kwlist, + group_pytype, &G, + ge_pytype, &p, + &u, + gchash_pytype, &hash, + grand_pytype, &rng) || + (rc = dsa_setup(dsapub_pytype, G, u, p, rng, hash)) == 0) + goto end; +end: + return (rc); +} + +static PyObject *dsameth_beginhash(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":beginhash")) return (0); + return (ghash_pywrap(DSA_HASH(me), gdsa_beginhash(DSA_D(me)), f_freeme)); +} + +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, PyString_AS_STRING(rc)); + GH_DESTROY(h); + return (rc); +} + +static PyObject *dsameth_sign(PyObject *me, PyObject *arg, PyObject *kw) +{ + gdsa_sig s = GDSA_SIG_INIT; + char *p; + int n; + mp *k = 0; + PyObject *rc = 0; + char *kwlist[] = { "msg", "k", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", kwlist, + &p, &n, convmp, &k)) + goto end; + if (n != DSA_D(me)->h->hashsz) + VALERR("bad message length (doesn't match hash size)"); + gdsa_sign(DSA_D(me), &s, 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) +{ + char *p; + int n; + gdsa_sig s = GDSA_SIG_INIT; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "s#(O&O&):verify", + &p, &n, convmp, &s.r, convmp, &s.s)) + goto end; + if (n != DSA_D(me)->h->hashsz) + VALERR("bad message length (doesn't match hash size)"); + rc = getbool(gdsa_verify(DSA_D(me), &s, p)); +end: + mp_drop(s.r); + mp_drop(s.s); + return (rc); +} + +static PyObject *dsapriv_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + PyObject *G, *p, *u = Py_None, *rng = rand_pyobj, *hash = sha_pyobj; + PyObject *rc = 0; + char *kwlist[] = { "G", "p", "u", "hash", "rng", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!OO!:new", kwlist, + group_pytype, &G, + ge_pytype, &p, + &u, + gchash_pytype, &hash, + grand_pytype, &rng) || + (rc = dsa_setup(dsapriv_pytype, G, p, u, rng, hash)) == 0) + goto end; +end: + return (rc); +} + +static PyMethodDef dsapub_pymethods[] = { +#define METHNAME(name) dsameth_##name + METH (beginhash, "D.beginhash() -> hash object") + METH (endhash, "D.endhash(H) -> BYTES") + METH (verify, "D.verify(MSG, (R, S)) -> true/false") +#undef METHNAME + { 0 } +}; + +static PyMethodDef dsapriv_pymethods[] = { +#define METHNAME(name) dsameth_##name + KWMETH(sign, "D.sign(MSG, k = K) -> R, S") +#undef METHNAME + { 0 } +}; + +static 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 PyMemberDef dsapriv_pymembers[] = { +#define MEMBERSTRUCT dsa_pyobj + MEMBER(u, T_OBJECT, READONLY, "D.u -> private key (exponent)") +#undef MEMBERSTRUCT + { 0 } +}; + +static PyTypeObject dsapub_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"DSA public key information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + dsapub_pymethods, /* @tp_methods@ */ + dsapub_pymembers, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject dsapriv_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"DSA private key information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + dsapriv_pymethods, /* @tp_methods@ */ + dsapriv_pymembers, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *kcdsapub_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + PyObject *G, *p, *u = 0, *rng = rand_pyobj, *hash = has160_pyobj; + PyObject *rc = 0; + char *kwlist[] = { "G", "p", "u", "hash", "rng", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O!|OO!:new", kwlist, + group_pytype, &G, + ge_pytype, &p, + &u, + gchash_pytype, &hash, + grand_pytype, &rng) || + (rc = dsa_setup(kcdsapub_pytype, G, p, u, rng, hash)) == 0) + goto end; +end: + return (rc); +} + +static PyObject *kcdsapriv_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + PyObject *G, *p, *u = Py_None, *rng = rand_pyobj, *hash = has160_pyobj; + PyObject *rc = 0; + char *kwlist[] = { "G", "p", "u", "hash", "rng", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!OO!:new", kwlist, + group_pytype, &G, + ge_pytype, &p, + &u, + gchash_pytype, &hash, + grand_pytype, &rng) || + (rc = dsa_setup(kcdsapriv_pytype, G, p, u, rng, hash)) == 0) + goto end; +end: + return (rc); +} + +static PyObject *kcdsameth_beginhash(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":beginhash")) return (0); + return (ghash_pywrap(DSA_HASH(me), gkcdsa_beginhash(DSA_D(me)), f_freeme)); +} + +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, PyString_AS_STRING(rc)); + GH_DESTROY(h); + return (rc); +} + +static PyObject *kcdsameth_sign(PyObject *me, PyObject *arg, PyObject *kw) +{ + gkcdsa_sig s = GKCDSA_SIG_INIT; + char *p; + int n; + mp *k = 0; + PyObject *r = 0, *rc = 0; + char *kwlist[] = { "msg", "k", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", kwlist, + &p, &n, convmp, &k)) + goto end; + if (n != 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 = PyString_AS_STRING(r); + gkcdsa_sign(DSA_D(me), &s, p, k); + rc = Py_BuildValue("(NN)", r, mp_pywrap(s.s)); +end: + Py_XDECREF(r); + mp_drop(k); + return (rc); +} + +static PyObject *kcdsameth_verify(PyObject *me, PyObject *arg) +{ + char *p; + int n, rn; + gkcdsa_sig s = GKCDSA_SIG_INIT; + PyObject *rc = 0; + + if (!PyArg_ParseTuple(arg, "s#(s#O&):verify", + &p, &n, &s.r, &rn, convmp, &s.s)) + goto end; + if (n != DSA_D(me)->h->hashsz) + VALERR("bad message length (doesn't match hash size)"); + if (rn != DSA_D(me)->h->hashsz) + VALERR("bad signature `r' length (doesn't match hash size)"); + rc = getbool(gkcdsa_verify(DSA_D(me), &s, p)); +end: + mp_drop(s.s); + return (rc); +} + +static PyMethodDef kcdsapub_pymethods[] = { +#define METHNAME(name) kcdsameth_##name + METH (beginhash, "D.beginhash() -> hash object") + METH (endhash, "D.endhash(H) -> BYTES") + METH (verify, "D.verify(MSG, (R, S)) -> true/false") +#undef METHNAME + { 0 } +}; + +static PyMethodDef kcdsapriv_pymethods[] = { +#define METHNAME(name) kcdsameth_##name + KWMETH(sign, "D.sign(MSG, k = K) -> R, S") +#undef METHNAME + { 0 } +}; + +static PyTypeObject kcdsapub_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"KCDSA public key information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + kcdsapub_pymethods, /* @tp_methods@ */ + dsapub_pymembers, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject kcdsapriv_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"KCDSA private key information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + kcdsapriv_pymethods, /* @tp_methods@ */ + dsapriv_pymembers, /* @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@ */ + _PyObject_Del, /* @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; + char *kwlist[] = { "n", "e", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist, + convmp, &rp.n, convmp, &rp.e)) + goto end; + 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)); + PyObject_DEL(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; + char *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 (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)); + PyObject_DEL(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 != 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; + char *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 *meth__RSAPriv_generate(PyObject *me, + PyObject *arg, PyObject *kw) +{ + grand *r = &rand_global; + unsigned nbits; + unsigned n = 0; + rsa_priv rp; + pgev evt = { 0 }; + char *kwlist[] = { "class", "nbits", "event", "rng", "nsteps", 0 }; + PyObject *rc = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&:generate", kwlist, + &me, convuint, &nbits, convpgev, &evt, + convgrand, &r, convuint, &n)) + goto end; + if (rsa_gen(&rp, nbits, r, n, evt.proc, evt.ctx)) + PGENERR; + rc = rsapriv_pywrap(&rp); +end: + droppgev(&evt); + return (rc); +} + +static PyGetSetDef rsapub_pygetset[] = { +#define GETSETNAME(op, name) rsa##op##_##name + GET (n, "R.n -> N") + GET (e, "R.e -> E") +#undef GETSETNAME + { 0 } +}; + +static PyMethodDef rsapub_pymethods[] = { +#define METHNAME(name) rsameth_##name + METH (pubop, "R.pubop(X) -> X^E (mod N)") +#undef METHNAME + { 0 } +}; + +static 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 PyMethodDef rsapriv_pymethods[] = { +#define METHNAME(name) rsameth_##name + KWMETH(privop, "R.privop(X, rng = None) -> X^D (mod N)") +#undef METHNAME + { 0 } +}; + +static PyTypeObject rsapub_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"RSA public key information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + rsapub_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + rsapub_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject rsapriv_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"RSA private key information.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + rsapriv_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + rsapriv_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- RSA padding schemes -----------------------------------------------*/ + +static PyObject *meth__p1crypt_encode(PyObject *me, + PyObject *arg, PyObject *kw) +{ + pkcs1 p1; + char *m, *ep; + int msz, epsz; + unsigned long nbits; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x; + char *kwlist[] = { "msg", "nbits", "ep", "rng", 0 }; + + p1.r = &rand_global; ep = 0; epsz = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", kwlist, + &m, &msz, convulong, &nbits, + &ep, &epsz, convgrand, &p1.r)) + goto end; + sz = (nbits + 7)/8; + p1.ep = ep; p1.epsz = epsz; + if (epsz + msz + 11 > sz) VALERR("buffer underflow"); + b = xmalloc(sz); + x = pkcs1_cryptencode(MP_NEW, m, msz, 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; + char *ep; + int epsz; + unsigned long nbits; + int n; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x = 0; + char *kwlist[] = { "ct", "nbits", "ep", "rng", 0 }; + + p1.r = &rand_global; ep = 0; epsz = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|s#O&:decode", kwlist, + convmp, &x, convulong, &nbits, + &ep, &epsz, convgrand, &p1.r)) + goto end; + sz = (nbits + 7)/8; + p1.ep = ep; p1.epsz = epsz; + if (epsz + 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; + char *m, *ep; + int msz, epsz; + unsigned long nbits; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x; + char *kwlist[] = { "msg", "nbits", "ep", "rng", 0 }; + + p1.r = &rand_global; ep = 0; epsz = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", kwlist, + &m, &msz, convulong, &nbits, + &ep, &epsz, convgrand, &p1.r)) + goto end; + sz = (nbits + 7)/8; + p1.ep = ep; p1.epsz = epsz; + if (epsz + msz + 11 > sz) VALERR("buffer underflow"); + b = xmalloc(sz); + x = pkcs1_sigencode(MP_NEW, m, msz, 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; + char *ep; + int epsz; + unsigned long nbits; + int n; + PyObject *hukairz; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x = 0; + char *kwlist[] = { "msg", "sig", "nbits", "ep", "rng", 0 }; + + p1.r = &rand_global; ep = 0; epsz = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|s#O&:decode", kwlist, + &hukairz, convmp, &x, convulong, &nbits, + &ep, &epsz, convgrand, &p1.r)) + goto end; + sz = (nbits + 7)/8; + p1.ep = ep; p1.epsz = epsz; + if (epsz + 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; + char *m, *ep; + int msz, epsz; + unsigned long nbits; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x; + char *kwlist[] = { "msg", "nbits", "mgf", "hash", "ep", "rng", 0 }; + + o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha; ep = 0; epsz = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&s#O&:encode", kwlist, + &m, &msz, convulong, &nbits, + convgccipher, &o.cc, + convgchash, &o.ch, + &ep, &epsz, + convgrand, &o.r)) + goto end; + sz = (nbits + 7)/8; + o.ep = ep; o.epsz = epsz; + if (2 * o.ch->hashsz + 2 + msz > sz) VALERR("buffer underflow"); + b = xmalloc(sz); + x = oaep_encode(MP_NEW, m, msz, 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; + char *ep; + int epsz; + unsigned long nbits; + int n; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x = 0; + char *kwlist[] = { "ct", "nbits", "mgf", "hash", "ep", "rng", 0 }; + + o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha; ep = 0; epsz = 0; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&s#O&:decode", kwlist, + convmp, &x, convulong, &nbits, + convgccipher, &o.cc, + convgchash, &o.ch, + &ep, &epsz, + convgrand, &o.r)) + goto end; + sz = (nbits + 7)/8; + o.ep = ep; o.epsz = epsz; + 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; + char *m; + int msz; + unsigned long nbits; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + mp *x = 0; + char *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, "s#O&|O&O&O&O&:encode", kwlist, + &m, &msz, 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, msz, 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; + char *m; + int msz; + unsigned long nbits; + PyObject *rc = 0; + octet *b = 0; + size_t sz; + int n; + mp *x = 0; + char *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, "s#O&O&|O&O&O&O&:decode", kwlist, + &m, &msz, 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, msz, b, sz, nbits, &p)) < 0) + VALERR("verification failed"); + rc = Py_None; Py_INCREF(rc); +end: + mp_drop(x); + xfree(b); + return (rc); +} + +/*----- Global stuff ------------------------------------------------------*/ + +static 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) + KWMETH(_RSAPriv_generate, "\ +generate(NBITS, [event = pgen_nullev, rng = rand, nsteps = 0]) -> R") +#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); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/pwsafe b/pwsafe new file mode 100755 index 0000000..82e039d --- /dev/null +++ b/pwsafe @@ -0,0 +1,314 @@ +#! /usr/bin/python2.2 + +import catacomb as C +import gdbm, struct +from sys import argv, exit, stdin, stdout, stderr +from getopt import getopt, GetoptError +from os import environ +from fnmatch import fnmatch + +file = '%s/.pwsafe' % environ['HOME'] + +class DecryptError (Exception): + pass + +class Crypto (object): + def __init__(me, c, h, m, ck, mk): + me.c = c(ck) + me.m = m(mk) + me.h = h + def encrypt(me, pt): + if me.c.__class__.blksz: + iv = C.rand.block(me.c.__class__.blksz) + me.c.setiv(iv) + else: + iv = '' + y = iv + me.c.encrypt(pt) + t = me.m().hash(y).done() + return t + y + def decrypt(me, ct): + t = ct[:me.m.__class__.tagsz] + y = ct[me.m.__class__.tagsz:] + if t != me.m().hash(y).done(): + raise DecryptError + iv = y[:me.c.__class__.blksz] + if me.c.__class__.blksz: me.c.setiv(iv) + return me.c.decrypt(y[me.c.__class__.blksz:]) + +class PPK (Crypto): + def __init__(me, pp, c, h, m, salt = None): + if not salt: salt = C.rand.block(h.hashsz) + tag = '%s\0%s' % (pp, salt) + Crypto.__init__(me, c, h, m, + h().hash('cipher:' + tag).done(), + h().hash('mac:' + tag).done()) + me.salt = salt + +class Buffer (object): + def __init__(me, s): + me.str = s + me.i = 0 + def get(me, n): + i = me.i + if n + i > len(me.str): + raise IndexError, 'buffer underflow' + me.i += n + return me.str[i:i + n] + def getbyte(me): + return ord(me.get(1)) + def unpack(me, fmt): + return struct.unpack(fmt, me.get(struct.calcsize(fmt))) + def getstring(me): + return me.get(me.unpack('>H')[0]) + def checkend(me): + if me.i != len(me.str): + raise ValueError, 'junk at end of buffer' + +def wrapstr(s): + return struct.pack('>H', len(s)) + s + +class PWIter (object): + def __init__(me, pw): + me.pw = pw + me.k = me.pw.db.firstkey() + def next(me): + k = me.k + while True: + if k is None: + raise StopIteration + if k[0] == '$': + break + k = me.pw.db.nextkey(k) + me.k = me.pw.db.nextkey(k) + return me.pw.unpack(me.pw.db[k])[0] +class PW (object): + def __init__(me, file, mode = 'r'): + me.db = gdbm.open(file, mode) + c = C.gcciphers[me.db['cipher']] + h = C.gchashes[me.db['hash']] + m = C.gcmacs[me.db['mac']] + tag = me.db['tag'] + ppk = PPK(C.ppread(tag), c, h, m, me.db['salt']) + try: + buf = Buffer(ppk.decrypt(me.db['key'])) + except DecryptError: + C.ppcancel(tag) + raise + me.ck = buf.getstring() + me.mk = buf.getstring() + buf.checkend() + me.k = Crypto(c, h, m, me.ck, me.mk) + me.magic = me.k.decrypt(me.db['magic']) + def keyxform(me, key): + return '$' + me.k.h().hash(me.magic).hash(key).done() + def changepp(me): + tag = me.db['tag'] + C.ppcancel(tag) + ppk = PPK(C.ppread(tag, C.PMODE_VERIFY), + me.k.c.__class__, me.k.h, me.k.m.__class__) + me.db['key'] = ppk.encrypt(wrapstr(me.ck) + wrapstr(me.mk)) + me.db['salt'] = ppk.salt + def pack(me, key, value): + w = wrapstr(key) + wrapstr(value) + pl = (len(w) + 255) & ~255 + w += '\0' * (pl - len(w)) + return me.k.encrypt(w) + def unpack(me, p): + buf = Buffer(me.k.decrypt(p)) + key = buf.getstring() + value = buf.getstring() + return key, value + def __getitem__(me, key): + return me.unpack(me.db[me.keyxform(key)])[1] + def __setitem__(me, key, value): + me.db[me.keyxform(key)] = me.pack(key, value) + def __delitem__(me, key): + del me.db[me.keyxform(key)] + def __iter__(me): + return PWIter(me) + +def cmd_create(av): + cipher = 'blowfish-cbc' + hash = 'rmd160' + mac = None + try: + opts, args = getopt(av, 'c:h:m:', ['cipher=', 'mac=', 'hash=']) + except GetoptError: + return 1 + 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 + else: + raise 'Barf!' + if len(args) > 2: + return 1 + if len(args) >= 1: + tag = args[0] + else: + tag = 'pwsafe' + db = gdbm.open(file, 'n', 0600) + pp = C.ppread(tag, C.PMODE_VERIFY) + if not mac: mac = hash + '-hmac' + c = C.gcciphers[cipher] + h = C.gchashes[hash] + m = C.gcmacs[mac] + ppk = PPK(pp, c, h, m) + ck = C.rand.block(c.keysz.default) + mk = C.rand.block(m.keysz.default) + k = Crypto(c, h, m, ck, mk) + db['tag'] = tag + db['salt'] = ppk.salt + db['cipher'] = cipher + db['hash'] = hash + db['mac'] = mac + db['key'] = ppk.encrypt(wrapstr(ck) + wrapstr(mk)) + db['magic'] = k.encrypt(C.rand.block(h.hashsz)) + +def cmd_changepp(av): + if len(av) != 0: + return 1 + pw = PW(file, 'w') + pw.changepp() + +def cmd_find(av): + if len(av) != 1: + return 1 + pw = PW(file) + print pw[av[0]] + +def cmd_store(av): + if len(av) < 1 or len(av) > 2: + return 1 + tag = av[0] + if len(av) < 2: + pp = C.getpass("Enter passphrase `%s': " % tag) + vpp = C.getpass("Confirm passphrase `%s': " % tag) + if pp != vpp: + raise ValueError, "passphrases don't match" + elif av[1] == '-': + pp = stdin.readline() + else: + pp = av[1] + pw = PW(file, 'w') + pw[av[0]] = pp + +def cmd_copy(av): + if len(av) < 1 or len(av) > 2: + return 1 + pw_in = PW(file) + pw_out = PW(av[0], 'w') + if len(av) >= 3: + pat = av[1] + else: + pat = None + for k in pw_in: + if pat is None or fnmatch(k, pat): + pw_out[k] = pw_in[k] + +def cmd_list(av): + if len(av) > 1: + return 1 + pw = PW(file) + if len(av) >= 1: + pat = av[0] + else: + pat = None + for k in pw: + if pat is None or fnmatch(k, pat): + print k + +def cmd_topixie(av): + if len(av) < 1 or len(av) > 2: + return 1 + pw = PW(file) + tag = av[0] + if len(av) >= 2: + pptag = av[1] + else: + pptag = av[0] + C.Pixie().set(pptag, pw[tag]) + +def asciip(s): + for ch in s: + if ch < ' ' or ch > '~': return False + return True +def present(s): + if asciip(s): return s + return C.ByteString(s) +def cmd_dump(av): + db = gdbm.open(file, 'r') + k = db.firstkey() + while True: + if k is None: break + print '%r: %r' % (present(k), present(db[k])) + k = db.nextkey(k) + +commands = { 'create': [cmd_create, + '[-c CIPHER] [-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]'], + 'dump' : [cmd_dump, '']} + +def version(): + print 'pwsafe 1.0.0' +def usage(fp): + print >>fp, 'Usage: pwsafe COMMAND [ARGS...]' +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. + +Commands provided: +''' + for c in commands: + print '%s %s' % (c, commands[c][1]) + +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 'Barf!' +if len(argv) < 1: + usage(stderr) + exit(1) + +if argv[0] in commands: + c = argv[0] + argv = argv[1:] +else: + c = 'find' +if commands[c][0](argv): + print >>stderr, 'Usage: pwsafe %s %s' % (c, commands[c][1]) + exit(1) diff --git a/rand.c b/rand.c new file mode 100644 index 0000000..6ae19ca --- /dev/null +++ b/rand.c @@ -0,0 +1,1169 @@ +/* -*-c-*- + * + * $Id$ + * + * 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" + +/*----- Main code ---------------------------------------------------------*/ + +PyTypeObject *grand_pytype, *truerand_pytype; +PyTypeObject *lcrand_pytype, *fibrand_pytype; +PyTypeObject *dsarand_pytype, *bbs_pytype, *bbspriv_pytype; +PyTypeObject *sslprf_pytype, *tlsdx_pytype, *tlsprf_pytype; +PyObject *rand_pyobj; + +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; + + if (strcmp(r->ops->name, "rand") == 0) ty = truerand_pytype; + else if (strcmp(r->ops->name, "lcrand") == 0) ty = lcrand_pytype; + else if (strcmp(r->ops->name, "fibrand") == 0) ty = fibrand_pytype; + else if (strcmp(r->ops->name, "dsarand") == 0) ty = dsarand_pytype; + else if (strcmp(r->ops->name, "bbs") == 0) ty = bbs_pytype; + return (grand_dopywrap(ty, r, f)); +} + +CONVFUNC(grand, grand *, GRAND_R) + +static PyObject *grmeth_byte(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":byte")) return (0); + return (PyInt_FromLong(grand_byte(GRAND_R(me)))); +} + +static PyObject *grmeth_word(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":word")) return (0); + return (getu32(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 (PyInt_Check(m)) { + long mm = PyInt_AS_LONG(m); + if (mm < 0) + goto negative; + if (mm <= 0xffffffff) + return (PyInt_FromLong(grand_range(GRAND_R(me), mm))); + } + if ((x = getmp(m)) == 0) + goto end; + if (MP_NEGP(x)) + goto negative; + y = mprand_range(MP_NEW, x, GRAND_R(me), 0); + MP_DROP(x); + return (mp_pywrap(y)); +negative: + TYERR("range must be nonnegative"); +end: + if (x) MP_DROP(x); + return (0); +} + +static PyObject *grmeth_mp(PyObject *me, PyObject *arg, PyObject *kw) +{ + size_t l; + mpw o; + char *kwlist[] = { "bits", "or", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", kwlist, + convszt, &l, convmpw, &o)) + goto end; + 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; + rc = bytestring_pywrap(0, n); + grand_fill(GRAND_R(me), PyString_AS_STRING(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) || + 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) || + 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) +{ + char *p; + int n; + grand *r = GRAND_R(me); + if (!PyArg_ParseTuple(arg, "s#:seedblock", &p, &n) || + checkop(r, GRAND_SEEDBLOCK, "seedblock")) + goto end; + r->ops->misc(r, GRAND_SEEDBLOCK, p, (size_t)n); + 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) || + 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) +{ + char *kwlist[] = { "rng", 0 }; + grand *r = GRAND_R(me); + grand *rr = &rand_global; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", kwlist, + convgrand, &rr) || + 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); + char *p, *q; + int sz; + PyObject *rc; + + if (!PyArg_ParseTuple(arg, "s#:mask", &p, &sz)) return (0); + rc = bytestring_pywrap(0, sz); + q = PyString_AS_STRING(rc); + GR_FILL(r, q, sz); + while (sz--) *q++ ^= *p++; + return (rc); +} + +static void grand_pydealloc(PyObject *me) +{ + grand_pyobj *g = (grand_pyobj *)me; + if (g->f & f_freeme) + GR_DESTROY(g->r); + PyObject_DEL(me); +} + +static PyObject *grget_name(PyObject *me, void *hunoz) + { return (PyString_FromString(GRAND_R(me)->ops->name)); } + +static PyObject *grget_cryptop(PyObject *me, void *hunoz) + { return (getbool(GRAND_R(me)->ops->f & GRAND_CRYPTO)); } + +static 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 PyMethodDef grand_pymethods[] = { +#define METHNAME(name) grmeth_##name + METH (byte, "R.byte() -> BYTE") + METH (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 PyTypeObject grand_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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_iternexr@ */ + grand_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + grand_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *lcrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw) +{ + uint32 n = 0; + char *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 PyTypeObject lcrand_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Linear congruential generator.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyObject *fibrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw) +{ + uint32 n = 0; + char *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 PyTypeObject fibrand_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Fibonacci generator.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- True random generator ---------------------------------------------*/ + +static PyObject *trmeth_gate(PyObject *me, PyObject *arg) +{ + grand *r = GRAND_R(me); + if (!PyArg_ParseTuple(arg, ":gate")) return (0); + r->ops->misc(r, RAND_GATE); + RETURN_ME; +} + +static PyObject *trmeth_stretch(PyObject *me, PyObject *arg) +{ + grand *r = GRAND_R(me); + if (!PyArg_ParseTuple(arg, ":stretch")) return (0); + r->ops->misc(r, RAND_STRETCH); + RETURN_ME; +} + +static PyObject *trmeth_key(PyObject *me, PyObject *arg) +{ + grand *r = GRAND_R(me); + char *p; int n; + if (!PyArg_ParseTuple(arg, "s#:key", &p, &n)) return (0); + r->ops->misc(r, RAND_KEY, p, n); + 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, PyObject *arg) +{ + grand *r = GRAND_R(me); + if (!PyArg_ParseTuple(arg, ":timer")) return (0); + r->ops->misc(r, RAND_TIMER); + RETURN_ME; +} + +static PyObject *truerand_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + char *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 PyMethodDef truerand_pymethods[] = { +#define METHNAME(name) trmeth_##name + METH (gate, "R.gate()") + METH (stretch, "R.stretch()") + METH (key, "R.key(BYTES)") + METH (seed, "R.seed(NBITS)") + METH (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 PyGetSetDef truerand_pygetset[] = { +#define GETSETNAME(op, name) tr##op##_##name + GET (goodbits, "R.goodbits -> good bits of entropy remaining") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject truerand_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"True random number source.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + truerand_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + truerand_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- SSL and TLS generators --------------------------------------------*/ + +static PyObject *sslprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *k, *s; + int ksz, ssz; + const gchash *hco = &md5, *hci = &sha; + PyObject *rc = 0; + char *kwlist[] = { "key", "seed", "ohash", "ihash", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", kwlist, + &k, &ksz, &s, &ssz, + convgchash, &hco, convgchash, &hci)) + goto end; + rc = grand_dopywrap(ty, sslprf_rand(hco, hci, k, ksz, s, ssz), f_freeme); +end: + return (rc); +} + +static PyObject *tlsdx_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *k, *s; + int ksz, ssz; + const gcmac *mc = &sha_hmac; + PyObject *rc = 0; + char *kwlist[] = { "key", "seed", "mac", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&:new", kwlist, + &k, &ksz, &s, &ssz, + convgcmac, &mc)) + goto end; + rc = grand_dopywrap(ty, tlsdx_rand(mc, k, ksz, s, ssz), f_freeme); +end: + return (rc); +} + +static PyObject *tlsprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *k, *s; + int ksz, ssz; + const gcmac *mcl = &md5_hmac, *mcr = &sha_hmac; + PyObject *rc = 0; + char *kwlist[] = { "key", "seed", "lmac", "rmac", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", kwlist, + &k, &ksz, &s, &ssz, + convgcmac, &mcl, convgcmac, &mcr)) + goto end; + rc = grand_dopywrap(ty, tlsprf_rand(mcl, mcr, k, ksz, s, ssz), f_freeme); +end: + return (rc); +} + +static PyTypeObject sslprf_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Random number generator for SSL master secret.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject tlsdx_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"TLS data expansion function.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static PyTypeObject tlsprf_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"TLS pseudorandom function.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- DSA generator -----------------------------------------------------*/ + +static PyObject *dsarand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *p; + int sz; + PyObject *rc = 0; + char *kwlist[] = { "seed", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &sz)) + goto end; + rc = grand_dopywrap(ty, dsarand_create(p, sz), f_freeme); +end: + return (0); +} + +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, PyString_AS_STRING(rc)); + return (rc); +} + +static PyGetSetDef dsarand_pygetset[] = { +#define GETSETNAME(op, name) dr##op##_##name + GET (seed, "R.seed -> current generator seed") +#undef GETSETNAME + { 0 } +}; + +static PyTypeObject dsarand_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Pseudorandom number generator for constructing DSA parameters.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + dsarand_pygetset, /* @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@ */ + _PyObject_Del, /* @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; + char *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, PyObject *arg) +{ + grand *r = GRAND_R(me); if (!PyArg_ParseTuple(arg, ":step")) return (0); + 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 (getu32(w)); +end: + return (0); +} + +static PyObject *bbsmeth_wrap(PyObject *me, PyObject *arg) +{ + grand *r = GRAND_R(me); if (!PyArg_ParseTuple(arg, ":wrap")) return (0); + 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 ((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 PyMethodDef bbs_pymethods[] = { +#define METHNAME(name) bbsmeth_##name + METH (step, "R.step(): steps the generator (not useful)") + METH (bits, "R.bits(N) -> W: returns N bits (<= 32) from the generator") + METH (wrap, "R.wrap(): flushes unused bits in internal buffer") +#undef METHNAME + { 0 } +}; + +static 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 PyTypeObject bbs_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Blum-Blum-Shub strong pseudorandom number generator.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + bbs_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + bbs_pygetset, /* @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@ */ + _PyObject_Del, /* @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; + char *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 *meth__BBSPriv_generate(PyObject *me, + PyObject *arg, PyObject *kw) +{ + bbs_priv bp = { 0 }; + mp *x = MP_TWO; + pgev evt = { 0 }; + unsigned nbits, n = 0; + grand *r = &rand_global; + char *kwlist[] = { "class", "nbits", "event", "rng", "nsteps", "seed", 0 }; + bbspriv_pyobj *rc = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist, + &me, convuint, &nbits, convpgev, &evt, + convgrand, &r, convuint, &n, convmp, &x)) + goto end; + if (bbs_gen(&bp, nbits, r, n, evt.proc, evt.ctx)) + VALERR("prime genration failed"); + 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 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") +#undef METHNAME + { 0 } +}; + +static 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 PyTypeObject bbspriv_pytype_skel = { + PyObject_HEAD_INIT(&PyType_Type) 0, /* Header */ + "catacomb.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@ */ +"Blum-Blum-Shub strong pseudorandom generator, with private key.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + bbspriv_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + bbspriv_pygetset, /* @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@ */ + _PyObject_Del, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Global stuff ------------------------------------------------------*/ + +static PyMethodDef methods[] = { +#define METHNAME(name) meth_##name + KWMETH(_BBSPriv_generate, "\ +generate(NBITS, [event = pgen_nullev, rng = rand, nsteps = 0, seed = 2])") +#undef METHNAME + { 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); + rand_noisesrc(RAND_GLOBAL, &noise_source); + rand_seed(RAND_GLOBAL, 160); + addmethods(methods); +} + +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); + rand_pyobj = grand_pywrap(&rand_global, 0); Py_INCREF(rand_pyobj); + INSERT("rand", rand_pyobj); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ad75776 --- /dev/null +++ b/setup.py @@ -0,0 +1,114 @@ +#! /usr/bin/python + +from distutils.core import setup, Extension +from os import * +from os import _exit +from errno import * +import sys +from sys import stdin, stdout, stderr + +def progoutput(cmd): + p = popen(cmd) + out = p.readline() + if p.read() != '': raise 'extra junk from %s' % cmd + p.close() + return out.rstrip('\n') + +incdirs = [] +libdirs = [] +libs = [] + +def libconfig(lib, ver): + config = lib + '-config' + if system('%s --check %s' % (config, ver)): + raise '%s version %s not found' % (lib, ver) + version = progoutput('%s --version' % config) + for i in progoutput('%s --cflags' % config).split(): + if i[:2] == '-I': incdirs.append(i[2:]) + else: raise 'strange cflags item %s' % i + for i in progoutput('%s --libs' % config).split(): + if i[:2] == '-L': libdirs.append(i[2:]) + elif i[:2] == '-l': libs.append(i[2:]) + else: raise 'strange libs item %s' % i + +def uniquify(l): + u = {} + o = [] + for i in l: + if i not in u: + o.append(i) + u[i] = 1 + return o + +cflags = [] +libs = [] +libconfig('mLib', '2.0.3') +libconfig('catacomb', '2.1.0') + +class SubprocessFailure (Exception): + def __init__(me, file, rc): + me.args = (file, rc) + me.file = file + me.rc = rc + def __str__(me): + if WIFEXITED(me.rc): + return '%s failed (rc = %d)' % (me.file, WEXITSTATUS(me.rc)) + elif WIFSIGNALED(me.rc): + return '%s died (signal %d)' % (me.file, WTERMSIG(me.rc)) + else: + return '%s died inexplicably' % (me.file) + +for g in ['algorithms.h']: + if type(g) == tuple: + fin, fout = g + else: + root, ext = path.splitext(g) + fin = root + '.py' + fout = g + stin = stat(fin) + try: + stout = stat(fout) + updatep = stin.st_mtime > stout.st_mtime + except OSError, err: + if err.errno == ENOENT: + updatep = True + else: + raise + if updatep: + kid = fork() + print 'running %s to create %s' % (fin, fout) + fnew = fout + '.new' + if kid == 0: + try: + out = file(fnew, 'w') + dup2(out.fileno(), stdout.fileno()) + out.close() + execl(sys.executable, sys.executable, fin) + except: + stderr.write('error running %s -> %s: %s\n' % + (fin, fout, sys.exc_info()[1])) + _exit(127) + _, rc = waitpid(kid, 0) + if rc: + raise SubprocessFailure, (fin, rc) + rename(fnew, fout) + +cat = Extension('catacomb._base', + ['catacomb.c', 'bytestring.c', + 'rand.c', 'algorithms.c', 'pubkey.c', 'pgen.c', + 'mp.c', 'field.c', 'ec.c', 'group.c', 'passphrase.c'], + ##extra_compile_args = ['-O0'], + include_dirs = uniquify(incdirs), + library_dirs = uniquify(libdirs), + libraries = uniquify(libs)) + +setup(name = 'Catacomb', + version = '2.1.0', + description = 'Interface to Catacomb cryptographic library', + url = 'http://tux.nsict.org/~mdw/Catacomb-2.1.0', + author = 'Straylight/Edgeware', + author_email = 'mdw@nsict.org', + license = 'GNU General Public License', + packages = ['catacomb'], + scripts = ['pwsafe'], + ext_modules = [cat])