Checkin, Debianized and more or less complete.
authormdw <mdw>
Mon, 26 Sep 2005 12:58:11 +0000 (12:58 +0000)
committermdw <mdw>
Mon, 26 Sep 2005 12:58:11 +0000 (12:58 +0000)
18 files changed:
.gdbinit
MANIFEST.in
Makefile
algorithms.c
buffer.c [new file with mode: 0644]
bytestring.c
catacomb-python.h
catacomb.c
catacomb/__init__.py
debian/changelog [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/rules [new file with mode: 0755]
ec.c
key.c
pubkey.c
setup.py
share.c [new file with mode: 0644]

index b48f913..3be9630 100644 (file)
--- a/.gdbinit
+++ b/.gdbinit
@@ -3,3 +3,4 @@ dir py/python2.4-2.4+2.4.1rc2
 set env LD_LIBRARY_PATH=/home/mdw/src/catacomb/deb-build/.libs
 set env PYTHONPATH=/usr/lib/python2.4/lib-dynload
 cd build/lib.linux-i686-2.4
+## set args test.py
index a00416c..eed9938 100644 (file)
@@ -1,3 +1,7 @@
 include README Makefile MANIFEST MANIFEST.in
 include *.c *.h
-recursive-include catacomb *.py
+include algorithms.py
+include pwsafe
+include debian/rules debian/control debian/changelog debian/copyright
+recursive-include catacomb *.py 
+prune py
index 37f171f..b0564e4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 ## Makefile
 
 PYTHON = python
-prefix=/usr/local
+prefix = /usr/local
 
 all: setup.py
        $(PYTHON) setup.py build
@@ -16,4 +16,4 @@ dist: setup.py
 install: setup.py
        $(PYTHON) setup.py install --prefix $(prefix)
 
-.PHONY: all clean dist
+.PHONY: all clean dist install
index 2113cc5..bd094fd 100644 (file)
@@ -777,9 +777,48 @@ static PyGetSetDef gchash_pygetset[] = {
   { 0 }
 };
 
+#define GHMETH_HASHU_(n, W, w)                                         \
+  static PyObject *ghmeth_hashu##w(PyObject *me, PyObject *arg)                \
+  {                                                                    \
+    uint##n x;                                                         \
+    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end; \
+    GH_HASHU##W(GHASH_H(me), x);                                       \
+    RETURN_ME;                                                         \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTCONV(GHMETH_HASHU_)
+
+#define GHMETH_HASHBUF_(n, W, w)                                       \
+  static PyObject *ghmeth_hashbuf##w(PyObject *me, PyObject *arg)      \
+  {                                                                    \
+    char *p;                                                           \
+    int sz;                                                            \
+    if (!PyArg_ParseTuple(arg, "s#:hashbuf" #w, &p, &sz)) goto end;    \
+    if (sz > MASK##n) TYERR("string too long");                                \
+    GH_HASHBUF##W(GHASH_H(me), p, sz);                                 \
+    RETURN_ME;                                                         \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTCONV(GHMETH_HASHBUF_)
+
+static PyObject *ghmeth_hashstrz(PyObject *me, PyObject *arg)
+{
+  char *p;
+  if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+  GH_HASHSTRZ(GHASH_H(me), p);
+  RETURN_ME;
+}
+
 static PyMethodDef ghash_pymethods[] = {
 #define METHNAME(name) ghmeth_##name
   METH (hash,                  "H.hash(M)")
+#define METHU_(n, W, w) METH(hashu##w, "H.hashu" #w "(WORD)")
+  DOUINTCONV(METHU_)
+#define METHBUF_(n, W, w) METH(hashbuf##w, "H.hashbuf" #w "(BYTES)")
+  DOUINTCONV(METHBUF_)
+  METH (hashstrz,              "H.hashstrz(STRING)")
   METH (done,                  "H.done() -> HASH")
 #undef METHNAME
   { 0 }
diff --git a/buffer.c b/buffer.c
new file mode 100644 (file)
index 0000000..8dc885a
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,540 @@
+/* -*-c-*-
+ *
+ * $Id$
+ *
+ * Reading and writing buffers of stuff
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the Python interface to Catacomb.
+ *
+ * Catacomb/Python is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * Catacomb/Python is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Catacomb/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct buf_pyobj {
+  PyObject_HEAD
+  buf b;
+} buf_pyobj;
+
+static PyTypeObject *rbuf_pytype, *wbuf_pytype;
+#define RBUF_PYCHECK(o) PyObject_TypeCheck((o), rbuf_pytype)
+#define WBUF_PYCHECK(o) PyObject_TypeCheck((o), wbuf_pytype)
+#define BUF_B(o) (&((buf_pyobj *)(o))->b)
+
+/*----- Exceptions --------------------------------------------------------*/
+
+static PyObject *buferr;
+
+#define BUFERR() do { PyErr_SetNone(buferr); goto end; } while (0)
+
+/*----- Read buffers ------------------------------------------------------*/
+
+static PyObject *rbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  char *p, *q;
+  int n;
+  buf_pyobj *me = 0;
+  static char *kwlist[] = { "data", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &n))
+    goto end;
+  me = (buf_pyobj *)ty->tp_alloc(ty, 0);
+  q = xmalloc(n);
+  memcpy(q, p, n);
+  buf_init(&me->b, q, n);
+end:
+  return ((PyObject *)me);
+}
+
+static void buf_pydealloc(PyObject *me)
+  { xfree(BBASE(BUF_B(me))); FREEOBJ(me); }
+
+static int rbuf_pysegcount(PyObject *me, int *nn)
+  { if (nn) *nn = BSZ(BUF_B(me)); return (1); }
+
+static int rbuf_pyreadbuf(PyObject *me, int seg, void **q)
+  { assert(seg == 0); *q = BBASE(BUF_B(me)); return (BSZ(BUF_B(me))); }
+
+static PyObject *rbmeth_skip(PyObject *me, PyObject *arg)
+{
+  size_t n;
+
+  if (!PyArg_ParseTuple(arg, "O&:skip", convszt, &n)) goto end;
+  if (!buf_get(BUF_B(me), n)) BUFERR();
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *rbmeth_get(PyObject *me, PyObject *arg)
+{
+  void *p;
+  size_t n;
+
+  if (!PyArg_ParseTuple(arg, "O&:get", convszt, &n)) goto end;
+  if ((p = buf_get(BUF_B(me), n)) == 0) BUFERR();
+  return (bytestring_pywrap(p, n));
+end:
+  return (0);
+}
+
+#define RBMETH_GETU_(n, W, w)                                          \
+  static PyObject *rbmeth_getu##w(PyObject *me, PyObject *arg)         \
+  {                                                                    \
+    uint##n x;                                                         \
+    if (!PyArg_ParseTuple(arg, ":getu" #w)) goto end;                  \
+    if (buf_getu##w(BUF_B(me), &x)) BUFERR();                          \
+    return (getu32(x));                                                        \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTCONV(RBMETH_GETU_)
+
+#define RBMETH_GETBLK_(n, W, w)                                                \
+  static PyObject *rbmeth_getblk##w(PyObject *me, PyObject *arg)       \
+  {                                                                    \
+    size_t sz;                                                         \
+    char *q;                                                           \
+    if (!PyArg_ParseTuple(arg, ":getblk" #w)) goto end;                        \
+    if ((q = buf_getmem##w(BUF_B(me), &sz)) == 0) BUFERR();            \
+    return (bytestring_pywrap(q, sz));                                 \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+BUF_DOSUFFIXES(RBMETH_GETBLK_)
+
+static PyObject *rbmeth_getmp(PyObject *me, PyObject *arg)
+{
+  mp *x;
+  if (!PyArg_ParseTuple(arg, ":getmp")) goto end;
+  if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR();
+  return (mp_pywrap(x));
+end:
+  return (0);
+}
+
+static PyObject *rbmeth_getgf(PyObject *me, PyObject *arg)
+{
+  mp *x;
+  if (!PyArg_ParseTuple(arg, ":getgf")) goto end;
+  if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR();
+  return (gf_pywrap(x));
+end:
+  return (0);
+}
+
+static PyObject *rbmeth_getecpt(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  PyObject *cobj = Py_None;
+  static char *kwlist[] = { "curve", 0 };
+  ec pt = EC_INIT;
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", kwlist, &cobj))
+    goto end;
+  if (cobj == Py_None) cobj = (PyObject *)ecpt_pytype;
+  if (!PyType_Check(cobj) ||
+      !PyType_IsSubtype((PyTypeObject *)cobj, ecpt_pytype))
+    TYERR("expected elliptic curve type");
+  if (buf_getec(BUF_B(me), &pt)) BUFERR();
+  return (ecpt_pywrapout(cobj, &pt));
+end:
+  return (0);
+}
+    
+static PyObject *rbmeth_getecptraw(PyObject *me, PyObject *arg)
+{
+  PyTypeObject *cobj = ecpt_pytype;
+  ec pt = EC_INIT;
+  if (!PyArg_ParseTuple(arg, "O!:getecptraw", eccurve_pytype, &cobj))
+    goto end;
+  if (ec_getraw(ECCURVE_C(cobj), BUF_B(me), &pt)) BUFERR();
+  return (ecpt_pywrapout(cobj, &pt));
+end:
+  return (0);
+}
+
+static PyObject *rbmeth_getge(PyObject *me, PyObject *arg)
+{
+  PyObject *gobj;
+  ge *x = 0;
+  if (!PyArg_ParseTuple(arg, "O!:getge", group_pytype, &gobj)) goto end;
+  x = G_CREATE(GROUP_G(gobj));
+  if (G_FROMBUF(GROUP_G(gobj), BUF_B(me), x)) BUFERR();
+  return (ge_pywrap(gobj, x));
+end:
+  if (x) G_DESTROY(GROUP_G(gobj), x);
+  return (0);
+}
+
+static PyObject *rbmeth_getgeraw(PyObject *me, PyObject *arg)
+{
+  PyObject *gobj;
+  ge *x = 0;
+  if (!PyArg_ParseTuple(arg, "O!:getgeraw", group_pytype, &gobj)) goto end;
+  x = G_CREATE(GROUP_G(gobj));
+  if (G_FROMRAW(GROUP_G(gobj), BUF_B(me), x)) BUFERR();
+  return (ge_pywrap(gobj, x));
+end:
+  if (x) G_DESTROY(GROUP_G(gobj), x);
+  return (0);
+}
+
+static PyObject *rbget_size(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(BSZ(BUF_B(me)))); }
+static PyObject *rbget_left(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(BLEFT(BUF_B(me)))); }
+static PyObject *rbget_endp(PyObject *me, void *hunoz)
+  { return (getbool(!BLEFT(BUF_B(me)))); }
+
+static PyGetSetDef rbuf_pygetset[] = {
+#define GETSETNAME(op, name) rb##op##_##name
+  GET  (size,                  "RBUF.size -> SIZE")
+  GET  (left,                  "RBUF.left -> REMAINDER")
+  GET  (endp,                  "RBUF.endp -> BOOL")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyMethodDef rbuf_pymethods[] = {
+#define METHNAME(func) rbmeth_##func
+  METH (skip,                  "RBUF.skip(N)")
+  METH (get,                   "RBUF.get(N) -> BYTES")
+#define RBMETH_DECL_GETU_(n, W, w)                                     \
+    METH(getu##w, "RBUF.getu" #w "() -> INT")
+  DOUINTCONV(RBMETH_DECL_GETU_)
+#define RBMETH_DECL_GETBLK_(n, W, w)                                   \
+    METH(getblk##w, "RBUF.getblk" #w "() -> INT")
+  BUF_DOSUFFIXES(RBMETH_DECL_GETBLK_)
+  METH (getmp,                 "RBUF.getmp() -> X")
+  METH (getgf,                 "RBUF.getgf() -> X")
+  KWMETH(getecpt,              "RBUF.getecpt(curve = None) -> P")
+  METH (getecptraw,            "RBUF.getecptraw(CURVE) -> P")
+  METH (getge,                 "RBUF.getge(GROUP) -> X")
+  METH (getgeraw,              "RBUF.getgeraw(GROUP) -> X")
+#undef METHNAME
+  { 0 }
+};
+
+static PyBufferProcs rbuf_pybuffer = {
+  rbuf_pyreadbuf,                      /* @bf_getreadbuffer@ */
+  0,                                   /* @bf_getwritebuffer@ */
+  rbuf_pysegcount,                     /* @bf_getsegcount@ */
+  0                                    /* @bf_getcharbuffer@ */
+};
+
+static PyTypeObject rbuf_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.ReadBuffer",               /* @tp_name@ */
+  sizeof(buf_pyobj),                   /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  buf_pydealloc,                       /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  &rbuf_pybuffer,                      /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "A read buffer.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  rbuf_pymethods,                      /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  rbuf_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@ */
+  rbuf_pynew,                          /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Write buffers -----------------------------------------------------*/
+
+static void ensure(PyObject *me, size_t n)
+{
+  buf *b = BUF_B(me);
+
+  if (BLEFT(b) < n) {
+    size_t nn = BSZ(b);
+    octet *p;
+    size_t want = BLEFT(b) + n;
+    while (nn < want) nn <<= 1;
+    p = xrealloc(BBASE(b), nn, BSZ(b));
+    BCUR(b) = p + BLEN(b);
+    BLIM(b) = p + nn;
+    BBASE(b) = p;
+  }
+}
+
+static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  char *p;
+  size_t n = 64;
+  buf_pyobj *me = 0;
+  static char *kwlist[] = { "size", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist,
+                                  convszt, &n))
+    goto end;
+  me = (buf_pyobj *)ty->tp_alloc(ty, 0);
+  p = xmalloc(n);
+  buf_init(&me->b, p, n);
+end:
+  return ((PyObject *)me);
+}
+
+static int wbuf_pysegcount(PyObject *me, int *nn)
+  { if (nn) *nn = BLEN(BUF_B(me)); return (1); }
+
+static int wbuf_pyreadbuf(PyObject *me, int seg, void **q)
+  { assert(seg == 0); *q = BBASE(BUF_B(me)); return (BLEN(BUF_B(me))); }
+
+static PyObject *wbmeth_zero(PyObject *me, PyObject *arg)
+{
+  void *p;
+  size_t n;
+  if (!PyArg_ParseTuple(arg, "O&:zero", convszt, &n)) return (0);
+  ensure(me, n);
+  p = buf_get(BUF_B(me), n); assert(p && BOK(BUF_B(me)));
+  memset(p, 0, n);
+  RETURN_ME;
+}
+
+static PyObject *wbmeth_put(PyObject *me, PyObject *arg)
+{
+  void *p;
+  int n;
+  if (!PyArg_ParseTuple(arg, "s#:put", &p, &n)) return (0);
+  ensure(me, n);
+  buf_put(BUF_B(me), p, n); assert(BOK(BUF_B(m)));
+  RETURN_ME;
+}
+
+#define WBMETH_PUTU_(n, W, w)                                          \
+  static PyObject *wbmeth_putu##w(PyObject *me, PyObject *arg)         \
+  {                                                                    \
+    uint##n i;                                                         \
+    if (!PyArg_ParseTuple(arg, "O&:putu" #w, convu##n, &i)) return (0);        \
+    ensure(me, SZ_##n);                                                        \
+    buf_putu##w(BUF_B(me), i); assert(BOK(BUF_B(me)));                 \
+    RETURN_ME;                                                         \
+  }
+DOUINTCONV(WBMETH_PUTU_)
+
+#define SZ_z 1
+#define WBMETH_PUTBLK_(n, W, w)                                                \
+  static PyObject *wbmeth_putblk##w(PyObject *me, PyObject *arg)       \
+  {                                                                    \
+    char *p;                                                           \
+    int sz;                                                            \
+    if (!PyArg_ParseTuple(arg, "s#:putblk" #w, &p, &sz)) return (0);   \
+    ensure(me, sz + SZ_##n);                                           \
+    buf_putmem##w(BUF_B(me), p, sz); assert(BOK(BUF_B(me)));           \
+    RETURN_ME;                                                         \
+  }
+BUF_DOSUFFIXES(WBMETH_PUTBLK_)
+
+static PyObject *wbmeth_putmp(PyObject *me, PyObject *arg)
+{
+  mp *x = 0;
+  if (!PyArg_ParseTuple(arg, "O&:putmp", convmp, &x)) return (0);
+  ensure(me, mp_octets(x) + 2);
+  buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
+  RETURN_ME;
+}
+
+static PyObject *wbmeth_putgf(PyObject *me, PyObject *arg)
+{
+  mp *x = 0;
+  if (!PyArg_ParseTuple(arg, "O&:putgf", convgf, &x)) return (0);
+  ensure(me, mp_octets(x) + 2);
+  buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
+  MP_DROP(x);
+  RETURN_ME;
+}
+
+static PyObject *wbmeth_putecpt(PyObject *me, PyObject *arg)
+{
+  ec pt = EC_INIT;
+  if (!PyArg_ParseTuple(arg, "O&:putecpt", convecpt, &pt)) return (0);
+  if (EC_ATINF(&pt)) ensure(me, 2);
+  else ensure(me, 4 + mp_octets(pt.x) + mp_octets(pt.y));
+  buf_putec(BUF_B(me), &pt); assert(BOK(BUF_B(me)));
+  EC_DESTROY(&pt);
+  RETURN_ME;
+}
+
+static PyObject *wbmeth_putecptraw(PyObject *me, PyObject *arg)
+{
+  PyObject *ptobj;
+  ec pt = EC_INIT;
+  if (!PyArg_ParseTuple(arg, "O!:putecptraw", ecptcurve_pytype, &ptobj))
+    return (0);
+  EC_OUT(ECPT_C(ptobj), &pt, ECPT_P(ptobj));
+  ensure(me, ECPT_C(ptobj)->f->noctets * 2 + 1);
+  ec_putraw(ECPT_C(ptobj), BUF_B(me), &pt); assert(BOK(BUF_B(me)));
+  EC_DESTROY(&pt);
+  RETURN_ME;
+}
+
+static PyObject *wbmeth_putge(PyObject *me, PyObject *arg)
+{
+  PyObject *geobj;
+  if (!PyArg_ParseTuple(arg, "O!:putge", ge_pytype, &geobj)) return (0);
+  ensure(me, GE_G(geobj)->noctets);
+  G_TOBUF(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
+  RETURN_ME;
+}
+
+static PyObject *wbmeth_putgeraw(PyObject *me, PyObject *arg)
+{
+  PyObject *geobj;
+  if (!PyArg_ParseTuple(arg, "O!:putgeraw", ge_pytype, &geobj)) return (0);
+  ensure(me, GE_G(geobj)->noctets);
+  G_TORAW(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
+  RETURN_ME;
+}
+
+static PyObject *wbget_size(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(BLEN(BUF_B(me)))); }
+
+static PyGetSetDef wbuf_pygetset[] = {
+#define GETSETNAME(op, name) wb##op##_##name
+  GET  (size,                  "WBUF.size -> SIZE")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyMethodDef wbuf_pymethods[] = {
+#define METHNAME(func) wbmeth_##func
+  METH (zero,                  "WBUF.skip(N)")
+  METH (put,                   "WBUF.put(BYTES)")
+#define WBMETH_DECL_PUTU_(n, W, w)                                     \
+    METH(putu##w, "WBUF.putu" #w "(INT)")
+  DOUINTCONV(WBMETH_DECL_PUTU_)
+#define WBMETH_DECL_PUTBLK_(n, W, w)                                   \
+    METH(putblk##w, "WBUF.putblk" #w "(BYTES)")
+  BUF_DOSUFFIXES(WBMETH_DECL_PUTBLK_)
+  METH (putmp,                 "WBUF.putmp(X)")
+  METH (putgf,                 "WBUF.putgf(X)")
+  KWMETH(putecpt,              "WBUF.putecpt(P)")
+  METH (putecptraw,            "WBUF.putecptraw(P)")
+  METH (putge,                 "WBUF.putge(X)")
+  METH (putgeraw,              "WBUF.putgeraw(X)")
+#undef METHNAME
+  { 0 }
+};
+
+static PyBufferProcs wbuf_pybuffer = {
+  wbuf_pyreadbuf,                      /* @bf_getreadbuffer@ */
+  0,                                   /* @bf_getwritebuffer@ */
+  wbuf_pysegcount,                     /* @bf_getsegcount@ */
+  0                                    /* @bf_getcharbuffer@ */
+};
+
+static PyTypeObject wbuf_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.WriteBuffer",              /* @tp_name@ */
+  sizeof(buf_pyobj),                   /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  buf_pydealloc,                       /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  &wbuf_pybuffer,                      /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "A write buffer.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  wbuf_pymethods,                      /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  wbuf_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@ */
+  wbuf_pynew,                          /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Initialization ----------------------------------------------------*/
+
+void buffer_pyinit(void)
+{
+  INITTYPE(rbuf, root);
+  INITTYPE(wbuf, root);
+}
+
+void buffer_pyinsert(PyObject *mod)
+{
+  INSEXC("BufferError", buferr, PyExc_Exception, 0);
+  INSERT("ReadBuffer", rbuf_pytype);
+  INSERT("WriteBuffer", wbuf_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
index 394d2f9..28d62b7 100644 (file)
@@ -34,9 +34,9 @@
 
 PyTypeObject *bytestring_pytype;
 
-PyObject *bytestring_pywrap(const void *p, size_t n)
+static PyObject *dowrap(PyTypeObject *ty, const void *p, size_t n)
 {
-  PyStringObject *x = PyObject_NewVar(PyStringObject, bytestring_pytype, n);
+  PyStringObject *x = (PyStringObject *)ty->tp_alloc(ty, n);
   if (p) memcpy(x->ob_sval, p, n);
   x->ob_sval[n] = 0;
 #ifdef CACHE_HASH
@@ -46,8 +46,22 @@ PyObject *bytestring_pywrap(const void *p, size_t n)
   return ((PyObject *)x);
 }
 
+PyObject *bytestring_pywrap(const void *p, size_t n)
+  { return (dowrap(bytestring_pytype, p, n)); }
+
 PyObject *bytestring_pywrapbuf(buf *b)
-  { return bytestring_pywrap(BCUR(b), BLEFT(b)); }
+  { return (dowrap(bytestring_pytype, BCUR(b), BLEFT(b))); }
+
+static PyObject *bytestring_pynew(PyTypeObject *ty,
+                                 PyObject *arg, PyObject *kw)
+{
+  const char *p;
+  int n;
+  static char *kwlist[] = { "data", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &n))
+    return (0);
+  return (dowrap(ty, p, n));
+}
 
 #define BINOP(name, op)                                                        \
   static PyObject *bytestring_py##name(PyObject *x, PyObject *y) {     \
@@ -160,7 +174,7 @@ static PyTypeObject bytestring_pytype_skel = {
   0,                                   /* @tp_dictoffset@ */
   0,                                   /* @tp_init@ */
   PyType_GenericAlloc,                 /* @tp_alloc@ */
-  0,                                   /* @tp_new@ */
+  bytestring_pynew,                    /* @tp_new@ */
   0,                                   /* @tp_free@ */
   0                                    /* @tp_is_gc@ */
 };
index ff99a78..b9b1fd5 100644 (file)
 #include <longintrepr.h>
 #include <structmember.h>
 
+#undef ULLONG_MAX
+#undef ULONG_LONG_MAX
+
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
 #include <mLib/macros.h>
+#include <mLib/quis.h>
 
 #include <catacomb/buf.h>
 
 #include <catacomb/passphrase.h>
 #include <catacomb/pixie.h>
 
+#include <catacomb/share.h>
+#include <catacomb/gfshare.h>
+
 /*----- Utility macros ----------------------------------------------------*/
 
 #define RETURN_OBJ(obj) do { Py_INCREF(obj); return (obj); } while (0)
 #define ZDIVERR(str) EXCERR(PyExc_ZeroDivisionError, str)
 #define SYNERR(str) EXCERR(PyExc_SyntaxError, str)
 #define SYSERR(str) EXCERR(PyExc_SystemError, str)
+#define INDEXERR(idx) do {                                             \
+  PyErr_SetObject(PyExc_KeyError, idx);                                        \
+  goto end;                                                            \
+} while (0)
 #define OSERR(name) do {                                               \
   PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);                 \
   goto end;                                                            \
   PyModule_AddObject(mod, name, _o);                                   \
 } while (0)
 
+#define INSEXC(name, var, base, meth)                                  \
+  INSERT(name, var = mkexc(mod, base, name, meth))
+
 #define METH(func, doc)                                                        \
   { #func, METHNAME(func), METH_VARARGS, doc },
 #define KWMETH(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 MODULES(_)                                                     \
+  _(bytestring) _(buffer)                                              \
+  _(rand) _(algorithms) _(pubkey) _(pgen)                              \
+  _(mp) _(field) _(ec) _(group)                                                \
+  _(passphrase) _(share) _(key)
 #define DOMODINIT(m) m##_pyinit();
 #define DOMODINSERT(m) m##_pyinsert(mod);
 #define INIT_MODULES do { MODULES(DOMODINIT) } while (0)
@@ -236,7 +250,6 @@ extern mp *getfe(field *, PyObject *);
 typedef struct fe_pyobj {
   PyObject_HEAD
   field *f;
-  PyObject *fobj; /* to keep it alive */
   mp *x;
 } fe_pyobj;
       
@@ -271,7 +284,7 @@ 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 *);
+extern int convecpt(PyObject *, void *);
 
 typedef struct eccurve_pyobj {
   PyHeapTypeObject ty;
@@ -460,8 +473,6 @@ extern PyTypeObject *gmac_pytype;
 extern PyObject *gmac_pywrap(PyObject *, gmac *, unsigned);
 extern int convgmac(PyObject *, void *);
 
-/*----- Public key crypto -------------------------------------------------*/
-
 /*----- Key generation ----------------------------------------------------*/
     
 typedef struct pfilt_pyobj {
@@ -500,14 +511,17 @@ extern PyObject *mexp_common(PyObject *, PyObject *, size_t,
                             void (*drop)(void *));
 
 extern int convulong(PyObject *, void *);
-extern int convu32(PyObject *, void *);
+#define DECL_CONVU_(n) extern int convu##n(PyObject *, void *);
+DOUINTSZ(DECL_CONVU_)
 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);
+#define DECL_GETU_(n) extern PyObject *getu##n(uint##n);
+DOUINTSZ(DECL_GETU_)
+extern PyObject * mkexc(PyObject *, PyObject *, const char *, PyMethodDef *);
 extern void *newtype(PyTypeObject *, const PyTypeObject *, const char *);
 extern PyTypeObject *inittype(PyTypeObject *);
 extern void addmethods(const PyMethodDef *);
index 0e1c711..47a94dd 100644 (file)
@@ -41,6 +41,16 @@ static void setconstants(PyObject *mod)
       C(PGEN_ABORT),
     C(MPW_MAX),
     C(PMODE_READ), C(PMODE_VERIFY),
+    C(KOPEN_READ), C(KOPEN_WRITE), C(KOPEN_NOFILE),
+    C(KEXP_FOREVER), C(KEXP_EXPIRE),
+    C(KF_ENCMASK), C(KENC_BINARY), C(KENC_MP), C(KENC_STRUCT),
+      C(KENC_ENCRYPT), C(KENC_STRING), C(KENC_EC),
+    C(KF_CATMASK), C(KCAT_SYMM), C(KCAT_PRIV), C(KCAT_PUB), C(KCAT_SHARE),
+    C(KF_NONSECRET),
+    C(KF_BURN), C(KF_OPT),
+#define ENTRY(tag, val, str) C(KERR_##tag),
+    KEY_ERRORS(ENTRY)
+#undef ENTRY
 #undef C
     { 0 }
   };
@@ -56,13 +66,15 @@ static void setconstants(PyObject *mod)
   }
 }
 
-PyObject *getu32(uint32 w)
-{
-  if (w <= 0x7fffffff)
-    return (PyInt_FromLong(w));
-  else
-    return (PyLong_FromUnsignedLong(w));
-}
+#define GETU_(n)                                                       \
+  PyObject *getu##n(uint##n w)                                         \
+  {                                                                    \
+    if (w <= MASK##n)                                                  \
+      return (PyInt_FromLong(w));                                      \
+    else                                                               \
+      return (PyLong_FromUnsignedLong(w));                             \
+  }
+DOUINTSZ(GETU_)
 
 PyObject *getbool(int b)
 {
@@ -97,18 +109,20 @@ 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);
-}
+#define CONVU_(n)                                                      \
+  int convu##n(PyObject *o, void *pp)                                  \
+  {                                                                    \
+    unsigned long u;                                                   \
+    uint##n *p = pp;                                                   \
+                                                                       \
+    if (!convulong(o, &u)) goto end;                                   \
+    if (u > MASK##n) TYERR("out of range");                            \
+    *p = u;                                                            \
+    return (1);                                                                \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTSZ(CONVU_)
 
 int convuint(PyObject *o, void *pp)
 {
@@ -221,6 +235,48 @@ end:
   return (z);
 }
 
+PyObject * mkexc(PyObject *mod, PyObject *base,
+                const char *name, PyMethodDef *mm)
+{
+  PyObject *nameobj = 0;
+  PyObject *dict = 0;
+  PyObject *exc = 0;
+  PyObject *func = 0;
+  PyObject *meth = 0;
+
+  if ((nameobj = PyString_FromFormat("%s.%s",
+                                    PyModule_GetName(mod),
+                                    name)) == 0 ||
+      (dict = PyDict_New()) == 0 ||
+      (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
+                               base, dict)) == 0)
+    goto fail;
+
+  if (mm) {
+    while (mm->ml_name) {
+      if ((func = PyCFunction_NewEx(mm, 0, mod)) == 0 ||
+         (meth = PyMethod_New(func, 0, exc)) == 0 ||
+         PyDict_SetItemString(dict, mm->ml_name, meth))
+       goto fail;
+      Py_DECREF(func); func = 0;
+      Py_DECREF(meth); meth = 0;
+      mm++;
+    }
+  }
+
+done:
+  Py_XDECREF(nameobj);
+  Py_XDECREF(dict);
+  return (exc);
+
+fail:
+  Py_XDECREF(exc);
+  Py_XDECREF(func);
+  Py_XDECREF(meth);
+  exc = 0;
+  goto done;
+}
+
 DA_DECL(method_v, PyMethodDef);
 static method_v global_pymethods = DA_INIT;
 void addmethods(const PyMethodDef *m)
@@ -287,9 +343,27 @@ PyTypeObject *inittype(PyTypeObject *tyskel)
   return (ty);
 }
 
+static PyObject *meth__ego(PyObject *me, PyObject *arg)
+{
+  char *argv0;
+  if (!PyArg_ParseTuple(arg, "s:_ego", &argv0))
+    return (0);
+  if (strcmp(QUIS, "<UNNAMED>") == 0)
+    ego(argv0);
+  RETURN_NONE;
+}
+
+static PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+  METH (_ego,                  "_ego(ARGV0)")
+#undef METHNAME
+  { 0 }
+};
+
 void init_base(void) {
   static const PyMethodDef mzero = { 0 };
   PyObject *mod;
+  addmethods(methods);
   INIT_MODULES;
   DA_PUSH(&global_pymethods, mzero);
   mod = Py_InitModule("catacomb._base", DA(&global_pymethods));
index 0b7f418..2b391eb 100644 (file)
@@ -28,6 +28,9 @@
 import _base
 import types as _types
 from binascii import hexlify as _hexify, unhexlify as _unhexify
+from sys import argv as _argv
+
+_base._ego(_argv[0])
 
 def _init():
   d = globals()
@@ -38,7 +41,8 @@ def _init():
   for i in ['MP', 'GF', 'Field',
             'ECPt', 'ECPtCurve', 'ECCurve', 'ECInfo',
             'DHInfo', 'BinDHInfo', 'RSAPriv', 'PrimeFilter', 'RabinMiller',
-            'Group', 'GE']:
+            'Group', 'GE',
+            'KeyData']:
     c = d[i]
     pre = '_' + i + '_'
     plen = len(pre)
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..73c4e19
--- /dev/null
@@ -0,0 +1,5 @@
+catacomb-python (1.0.0) experimental; urgency=low
+
+  * Debianization!
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Mon, 26 Sep 2005 11:27:00 +0100
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..3b20640
--- /dev/null
@@ -0,0 +1,27 @@
+Source: catacomb-python
+Section: libs
+Priority: extra
+Maintainer: Mark Wooding <mdw@nsict.org>
+Standards-Version: 3.1.1
+
+Package: python-catacomb
+Architecture: all
+Depends: python, python2.3-catacomb
+Description: Python bindings for the Catacomb cryptographic library.
+ This is a dummy package for making sure you have the right package
+ for the default Debian Python installation.
+
+Package: python2.3-catacomb
+Architecture: any
+Depends: ${shlibs:Depends}, python2.3
+Description: Python bindings for the Catacomb cryptographic library.
+
+Package: python2.4-catacomb
+Architecture: any
+Depends: ${shlibs:Depends}, python2.4
+Description: Python bindings for the Catacomb cryptographic library.
+
+Package: python-catacomb-bin
+Architecture: all
+Depends: python, python-catacomb
+Description: Catacomb utilities in Python
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..050e1e1
--- /dev/null
@@ -0,0 +1,16 @@
+Catacomb/Python is copyright (c) 2005 Straylight/Edgeware
+
+Catacomb/Python is free software; you can redistribute it and/or modify
+it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+Catacomb/Python is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have a copy of the GNU Library General Public License in
+/usr/share/common-licenses/LGPL-2; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+USA.
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..94c8a31
--- /dev/null
@@ -0,0 +1,67 @@
+#! /usr/bin/make -f
+
+export DH_COMPAT = 4
+
+DEFVERSION = 2.3
+VERSIONS = $(DEFVERSION) 2.4
+
+build: build-stamp
+
+build-stamp:
+       for v in $(VERSIONS); do python$$v setup.py build; done
+       touch build-stamp
+
+clean:
+       dh_clean
+       rm -rf build build-stamp
+
+install: build
+       dh_clean
+       for v in $(VERSIONS); do \
+         python$$v setup.py build; \
+         python$$v setup.py install --root=debian/python$$v-catacomb; \
+       done
+       mkdir -p debian/python-catacomb
+       mkdir -p debian/python-catacomb-bin/usr/bin
+       mv debian/python$(DEFVERSION)-catacomb/usr/bin/* \
+         debian/python-catacomb-bin/usr/bin
+       for v in $(VERSIONS); do \
+         rm -rf debian/python$$v-catacomb/usr/bin; \
+       done
+
+binary-indep: install
+       dh_testdir -i
+       dh_testroot -i
+       dh_compress -i
+       dh_installdocs -i
+       dh_gencontrol -i
+       dh_fixperms -i
+       dh_installdeb -i
+       dh_md5sums -i
+       dh_builddeb -i
+
+binary-arch: install
+       dh_testdir -a
+       dh_testroot -a
+       dh_compress -a
+       dh_installdocs -a
+       dh_strip -a
+       dh_shlibdeps -a
+       dh_gencontrol -a
+       dh_fixperms -a
+       dh_installdeb -a
+       dh_md5sums -a
+       dh_builddeb -a
+
+binary: binary-indep binary-arch
+
+source:
+       rm -rf dist/*.tar.gz dist/=deb=
+       python$(DEFVERSION) setup.py sdist
+       mkdir dist/=deb=
+       cd dist/=deb=; tar xvfz ../*.tar.gz
+       d=`pwd`; cd ..; dpkg-source -i -i'/\.svn/' -b $$d/dist/=deb=/*
+       rm -rf dist/=deb=
+
+.PHONY: binary binary-arch binary-indep clean install source build
+
diff --git a/ec.c b/ec.c
index 985e94d..1666504 100644 (file)
--- a/ec.c
+++ b/ec.c
@@ -967,7 +967,7 @@ static PyObject *meth__ECCurve_parse(PyObject *me, PyObject *arg)
   ec_curve *c;
   PyObject *rc = 0;
 
-  if (!PyArg_ParseTuple(arg, "Os", &me, &p))
+  if (!PyArg_ParseTuple(arg, "Os:parse", &me, &p))
     goto end;
   qd.p = p;
   qd.e = 0;
diff --git a/key.c b/key.c
index a7d3621..9de143e 100644 (file)
--- a/key.c
+++ b/key.c
 
 #include "catacomb-python.h"
 
+/*----- Exceptions --------------------------------------------------------*/
+
+static PyObject *keyexc;
+static PyObject *keyioexc;
+static PyObject *keyfilebrokenexc;
+
+static PyObject *kxmeth___init__(PyObject *me, PyObject *arg)
+{
+  int err;
+  PyObject *x = 0;
+
+  if (!PyArg_ParseTuple(arg, "Oi:__init__", &me, &err) ||
+      (x = PyInt_FromLong(err)) == 0 ||
+      PyObject_SetAttrString(me, "err", x))
+    goto fail;
+  Py_DECREF(x); x = 0;
+  if ((x = PyString_FromString(key_strerror(err))) == 0 ||
+      PyObject_SetAttrString(me, "errstring", x))
+    goto fail;
+  Py_DECREF(x); x = 0;
+  if ((x = PyString_FromString(key_strerror(err))) == 0 ||
+      PyObject_SetAttrString(me, "errstring", x))
+    goto fail;
+  Py_DECREF(x); x = 0;
+  if ((x = PySequence_GetSlice(arg, 1, PySequence_Size(arg))) == 0 ||
+      PyObject_SetAttrString(me, "args", x))
+    goto fail;
+  Py_DECREF(x); x = 0;
+  RETURN_NONE;
+
+fail:
+  Py_XDECREF(x);
+  return (0);
+}
+
+static PyObject *kxmeth___str__(PyObject *me, PyObject *arg)
+{
+  long err;
+  const char *errstr, *errtag;
+  PyObject *x = 0;
+  PyObject *rc = 0;
+
+  static const char *const tab[] = {
+#define ENTRY(tag, num, str) "KERR_" #tag,
+    KEY_ERRORS(ENTRY)
+#undef ENTRY
+  };
+
+  if (!PyArg_ParseTuple(arg, "O:__str__", &me) ||
+      (x = PyObject_GetAttrString(me, "err")) == 0 ||
+      (err = PyInt_AsLong(x), PyErr_Occurred()))
+    goto done;
+  Py_DECREF(x); x = 0;
+  err = -err;
+  if (err >= 0 && err < N(tab)) errtag = tab[err];
+  else errtag = "<unknown>";
+  if ((x = PyObject_GetAttrString(me, "errstring")) == 0 ||
+      (errstr = PyString_AsString(x)) == 0)
+    goto done;
+  rc = PyString_FromFormat("%s (%ld): %s", errtag, -err, errstr);
+
+done:
+  Py_XDECREF(x);
+  return (rc);
+}
+
+static PyMethodDef keyexc_pymethods[] = {
+#define METHNAME(func) kxmeth_##func
+  METH (__init__,              "KeyError(CODE)")
+  METH (__str__,               "E.__str__() -> STRING")
+#undef METHNAME
+  { 0 }
+};
+
+static void keyexc_raise(int err)
+{
+  PyObject *arg = Py_BuildValue("(i)", err);
+  if (arg) PyErr_SetObject(keyexc, arg);
+  Py_XDECREF(arg);
+}
+#define KEYERR(err) do { keyexc_raise(err); goto end; } while (0)
+#define KEYIOERR(name) do {                                            \
+  PyErr_SetFromErrnoWithFilename(keyioexc, name);                      \
+  goto end;                                                            \
+} while (0)
+#define KEYFILEBROKEN(name) do {                                       \
+  PyErr_SetFromErrnoWithFilename(keyfilebrokenexc, name);              \
+  goto end;                                                            \
+} while (0)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct keydata_pyobj {
+  PyObject_HEAD
+  key_data *kd;
+} keydata_pyobj;
+
+static PyTypeObject *keydata_pytype;
+static PyTypeObject *keydatabin_pytype;
+static PyTypeObject *keydataenc_pytype;
+static PyTypeObject *keydatamp_pytype;
+static PyTypeObject *keydatastruct_pytype;
+static PyTypeObject *keydatastr_pytype;
+static PyTypeObject *keydataec_pytype;
+#define KEYDATA_PYCHECK(o) PyObject_TypeCheck(o, keydata_pytype)
+#define KEYDATA_KD(o) (((keydata_pyobj *)(o))->kd)
+
+typedef struct subkeyiter_pyobj {
+  PyObject_HEAD
+  key_subkeyiter i;
+  PyObject *kdobj;
+} subkeyiter_pyobj;
+
+static PyTypeObject *subkeyiter_pytype;
+#define SUBKEYITER_PYCHECK(o) PyObject_TypeCheck(o, subkeyiter_pytype);
+#define SUBKEYITER_I(o) (&((subkeyiter_pyobj *)(o))->i)
+#define SUBKEYITER_KDOBJ(o) (((subkeyiter_pyobj *)(o))->kdobj)
+
+typedef struct keyfile_pyobj {
+  PyObject_HEAD
+  key_file kf;
+} keyfile_pyobj;
+
+static PyTypeObject *keyfile_pytype;
+#define KEYFILE_PYCHECK(o) PyObject_TypeCheck(o, keyfile_pytype)
+#define KEYFILE_KF(o) (&((keyfile_pyobj *)(o))->kf)
+
+typedef struct key_pyobj {
+  PyObject_HEAD
+  key *k;
+  PyObject *kfobj;
+} key_pyobj;
+
+static PyTypeObject *key_pytype;
+#define KEY_PYCHECK(o) PyObject_TypeCheck(o, key_pytype)
+#define KEY_K(o) (((key_pyobj *)(o))->k)
+#define KEY_KFOBJ(o) (((key_pyobj *)(o))->kfobj)
+#define KEY_KF(o) KEYFILE_KF(KEY_KFOBJ(o))
+
+typedef struct keyiter_pyobj {
+  PyObject_HEAD
+  key_iter i;
+  PyObject *kfobj;
+} keyiter_pyobj;
+
+static PyTypeObject *keyiter_pytype;
+#define KEYITER_PYCHECK(o) PyObject_TypeCheck(o, keyiter_pytype)
+#define KEYITER_I(o) (&((keyiter_pyobj *)(o))->i)
+#define KEYITER_KFOBJ(o) (((keyiter_pyobj *)(o))->kfobj)
+#define KEYITER_KF(o) KEYFILE_KF(KEYITER_KFOBJ(o))
+
+typedef struct keyattrs_pyobj {
+  PyObject_HEAD
+  PyObject *kobj;
+} keyattrs_pyobj;
+
+static PyTypeObject *keyattrs_pytype;
+#define KEYATTRS_PYCHECK(o) PyObject_TypeCheck(o, keyattrs_pytype)
+#define KEYATTRS_KOBJ(o) (((keyattrs_pyobj *)(o))->kobj)
+#define KEYATTRS_KF(o) KEY_KF(KEYATTRS_KOBJ(o))
+#define KEYATTRS_K(o) KEY_K(KEYATTRS_KOBJ(o))
+
+typedef struct keyattriter_pyobj {
+  PyObject_HEAD
+  key_attriter i;
+  PyObject *kobj;
+} keyattriter_pyobj;
+
+static PyTypeObject *keyattriter_pytype;
+#define KEYATTRITER_PYCHECK(o) PyObject_TypeCheck(o, keyattriter_pytype)
+#define KEYATTRITER_I(o) (&((keyattriter_pyobj *)(o))->i)
+#define KEYATTRITER_KOBJ(o) (((keyattriter_pyobj *)(o))->kobj)
+#define KEYATTRITER_K(o) KEY_K(KEYATTRITER_KOBJ(o))
+#define KEYATTRITER_KFOBJ(o) KEY_KFOBJ(KEYATTRITER_KOBJ(o))
+#define KEYATTRITER_KF(o) KEY_KF(KEYATTRITER_KOBJ(o))
+
+/*----- Filters -----------------------------------------------------------*/
+
+static int convfilter(PyObject *x, void *p)
+{
+  key_filter *f = p;
+  const char *fs;
+  char *end;
+  int n;
+  PyObject *a = 0, *b = 0;
+  int err;
+  int rc = 0;
+
+  if ((fs = PyString_AsString(x)) != 0) {
+    if ((err = key_readflags(fs, &end, &f->f, &f->m)) != 0)
+      KEYERR(err);
+    if (*end)
+      KEYERR(KERR_BADFLAGS);
+  } else {
+    PyErr_Clear();
+    if (!PySequence_Check(x))
+      goto tyerr;
+    else if ((n = PySequence_Size(x)) < 0)
+      goto end;
+    else if (n != 2)
+      goto tyerr;
+    else if ((a = PySequence_GetItem(x, 0)) == 0 || convuint(a, &f->f) ||
+            (b = PySequence_GetItem(x, 1)) == 0 || convuint(b, &f->m))
+      goto end;
+  }
+  rc = 1;
+  goto end;
+tyerr:
+  TYERR("expected flag string or flag/mask pair");
+end:
+  Py_XDECREF(a);
+  Py_XDECREF(b);
+  return (rc);
+}
+
+static int convflags(PyObject *x, void *p)
+{
+  unsigned *f = p;
+  const char *fs;
+  char *end;
+  int err;
+  int rc = 0;
+
+  if (convuint(x, p))
+    return (1);
+  else {
+    PyErr_Clear();
+    if ((fs = PyString_AsString(x)) != 0) {
+      if ((err = key_readflags(fs, &end, f, 0)) != 0)
+       KEYERR(err);
+      if (*end)
+       KEYERR(KERR_BADFLAGS);
+    } else {
+      PyErr_Clear();
+      goto tyerr;
+    }
+  }
+  rc = 1;
+  goto end;
+tyerr:
+  TYERR("expected flag string or flag/mask pair");
+end:
+  return (rc);
+}
+
+static PyObject *meth__KeyData_readflags(PyObject *me, PyObject *arg)
+{
+  const char *p;
+  char *end;
+  unsigned f, m;
+  PyObject *rc = 0;
+  int err;
+
+  if (!PyArg_ParseTuple(arg, "Os:key_readflags", &me, &p))
+    goto end;
+  if ((err = key_readflags(p, &end, &f, &m)) != 0)
+    KEYERR(err);
+  rc = Py_BuildValue("(NNs)", getu32(f), getu32(m), end);
+end:
+  return (rc);
+}
+
+static PyObject *meth__KeyData_writeflags(PyObject *me, PyObject *arg)
+{
+  dstr d = DSTR_INIT;
+  PyObject *rc;
+  unsigned f;
+
+  if (!PyArg_ParseTuple(arg, "OO&:key_writeflags", &me, convuint, &f))
+    return (0);
+  key_writeflags(f, &d);
+  rc = PyString_FromStringAndSize(d.buf, d.len);
+  dstr_destroy(&d);
+  return (rc);
+}
+
+/*----- Key data ----------------------------------------------------------*/
+
+static PyObject *keydata_pywrap(key_data *kd)
+{
+  PyTypeObject *ty;
+  keydata_pyobj *kdobj;
+
+  switch (kd->e & KF_ENCMASK) {
+    case KENC_BINARY: ty = keydatabin_pytype; break;
+    case KENC_ENCRYPT: ty = keydataenc_pytype; break;
+    case KENC_MP: ty = keydatamp_pytype; break;
+    case KENC_STRUCT: ty = keydatastruct_pytype; break;
+    case KENC_STRING: ty = keydatastr_pytype; break;
+    case KENC_EC: ty = keydataec_pytype; break;
+    default: abort();
+  }
+  kdobj = PyObject_NEW(keydata_pyobj, ty);
+  kdobj->kd = kd;
+  return ((PyObject *)kdobj);
+}
+
+static void keydata_pydealloc(PyObject *me)
+{
+  key_drop(KEYDATA_KD(me));
+  FREEOBJ(me);
+}
+
+static PyObject *kdmeth_matchp(PyObject *me, PyObject *arg)
+{
+  key_filter f;
+  
+  if (!PyArg_ParseTuple(arg, "O&:matchp", convfilter, &f))
+    return (0);
+  return (getbool(KEY_MATCH(KEYDATA_KD(me), &f)));
+}
+
+static PyObject *kdmeth_split(PyObject *me, PyObject *arg)
+{
+  if (!PyArg_ParseTuple(arg, ":split"))
+    return (0);
+  key_split(&KEYDATA_KD(me));
+  RETURN_ME;
+}
+
+static PyObject *kdmeth_write(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  key_filter f = { 0, 0 };
+  dstr d = DSTR_INIT;
+  PyObject *rc = 0;
+  static char *kwlist[] = { "filter", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:write", kwlist,
+                                  convfilter, &f))
+    return (0);
+  key_write(KEYDATA_KD(me), &d, &f);
+  rc = PyString_FromStringAndSize(d.buf, d.len);
+  dstr_destroy(&d);
+  return (rc);
+}
+
+static PyObject *kdmeth_encode(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  key_filter f = { 0, 0 };
+  dstr d = DSTR_INIT;
+  PyObject *rc = 0;
+  static char *kwlist[] = { "filter", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:encode", kwlist,
+                                  convfilter, &f))
+    return (0);
+  key_encode(KEYDATA_KD(me), &d, &f);
+  rc = bytestring_pywrap(d.buf, d.len);
+  dstr_destroy(&d);
+  return (rc);
+}
+
+static PyObject *kdmeth_plock(PyObject *me, PyObject *arg)
+{
+  char *tag;
+  int err;
+  PyObject *rc = 0;
+  key_data *kd;
+  
+  if (!PyArg_ParseTuple(arg, "s:plock", &tag))
+    goto end;
+  if ((err = key_plock(&kd, KEYDATA_KD(me), tag)) != 0)
+    KEYERR(err);
+  rc = keydata_pywrap(kd);
+end:
+  return (rc);
+}
+
+static PyObject *kdmeth_lock(PyObject *me, PyObject *arg)
+{
+  char *p;
+  int n;
+  PyObject *rc = 0;
+  key_data *kd;
+  
+  if (!PyArg_ParseTuple(arg, "s#:lock", &p, &n))
+    goto end;
+  key_lock(&kd, KEYDATA_KD(me), p, n);
+  rc = keydata_pywrap(kd);
+end:
+  return (rc);
+}
+
+static PyObject *meth__KeyData_read(PyObject *me, PyObject *arg)
+{
+  const char *p;
+  char *end;
+  key_data *kd;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "Os:read", &me, &p))
+    goto end;
+  if ((kd = key_read(p, &end)) == 0)
+    KEYERR(KERR_MALFORMED);
+  rc = Py_BuildValue("(Ns)", keydata_pywrap(kd), end);
+end:
+  return (rc);
+}
+
+static PyObject *meth__KeyData_decode(PyObject *me, PyObject *arg)
+{
+  const char *p;
+  int n;
+  key_data *kd;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "Os#:decode", &me, &p, &n))
+    goto end;
+  if ((kd = key_decode(p, n)) == 0)
+    KEYERR(KERR_MALFORMED);
+  rc =  keydata_pywrap(kd);
+end:
+  return (rc);
+}
+
+static PyObject *kdget_flags(PyObject *me, void *hunoz)
+  { return (getu32(KEYDATA_KD(me)->e)); }
+
+static PyMethodDef keydata_pymethods[] = {
+#define METHNAME(func) kdmeth_##func
+  METH (matchp,                "KD.matchp(FILTER) -> BOOL")
+  METH (split,                 "KD.split()")
+  KWMETH(write,                        "KD.write(filter = <any>) -> STRING")
+  KWMETH(encode,               "KD.encode(filter = <any>) -> BYTES")
+  METH (plock,                 "KD.plock(TAG) -> ENCRYPTED-KD")
+  METH (lock,                  "KD.lock(KEY) -> ENCRYPTED-KD")
+#undef METHNAME
+  { 0 }
+};
+
+static PyGetSetDef keydata_pygetset[] = {
+#define GETSETNAME(op, name) kd##op##_##name
+  GET  (flags,                 "KD.flags -> FLAGS")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject keydata_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyData",                  /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  keydata_pydealloc,                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data base class.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  keydata_pymethods,                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keydata_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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keydatabin_pynew(PyTypeObject *ty,
+                                 PyObject *arg, PyObject *kw)
+{
+  char *p;
+  int n;
+  unsigned f = 0;
+  keydata_pyobj *me = 0;
+  static char *kwlist[] = { "key", "flags", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", kwlist,
+                                  &p, &n, convflags, &f))
+    goto end;
+  me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+  me->kd = key_newbinary(f & ~KF_ENCMASK, p, n);
+end:
+  return ((PyObject *)me);
+}
+
+static PyObject *kdbget_bin(PyObject *me, void *hunoz)
+  { return (bytestring_pywrap(KEYDATA_KD(me)->u.k.k,
+                             KEYDATA_KD(me)->u.k.sz)); }
+
+static PyGetSetDef keydatabin_pygetset[] = {
+#define GETSETNAME(op, name) kdb##op##_##name
+  GET  (bin,                   "KD.bin -> BYTES")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject keydatabin_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyDataBinary",            /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data for binary keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keydatabin_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@ */
+  keydatabin_pynew,                    /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keydataenc_pynew(PyTypeObject *ty,
+                                 PyObject *arg, PyObject *kw)
+{
+  char *p;
+  int n;
+  unsigned f = 0;
+  keydata_pyobj *me = 0;
+  static char *kwlist[] = { "key", "flags", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", kwlist,
+                                  &p, &n, convflags, &f))
+    goto end;
+  me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+  me->kd = key_newencrypted(f & ~KF_ENCMASK, p, n);
+end:
+  return ((PyObject *)me);
+}
+
+static PyObject *kdemeth_plock(PyObject *me, PyObject *arg)
+{
+  char *hunoz;
+  if (!PyArg_ParseTuple(arg, "s:plock", &hunoz)) goto end;
+  KEYERR(KERR_WRONGTYPE);
+end:
+  return (0);
+}
+
+static PyObject *kdemeth_lock(PyObject *me, PyObject *arg)
+{
+  char *hunoz;
+  int hukairz;
+  if (!PyArg_ParseTuple(arg, "s#:lock", &hunoz, &hukairz)) goto end;
+  KEYERR(KERR_WRONGTYPE);
+end:
+  return (0);
+}
+
+static PyObject *kdemeth_punlock(PyObject *me, PyObject *arg)
+{
+  char *tag;
+  int err;
+  PyObject *rc = 0;
+  key_data *kd;
+  
+  if (!PyArg_ParseTuple(arg, "s:punlock", &tag))
+    goto end;
+  if ((err = key_punlock(&kd, KEYDATA_KD(me), tag)) != 0)
+    KEYERR(err);
+  rc = keydata_pywrap(kd);
+end:
+  return (rc);
+}
+
+static PyObject *kdemeth_unlock(PyObject *me, PyObject *arg)
+{
+  char *p;
+  int n;
+  int err;
+  PyObject *rc = 0;
+  key_data *kd;
+  
+  if (!PyArg_ParseTuple(arg, "s#:unlock", &p, &n))
+    goto end;
+  if ((err = key_unlock(&kd, KEYDATA_KD(me), p, n)) != 0)
+    KEYERR(err);
+  rc = keydata_pywrap(kd);
+end:
+  return (rc);
+}
+
+#define kdeget_ct kdbget_bin
+
+static PyMethodDef keydataenc_pymethods[] = {
+#define METHNAME(func) kdemeth_##func
+  METH (plock,                 "KD.plock(TAG) -> ENCRYPTED-KD")
+  METH (lock,                  "KD.lock(KEY) -> ENCRYPTED-KD")
+  METH (punlock,               "KD.punlock(TAG) -> KD")
+  METH (unlock,                "KD.unlock(KEY) -> KD")
+#undef METHNAME
+  { 0 }
+};
+
+static PyGetSetDef keydataenc_pygetset[] = {
+#define GETSETNAME(op, name) kde##op##_##name
+  GET  (ct,                    "KD.ct -> BYTES")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject keydataenc_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyDataEncrypted",         /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data for encrypted keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  keydataenc_pymethods,                        /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keydataenc_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@ */
+  keydataenc_pynew,                    /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keydatamp_pynew(PyTypeObject *ty,
+                                PyObject *arg, PyObject *kw)
+{
+  mp *x = 0;
+  unsigned f = 0;
+  keydata_pyobj *me = 0;
+  static char *kwlist[] = { "key", "flags", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
+                                  convmp, &x, convflags, &f))
+    goto end;
+  me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+  me->kd = key_newmp(f & ~KF_ENCMASK, x);
+end:
+  mp_drop(x);
+  return ((PyObject *)me);
+}
+
+static PyObject *kdmget_mp(PyObject *me, void *hunoz)
+  { return (mp_pywrap(MP_COPY(KEYDATA_KD(me)->u.m))); }
+
+static PyGetSetDef keydatamp_pygetset[] = {
+#define GETSETNAME(op, name) kdm##op##_##name
+  GET  (mp,                    "KD.mp -> X")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject keydatamp_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyDataMP",                        /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data for large-integer keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keydatamp_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@ */
+  keydatamp_pynew,                     /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keydatastr_pynew(PyTypeObject *ty,
+                                 PyObject *arg, PyObject *kw)
+{
+  char *p;
+  unsigned f = 0;
+  keydata_pyobj *me = 0;
+  static char *kwlist[] = { "key", "flags", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:new", kwlist,
+                                  &p, convflags, &f))
+    goto end;
+  me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+  me->kd = key_newstring(f & ~KF_ENCMASK, p);
+end:
+  return ((PyObject *)me);
+}
+
+static PyObject *kdsget_str(PyObject *me, void *hunoz)
+  { return (PyString_FromString(KEYDATA_KD(me)->u.p)); }
+
+static PyGetSetDef keydatastr_pygetset[] = {
+#define GETSETNAME(op, name) kds##op##_##name
+  GET  (str,                   "KD.str -> STRING")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject keydatastr_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyDataString",            /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data for string keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keydatastr_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@ */
+  keydatastr_pynew,                    /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keydataec_pynew(PyTypeObject *ty,
+                                PyObject *arg, PyObject *kw)
+{
+  ec x = EC_INIT;
+  unsigned f = 0;
+  keydata_pyobj *me = 0;
+  static char *kwlist[] = { "key", "flags", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
+                                  convecpt, &x, convflags, &f))
+    goto end;
+  me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+  me->kd = key_newec(f & ~KF_ENCMASK, &x);
+end:
+  EC_DESTROY(&x);
+  return ((PyObject *)me);
+}
+
+static PyObject *kdeget_ecpt(PyObject *me, void *hunoz)
+{
+  ec pt = EC_INIT;
+  EC_COPY(&pt, &KEYDATA_KD(me)->u.e);
+  return (ecpt_pywrapout(ecpt_pytype, &pt));
+}
+
+static PyGetSetDef keydataec_pygetset[] = {
+#define GETSETNAME(op, name) kde##op##_##name
+  GET  (ecpt,                  "KD.ecpt -> ECPT")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject keydataec_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyDataECPt",              /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data for elliptic-curve keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keydataec_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@ */
+  keydataec_pynew,                     /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *subkeyiter_make(PyObject *kdobj)
+{
+  subkeyiter_pyobj *me = PyObject_NEW(subkeyiter_pyobj, subkeyiter_pytype);
+  me->kdobj = kdobj;
+  Py_INCREF(kdobj);
+  key_mksubkeyiter(&me->i, KEYDATA_KD(kdobj));
+  return ((PyObject *)me);
+}
+
+static PyObject *subkeyiter_next(PyObject *me)
+{
+  const char *tag;
+  if (!key_nextsubkey(SUBKEYITER_I(me), &tag, 0))
+    return (0);
+  return (PyString_FromString(tag));
+}
+
+static void subkeyiter_pydealloc(PyObject *me)
+{
+  Py_DECREF(SUBKEYITER_KDOBJ(me));
+  FREEOBJ(me);
+}
+
+static PyTypeObject subkeyiter_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.SubKeyIter",               /* @tp_name@ */
+  sizeof(subkeyiter_pyobj),            /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  subkeyiter_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@ */
+"Iterator for structured keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  subkeyiter_next,                     /* @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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keydatastruct_pynew(PyTypeObject *ty,
+                                    PyObject *arg, PyObject *kw)
+{
+  PyObject *sub = 0;
+  PyObject *it = 0, *name = 0, *val = 0;
+  char *p;
+  keydata_pyobj *me = 0;
+  key_data *kd = 0;
+  static char *kwlist[] = { "subkeys", 0 };
+
+  Py_INCREF(arg); Py_INCREF(kw);
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", kwlist, &sub))
+    goto end;
+  kd = key_newstruct();
+  if (sub) {
+    if (!PyMapping_Check(sub))
+      TYERR("subkeys must be an iterable mapping");
+    if ((it = PyObject_GetIter(sub)) == 0)
+      goto end;
+    while ((name = PyIter_Next(it)) != 0) {
+      if ((p = PyString_AsString(name)) == 0 ||
+         (val = PyObject_GetItem(sub, name)) == 0)
+       goto end;
+      if (!KEYDATA_PYCHECK(val))
+       TYERR("subkey objects must be subclasses of KeyData");
+      key_structset(kd, p, KEYDATA_KD(val));
+      Py_DECREF(name); name = 0;
+      Py_DECREF(val); val = 0;
+    }
+    if (PyErr_Occurred())
+      goto end;
+    Py_DECREF(it); it = 0;
+  }
+  me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
+  me->kd = kd;
+end:
+  if (kd && !me) key_drop(kd);
+  Py_XDECREF(name); Py_XDECREF(val); Py_XDECREF(it);
+  Py_XDECREF(arg); Py_XDECREF(kw);
+  return ((PyObject *)me);
+}
+
+static PyObject *keydatastruct_pylookup(PyObject *me, PyObject *key)
+{
+  const char *tag;
+  key_data *kd;
+  PyObject *rc = 0;
+
+  if ((tag = PyString_AsString(key)) == 0)
+    goto end;
+  if ((kd = key_structfind(KEYDATA_KD(me), tag)) == 0)
+    INDEXERR(key);
+  key_incref(kd);
+  rc = keydata_pywrap(kd);
+end:
+  return (rc);
+}
+
+static int keydatastruct_pystore(PyObject *me,
+                                PyObject *key, PyObject *value)
+{
+  const char *tag;
+  int rc = -1;
+
+  if ((tag = PyString_AsString(key)) == 0)
+    goto end;
+  if (value) {
+    if (!KEYDATA_PYCHECK(value))
+      TYERR("expected KeyData value");
+    key_structset(KEYDATA_KD(me), tag, KEYDATA_KD(value));
+  } else {
+    if (!key_structfind(KEYDATA_KD(me), tag))
+      INDEXERR(key);
+    key_structset(KEYDATA_KD(me), tag, 0);
+  }
+  rc = 0;
+end:
+  return (rc);
+}
+
+static PyMappingMethods keydatastruct_pymapping = {
+  0,                                   /* @mp_length@ */
+  keydatastruct_pylookup,              /* @mp_subscript@ */
+  keydatastruct_pystore,               /* @mp_ass_subscript@ */
+};
+
+static PyTypeObject keydatastruct_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyDataStructured",                /* @tp_name@ */
+  sizeof(keydata_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  &keydatastruct_pymapping,            /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Key data for structured keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  subkeyiter_make,                     /* @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@ */
+  keydatastruct_pynew,                 /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Key attributes ----------------------------------------------------*/
+
+static PyObject *keyattriter_make(PyObject *kaobj)
+{
+  PyObject *kobj = KEYATTRS_KOBJ(kaobj);
+  keyattriter_pyobj *me = PyObject_NEW(keyattriter_pyobj,
+                                      keyattriter_pytype);
+  me->kobj = kobj;
+  Py_INCREF(kobj);
+  key_mkattriter(&me->i, KEY_K(kobj));
+  return ((PyObject *)me);
+}
+
+static PyObject *keyattriter_next(PyObject *me)
+{
+  const char *name;
+
+  if (!key_nextattr(KEYATTRITER_I(me), &name, 0))
+    return (0);
+  return (PyString_FromString(name));
+}
+
+static void keyattriter_pydealloc(PyObject *me)
+{
+  Py_DECREF(KEYATTRITER_KOBJ(me));
+  FREEOBJ(me);
+}
+
+static PyTypeObject keyattriter_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyAttributeIter",         /* @tp_name@ */
+  sizeof(keyattriter_pyobj),           /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  keyattriter_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@ */
+"Iterator for key attributes.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  keyattriter_next,                    /* @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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *keyattrs_pylookup(PyObject *me, PyObject *key)
+{
+  const char *attr;
+  const char *value;
+  PyObject *rc = 0;
+
+  if ((attr = PyString_AsString(key)) == 0)
+    goto end;
+  if ((value = key_getattr(KEYATTRS_KF(me), KEYATTRS_K(me), attr)) == 0)
+    INDEXERR(key);
+  rc = PyString_FromString(value);
+end:
+  return (rc);
+}
+
+static int keyattrs_pystore(PyObject *me,
+                           PyObject *key, PyObject *value)
+{
+  const char *attr;
+  const char *val;
+  int err;
+  int rc = -1;
+
+  if ((attr = PyString_AsString(key)) == 0)
+    goto end;
+  if (value) {
+    if ((val = PyString_AsString(value)) == 0)
+      goto end;
+    if ((err = key_putattr(KEYATTRS_KF(me), KEYATTRS_K(me),
+                          attr, val)) != 0)
+      KEYERR(err);
+  } else {
+    if (!key_getattr(KEYATTRS_KF(me), KEYATTRS_K(me), attr))
+      INDEXERR(key);
+    if ((err = key_putattr(KEYATTRS_KF(me), KEYATTRS_K(me), attr, 0)) != 0)
+      KEYERR(err);
+  }
+  rc = 0;
+end:
+  return (rc);
+}
+
+static PyObject *keyattrs_make(PyObject *kobj)
+{
+  keyattrs_pyobj *me = PyObject_NEW(keyattrs_pyobj, keyattrs_pytype);
+  me->kobj = kobj;
+  Py_INCREF(kobj);
+  return ((PyObject *)me);
+}
+
+static void keyattrs_pydealloc(PyObject *me)
+{
+  Py_DECREF(KEYATTRS_KOBJ(me));
+  FREEOBJ(me);
+}
+
+static PyMappingMethods keyattrs_pymapping = {
+  0,
+  keyattrs_pylookup,
+  keyattrs_pystore
+};
+
+static PyTypeObject keyattrs_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyAttributes",            /* @tp_name@ */
+  sizeof(keyattrs_pyobj),              /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  keyattrs_pydealloc,                  /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  &keyattrs_pymapping,                 /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Proxy thing for talking about key attributes.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  keyattriter_make,                    /* @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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Key objects -------------------------------------------------------*/
+
+static PyObject *key_dowrap(PyTypeObject *ty, PyObject *kfobj, key *k)
+{
+  key_pyobj *kobj = (key_pyobj *)ty->tp_alloc(ty, 0);
+  kobj->kfobj = kfobj;
+  Py_INCREF(kfobj);
+  kobj->k = k;
+  return ((PyObject *)kobj);
+}
+
+static PyObject *key_pywrap(PyObject *kfobj, key *k)
+  { return (key_dowrap(key_pytype, kfobj, k)); }
+
+static PyObject *key_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  PyObject *kfobj;
+  uint32 id;
+  char *type;
+  long exptime = KEXP_FOREVER;
+  static char *kwlist[] = { "keyfile", "id", "type", "exptime", 0 };
+  key *k;
+  int err;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&sl:new", kwlist,
+                                  keyfile_pytype, &kfobj, convu32, &id,
+                                  &type, &exptime))
+    goto end;
+  if ((err = key_new(KEYFILE_KF(kfobj), id, type, exptime, &k)) == 0)
+    KEYERR(err);
+  return (key_dowrap(ty, kfobj, k));
+end:
+  return (0);
+}
+
+static void key_pydealloc(PyObject *me)
+{
+  Py_DECREF(KEY_KFOBJ(me));
+  FREEOBJ(me);
+}
+
+static PyObject *kmeth_delete(PyObject *me, PyObject *arg)
+{
+  int err;
+  
+  if (!PyArg_ParseTuple(arg, ":delete")) goto end;
+  if ((err = key_delete(KEY_KF(me), KEY_K(me))) != 0) KEYERR(err);
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *kmeth_expire(PyObject *me, PyObject *arg)
+{
+  int err;
+  
+  if (!PyArg_ParseTuple(arg, ":expire")) goto end;
+  if ((err = key_expire(KEY_KF(me), KEY_K(me))) != 0) KEYERR(err);
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *kmeth_used(PyObject *me, PyObject *arg)
+{
+  long t;
+  int err;
+  
+  if (!PyArg_ParseTuple(arg, "l:used", &t)) goto end;
+  if ((err = key_used(KEY_KF(me), KEY_K(me), t)) != 0) KEYERR(err);
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *kmeth_extract(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  key_filter f = { 0, 0 };
+  PyObject *file;
+  PyObject *nameobj;
+  char *name;
+  FILE *fp;
+  static char *kwlist[] = { "file", "filter", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:extract", kwlist,
+                                  &PyFile_Type, &file,
+                                  convfilter, &f) ||
+      (fp = PyFile_AsFile(file)) == 0 ||
+      (nameobj = PyFile_Name(file)) == 0 ||
+      (name = PyString_AsString(nameobj)) == 0)
+    goto end;
+  if (key_extract(KEY_KF(me), KEY_K(me), fp, &f))
+    OSERR(name);
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *kmeth_fingerprint(PyObject *me,
+                                  PyObject *arg, PyObject *kw)
+{
+  ghash *h;
+  key_filter f = { KF_NONSECRET, KF_NONSECRET };
+  static char *kwlist[] = { "hash", "filter", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:fingerprint", kwlist,
+                                  convghash, &h, convfilter, &f))
+    return (0);
+  return (getbool(key_fingerprint(KEY_K(me), h, &f)));
+}
+
+static PyObject *kget_id(PyObject *me, void *hunoz)
+  { return (getu32(KEY_K(me)->id)); }
+static PyObject *kget_type(PyObject *me, void *hunoz)
+  { return (PyString_FromString(KEY_K(me)->type)); }
+static PyObject *kget_exptime(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(KEY_K(me)->exp)); }
+static PyObject *kget_deltime(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(KEY_K(me)->del)); }
+static PyObject *kget_expiredp(PyObject *me, void *hunoz)
+  { return (getbool(key_expired(KEY_K(me)))); }
+static PyObject *kget_attr(PyObject *me, void *hunoz)
+  { return (keyattrs_make(me)); }
+
+static PyObject *kget_data(PyObject *me, void *hunoz)
+{
+  key_data *kd = KEY_K(me)->k;
+  key_incref(kd);
+  return (keydata_pywrap(kd));
+}
+static int kset_data(PyObject *me, PyObject *x, void *hunoz)
+{
+  int err;
+  if (!KEYDATA_PYCHECK(x)) TYERR("expected KeyData object");
+  if ((err = key_setkeydata(KEY_KF(me), KEY_K(me), KEYDATA_KD(x))) != 0)
+    KEYERR(err);
+  return (0);
+end:
+  return (-1);
+}
+
+static PyObject *kget_fulltag(PyObject *me, void *hunoz)
+{
+  dstr d = DSTR_INIT;
+  PyObject *rc;
+
+  key_fulltag(KEY_K(me), &d);
+  rc = PyString_FromStringAndSize(d.buf, d.len);
+  dstr_destroy(&d);
+  return (rc);
+}
+
+static PyObject *kget_tag(PyObject *me, void *hunoz)
+{
+  if (!KEY_K(me)->tag) RETURN_NONE;
+  return (PyString_FromString(KEY_K(me)->tag));
+}
+static int kset_tag(PyObject *me, PyObject *x, void *hunoz)
+{
+  int err;
+  char *tag;
+
+  if (x == Py_None) tag = 0;
+  else if ((tag = PyString_AsString(x)) == 0) goto end;
+  if ((err = key_settag(KEY_KF(me), KEY_K(me), tag)) != 0) KEYERR(err);
+  return (0);
+end:
+  return (-1);
+}
+
+static PyObject *kget_comment(PyObject *me, void *hunoz)
+{
+  if (!KEY_K(me)->c) RETURN_NONE;
+  return (PyString_FromString(KEY_K(me)->c));
+}
+static int kset_comment(PyObject *me, PyObject *x, void *hunoz)
+{
+  int err;
+  char *c;
+
+  if (x == Py_None) c = 0;
+  else if ((c = PyString_AsString(x)) == 0) goto end;
+  if ((err = key_setcomment(KEY_KF(me), KEY_K(me), c)) != 0) KEYERR(err);
+  return (0);
+end:
+  return (-1);
+}
+
+static PyMethodDef key_pymethods[] = {
+#define METHNAME(func) kmeth_##func
+  METH (delete,        "KEY.delete()")
+  METH (expire,        "KEY.expire()")
+  METH (used,          "KEY.used(TIME)")
+  KWMETH(extract,      "KEY.extract(FILE, filter = '')")
+  KWMETH(fingerprint,  "KEY.fingerprint(HASH, filtr = '-secret')")
+#undef METHNAME
+  { 0 }
+};
+
+static PyGetSetDef key_pygetset[] = {
+#define GETSETNAME(op, name) k##op##_##name
+  GET  (id,            "KEY.id -> ID")
+  GETSET(tag,          "KEY.tag -> TAG")
+  GET  (type,          "KEY.type -> TYPE")
+  GET  (exptime,       "KEY.exptime -> TIME")
+  GET  (deltime,       "KEY.deltime -> TIME")
+  GET  (expiredp,      "KEY.expiredp -> BOOL")
+  GET  (attr,          "KEY.attr -> ATTRIBUTES")
+  GETSET(data,         "KEY.data -> KD")
+  GETSET(comment,      "KEY.comment -> COMMENT")
+  GET  (fulltag,       "KEY.fulltag -> FULLTAG")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject key_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.Key",                      /* @tp_name@ */
+  sizeof(key_pyobj),                   /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  key_pydealloc,                       /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_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 object.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  key_pymethods,                       /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  key_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@ */
+  key_pynew,                           /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Key iteration -----------------------------------------------------*/
+
+static PyObject *keyiter_new(PyObject *kfobj)
+{
+  keyiter_pyobj *me = PyObject_NEW(keyiter_pyobj, keyiter_pytype);
+  key_mkiter(&me->i, KEYFILE_KF(kfobj));
+  me->kfobj = kfobj;
+  Py_INCREF(kfobj);
+  return ((PyObject *)me);
+}
+
+static PyObject *keyiter_next(PyObject *me)
+{
+  key *k;
+
+  if ((k = key_next(KEYITER_I(me))) == 0)
+    return (0);
+  return (key_pywrap(KEYITER_KFOBJ(me), k));
+}
+
+static void keyiter_pydealloc(PyObject *me)
+{
+  Py_DECREF(KEYITER_KFOBJ(me));
+  FREEOBJ(me);
+}
+
+static PyTypeObject keyiter_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyFileIter",              /* @tp_name@ */
+  sizeof(keyiter_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  keyiter_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@ */
+"Keyring iterator.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  keyiter_next,                                /* @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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
 /*----- Key files ---------------------------------------------------------*/
 
+struct reportinfo {
+  PyObject *func;
+  int stop;
+};
+
+static void pythonreporter(const char *file, int line,
+                          const char *msg, void *p)
+{
+  struct reportinfo *ri = p;
+  PyObject *res = 0;
+
+  if (ri->stop)
+    return;
+  if (!ri->func)
+    key_moan(file, line, msg, 0);
+  else if ((res = PyObject_CallFunction(ri->func, "sis",
+                                       file, line, msg)) == 0)
+    ri->stop = 1;
+  else
+    Py_DECREF(res);
+}
+
 static PyObject *keyfile_pynew(PyTypeObject *ty,
                               PyObject *arg, PyObject *kw)
 {
-  
+  struct reportinfo ri = { 0, 0 };
+  char *file = 0;
+  unsigned how = KOPEN_READ;
+  keyfile_pyobj *rc = 0;
+  static char *kwlist[] = { "file", "how", "report", 0 };
+
+  Py_XINCREF(arg); Py_XINCREF(kw);
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", kwlist,
+                                  &file, &how, &ri.func))
+    goto end;
+  if (ri.func && !PyCallable_Check(ri.func))
+    TYERR("reporter function not callable");
+  if ((rc = (keyfile_pyobj *)ty->tp_alloc(ty, 0)) == 0)
+    goto end;
+  if (key_open(&rc->kf, file, how, pythonreporter, &ri))
+    OSERR(file);
+  if (ri.stop) {
+    key_discard(&rc->kf);
+    goto end;
+  }
+  goto done;
+
+end:
+  if (rc) {
+    FREEOBJ(rc);
+    rc = 0;
+  }
+done:
+  Py_XDECREF(arg); Py_XDECREF(kw);
+  return ((PyObject *)rc);
+}
+
+static void keyfile_pydealloc(PyObject *me)
+{
+  key_discard(KEYFILE_KF(me));
+  FREEOBJ(me);
+}
+
+static PyObject *kfmeth_save(PyObject *me, PyObject *arg)
+{
+  if (!PyArg_ParseTuple(arg, ":save"))
+    goto end;
+  switch (key_save(KEYFILE_KF(me))) {
+    case KWRITE_OK:
+      RETURN_ME;
+    case KWRITE_FAIL:
+      KEYIOERR(KEYFILE_KF(me)->name);
+    case KWRITE_BROKEN:
+      KEYFILEBROKEN(KEYFILE_KF(me)->name);
+    default:
+      abort();
+  }
+end:
+  return (0);
+}
+
+static PyObject *kfmeth_merge(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  struct reportinfo ri = { 0, 0 };
+  char *name;
+  PyObject *x = 0;
+  FILE *fp = 0;
+  int rc;
+  static char *kwlist[] = { "file", "report", 0 };
+
+  Py_XINCREF(arg); Py_XINCREF(kw);
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O:merge", kwlist,
+                                  &PyFile_Type, &x, &ri.func))
+    goto end;
+  if (ri.func && !PyCallable_Check(ri.func))
+    TYERR("reporter function not callable");
+  if ((fp = PyFile_AsFile(x)) == 0)
+    goto end;
+  x = PyFile_Name(x);
+  if ((name = PyString_AsString(x)) == 0)
+    goto end;
+  rc = key_merge(KEYFILE_KF(me), name, fp, pythonreporter, &ri);
+  if (ri.stop)
+    goto end;
+  if (rc != 0)
+    KEYERR(rc);
+  Py_XDECREF(arg); Py_XDECREF(kw);
+  RETURN_ME;
+
+end:
+  Py_XDECREF(arg); Py_XDECREF(kw);
+  return (0);
+}
+
+static PyObject *kfmeth_byid(PyObject *me, PyObject *arg)
+{
+  uint32 id;
+  key *k;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "O&:byid", convu32, &id)) goto end;
+  if ((k = key_byid(KEYFILE_KF(me), id)) == 0) KEYERR(KERR_NOTFOUND);
+  rc = key_pywrap(me, k);
+end:
+  return (rc);
+}
+
+static PyObject *kfmeth_bytype(PyObject *me, PyObject *arg)
+{
+  char *type;
+  key *k;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "s:bytype", &type)) goto end;
+  if ((k = key_bytype(KEYFILE_KF(me), type)) == 0) RETURN_NONE;
+  rc = key_pywrap(me, k);
+end:
+  return (rc);
+}
+
+static PyObject *bytag(PyObject *me, PyObject *tagobj)
+{
+  uint32 id;
+  char *tag;
+  key *k;
+  PyObject *rc = 0;
+
+  if (convu32(tagobj, &id))
+    k = key_byid(KEYFILE_KF(me), id);
+  else {
+    PyErr_Clear();
+    if ((tag = PyString_AsString(tagobj)) == 0)
+      goto end;
+    k = key_bytag(KEYFILE_KF(me), tag);
+  }
+  if (!k) RETURN_NONE;
+  rc = key_pywrap(me, k);
+end:
+  return (rc);
+}
+
+static PyObject *kfmeth_bytag(PyObject *me, PyObject *arg)
+{
+  PyObject *tagobj;
+
+  if (!PyArg_ParseTuple(arg, "O:bytag", &tagobj)) return (0);
+  return (bytag(me, tagobj));
+}
+
+static PyObject *keyfile_pylookup(PyObject *me, PyObject *key)
+{
+  PyObject *rc = bytag(me, key);
+  if (!rc) goto end;
+  if (rc == Py_None) {
+    Py_DECREF(rc);
+    rc = 0;
+    INDEXERR(key);
+  }
+end:
+  return (rc);
+}  
+
+static PyObject *kfmeth_newkey(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  uint32 id;
+  char *type;
+  long exptime = KEXP_FOREVER;
+  static char *kwlist[] = { "id", "type", "exptime", 0 };
+  key *k;
+  int err;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&sl:new", kwlist,
+                                  convu32, &id, &type, &exptime))
+    goto end;
+  if ((err = key_new(KEYFILE_KF(me), id, type, exptime, &k)) == 0)
+    KEYERR(err);
+  return (key_pywrap(me, k));
+end:
+  return (0);
+}
+
+static PyObject *kfmeth_qtag(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  key *k;
+  key_data **kd;
+  PyObject *newkdobj = 0;
+  char *tag;
+  PyObject *kdobj;
+  dstr d = DSTR_INIT;
+  PyObject *rc = 0;
+  static char *kwlist[] = { "tag", "new", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O!:qtag", kwlist,
+                                  &tag, keydata_pytype, &newkdobj))
+    goto end;
+  if (key_qtag(KEYFILE_KF(me), tag, &d, &k, &kd))
+    KEYERR(KERR_NOTFOUND);
+  key_incref(*kd);
+  kdobj = keydata_pywrap(*kd);
+  if (newkdobj) {
+    if (!(KEYFILE_KF(me)->f & KF_WRITE))
+      KEYERR(KERR_READONLY);
+    key_drop(*kd);
+    *kd = KEYDATA_KD(newkdobj);
+    key_incref(*kd);
+  }
+  rc = Py_BuildValue("(s#NN)", d.buf, d.len, key_pywrap(me, k), kdobj);
+end:
+  return (rc);
+}
+
+static PyObject *kfget_name(PyObject *me, void *hunoz)
+  { return (PyString_FromString(KEYFILE_KF(me)->name)); }
+static PyObject *kfget_modifiedp(PyObject *me, void *hunoz)
+  { return (getbool(KEYFILE_KF(me)->f & KF_MODIFIED)); }
+static PyObject *kfget_writep(PyObject *me, void *hunoz)
+  { return (getbool(KEYFILE_KF(me)->f & KF_WRITE)); }
+static PyObject *kfget_filep(PyObject *me, void *hunoz)
+  { return (getbool(!!KEYFILE_KF(me)->fp)); }
+
+static PyMethodDef keyfile_pymethods[] = {
+#define METHNAME(func) kfmeth_##func
+  METH (save,          "KF.save()")
+  KWMETH(merge,                "KF.merge(FILE, report = <built-in-reporter>)")
+  KWMETH(newkey,       "KF.newkey(ID, TYPE, exptime = KEXP_FOREVER) -> KEY")
+  METH (byid,          "KF.byid(KEYID) -> KEY|None")
+  METH (bytype,        "KF.bytype(TYPE) -> KEY|None")
+  METH (bytag,         "KF.bytag(TAG) -> KEY|None")
+  KWMETH(qtag,         "KF.qtag(TAG, new = KD) -> FULLTAG, KEY, OLDKD")
+#undef METHNAME
+  { 0 }
+};
+
+static PyGetSetDef keyfile_pygetset[] = {
+#define GETSETNAME(op, name) kf##op##_##name
+  GET  (name,          "KF.name -> file name")
+  GET  (modifiedp,     "KF.modifiedp -> has keyring been modified?")
+  GET  (writep,        "KF.writep -> is keyring modifiable?")
+  GET  (filep,         "KF.filep -> does keyring have a backing file?")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyMappingMethods keyfile_pymapping = {
+  0,
+  keyfile_pylookup,
+  0
+};
+
+static PyTypeObject keyfile_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "catacomb.KeyFile",                  /* @tp_name@ */
+  sizeof(keyfile_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  keyfile_pydealloc,                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  &keyfile_pymapping,                  /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Keyring file.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  keyiter_new,                         /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  keyfile_pymethods,                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  keyfile_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@ */
+  keyfile_pynew,                       /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Global stuff ------------------------------------------------------*/
+
+static PyObject *meth_barf(PyObject *me, PyObject *arg)
+{
+  int err;
+
+  if (PyArg_ParseTuple(arg, "i:barf", &err))
+    KEYERR(err);
+end:
+  return (0);
+}
+
+static PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+  METH (_KeyData_readflags,
+          "KeyData.readflags(STRING) -> (FLAGS, MASK, REST)")
+  METH (_KeyData_writeflags,   "KeyData.writeflags(FLAGS) -> STRING")
+  METH (_KeyData_read,         "KeyData.read(STRING) -> (KD, REST)")
+  METH (_KeyData_decode,       "KeyData.read(BYTES) -> KD")
+  METH (barf,                  "barf(ERR)")
+#undef METHNAME
+  { 0 }
+};
+
+/*----- Initialization ----------------------------------------------------*/
+
+void key_pyinit(void)
+{
+  INITTYPE(keyfile, root);
+  INITTYPE(key, root);
+  INITTYPE(keyiter, root);
+  INITTYPE(keydata, root);
+  INITTYPE(keydatabin, keydata);
+  INITTYPE(keydataenc, keydata);
+  INITTYPE(keydatastr, keydata);
+  INITTYPE(keydatamp, keydata);
+  INITTYPE(keydataec, keydata);
+  INITTYPE(keydatastruct, keydata);
+  INITTYPE(subkeyiter, root);
+  INITTYPE(keyattrs, root);
+  INITTYPE(keyattriter, root);
+  addmethods(methods);
+}
+
+void key_pyinsert(PyObject *mod)
+{
+  INSEXC("KeyError", keyexc, PyExc_Exception, keyexc_pymethods);
+  INSEXC("KeyFileIOError", keyioexc, PyExc_OSError, 0);
+  INSEXC("KeyFileBroken", keyfilebrokenexc, keyioexc, 0);
+  INSERT("KeyFile", keyfile_pytype);
+  INSERT("KeyFileIter", keyiter_pytype);
+  INSERT("Key", key_pytype);
+  INSERT("KeyAttributes", keyattrs_pytype);
+  INSERT("KeyAttributeIter", keyattriter_pytype);
+  INSERT("KeyData", keydata_pytype);
+  INSERT("KeyDataBinary", keydatabin_pytype);
+  INSERT("KeyDataEncrypted", keydataenc_pytype);
+  INSERT("KeyDataMP", keydatamp_pytype);
+  INSERT("KeyDataECPt", keydataec_pytype);
+  INSERT("KeyDataString", keydatastr_pytype);
+  INSERT("KeyDataStructured", keydatastruct_pytype);
+  INSERT("SubKeyIter", subkeyiter_pytype);
 }
 
 /*----- That's all, folks -------------------------------------------------*/
index 862fe51..52d0ca5 100644 (file)
--- a/pubkey.c
+++ b/pubkey.c
@@ -378,7 +378,7 @@ static PyObject *kcdsameth_sign(PyObject *me, PyObject *arg, PyObject *kw)
   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);
+  s.r = (octet *)PyString_AS_STRING(r);
   gkcdsa_sign(DSA_D(me), &s, p, k);
   rc = Py_BuildValue("(NN)", r, mp_pywrap(s.s));
 end:
index 0fa8848..adf9474 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -40,8 +40,6 @@ def uniquify(l):
       u[i] = 1
   return o
 
-cflags = []
-libs = []
 libconfig('catacomb', '2.1.0')
 libconfig('mLib', '2.0.3')
 
@@ -94,20 +92,21 @@ for g in ['algorithms.h']:
     rename(fnew, fout)
 
 cat = Extension('catacomb._base',
-                ['catacomb.c', 'bytestring.c',
+                ['catacomb.c', 'bytestring.c', 'buffer.c',
                  'rand.c', 'algorithms.c', 'pubkey.c', 'pgen.c',
-                 'mp.c', 'field.c', 'ec.c', 'group.c', 'passphrase.c'],
+                 'mp.c', 'field.c', 'ec.c', 'group.c', 'passphrase.c',
+                 'share.c', 'key.c'],
                 ##extra_compile_args = ['-O0'],
                 include_dirs = uniquify(incdirs),
                 library_dirs = uniquify(libdirs),
                 libraries = uniquify(libs))
 
-setup(name = 'Catacomb',
-      version = '2.1.0',
+setup(name = 'catacomb-python',
+      version = '1.0.0',
       description = 'Interface to Catacomb cryptographic library',
-      url = 'http://tux.nsict.org/~mdw/Catacomb-2.1.0',
+      url = 'http://www.distorted.org.uk/~mdw/Catacomb-2.1.0',
       author = 'Straylight/Edgeware',
-      author_email = 'mdw@nsict.org',
+      author_email = 'mdw@distorted.org.uk',
       license = 'GNU General Public License',
       packages = ['catacomb'],
       scripts = ['pwsafe'],
diff --git a/share.c b/share.c
new file mode 100644 (file)
index 0000000..38d966e
--- /dev/null
+++ b/share.c
@@ -0,0 +1,640 @@
+/* -*-c-*-
+ *
+ * $Id$
+ *
+ * Secret sharing
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the Python interface to Catacomb.
+ *
+ * Catacomb/Python is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * Catacomb/Python is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Catacomb/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "catacomb-python.h"
+
+/*----- GF(2^8)-based secret-sharing --------------------------------------*/
+
+typedef struct gfshare_pyobj {
+  PyObject_HEAD
+  gfshare s;
+} gfshare_pyobj;
+
+static PyTypeObject
+  *gfshare_pytype, *gfsharesplit_pytype, *gfsharejoin_pytype;
+#define GFSHARE_PYCHECK(o) PyObject_TypeCheck((o), gfshare_pytype)
+#define GFSHARESPLIT_PYCHECK(o) PyObject_TypeCheck((o), gfsharesplit_pytype)
+#define GFSHAREJOIN_PYCHECK(o) PyObject_TypeCheck((o), gfsharejoin_pytype)
+#define GFSHARE_S(o) (&((gfshare_pyobj *)(o))->s)
+
+static void gfshare_pydealloc(PyObject *me)
+{
+  gfshare_destroy(GFSHARE_S(me));
+  FREEOBJ(me);
+}
+
+static PyObject *gfsget_threshold(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GFSHARE_S(me)->t)); }
+static PyObject *gfsget_size(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GFSHARE_S(me)->sz)); }
+
+static PyGetSetDef gfshare_pygetset[]= {
+#define GETSETNAME(op, name) gfs##op##_##name
+  GET  (threshold,     "S.threshold -> THRESHOLD")
+  GET  (size,          "S.size -> SECRETSZ")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject gfshare_pytype_skel = {
+  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
+  "catacomb.GFShare",                  /* @tp_name@ */
+  sizeof(gfshare_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gfshare_pydealloc,                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Binary-field secret sharing base class.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  gfshare_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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *gfsharesplit_pynew(PyTypeObject *ty,
+                                   PyObject *arg, PyObject *kw)
+{
+  char *p;
+  int n;
+  unsigned t;
+  grand *r = &rand_global;
+  gfshare_pyobj *s;
+  char *kwlist[] = { "threshold", "secret", "rng", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s#|O&:new", kwlist,
+                                  convuint, &t, &p, &n, convgrand, &r))
+    goto end;
+  if (!t || t > 255) VALERR("threshold must be nonzero and < 256");
+  s = (gfshare_pyobj *)ty->tp_alloc(ty, 0);
+  gfshare_create(&s->s, t, n);
+  gfshare_mkshares(&s->s, r, p);
+  return ((PyObject *)s);
+end:
+  return (0);
+}
+
+static PyObject *gfsmeth_get(PyObject *me, PyObject *arg)
+{
+  unsigned i;
+  PyObject *rc = 0;
+  if (!PyArg_ParseTuple(arg, "O&:get", convuint, &i)) goto end;
+  if (i >= 255) VALERR("index must be < 255");
+  rc = bytestring_pywrap(0, GFSHARE_S(me)->sz);
+  gfshare_get(GFSHARE_S(me), i, PyString_AS_STRING(rc));
+end:
+  return (rc);
+}
+
+static PyMethodDef gfsharesplit_pymethods[] = {
+#define METHNAME(name) gfsmeth_##name
+  METH (get,           "S.get(I) -> SHARE")
+#undef METHNAME
+  { 0 }
+};
+
+static PyTypeObject gfsharesplit_pytype_skel = {
+  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
+  "catacomb.GFShareSplit",             /* @tp_name@ */
+  sizeof(gfshare_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gfshare_pydealloc,                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Binary field secret sharing: split secret into shares.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  gfsharesplit_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@ */
+  gfsharesplit_pynew,                  /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *gfsharejoin_pynew(PyTypeObject *ty,
+                                  PyObject *arg, PyObject *kw)
+{
+  unsigned t, sz;
+  gfshare_pyobj *s;
+  char *kwlist[] = { "threshold", "size", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+                                  convuint, &t, convuint, &sz))
+    goto end;
+  if (!t || t > 255) VALERR("threshold must be nonzero and < 256");
+  s = (gfshare_pyobj *)ty->tp_alloc(ty, 0);
+  gfshare_create(&s->s, t, sz);
+  return ((PyObject *)s);
+end:
+  return (0);
+}
+
+static PyObject *gfsmeth_addedp(PyObject *me, PyObject *arg)
+{
+  unsigned i;
+  if (!PyArg_ParseTuple(arg, "O&:addedp", convuint, &i)) goto end;
+  if (i > 254) VALERR("index must be < 255");
+  return (getbool(gfshare_addedp(GFSHARE_S(me), i)));
+end:
+  return (0);
+}
+
+static PyObject *gfsmeth_add(PyObject *me, PyObject *arg)
+{
+  unsigned i;
+  char *p;
+  int n;
+  if (!PyArg_ParseTuple(arg, "O&s#:add", convuint, &i, &p, &n)) goto end;
+  if (i > 254) VALERR("index must be < 255");
+  if (n != GFSHARE_S(me)->sz) VALERR("bad share size");
+  if (gfshare_addedp(GFSHARE_S(me), i)) VALERR("this share already added");
+  if (GFSHARE_S(me)->i >= GFSHARE_S(me)->t) VALERR("enough shares already");
+  gfshare_add(GFSHARE_S(me), i, p);
+  return (PyInt_FromLong(GFSHARE_S(me)->t - GFSHARE_S(me)->i));
+end:
+  return (0);
+}
+
+static PyObject *gfsmeth_combine(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  if (!PyArg_ParseTuple(arg, ":combine")) goto end;
+  if (GFSHARE_S(me)->i < GFSHARE_S(me)->t) VALERR("not enough shares yet");
+  rc = bytestring_pywrap(0, GFSHARE_S(me)->sz);
+  gfshare_combine(GFSHARE_S(me), PyString_AS_STRING(rc));
+end:
+  return (rc);
+}
+
+static PyMethodDef gfsharejoin_pymethods[] = {
+#define METHNAME(name) gfsmeth_##name
+  METH (addedp,        "S.addedp(I) -> BOOL")
+  METH (add,           "S.add(I, SHARE) -> REMAIN")
+  METH (combine,       "S.combine() -> SECRET")
+#undef METHNAME
+  { 0 }
+};
+
+static PyObject *gfsget_remain(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GFSHARE_S(me)->t - GFSHARE_S(me)->i)); }
+
+static PyGetSetDef gfsharejoin_pygetset[]= {
+#define GETSETNAME(op, name) gfs##op##_##name
+  GET  (remain,        "S.remain -> REMAIN")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject gfsharejoin_pytype_skel = {
+  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
+  "catacomb.GFShareJoin",              /* @tp_name@ */
+  sizeof(gfshare_pyobj),               /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gfshare_pydealloc,                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Binary field secret sharing: join shares to recover secret.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  gfsharejoin_pymethods,               /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  gfsharejoin_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@ */
+  gfsharejoin_pynew,                   /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Prime-field secret-sharing ----------------------------------------*/
+
+typedef struct share_pyobj {
+  PyObject_HEAD
+  share s;
+} share_pyobj;
+
+static PyTypeObject
+  *share_pytype, *sharesplit_pytype, *sharejoin_pytype;
+#define SHARE_PYCHECK(o) PyObject_TypeCheck((o), share_pytype)
+#define SHARESPLIT_PYCHECK(o) PyObject_TypeCheck((o), sharesplit_pytype)
+#define SHAREJOIN_PYCHECK(o) PyObject_TypeCheck((o), sharejoin_pytype)
+#define SHARE_S(o) (&((share_pyobj *)(o))->s)
+
+static void share_pydealloc(PyObject *me)
+{
+  share_destroy(SHARE_S(me));
+  FREEOBJ(me);
+}
+
+static PyObject *sget_threshold(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(SHARE_S(me)->t)); }
+static PyObject *sget_modulus(PyObject *me, void *hunoz)
+  { return (mp_pywrap(SHARE_S(me)->p)); }
+
+static PyGetSetDef share_pygetset[]= {
+#define GETSETNAME(op, name) s##op##_##name
+  GET  (threshold,     "S.threshold -> THRESHOLD")
+  GET  (modulus,       "S.modulus -> MODULUS")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject share_pytype_skel = {
+  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
+  "catacomb.Share",                    /* @tp_name@ */
+  sizeof(share_pyobj),                 /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  share_pydealloc,                     /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Prime-field secret sharing base class.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  share_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@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *sharesplit_pynew(PyTypeObject *ty,
+                                 PyObject *arg, PyObject *kw)
+{
+  mp *sec = 0;
+  unsigned t;
+  grand *r = &rand_global;
+  mp *m = 0;
+  share_pyobj *s;
+  char *kwlist[] = { "threshold", "secret", "modulus", "rng", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&)&|O&O&:new", kwlist,
+                                  convuint, &t, convmp, &sec,
+                                  convmp, &m, convgrand, &r))
+    goto end;
+  if (!t) VALERR("threshold must be nonzero");
+  s = (share_pyobj *)ty->tp_alloc(ty, 0);
+  share_create(&s->s, t);
+  s->s.p = m;
+  share_mkshares(&s->s, r, sec);
+  mp_drop(sec);
+  return ((PyObject *)s);
+end:
+  mp_drop(m);
+  mp_drop(sec);
+  return (0);
+}
+
+static PyObject *smeth_get(PyObject *me, PyObject *arg)
+{
+  unsigned i;
+  PyObject *rc = 0;
+  if (!PyArg_ParseTuple(arg, "O&:get", convuint, &i)) goto end;
+  rc = mp_pywrap(share_get(SHARE_S(me), MP_NEW, i));
+end:
+  return (rc);
+}
+
+static PyMethodDef sharesplit_pymethods[] = {
+#define METHNAME(name) smeth_##name
+  METH (get,           "S.get(I) -> SHARE")
+#undef METHNAME
+  { 0 }
+};
+
+static PyTypeObject sharesplit_pytype_skel = {
+  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
+  "catacomb.ShareSplit",               /* @tp_name@ */
+  sizeof(share_pyobj),                 /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  share_pydealloc,                     /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Prime field secret sharing: split secret into shares.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  sharesplit_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@ */
+  sharesplit_pynew,                    /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *sharejoin_pynew(PyTypeObject *ty,
+                                PyObject *arg, PyObject *kw)
+{
+  unsigned t;
+  mp *m = 0;
+  share_pyobj *s;
+  char *kwlist[] = { "threshold", "modulus", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+                                  convuint, &t, convmp, &m))
+    goto end;
+  if (!t) VALERR("threshold must be nonzero");
+  s = (share_pyobj *)ty->tp_alloc(ty, 0);
+  share_create(&s->s, t);
+  s->s.p = m;
+  return ((PyObject *)s);
+end:
+  mp_drop(m);
+  return (0);
+}
+
+static PyObject *smeth_addedp(PyObject *me, PyObject *arg)
+{
+  unsigned i;
+  if (!PyArg_ParseTuple(arg, "O&:addedp", convuint, &i)) goto end;
+  return (getbool(share_addedp(SHARE_S(me), i)));
+end:
+  return (0);
+}
+
+static PyObject *smeth_add(PyObject *me, PyObject *arg)
+{
+  unsigned i;
+  mp *s = 0;
+  PyObject *rc = 0;
+  if (!PyArg_ParseTuple(arg, "O&O&:add", convuint, &i, convmp, &s)) goto end;
+  if (MP_NEGP(s) || MP_CMP(s, >=, SHARE_S(me)->p))
+    VALERR("share out of range");
+  if (share_addedp(SHARE_S(me), i)) VALERR("this share already added");
+  if (SHARE_S(me)->i >= SHARE_S(me)->t) VALERR("enough shares already");
+  share_add(SHARE_S(me), i, s);
+  rc = PyInt_FromLong(SHARE_S(me)->t - SHARE_S(me)->i);
+end:
+  mp_drop(s);
+  return (rc);
+}
+
+static PyObject *smeth_combine(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  if (!PyArg_ParseTuple(arg, ":combine")) goto end;
+  if (SHARE_S(me)->i < SHARE_S(me)->t) VALERR("not enough shares yet");
+  rc = mp_pywrap(share_combine(SHARE_S(me)));
+end:
+  return (rc);
+}
+
+static PyMethodDef sharejoin_pymethods[] = {
+#define METHNAME(name) smeth_##name
+  METH (addedp,        "S.addedp(I) -> BOOL")
+  METH (add,           "S.add(I, SHARE) -> REMAIN")
+  METH (combine,       "S.combine() -> SECRET")
+#undef METHNAME
+  { 0 }
+};
+
+static PyObject *sget_remain(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(SHARE_S(me)->t - SHARE_S(me)->i)); }
+
+static PyGetSetDef sharejoin_pygetset[]= {
+#define GETSETNAME(op, name) s##op##_##name
+  GET  (remain,        "S.remain -> REMAIN")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyTypeObject sharejoin_pytype_skel = {
+  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
+  "catacomb.ShareJoin",                        /* @tp_name@ */
+  sizeof(share_pyobj),                 /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  share_pydealloc,                     /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Prime field secret sharing: join shares to recover secret.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  sharejoin_pymethods,                 /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  sharejoin_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@ */
+  sharejoin_pynew,                     /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Global stuff ------------------------------------------------------*/
+
+void share_pyinit(void)
+{
+  INITTYPE(gfshare, root);
+  INITTYPE(gfsharesplit, gfshare);
+  INITTYPE(gfsharejoin, gfshare);
+  INITTYPE(share, root);
+  INITTYPE(sharesplit, share);
+  INITTYPE(sharejoin, share);
+}
+
+void share_pyinsert(PyObject *mod)
+{
+  INSERT("GFShare", gfshare_pytype);
+  INSERT("GFShareSplit", gfsharesplit_pytype);
+  INSERT("GFShareJoin", gfsharejoin_pytype);
+  INSERT("Share", share_pytype);
+  INSERT("ShareSplit", sharesplit_pytype);
+  INSERT("ShareJoin", sharejoin_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/