From d7ab1bab81155baa763449d5afa81e16df98dbe7 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sun, 10 Apr 2005 18:03:11 +0000 Subject: [PATCH] Initial check-in of catacomb-python. Organization: Straylight/Edgeware From: mdw --- .gdbinit | 4 + .skelrc | 9 + MANIFEST.in | 3 + Makefile | 19 + README | 69 ++ algorithms.c | 1192 ++++++++++++++++++++++ algorithms.h | 276 +++++ algorithms.py | 72 ++ bytestring.c | 183 ++++ catacomb-python.h | 520 ++++++++++ catacomb.c | 279 +++++ catacomb/__init__.py | 472 +++++++++ ec.c | 1561 ++++++++++++++++++++++++++++ field.c | 998 ++++++++++++++++++ gf.c | 61 ++ group.c | 1365 +++++++++++++++++++++++++ key.c | 41 + mp.c | 2290 ++++++++++++++++++++++++++++++++++++++++++ passphrase.c | 244 +++++ pgen.c | 1078 ++++++++++++++++++++ pubkey.c | 1155 +++++++++++++++++++++ pwsafe | 314 ++++++ rand.c | 1169 +++++++++++++++++++++ setup.py | 114 +++ 24 files changed, 13488 insertions(+) create mode 100644 .gdbinit create mode 100644 .skelrc create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README create mode 100644 algorithms.c create mode 100644 algorithms.h create mode 100644 algorithms.py create mode 100644 bytestring.c create mode 100644 catacomb-python.h create mode 100644 catacomb.c create mode 100644 catacomb/__init__.py create mode 100644 ec.c create mode 100644 field.c create mode 100644 gf.c create mode 100644 group.c create mode 100644 key.c create mode 100644 mp.c create mode 100644 passphrase.c create mode 100644 pgen.c create mode 100644 pubkey.c create mode 100755 pwsafe create mode 100644 rand.c create mode 100644 setup.py 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]) -- [mdw]