chiark / gitweb /
Initial check-in of catacomb-python.
authormdw <mdw>
Sun, 10 Apr 2005 18:03:11 +0000 (18:03 +0000)
committermdw <mdw>
Sun, 10 Apr 2005 18:03:11 +0000 (18:03 +0000)
24 files changed:
.gdbinit [new file with mode: 0644]
.skelrc [new file with mode: 0644]
MANIFEST.in [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
algorithms.c [new file with mode: 0644]
algorithms.h [new file with mode: 0644]
algorithms.py [new file with mode: 0644]
bytestring.c [new file with mode: 0644]
catacomb-python.h [new file with mode: 0644]
catacomb.c [new file with mode: 0644]
catacomb/__init__.py [new file with mode: 0644]
ec.c [new file with mode: 0644]
field.c [new file with mode: 0644]
gf.c [new file with mode: 0644]
group.c [new file with mode: 0644]
key.c [new file with mode: 0644]
mp.c [new file with mode: 0644]
passphrase.c [new file with mode: 0644]
pgen.c [new file with mode: 0644]
pubkey.c [new file with mode: 0644]
pwsafe [new file with mode: 0755]
rand.c [new file with mode: 0644]
setup.py [new file with mode: 0644]

diff --git a/.gdbinit b/.gdbinit
new file mode 100644 (file)
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 (file)
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 (file)
index 0000000..a00416c
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..6eda802
--- /dev/null
@@ -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 (file)
index 0000000..26099eb
--- /dev/null
@@ -0,0 +1,276 @@
+/* algorithms.h [generated] */
+
+#include <catacomb/des.h>
+#include <catacomb/des-ecb.h>
+#include <catacomb/des-cbc.h>
+#include <catacomb/des-cfb.h>
+#include <catacomb/des-ofb.h>
+#include <catacomb/des-counter.h>
+#include <catacomb/desx.h>
+#include <catacomb/desx-ecb.h>
+#include <catacomb/desx-cbc.h>
+#include <catacomb/desx-cfb.h>
+#include <catacomb/desx-ofb.h>
+#include <catacomb/desx-counter.h>
+#include <catacomb/des3.h>
+#include <catacomb/des3-ecb.h>
+#include <catacomb/des3-cbc.h>
+#include <catacomb/des3-cfb.h>
+#include <catacomb/des3-ofb.h>
+#include <catacomb/des3-counter.h>
+#include <catacomb/mars.h>
+#include <catacomb/mars-ecb.h>
+#include <catacomb/mars-cbc.h>
+#include <catacomb/mars-cfb.h>
+#include <catacomb/mars-ofb.h>
+#include <catacomb/mars-counter.h>
+#include <catacomb/idea.h>
+#include <catacomb/idea-ecb.h>
+#include <catacomb/idea-cbc.h>
+#include <catacomb/idea-cfb.h>
+#include <catacomb/idea-ofb.h>
+#include <catacomb/idea-counter.h>
+#include <catacomb/safer.h>
+#include <catacomb/safer-ecb.h>
+#include <catacomb/safer-cbc.h>
+#include <catacomb/safer-cfb.h>
+#include <catacomb/safer-ofb.h>
+#include <catacomb/safer-counter.h>
+#include <catacomb/safersk.h>
+#include <catacomb/safersk-ecb.h>
+#include <catacomb/safersk-cbc.h>
+#include <catacomb/safersk-cfb.h>
+#include <catacomb/safersk-ofb.h>
+#include <catacomb/safersk-counter.h>
+#include <catacomb/blowfish.h>
+#include <catacomb/blowfish-ecb.h>
+#include <catacomb/blowfish-cbc.h>
+#include <catacomb/blowfish-cfb.h>
+#include <catacomb/blowfish-ofb.h>
+#include <catacomb/blowfish-counter.h>
+#include <catacomb/twofish.h>
+#include <catacomb/twofish-ecb.h>
+#include <catacomb/twofish-cbc.h>
+#include <catacomb/twofish-cfb.h>
+#include <catacomb/twofish-ofb.h>
+#include <catacomb/twofish-counter.h>
+#include <catacomb/tea.h>
+#include <catacomb/tea-ecb.h>
+#include <catacomb/tea-cbc.h>
+#include <catacomb/tea-cfb.h>
+#include <catacomb/tea-ofb.h>
+#include <catacomb/tea-counter.h>
+#include <catacomb/xtea.h>
+#include <catacomb/xtea-ecb.h>
+#include <catacomb/xtea-cbc.h>
+#include <catacomb/xtea-cfb.h>
+#include <catacomb/xtea-ofb.h>
+#include <catacomb/xtea-counter.h>
+#include <catacomb/rc2.h>
+#include <catacomb/rc2-ecb.h>
+#include <catacomb/rc2-cbc.h>
+#include <catacomb/rc2-cfb.h>
+#include <catacomb/rc2-ofb.h>
+#include <catacomb/rc2-counter.h>
+#include <catacomb/rc5.h>
+#include <catacomb/rc5-ecb.h>
+#include <catacomb/rc5-cbc.h>
+#include <catacomb/rc5-cfb.h>
+#include <catacomb/rc5-ofb.h>
+#include <catacomb/rc5-counter.h>
+#include <catacomb/skipjack.h>
+#include <catacomb/skipjack-ecb.h>
+#include <catacomb/skipjack-cbc.h>
+#include <catacomb/skipjack-cfb.h>
+#include <catacomb/skipjack-ofb.h>
+#include <catacomb/skipjack-counter.h>
+#include <catacomb/cast128.h>
+#include <catacomb/cast128-ecb.h>
+#include <catacomb/cast128-cbc.h>
+#include <catacomb/cast128-cfb.h>
+#include <catacomb/cast128-ofb.h>
+#include <catacomb/cast128-counter.h>
+#include <catacomb/cast256.h>
+#include <catacomb/cast256-ecb.h>
+#include <catacomb/cast256-cbc.h>
+#include <catacomb/cast256-cfb.h>
+#include <catacomb/cast256-ofb.h>
+#include <catacomb/cast256-counter.h>
+#include <catacomb/square.h>
+#include <catacomb/square-ecb.h>
+#include <catacomb/square-cbc.h>
+#include <catacomb/square-cfb.h>
+#include <catacomb/square-ofb.h>
+#include <catacomb/square-counter.h>
+#include <catacomb/rijndael.h>
+#include <catacomb/rijndael-ecb.h>
+#include <catacomb/rijndael-cbc.h>
+#include <catacomb/rijndael-cfb.h>
+#include <catacomb/rijndael-ofb.h>
+#include <catacomb/rijndael-counter.h>
+#include <catacomb/rijndael192.h>
+#include <catacomb/rijndael192-ecb.h>
+#include <catacomb/rijndael192-cbc.h>
+#include <catacomb/rijndael192-cfb.h>
+#include <catacomb/rijndael192-ofb.h>
+#include <catacomb/rijndael192-counter.h>
+#include <catacomb/rijndael256.h>
+#include <catacomb/rijndael256-ecb.h>
+#include <catacomb/rijndael256-cbc.h>
+#include <catacomb/rijndael256-cfb.h>
+#include <catacomb/rijndael256-ofb.h>
+#include <catacomb/rijndael256-counter.h>
+#include <catacomb/serpent.h>
+#include <catacomb/serpent-ecb.h>
+#include <catacomb/serpent-cbc.h>
+#include <catacomb/serpent-cfb.h>
+#include <catacomb/serpent-ofb.h>
+#include <catacomb/serpent-counter.h>
+#include <catacomb/noekeon.h>
+#include <catacomb/noekeon-ecb.h>
+#include <catacomb/noekeon-cbc.h>
+#include <catacomb/noekeon-cfb.h>
+#include <catacomb/noekeon-ofb.h>
+#include <catacomb/noekeon-counter.h>
+#include <catacomb/rc4.h>
+#include <catacomb/seal.h>
+
+#include <catacomb/md2.h>
+#include <catacomb/md2-mgf.h>
+#include <catacomb/md2-hmac.h>
+#include <catacomb/md4.h>
+#include <catacomb/md4-mgf.h>
+#include <catacomb/md4-hmac.h>
+#include <catacomb/md5.h>
+#include <catacomb/md5-mgf.h>
+#include <catacomb/md5-hmac.h>
+#include <catacomb/tiger.h>
+#include <catacomb/tiger-mgf.h>
+#include <catacomb/tiger-hmac.h>
+#include <catacomb/has160.h>
+#include <catacomb/has160-mgf.h>
+#include <catacomb/has160-hmac.h>
+#include <catacomb/sha.h>
+#include <catacomb/sha-mgf.h>
+#include <catacomb/sha-hmac.h>
+#include <catacomb/sha224.h>
+#include <catacomb/sha224-mgf.h>
+#include <catacomb/sha224-hmac.h>
+#include <catacomb/sha256.h>
+#include <catacomb/sha256-mgf.h>
+#include <catacomb/sha256-hmac.h>
+#include <catacomb/sha384.h>
+#include <catacomb/sha384-mgf.h>
+#include <catacomb/sha384-hmac.h>
+#include <catacomb/sha512.h>
+#include <catacomb/sha512-mgf.h>
+#include <catacomb/sha512-hmac.h>
+#include <catacomb/rmd128.h>
+#include <catacomb/rmd128-mgf.h>
+#include <catacomb/rmd128-hmac.h>
+#include <catacomb/rmd160.h>
+#include <catacomb/rmd160-mgf.h>
+#include <catacomb/rmd160-hmac.h>
+#include <catacomb/rmd256.h>
+#include <catacomb/rmd256-mgf.h>
+#include <catacomb/rmd256-hmac.h>
+#include <catacomb/rmd320.h>
+#include <catacomb/rmd320-mgf.h>
+#include <catacomb/rmd320-hmac.h>
+#include <catacomb/whirlpool.h>
+#include <catacomb/whirlpool-mgf.h>
+#include <catacomb/whirlpool-hmac.h>
+#include <catacomb/whirlpool256.h>
+#include <catacomb/whirlpool256-mgf.h>
+#include <catacomb/whirlpool256-hmac.h>
+
+#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 (file)
index 0000000..cc209c6
--- /dev/null
@@ -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 <catacomb/%s.h>' % i
+  for j in pmodes:
+    print '#include <catacomb/%s-%s.h>' % (i, j)
+for i in streamciphers:
+  print '#include <catacomb/%s.h>' % i  
+print
+for i in hashes:
+  print '#include <catacomb/%s.h>' % i
+  for j in hmodes:
+    print '#include <catacomb/%s-%s.h>' % (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 (file)
index 0000000..c649125
--- /dev/null
@@ -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 (file)
index 0000000..c93ef07
--- /dev/null
@@ -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 <Python.h>
+#include <longintrepr.h>
+#include <structmember.h>
+
+#include <mLib/darray.h>
+#include <mLib/dstr.h>
+#include <mLib/macros.h>
+
+#include <catacomb/buf.h>
+
+#include <catacomb/grand.h>
+#include <catacomb/rand.h>
+#include <catacomb/noise.h>
+#include <catacomb/bbs.h>
+#include <catacomb/mprand.h>
+#include <catacomb/lcrand.h>
+#include <catacomb/fibrand.h>
+#include <catacomb/dsarand.h>
+#include <catacomb/sslprf.h>
+#include <catacomb/tlsprf.h>
+
+#include <catacomb/gcipher.h>
+#include <catacomb/ghash.h>
+#include <catacomb/gmac.h>
+#include <catacomb/md5.h>
+#include <catacomb/md5-hmac.h>
+#include <catacomb/sha.h>
+#include <catacomb/sha-mgf.h>
+#include <catacomb/sha-hmac.h>
+
+#include <catacomb/mp.h>
+#include <catacomb/mpint.h>
+#include <catacomb/mpmul.h>
+#include <catacomb/mpcrt.h>
+#include <catacomb/mpmont.h>
+#include <catacomb/mpbarrett.h>
+#include <catacomb/mpreduce.h>
+
+#include <catacomb/pgen.h>
+#include <catacomb/pfilt.h>
+#include <catacomb/strongprime.h>
+#include <catacomb/limlee.h>
+#include <catacomb/dh.h>
+#include <catacomb/ptab.h>
+#include <catacomb/bintab.h>
+#include <catacomb/dsa.h>
+
+#include <catacomb/gf.h>
+#include <catacomb/gfreduce.h>
+#include <catacomb/gfn.h>
+
+#include <catacomb/field.h>
+#include <catacomb/field-guts.h>
+
+#include <catacomb/ec.h>
+#include <catacomb/ec-raw.h>
+#include <catacomb/ectab.h>
+
+#include <catacomb/group.h>
+#include <catacomb/group-guts.h>
+
+#include <catacomb/gdsa.h>
+#include <catacomb/gkcdsa.h>
+#include <catacomb/rsa.h>
+
+#include <catacomb/key.h>
+#include <catacomb/passphrase.h>
+#include <catacomb/pixie.h>
+
+/*----- 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 (file)
index 0000000..742ce5f
--- /dev/null
@@ -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 (file)
index 0000000..b7f40fa
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..a72bd90
--- /dev/null
@@ -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 (file)
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 (file)
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 (executable)
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 (file)
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 (file)
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])