chiark / gitweb /
Merge branch '1.2.x' into 1.3.x
authorMark Wooding <mdw@distorted.org.uk>
Wed, 27 Nov 2019 15:11:08 +0000 (15:11 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 27 Nov 2019 15:11:08 +0000 (15:11 +0000)
* 1.2.x: (89 commits)
  t/: Add a test suite.
  ec.c: Don't lose error status when constructing points from a sequence.
  ec.c: Free partially constructed points coordinatewise.
  *.c: Be more careful about `PySequence_Size'.
  key.c: Reformat the rest of the `KeyError' constructor.
  key.c: Parse `KeyError' constructor arguments by hand.
  catacomb-python.h: Add a macro for raising `OverflowError'.
  key.c: Collect `KeyError' argument count as a separate step.
  key.c: Use tuple functions on `KeyError' argument tuple.
  key.c: Rename sad-path label to `end'.
  key.c: Delete duplicate setting of `errstring'.
  util.c (mkexc): Populate dictionary before constructing exception class.
  key.c: Only set the error code.
  catacomb.c, util.c: Publish negative constants correctly.
  field.c: Delete the completely unused `getfe' function.
  key.c (convfilter): Fix sense of error tests.
  buffer.c, ec.c: Fix required size for EC `buffer' encoding.
  algorithms.c: Fix `max' property name in docstrings.
  catacomb/__init__.py (_HashBase): Check that integers are within bounds.
  debian/rules: Build using the provided Makefile.
  ...

19 files changed:
1  2 
MANIFEST.in
algorithms.c
algorithms.py
buffer.c
catacomb-python.h
catacomb.c
catacomb/__init__.py
debian/changelog
debian/control
ec.c
field.c
group.c
key.c
mp.c
pgen.c
pubkey.c
rand.c
setup.py
util.c

diff --combined MANIFEST.in
index 36bf4c2f8b24ff929042fe39dea80074da38ef38,c09417be9d064d3246c7c66838956da8e2740bed..33ce54af5430f17382557d18e9c563f915eb5c05
@@@ -1,4 -1,4 +1,4 @@@
 -### Manifest template for Catacomb/Python.
 +### Manifest template for Catacomb/Python. -*-conf-*-
  
  ## Generated build machinery.
  include COPYING auto-version mdwsetup.py pysetup.mk
@@@ -8,16 -8,11 +8,17 @@@ include MANIFEST.in setup.py Makefil
  
  ## C extension code.
  include *.c *.h
+ include t/*.py t/keyring
  include algorithms.py
  exclude algorithms.h
 +
 +## Scripts.
 +include pock
  include pwsafe
  
 +## Manual pages.
 +include *.[13]
 +
  ## Python wrapping.
  recursive-include catacomb *.py
  
diff --combined algorithms.c
index d73981e252f0e5a0c8de0a2d29bee7d9b48bce0d,edd73fd8119224577430b7aa7062bc990a58453c..37d52cd49d88dafad281bbb87e059f27799c6603
@@@ -83,11 -83,11 +83,11 @@@ PyObject *keysz_pywrap(const octet *k
  static PyObject *keyszany_pynew(PyTypeObject *ty,
                                PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "default", 0 };
 +  static const char *const kwlist[] = { "default", 0 };
    int dfl;
    keysz_pyobj *o;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i:new", kwlist, &dfl))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i:new", KWLIST, &dfl))
      goto end;
    if (dfl < 0) VALERR("key size cannot be negative");
    o = (keysz_pyobj *)ty->tp_alloc(ty, 0);
@@@ -100,16 -100,18 +100,16 @@@ end
  static PyObject *keyszrange_pynew(PyTypeObject *ty,
                                  PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "default", "min", "max", "mod", 0 };
 +  static const char *const kwlist[] = { "default", "min", "max", "mod", 0 };
    int dfl, min = 0, max = 0, mod = 1;
    keyszrange_pyobj *o;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|iii:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|iii:new", KWLIST,
                                   &dfl, &min, &max, &mod))
      goto end;
 -  if (dfl < 0 || min < 0 || max < 0)
 -    VALERR("key size cannot be negative");
 -  if (min > dfl || (max && dfl > max))
 -    VALERR("bad key size bounds");
 -  if (mod <= 0 || dfl % mod || min % mod || max % mod)
 +  if (dfl < 0 || min < 0) VALERR("key size cannot be negative");
 +  if (min > dfl || (max && dfl > max)) VALERR("bad key size bounds");
 +  if (mod <= 0 || dfl%mod || min%mod || max%mod)
      VALERR("bad key size modulus");
    o = (keyszrange_pyobj *)ty->tp_alloc(ty, 0);
    o->dfl = dfl;
@@@ -124,20 -126,20 +124,19 @@@ end
  static PyObject *keyszset_pynew(PyTypeObject *ty,
                                PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "default", "set", 0 };
 +  static const char *const kwlist[] = { "default", "set", 0 };
    int dfl, i, n, xx;
    PyObject *set = 0;
    PyObject *x = 0, *l = 0;
    keyszset_pyobj *o = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|O:new", kwlist,
 -                                        &dfl, &set))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|O:new", KWLIST, &dfl, &set))
      goto end;
    if (!set) set = PyTuple_New(0);
    else Py_INCREF(set);
    if (!PySequence_Check(set)) TYERR("want a sequence");
-   n = PySequence_Size(set);
-   l = PyList_New(0);
-   if (PyErr_Occurred()) goto end;
+   n = PySequence_Size(set); if (n < 0) goto end;
+   l = PyList_New(0); if (!l) goto end;
    if (dfl < 0) VALERR("key size cannot be negative");
    x = PyInt_FromLong(dfl);
    PyList_Append(l, x);
@@@ -206,7 -208,7 +205,7 @@@ static PyMemberDef keysz_pymembers[] = 
  static PyGetSetDef keyszany_pygetset[] = {
  #define GETSETNAME(op, name) ka##op##_##name
    GET (min,                   "KSZ.min -> smallest allowed key size")
-   GET (max,                   "KSZ.min -> largest allowed key size")
+   GET (max,                   "KSZ.max -> largest allowed key size")
  #undef GETSETNAME
    { 0 }
  };
  static PyMemberDef keyszrange_pymembers[] = {
  #define MEMBERSTRUCT keyszrange_pyobj
    MEMBER(min, T_INT, READONLY,        "KSZ.min -> smallest allowed key size")
-   MEMBER(max, T_INT, READONLY,        "KSZ.min -> largest allowed key size")
+   MEMBER(max, T_INT, READONLY,        "KSZ.max -> largest allowed key size")
    MEMBER(mod, T_INT, READONLY,
         "KSZ.mod -> key size must be a multiple of this")
  #undef MEMBERSTRUCT
  static PyGetSetDef keyszset_pygetset[] = {
  #define GETSETNAME(op, name) ks##op##_##name
    GET (min,                   "KSZ.min -> smallest allowed key size")
-   GET (max,                   "KSZ.min -> largest allowed key size")
+   GET (max,                   "KSZ.max -> largest allowed key size")
  #undef GETSETNAME
    { 0 }
  };
@@@ -261,7 -263,7 +260,7 @@@ static PyTypeObject keysz_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key size constraints.",
 +"Key size constraints.  Abstract.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -309,8 -311,7 +308,8 @@@ static PyTypeObject keyszany_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key size constraints.  This object imposes no constraints on size.",
 +"KeySZAny(DEFAULT)\n\
 +  Key size constraints.  This object imposes no constraints on size.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -358,9 -359,8 +357,9 @@@ static PyTypeObject keyszrange_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key size constraints.  This object asserts minimum and maximum (if\n\
 -sizes, and requires the key length to be a multiple of some value.",
 +"KeySZRange(DEFAULT, [min = 0], [max = 0], [mod = 1])\n\
 +  Key size constraints.  Key size must be between MIN and MAX inclusive,\n\
 +  and be a multiple of MOD.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -408,8 -408,8 +407,8 @@@ static PyTypeObject keyszset_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key size constraints.  This object requires the key to be one of a\n\
 -few listed sizes.",
 +"KeySZSet(DEFAULT, SEQ)\n\
 +  Key size constraints.  Key size must be DEFAULT or one in SEQ.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -457,27 -457,29 +456,27 @@@ PyTypeObject *gccipher_pytype, *gcipher
  CONVFUNC(gccipher, gccipher *, GCCIPHER_CC)
  CONVFUNC(gcipher, gcipher *, GCIPHER_C)
  
 -PyObject *gcipher_pywrap(PyObject *cobj, gcipher *c, unsigned f)
 +PyObject *gcipher_pywrap(PyObject *cobj, gcipher *c)
  {
    gcipher_pyobj *g;
    if (!cobj) cobj = gccipher_pywrap((/*unconst*/ gccipher *)GC_CLASS(c));
    else Py_INCREF(cobj);
    g = PyObject_NEW(gcipher_pyobj, (PyTypeObject *)cobj);
    g->c = c;
 -  g->f = f;
    return ((PyObject *)g);
  }
  
  static PyObject *gcipher_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "k", 0 };
 +  static const char *const kwlist[] = { "k", 0 };
    char *k;
    Py_ssize_t sz;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
      goto end;
    if (keysz(sz, GCCIPHER_CC(ty)->keysz) != sz) VALERR("bad key length");
    return (gcipher_pywrap((PyObject *)ty,
 -                       GC_INIT(GCCIPHER_CC(ty), k, sz),
 -                       f_freeme));
 +                       GC_INIT(GCCIPHER_CC(ty), k, sz)));
  end:
    return (0);
  }
@@@ -501,7 -503,8 +500,7 @@@ PyObject *gccipher_pywrap(gccipher *cc
  
  static void gcipher_pydealloc(PyObject *me)
  {
 -  if (GCIPHER_F(me) & f_freeme)
 -    GC_DESTROY(GCIPHER_C(me));
 +  GC_DESTROY(GCIPHER_C(me));
    Py_DECREF(me->ob_type);
    FREEOBJ(me);
  }
@@@ -710,1082 -713,6 +709,1082 @@@ static PyTypeObject gcipher_pytype_ske
    0                                   /* @tp_is_gc@ */
  };
  
 +/*----- Authenticated encryption ------------------------------------------*/
 +
 +PyTypeObject *gcaead_pytype, *gaeadkey_pytype;
 +PyTypeObject *gcaeadaad_pytype, *gaeadaad_pytype;
 +PyTypeObject *gcaeadenc_pytype, *gaeadenc_pytype;
 +PyTypeObject *gcaeaddec_pytype, *gaeaddec_pytype;
 +
 +CONVFUNC(gcaead, gcaead *, GCAEAD_AEC)
 +CONVFUNC(gaeadkey, gaead_key *, GAEADKEY_K)
 +
 +PyObject *gaeadkey_pywrap(PyObject *cobj, gaead_key *k)
 +{
 +  gaeadkey_pyobj *gk;
 +
 +  if (!cobj) cobj = gcaead_pywrap((/*unconst*/ gcaead *)GAEAD_CLASS(k));
 +  else Py_INCREF(cobj);
 +  gk = PyObject_NEW(gaeadkey_pyobj, (PyTypeObject *)cobj);
 +  gk->k = k;
 +  return ((PyObject *)gk);
 +}
 +
 +static PyObject *gaeadkey_pynew(PyTypeObject *ty,
 +                              PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "k", 0 };
 +  char *k;
 +  Py_ssize_t sz;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
 +    goto end;
 +  if (keysz(sz, GCAEAD_AEC(ty)->keysz) != sz) VALERR("bad key length");
 +  return (gaeadkey_pywrap((PyObject *)ty,
 +                        GAEAD_KEY(GCAEAD_AEC(ty), k, sz)));
 +end:
 +  return (0);
 +}
 +
 +PyObject *gcaead_pywrap(gcaead *aec)
 +{
 +  gcaead_pyobj *gck;
 +  gcaeadaad_pyobj *gca;
 +  gcaeadenc_pyobj *gce;
 +  gcaeaddec_pyobj *gcd;
 +
 +#define MKTYPE(obj, thing, newfn, namefmt) do {                               \
 +  (obj) = newtype(gcaead_pytype, 0, 0);                                       \
 +  (obj)->ty.ht_name = PyString_FromFormat(namefmt, aec->name);                \
 +  (obj)->ty.ht_type.tp_name = PyString_AS_STRING((obj)->ty.ht_name);  \
 +  (obj)->ty.ht_type.tp_basicsize = sizeof(gaead##thing##_pyobj);      \
 +  (obj)->ty.ht_type.tp_base = gaead##thing##_pytype;                  \
 +  Py_INCREF(gaead##thing##_pytype);                                   \
 +  (obj)->ty.ht_type.tp_flags =                                                \
 +    (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE); \
 +  (obj)->ty.ht_type.tp_alloc = PyType_GenericAlloc;                   \
 +  (obj)->ty.ht_type.tp_free = 0;                                      \
 +  (obj)->ty.ht_type.tp_new = newfn;                                   \
 +  typeready(&(obj)->ty.ht_type);                                      \
 +} while (0)
 +
 +  MKTYPE(gck, key, gaeadkey_pynew, "%s(key)");
 +  MKTYPE(gca, aad, abstract_pynew, "%s(aad-hash)");
 +  MKTYPE(gce, enc, abstract_pynew, "%s(encrypt)");
 +  MKTYPE(gcd, dec, abstract_pynew, "%s(decrypt)");
 +
 +#undef MKTYPE
 +
 +  gck->aec = aec; gck->aad = gca; gck->enc = gce; gck->dec = gcd;
 +  gca->key = gce->key = gcd->key = gck;
 +  return ((PyObject *)gck);
 +}
 +
 +static void gaeadkey_pydealloc(PyObject *me)
 +  { GAEAD_DESTROY(GAEADKEY_K(me)); Py_DECREF(me->ob_type); FREEOBJ(me); }
 +
 +static PyObject *gcaeget_name(PyObject *me, void *hunoz)
 +  { return (PyString_FromString(GCAEAD_AEC(me)->name)); }
 +
 +static PyObject *gcaeget_keysz(PyObject *me, void *hunoz)
 +  { return (keysz_pywrap(GCAEAD_AEC(me)->keysz)); }
 +
 +static PyObject *gcaeget_noncesz(PyObject *me, void *hunoz)
 +  { return (keysz_pywrap(GCAEAD_AEC(me)->noncesz)); }
 +
 +static PyObject *gcaeget_tagsz(PyObject *me, void *hunoz)
 +  { return (keysz_pywrap(GCAEAD_AEC(me)->tagsz)); }
 +
 +static PyObject *gcaeget_blksz(PyObject *me, void *hunoz)
 +  { return (PyInt_FromLong(GCAEAD_AEC(me)->blksz)); }
 +
 +static PyObject *gcaeget_bufsz(PyObject *me, void *hunoz)
 +  { return (PyInt_FromLong(GCAEAD_AEC(me)->bufsz)); }
 +
 +static PyObject *gcaeget_ohd(PyObject *me, void *hunoz)
 +  { return (PyInt_FromLong(GCAEAD_AEC(me)->ohd)); }
 +
 +static PyObject *gcaeget_flags(PyObject *me, void *hunoz)
 +  { return (PyInt_FromLong(GCAEAD_AEC(me)->f)); }
 +
 +static PyGetSetDef gcaead_pygetset[] = {
 +#define GETSETNAME(op, name) gcae##op##_##name
 +  GET (keysz,                 "AEC.keysz -> acceptable key sizes")
 +  GET (noncesz,               "AEC.noncesz -> acceptable nonce sizes")
 +  GET (tagsz,                 "AEC.tagsz -> acceptable tag sizes")
 +  GET (blksz,                 "AEC.blksz -> block size, or zero")
 +  GET (bufsz,                 "AEC.bufsz -> amount of data buffered internally")
 +  GET (ohd,                   "AEC.ohd -> maximum encryption overhead")
 +  GET (name,                  "AEC.name -> name of this kind of AEAD scheme")
 +  GET (flags,                 "AEC.flags -> mask of `AEADF_...' flags")
 +#undef GETSETNAME
 +  { 0 }
 +};
 +
 +static PyObject *gaekmeth_aad(PyObject *me, PyObject *arg)
 +{
 +  const gaead_key *k = GAEADKEY_K(me);
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTuple(arg, ":aad")) return (0);
 +  if (k->ops->c->f&AEADF_AADNDEP)
 +    VALERR("aad must be associated with enc/dec op");
 +  rc = gaeadaad_pywrap((PyObject *)GCAEAD_AAD(me->ob_type),
 +                     GAEAD_AAD(k), 0, 0);
 +end:
 +  return (rc);
 +}
 +
 +static int check_aead_encdec(const gcaead *aec, unsigned *f_out, size_t nsz,
 +                           PyObject *hszobj, size_t *hsz_out,
 +                           PyObject *mszobj, size_t *msz_out,
 +                           PyObject *tszobj, size_t *tsz_out)
 +{
 +  unsigned f = 0, miss;
 +  int rc = -1;
 +
 +  if (hszobj != Py_None)
 +    { f |= AEADF_PCHSZ; if (!convszt(hszobj, hsz_out)) goto end; }
 +  if (mszobj != Py_None)
 +    { f |= AEADF_PCMSZ; if (!convszt(mszobj, msz_out)) goto end; }
 +  if (tszobj != Py_None)
 +    { f |= AEADF_PCTSZ; if (!convszt(tszobj, tsz_out)) goto end; }
 +  miss = aec->f&~f;
 +  if (miss&AEADF_PCHSZ) VALERR("header length precommitment required");
 +  if (miss&AEADF_PCMSZ) VALERR("message length precommitment required");
 +  if (miss&AEADF_PCTSZ) VALERR("tag length precommitment required");
 +  if (keysz(nsz, aec->noncesz) != nsz) VALERR("bad nonce length");
 +  if (tszobj != Py_None && keysz(*tsz_out, aec->tagsz) != *tsz_out)
 +    VALERR("bad tag length");
 +  *f_out = f | aec->f; rc = 0;
 +end:
 +  return (rc);
 +}
 +
 +static PyObject *gaekmeth_enc(PyObject *me, PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "nonce", "hsz", "msz", "tsz", 0 };
 +  const gaead_key *k = GAEADKEY_K(me);
 +  gaead_enc *e;
 +  PyObject *rc = 0;
 +  char *n; Py_ssize_t nsz;
 +  PyObject *hszobj = Py_None, *mszobj = Py_None, *tszobj = Py_None;
 +  size_t hsz = 0, msz = 0, tsz = 0;
 +  unsigned f;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:enc", KWLIST,
 +                                 &n, &nsz, &hszobj, &mszobj, &tszobj))
 +    goto end;
 +  if (check_aead_encdec(k->ops->c, &f, nsz,
 +                      hszobj, &hsz, mszobj, &msz, tszobj, &tsz))
 +    goto end;
 +  e = GAEAD_ENC(GAEADKEY_K(me), n, nsz, hsz, msz, tsz);
 +  if (!e) VALERR("bad aead parameter combination");
 +  rc = gaeadenc_pywrap((PyObject *)GCAEAD_ENC(me->ob_type),
 +                     e, f, hsz, msz, tsz);
 +end:
 +  return (rc);
 +}
 +
 +static PyObject *gaekmeth_dec(PyObject *me, PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "nonce", "hsz", "csz", "tsz", 0 };
 +  const gaead_key *k = GAEADKEY_K(me);
 +  gaead_dec *d;
 +  PyObject *rc = 0;
 +  char *n; Py_ssize_t nsz;
 +  PyObject *hszobj = Py_None, *cszobj = Py_None, *tszobj = Py_None;
 +  size_t hsz = 0, csz = 0, tsz = 0;
 +  unsigned f;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:dec", KWLIST,
 +                                 &n, &nsz, &hszobj, &cszobj, &tszobj))
 +    goto end;
 +  if (check_aead_encdec(k->ops->c, &f, nsz,
 +                      hszobj, &hsz, cszobj, &csz, tszobj, &tsz))
 +    goto end;
 +  d = GAEAD_DEC(GAEADKEY_K(me), n, nsz, hsz, csz, tsz);
 +  if (!d) VALERR("bad aead parameter combination");
 +  rc = gaeaddec_pywrap((PyObject *)GCAEAD_DEC(me->ob_type),
 +                     d, f, hsz, csz, tsz);
 +end:
 +  return (rc);
 +}
 +
 +static PyMethodDef gaeadkey_pymethods[] = {
 +#define METHNAME(name) gaekmeth_##name
 +  METH  (aad,                 "KEY.aad() -> AAD")
 +  KWMETH(enc,                 "KEY.enc(NONCE, [hsz], [msz], [tsz]) -> ENC")
 +  KWMETH(dec,                 "KEY.dec(NONCE, [hsz], [csz], [tsz]) -> DEC")
 +#undef METHNAME
 +  { 0 }
 +};
 +
 +PyObject *gaeadaad_pywrap(PyObject *cobj, gaead_aad *a,
 +                        unsigned f, size_t hsz)
 +{
 +  gaeadaad_pyobj *ga;
 +
 +  assert(cobj); Py_INCREF(cobj);
 +  ga = PyObject_NEW(gaeadaad_pyobj, (PyTypeObject *)cobj);
 +  ga->a = a; ga->f = f; ga->hsz = hsz; ga->hlen = 0;
 +  return ((PyObject *)ga);
 +}
 +
 +static void gaeadaad_pydealloc(PyObject *me)
 +{
 +  gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
 +
 +  if (ga->a) GAEAD_DESTROY(ga->a);
 +  Py_DECREF(me->ob_type); FREEOBJ(me);
 +}
 +
 +static int gaea_check(PyObject *me)
 +{
 +  gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
 +  int rc = -1;
 +
 +  if ((ga->f&AEADF_DEAD) || !ga->a) VALERR("aad object no longer active");
 +  rc = 0;
 +end:
 +  return (rc);
 +}
 +
 +static void gaea_invalidate(gaeadaad_pyobj *ga)
 +  { if (ga) ga->f |= AEADF_DEAD; }
 +
 +static void gaea_sever(gaeadaad_pyobj **ga_inout)
 +{
 +  gaeadaad_pyobj *ga = *ga_inout;
 +  if (ga) { ga->f |= AEADF_DEAD; ga->a = 0; Py_DECREF(ga); *ga_inout = 0; }
 +}
 +
 +static PyObject *gaeaget_hsz(PyObject *me, void *hunoz)
 +{
 +  if (gaea_check(me)) return (0);
 +  else if (GAEADAAD_F(me)&AEADF_PCHSZ) return getulong(GAEADAAD_HSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaeaget_hlen(PyObject *me, void *hunoz)
 +  { return (gaea_check(me) ? 0 : getulong(GAEADAAD_HLEN(me))); }
 +
 +static PyGetSetDef gaeadaad_pygetset[] = {
 +#define GETSETNAME(op, name) gaea##op##_##name
 +  GET (hsz,                   "AAD.hsz -> precommitted header length or `None'")
 +  GET (hlen,                  "AAD.hlen -> header length so far")
 +#undef GETSETNAME
 +  { 0 }
 +};
 +
 +static PyObject *gaeameth_copy(PyObject *me, PyObject *arg)
 +{
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTuple(arg, ":copy")) goto end;
 +  if (gaea_check(me)) goto end;
 +  if (GAEADAAD_F(me)&AEADF_AADNDEP)
 +    VALERR("can't duplicate nonce-dependent aad");
 +  rc = gaeadaad_pywrap((PyObject *)me->ob_type,
 +                     GAEAD_DUP(GAEADAAD_A(me)), 0, 0);
 +end:
 +  return (rc);
 +}
 +
 +static int gaeadaad_hash(PyObject *me, const void *h, size_t hsz)
 +{
 +  gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
 +  int rc = -1;
 +
 +  if (gaea_check(me)) goto end;
 +  if ((ga->f&AEADF_NOAAD) && hsz)
 +    VALERR("header data not permitted");
 +  if ((ga->f&AEADF_PCHSZ) && hsz > ga->hsz - ga->hlen)
 +    VALERR("too large for precommitted header length");
 +  GAEAD_HASH(ga->a, h, hsz); ga->hlen += hsz;
 +  rc = 0;
 +end:
 +  return (rc);
 +}
 +
 +
 +static PyObject *gaeameth_hash(PyObject *me, PyObject *arg)
 +{
 +  char *h; Py_ssize_t hsz;
 +
 +  if (!PyArg_ParseTuple(arg, "s#:hash", &h, &hsz)) return (0);
 +  if (gaeadaad_hash(me, h, hsz)) return (0);
 +  RETURN_ME;
 +}
 +
 +#define GAEAMETH_HASHU_(n, W, w)                                      \
 +  static PyObject *gaeameth_hashu##w(PyObject *me, PyObject *arg)     \
 +  {                                                                   \
 +    uint##n x; octet b[SZ_##W];                                               \
 +    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
 +    STORE##W(b, x); if (gaeadaad_hash(me, b, sizeof(b))) return (0);  \
 +    RETURN_ME;                                                                \
 +  }
 +DOUINTCONV(GAEAMETH_HASHU_)
 +
 +#define GAEAMETH_HASHBUF_(n, W, w)                                    \
 +  static PyObject *gaeameth_hashbuf##w(PyObject *me, PyObject *arg)   \
 +  {                                                                   \
 +    char *p; Py_ssize_t sz; octet b[SZ_##W];                          \
 +    if (!PyArg_ParseTuple(arg, "s#:hashbuf" #w, &p, &sz)) goto end;   \
 +    if (sz > MASK##n) TYERR("string too long");                               \
 +    STORE##W(b, sz); if (gaeadaad_hash(me, b, sizeof(b))) goto end;   \
 +    if (gaeadaad_hash(me, p, sz)) goto end;                           \
 +    RETURN_ME;                                                                \
 +  end:                                                                        \
 +    return (0);                                                               \
 +  }
 +DOUINTCONV(GAEAMETH_HASHBUF_)
 +
 +static PyObject *gaeameth_hashstrz(PyObject *me, PyObject *arg)
 +{
 +  char *p;
 +  if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
 +  if (gaeadaad_hash(me, p, strlen(p) + 1)) return (0);
 +  RETURN_ME;
 +}
 +
 +static PyMethodDef gaeadaad_pymethods[] = {
 +#define METHNAME(name) gaeameth_##name
 +  METH  (copy,                        "AAD.copy() -> AAD'")
 +  METH  (hash,                        "AAD.hash(H)")
 +#define METHU_(n, W, w) METH(hashu##w, "AAD.hashu" #w "(WORD)")
 +  DOUINTCONV(METHU_)
 +#undef METHU_
 +#define METHBUF_(n, W, w) METH(hashbuf##w, "AAD.hashbuf" #w "(BYTES)")
 +  DOUINTCONV(METHBUF_)
 +#undef METHBUF_
 +  METH  (hashstrz,            "AAD.hashstrz(STRING)")
 +#undef METHNAME
 +  { 0 }
 +};
 +
 +PyObject *gaeadenc_pywrap(PyObject *cobj, gaead_enc *e, unsigned f,
 +                        size_t hsz, size_t msz, size_t tsz)
 +{
 +  gaeadenc_pyobj *ge;
 +
 +  assert(cobj); Py_INCREF(cobj);
 +  ge = PyObject_NEW(gaeadenc_pyobj, (PyTypeObject *)cobj);
 +  ge->e = e; ge->f = f; ge->hsz = hsz; ge->msz = msz; ge->tsz = tsz;
 +  ge->aad = 0; ge->mlen = 0;
 +  return ((PyObject *)ge);
 +}
 +
 +static void gaeadenc_pydealloc(PyObject *me)
 +{
 +  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
 +
 +  gaea_sever(&ge->aad); GAEAD_DESTROY(ge->e);
 +  Py_DECREF(me->ob_type); FREEOBJ(me);
 +}
 +
 +static PyObject *gaeeget_hsz(PyObject *me, void *hunoz)
 +{
 +  if (GAEADENC_F(me)&AEADF_PCHSZ) return getulong(GAEADENC_HSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaeeget_msz(PyObject *me, void *hunoz)
 +{
 +  if (GAEADENC_F(me)&AEADF_PCMSZ) return getulong(GAEADENC_MSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaeeget_tsz(PyObject *me, void *hunoz)
 +{
 +  if (GAEADENC_F(me)&AEADF_PCTSZ) return getulong(GAEADENC_TSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaeeget_mlen(PyObject *me, void *hunoz)
 +  { return getulong(GAEADENC_MLEN(me)); }
 +
 +static PyGetSetDef gaeadenc_pygetset[] = {
 +#define GETSETNAME(op, name) gaee##op##_##name
 +  GET (hsz,                   "ENC.hsz -> precommitted header length or `None'")
 +  GET (msz,                   "ENC.msz -> precommitted message length or `None'")
 +  GET (tsz,                   "ENC.tsz -> precommitted tag length or `None'")
 +  GET (mlen,                  "ENC.mlen -> message length so far")
 +#undef GETSETNAME
 +  { 0 }
 +};
 +
 +static PyObject *gaeemeth_aad(PyObject *me, PyObject *arg)
 +{
 +  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTuple(arg, ":aad")) return (0);
 +  if (!(ge->f&AEADF_AADNDEP))
 +    rc = gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(ge->ob_type)->aad,
 +                       GAEAD_AAD(ge->e), 0, 0);
 +  else {
 +    if ((ge->f&AEADF_AADFIRST) && ge->mlen)
 +      VALERR("too late for aad");
 +    if (!ge->aad)
 +      ge->aad = (gaeadaad_pyobj *)
 +      gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(ge->ob_type)->aad,
 +                      GAEAD_AAD(ge->e), ge->f&AEADF_PCHSZ, ge->hsz);
 +    Py_INCREF(ge->aad);
 +    rc = (PyObject *)ge->aad;
 +  }
 +end:
 +  return (rc);
 +}
 +
 +static PyObject *gaeemeth_reinit(PyObject *me, PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "nonce", "hsz", "msz", "tsz", 0 };
 +  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
 +  char *n; Py_ssize_t nsz;
 +  PyObject *hszobj = Py_None, *mszobj = Py_None, *tszobj = Py_None;
 +  size_t hsz = 0, msz = 0, tsz = 0;
 +  unsigned f;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:enc", KWLIST,
 +                                 &n, &nsz, &hszobj, &mszobj, &tszobj))
 +    goto end;
 +  if (check_aead_encdec(ge->e->ops->c, &f, nsz,
 +                      hszobj, &hsz, mszobj, &msz, tszobj, &tsz))
 +    goto end;
 +  if (GAEAD_REINIT(ge->e, n, nsz, hsz, msz, tsz))
 +    VALERR("bad aead parameter combination");
 +  gaea_sever(&ge->aad);
 +  ge->f = f; ge->hsz = hsz; ge->msz = msz; ge->tsz = tsz;
 +end:
 +  return (0);
 +}
 +
 +static PyObject *gaeemeth_encrypt(PyObject *me, PyObject *arg)
 +{
 +  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
 +  char *m; Py_ssize_t msz;
 +  char *c = 0; size_t csz; buf b;
 +  int err;
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTuple(arg, "s#:encrypt", &m, &msz)) goto end;
 +  if (ge->f&AEADF_AADFIRST) {
 +    if ((ge->f&AEADF_PCHSZ) && (ge->aad ? ge->aad->hlen : 0) != ge->hsz)
 +      VALERR("header doesn't match precommitted length");
 +    gaea_invalidate(ge->aad);
 +  }
 +  if ((ge->f&AEADF_PCMSZ) && msz > ge->msz - ge->mlen)
 +    VALERR("too large for precommitted message length");
 +  csz = msz + ge->e->ops->c->bufsz; c = xmalloc(csz); buf_init(&b, c, csz);
 +  err = GAEAD_ENCRYPT(ge->e, m, msz, &b); assert(!err); (void)err;
 +  buf_flip(&b); rc = bytestring_pywrapbuf(&b); ge->mlen += msz;
 +end:
 +  xfree(c);
 +  return (rc);
 +}
 +
 +static PyObject *gaeemeth_done(PyObject *me, PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "tsz", "aad", 0 };
 +  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
 +  PyObject *aad = Py_None;
 +  char *c = 0; size_t csz; buf b;
 +  PyObject *tszobj = Py_None; PyObject *tag; size_t tsz;
 +  int err;
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:done", KWLIST,
 +                                 &tszobj, &aad))
 +    goto end;
 +  if (tszobj != Py_None && !convszt(tszobj, &tsz)) goto end;
 +  if (aad != Py_None &&
 +      !PyObject_TypeCheck(aad,
 +                        (PyTypeObject *)GCAEADENC_KEY(me->ob_type)->aad))
 +    TYERR("wanted aad");
 +  if ((ge->f&AEADF_AADNDEP) && aad != Py_None && aad != (PyObject *)ge->aad)
 +    VALERR("mismatched aad");
 +  if ((ge->f&AEADF_PCHSZ) &&
 +      (aad == Py_None ? 0 : GAEADAAD_HLEN(aad)) != ge->hsz)
 +    VALERR("header doesn't match precommitted length");
 +  if ((ge->f&AEADF_PCMSZ) && ge->mlen != ge->msz)
 +    VALERR("message doesn't match precommitted length");
 +  if (tszobj == Py_None) {
 +    if (ge->f&AEADF_PCTSZ) tsz = ge->tsz;
 +    else tsz = keysz(0, ge->e->ops->c->tagsz);
 +  } else {
 +    if ((ge->f&AEADF_PCTSZ) && tsz != ge->tsz)
 +      VALERR("tag length doesn't match precommitted value");
 +    if (keysz(tsz, ge->e->ops->c->tagsz) != tsz) VALERR("bad tag length");
 +  }
 +  csz = ge->e->ops->c->bufsz; c = xmalloc(csz); buf_init(&b, c, csz);
 +  tag = bytestring_pywrap(0, tsz);
 +  err = GAEAD_DONE(ge->e, aad == Py_None ? 0 : GAEADAAD_A(aad), &b,
 +                 PyString_AS_STRING(tag), tsz);
 +  assert(!err); (void)err;
 +  buf_flip(&b); rc = Py_BuildValue("NN", bytestring_pywrapbuf(&b), tag);
 +end:
 +  xfree(c);
 +  return (rc);
 +}
 +
 +static PyMethodDef gaeadenc_pymethods[] = {
 +#define METHNAME(name) gaeemeth_##name
 +  METH  (aad,                 "ENC.aad() -> AAD")
 +  KWMETH(reinit,              "ENC.reinit(NONCE, [hsz], [msz], [tsz])")
 +  METH  (encrypt,             "ENC.encrypt(MSG) -> CT")
 +  KWMETH(done,                        "ENC.done([tsz], [aad]) -> CT, TAG")
 +#undef METHNAME
 +  { 0 }
 +};
 +
 +PyObject *gaeaddec_pywrap(PyObject *cobj, gaead_dec *d, unsigned f,
 +                        size_t hsz, size_t csz, size_t tsz)
 +{
 +  gaeaddec_pyobj *gd;
 +  assert(cobj); Py_INCREF(cobj);
 +  gd = PyObject_NEW(gaeaddec_pyobj, (PyTypeObject *)cobj);
 +  gd->d = d; gd->f = f; gd->hsz = hsz; gd->csz = csz; gd->tsz = tsz;
 +  gd->aad = 0; gd->clen = 0;
 +  return ((PyObject *)gd);
 +}
 +
 +static void gaeaddec_pydealloc(PyObject *me)
 +{
 +  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
 +
 +  gaea_sever(&gd->aad); GAEAD_DESTROY(GAEADDEC_D(me));
 +  Py_DECREF(me->ob_type); FREEOBJ(me);
 +}
 +
 +static PyObject *gaedget_hsz(PyObject *me, void *hunoz)
 +{
 +  if (GAEADDEC_F(me)&AEADF_PCHSZ) return getulong(GAEADDEC_HSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaedget_csz(PyObject *me, void *hunoz)
 +{
 +  if (GAEADDEC_F(me)&AEADF_PCMSZ) return getulong(GAEADDEC_CSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaedget_tsz(PyObject *me, void *hunoz)
 +{
 +  if (GAEADDEC_F(me)&AEADF_PCTSZ) return getulong(GAEADDEC_TSZ(me));
 +  else RETURN_NONE;
 +}
 +
 +static PyObject *gaedget_clen(PyObject *me, void *hunoz)
 +  { return getulong(GAEADDEC_CLEN(me)); }
 +
 +static PyGetSetDef gaeaddec_pygetset[] = {
 +#define GETSETNAME(op, name) gaed##op##_##name
 +  GET (hsz,                   "DEC.hsz -> precommitted header length or `None'")
 +  GET (csz,                   "DEC.csz -> precommitted ciphertext length or `None'")
 +  GET (tsz,                   "DEC.tsz -> precommitted tag length or `None'")
 +  GET (clen,                  "DEC.clen -> ciphertext length so far")
 +#undef GETSETNAME
 +  { 0 }
 +};
 +
 +static PyObject *gaedmeth_aad(PyObject *me, PyObject *arg)
 +{
 +  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
 +
 +  if (!PyArg_ParseTuple(arg, ":aad")) return (0);
 +  if (!(gd->f&AEADF_AADNDEP))
 +    return (gaeadaad_pywrap((PyObject *)GCAEADDEC_KEY(gd->ob_type)->aad,
 +                          GAEAD_AAD(gd->d), 0, 0));
 +  else {
 +    if (!gd->aad)
 +      gd->aad = (gaeadaad_pyobj *)
 +      gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(gd->ob_type)->aad,
 +                      GAEAD_AAD(gd->d), gd->f&AEADF_PCHSZ, gd->hsz);
 +    Py_INCREF(gd->aad);
 +    return ((PyObject *)gd->aad);
 +  }
 +}
 +
 +static PyObject *gaedmeth_reinit(PyObject *me, PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "nonce", "hsz", "csz", "tsz", 0 };
 +  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
 +  char *n; Py_ssize_t nsz;
 +  PyObject *hszobj = Py_None, *cszobj = Py_None, *tszobj = Py_None;
 +  size_t hsz = 0, csz = 0, tsz = 0;
 +  unsigned f;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:enc", KWLIST,
 +                                 &n, &nsz, &hszobj, &cszobj, &tszobj))
 +    goto end;
 +  if (check_aead_encdec(gd->d->ops->c, &f, nsz,
 +                      hszobj, &hsz, cszobj, &csz, tszobj, &tsz))
 +    goto end;
 +  if (GAEAD_REINIT(gd->d, n, nsz, hsz, csz, tsz))
 +    VALERR("bad aead parameter combination");
 +  gaea_sever(&gd->aad);
 +  gd->f = f; gd->hsz = hsz; gd->csz = csz; gd->tsz = tsz;
 +end:
 +  return (0);
 +}
 +
 +static PyObject *gaedmeth_decrypt(PyObject *me, PyObject *arg)
 +{
 +  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
 +  char *c; Py_ssize_t csz;
 +  char *m = 0; size_t msz; buf b;
 +  int err;
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTuple(arg, "s#:decrypt", &c, &csz)) goto end;
 +  if (gd->f&AEADF_AADFIRST) {
 +    if ((gd->f&AEADF_PCHSZ) && (gd->aad ? gd->aad->hlen : 0) != gd->hsz)
 +      VALERR("header doesn't match precommitted length");
 +    gaea_invalidate(gd->aad);
 +  }
 +  if ((gd->f&AEADF_PCMSZ) && csz > gd->csz - gd->clen)
 +    VALERR("too large for precommitted message length");
 +  msz = csz + gd->d->ops->c->bufsz; m = xmalloc(msz); buf_init(&b, m, msz);
 +  err = GAEAD_DECRYPT(gd->d, c, csz, &b); assert(!err); (void)err;
 +  buf_flip(&b); rc = bytestring_pywrapbuf(&b); gd->clen += csz;
 +end:
 +  xfree(m);
 +  return (rc);
 +}
 +
 +static PyObject *gaedmeth_done(PyObject *me, PyObject *arg, PyObject *kw)
 +{
 +  static const char *const kwlist[] = { "tag", "aad", 0 };
 +  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
 +  PyObject *aad = Py_None;
 +  char *t; Py_ssize_t tsz;
 +  char *m = 0; size_t msz; buf b;
 +  int err;
 +  PyObject *rc = 0;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O:done", KWLIST,
 +                                 &t, &tsz, &aad))
 +    goto end;
 +  if (aad != Py_None &&
 +      !PyObject_TypeCheck(aad,
 +                        (PyTypeObject *)GCAEADENC_KEY(me->ob_type)->aad))
 +    TYERR("wanted aad");
 +  if ((gd->f&AEADF_AADNDEP) && aad != Py_None && aad != (PyObject *)gd->aad)
 +    VALERR("mismatched aad");
 +  if ((gd->f&AEADF_PCHSZ) &&
 +      (aad == Py_None ? 0 : GAEADAAD_HLEN(aad)) != gd->hsz)
 +    VALERR("header doesn't match precommitted length");
 +  if ((gd->f&AEADF_PCMSZ) && gd->clen != gd->csz)
 +    VALERR("message doesn't match precommitted length");
 +  if ((gd->f&AEADF_PCTSZ) && tsz != gd->tsz)
 +    VALERR("tag length doesn't match precommitted value");
 +  if (keysz(tsz, gd->d->ops->c->tagsz) != tsz) VALERR("bad tag length");
 +  msz = gd->d->ops->c->bufsz; m = xmalloc(msz); buf_init(&b, m, msz);
 +  err = GAEAD_DONE(gd->d, aad == Py_None ? 0 : GAEADAAD_A(aad), &b, t, tsz);
 +  assert(err >= 0);
 +  if (!err) VALERR("decryption failed");
 +  buf_flip(&b); rc = bytestring_pywrapbuf(&b);
 +end:
 +  xfree(m);
 +  return (rc);
 +}
 +
 +static PyMethodDef gaeaddec_pymethods[] = {
 +#define METHNAME(name) gaedmeth_##name
 +  METH  (aad,                 "DEC.aad() -> AAD")
 +  KWMETH(reinit,              "DEC.reinit(NONCE, [hsz], [csz], [tsz])")
 +  METH  (decrypt,             "DEC.decrypt(CT) -> MSG")
 +  KWMETH(done,                        "DEC.done(TAG, [aad]) -> MSG | None")
 +#undef METHNAME
 +  { 0 }
 +};
 +
 +static PyTypeObject gcaead_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GCAEAD",                           /* @tp_name@ */
 +  sizeof(gcaead_pyobj),                       /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  0,                                  /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated encryption (key) metaclass.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  0,                                  /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  gcaead_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 PyTypeObject gaeadkey_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEKey",                           /* @tp_name@ */
 +  sizeof(gaeadkey_pyobj),             /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  gaeadkey_pydealloc,                 /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated encryption key.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  gaeadkey_pymethods,                 /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  0,                                  /* @tp_getset@ */
 +  0,                                  /* @tp_base@ */
 +  0,                                  /* @tp_dict@ */
 +  0,                                  /* @tp_descr_get@ */
 +  0,                                  /* @tp_descr_set@ */
 +  0,                                  /* @tp_dictoffset@ */
 +  0,                                  /* @tp_init@ */
 +  PyType_GenericAlloc,                        /* @tp_alloc@ */
 +  abstract_pynew,                     /* @tp_new@ */
 +  0,                                  /* @tp_free@ */
 +  0                                   /* @tp_is_gc@ */
 +};
 +
 +static PyTypeObject gcaeadaad_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEAADClass",                      /* @tp_name@ */
 +  sizeof(gcaeadaad_pyobj),            /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  0,                                  /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated encryption additional-data hash metaclass.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  0,                                  /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  0,                                  /* @tp_getset@ */
 +  0,                                  /* @tp_base@ */
 +  0,                                  /* @tp_dict@ */
 +  0,                                  /* @tp_descr_get@ */
 +  0,                                  /* @tp_descr_set@ */
 +  0,                                  /* @tp_dictoffset@ */
 +  0,                                  /* @tp_init@ */
 +  PyType_GenericAlloc,                        /* @tp_alloc@ */
 +  abstract_pynew,                     /* @tp_new@ */
 +  0,                                  /* @tp_free@ */
 +  0                                   /* @tp_is_gc@ */
 +};
 +
 +static PyTypeObject gaeadaad_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEAAD",                           /* @tp_name@ */
 +  sizeof(gaeadaad_pyobj),             /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  gaeadaad_pydealloc,                 /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated encryption AAD hash.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  gaeadaad_pymethods,                 /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  gaeadaad_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 PyTypeObject gcaeadenc_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEEncClass",                      /* @tp_name@ */
 +  sizeof(gcaeadenc_pyobj),            /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  0,                                  /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated encryption operation metaclass.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  0,                                  /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  0,                                  /* @tp_getset@ */
 +  0,                                  /* @tp_base@ */
 +  0,                                  /* @tp_dict@ */
 +  0,                                  /* @tp_descr_get@ */
 +  0,                                  /* @tp_descr_set@ */
 +  0,                                  /* @tp_dictoffset@ */
 +  0,                                  /* @tp_init@ */
 +  PyType_GenericAlloc,                        /* @tp_alloc@ */
 +  abstract_pynew,                     /* @tp_new@ */
 +  0,                                  /* @tp_free@ */
 +  0                                   /* @tp_is_gc@ */
 +};
 +
 +static PyTypeObject gaeadenc_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEEnc",                           /* @tp_name@ */
 +  sizeof(gaeadenc_pyobj),             /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  gaeadenc_pydealloc,                 /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated encryption operation.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  gaeadenc_pymethods,                 /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  gaeadenc_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 PyTypeObject gcaeaddec_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEDecClass",                      /* @tp_name@ */
 +  sizeof(gcaeaddec_pyobj),            /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  0,                                  /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated decryption operation metaclass.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  0,                                  /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  0,                                  /* @tp_getset@ */
 +  0,                                  /* @tp_base@ */
 +  0,                                  /* @tp_dict@ */
 +  0,                                  /* @tp_descr_get@ */
 +  0,                                  /* @tp_descr_set@ */
 +  0,                                  /* @tp_dictoffset@ */
 +  0,                                  /* @tp_init@ */
 +  PyType_GenericAlloc,                        /* @tp_alloc@ */
 +  abstract_pynew,                     /* @tp_new@ */
 +  0,                                  /* @tp_free@ */
 +  0                                   /* @tp_is_gc@ */
 +};
 +
 +static PyTypeObject gaeaddec_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "GAEDec",                           /* @tp_name@ */
 +  sizeof(gaeaddec_pyobj),             /* @tp_basicsize@ */
 +  0,                                  /* @tp_itemsize@ */
 +
 +  gaeaddec_pydealloc,                 /* @tp_dealloc@ */
 +  0,                                  /* @tp_print@ */
 +  0,                                  /* @tp_getattr@ */
 +  0,                                  /* @tp_setattr@ */
 +  0,                                  /* @tp_compare@ */
 +  0,                                  /* @tp_repr@ */
 +  0,                                  /* @tp_as_number@ */
 +  0,                                  /* @tp_as_sequence@ */
 +  0,                                  /* @tp_as_mapping@ */
 +  0,                                  /* @tp_hash@ */
 +  0,                                  /* @tp_call@ */
 +  0,                                  /* @tp_str@ */
 +  0,                                  /* @tp_getattro@ */
 +  0,                                  /* @tp_setattro@ */
 +  0,                                  /* @tp_as_buffer@ */
 +  Py_TPFLAGS_DEFAULT |                        /* @tp_flags@ */
 +    Py_TPFLAGS_BASETYPE,
 +
 +  /* @tp_doc@ */
 +"Authenticated decryption operation.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  gaeaddec_pymethods,                 /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  gaeaddec_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@ */
 +};
 +
  /*----- Hash functions ----------------------------------------------------*/
  
  PyTypeObject *gchash_pytype, *ghash_pytype;
@@@ -1795,10 -722,10 +1794,10 @@@ CONVFUNC(ghash, ghash *, GHASH_H
  
  static PyObject *ghash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { 0 };
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist))
 +  static const char *const kwlist[] = { 0 };
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST))
      goto end;
 -  return (ghash_pywrap((PyObject *)ty, GH_INIT(GCHASH_CH(ty)), f_freeme));
 +  return (ghash_pywrap((PyObject *)ty, GH_INIT(GCHASH_CH(ty))));
  end:
    return (0);
  }
@@@ -1820,19 -747,21 +1819,19 @@@ PyObject *gchash_pywrap(gchash *ch
    return ((PyObject *)g);
  }
  
 -PyObject *ghash_pywrap(PyObject *cobj, ghash *h, unsigned f)
 +PyObject *ghash_pywrap(PyObject *cobj, ghash *h)
  {
    ghash_pyobj *g;
    if (!cobj) cobj = gchash_pywrap((/*unconst*/ gchash *)GH_CLASS(h));
    else Py_INCREF(cobj);
    g = PyObject_NEW(ghash_pyobj, (PyTypeObject *)cobj);
    g->h = h;
 -  g->f = f;
    return ((PyObject *)g);
  }
  
  static void ghash_pydealloc(PyObject *me)
  {
 -  if (GHASH_F(me) & f_freeme)
 -    GH_DESTROY(GHASH_H(me));
 +  GH_DESTROY(GHASH_H(me));
    Py_DECREF(me->ob_type);
    FREEOBJ(me);
  }
@@@ -1846,12 -775,6 +1845,12 @@@ static PyObject *gchget_hashsz(PyObjec
  static PyObject *gchget_bufsz(PyObject *me, void *hunoz)
    { return (PyInt_FromLong(GCHASH_CH(me)->bufsz)); }
  
 +static PyObject *ghmeth_copy(PyObject *me, PyObject *arg)
 +{
 +  if (!PyArg_ParseTuple(arg, ":copy")) return (0);
 +  return (ghash_pywrap((PyObject *)me->ob_type, GH_COPY(GHASH_H(me))));
 +}
 +
  static PyObject *ghmeth_hash(PyObject *me, PyObject *arg)
  {
    char *p;
    static PyObject *ghmeth_hashu##w(PyObject *me, PyObject *arg)               \
    {                                                                   \
      uint##n x;                                                                \
 -    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end;        \
 +    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
      GH_HASHU##W(GHASH_H(me), x);                                      \
      RETURN_ME;                                                                \
 -  end:                                                                        \
 -    return (0);                                                               \
    }
  DOUINTCONV(GHMETH_HASHU_)
  
@@@ -1916,7 -841,6 +1915,7 @@@ static PyGetSetDef gchash_pygetset[] = 
  
  static PyMethodDef ghash_pymethods[] = {
  #define METHNAME(name) ghmeth_##name
 +  METH        (copy,                  "H.copy() -> HH")
    METH        (hash,                  "H.hash(M)")
  #define METHU_(n, W, w) METH(hashu##w, "H.hashu" #w "(WORD)")
    DOUINTCONV(METHU_)
@@@ -2036,27 -960,29 +2035,27 @@@ CONVFUNC(gmhash, ghash *, GHASH_H
  
  static PyObject *gmac_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "k", 0 };
 +  static const char *const kwlist[] = { "k", 0 };
    char *k;
    Py_ssize_t sz;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
      goto end;
    if (keysz(sz, GCMAC_CM(ty)->keysz) != sz) VALERR("bad key length");
    return (gmac_pywrap((PyObject *)ty,
 -                    GM_KEY(GCMAC_CM(ty), k, sz),
 -                    f_freeme));
 +                    GM_KEY(GCMAC_CM(ty), k, sz)));
  end:
    return (0);
  }
  
  static PyObject *gmhash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { 0 };
 +  static const char *const kwlist[] = { 0 };
    ghash_pyobj *g;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist)) return (0);
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) return (0);
    g = PyObject_NEW(ghash_pyobj, ty);
    g->h = GM_INIT(GMAC_M(ty));
 -  g->f = f_freeme;
    Py_INCREF(ty);
    return ((PyObject *)g);
  }
@@@ -2078,7 -1004,7 +2077,7 @@@ PyObject *gcmac_pywrap(gcmac *cm
    return ((PyObject *)g);
  }
  
 -PyObject *gmac_pywrap(PyObject *cobj, gmac *m, unsigned f)
 +PyObject *gmac_pywrap(PyObject *cobj, gmac *m)
  {
    gmac_pyobj *g;
    if (!cobj) cobj = gcmac_pywrap((/*unconst*/ gcmac *)GM_CLASS(m));
    g->ty.ht_type.tp_new = gmhash_pynew;
    typeready(&g->ty.ht_type);
    g->m = m;
 -  g->f = f;
    return ((PyObject *)g);
  }
  
  static void gmac_pydealloc(PyObject *me)
  {
 -  if (GMAC_F(me) & f_freeme)
 -    GM_DESTROY(GMAC_M(me));
 +  GM_DESTROY(GMAC_M(me));
    Py_DECREF(me->ob_type);
    PyType_Type.tp_dealloc(me);
  }
@@@ -2292,13 -1220,13 +2291,13 @@@ CONVFUNC(poly1305hash, poly1305_ctx *, 
  static PyObject *poly1305hash_pynew(PyTypeObject *ty,
                                    PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "mask", 0 };
 +  static const char *const kwlist[] = { "mask", 0 };
    poly1305key_pyobj *pk = (poly1305key_pyobj *)ty;
    poly1305hash_pyobj *ph;
    char *m = 0;
    Py_ssize_t sz;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#:new", kwlist, &m, &sz))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#:new", KWLIST, &m, &sz))
      return (0);
    if (m && sz != POLY1305_MASKSZ) VALERR("bad mask length");
    ph = PyObject_NEW(poly1305hash_pyobj, ty);
  static PyObject *poly1305key_pynew(PyTypeObject *ty,
                                   PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "k", 0 };
 +  static const char *const kwlist[] = { "k", 0 };
    poly1305key_pyobj *pk;
    char *k;
    Py_ssize_t sz;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
      goto end;
    if (keysz(sz, poly1305_keysz) != sz) VALERR("bad key length");
  
@@@ -2380,9 -1308,11 +2379,9 @@@ static PyObject *polymeth_hash(PyObjec
    {                                                                   \
      uint##n x;                                                                \
      octet b[SZ_##W];                                                  \
 -    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end;        \
 +    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
      STORE##W(b, x); poly1305_hash(P1305_CTX(me), b, sizeof(b));               \
      RETURN_ME;                                                                \
 -  end:                                                                        \
 -    return (0);                                                               \
    }
  DOUINTCONV(POLYMETH_HASHU_)
  
@@@ -2554,7 -1484,7 +2553,7 @@@ static PyTypeObject poly1305key_pytype_
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Poly1305 key.",
 +"poly1305(K): Poly1305 key.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -2637,7 -1567,7 +2636,7 @@@ static PyTypeObject poly1305hash_pytype
      if (!PyArg_ParseTuple(arg, "s#s#:" #hdance "_prf",                        \
                          &k, &ksz, &n, &nsz))                          \
        goto end;                                                               \
-     if (ksz != DANCE##_KEYSZ) VALERR("bad key length");                       \
+     if (ksz != keysz(ksz, dance##_keysz)) VALERR("bad key length");   \
      if (nsz != HDANCE##_INSZ) VALERR("bad input length");             \
      rc = bytestring_pywrap(0, HSALSA20_OUTSZ);                                \
      dance##_init(&dance, k, ksz, 0);                                  \
@@@ -2670,8 -1600,8 +2669,8 @@@ static PyObject *kxvik_pynew(PyTypeObje
  {
    unsigned n = 24;
    kxvik_pyobj *rc = 0;
 -  char *kwlist[] = { "nround", 0 };
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist,
 +  static const char *const kwlist[] = { "nround", 0 };
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
                                   convuint, &n))
      goto end;
    rc = (kxvik_pyobj *)ty->tp_alloc(ty, 0);
    return ((PyObject *)rc);
  }
  
 +static PyObject *kxvikmeth_copy(PyObject *me, PyObject *arg)
 +{
 +  kxvik_pyobj *k = (kxvik_pyobj *)me, *rc = 0;
 +  if (!PyArg_ParseTuple(arg, ":copy")) goto end;
 +  rc = (kxvik_pyobj *)k->ob_type->tp_alloc(k->ob_type, 0);
 +  rc->s = k->s; rc->n = k->n;
 +end:
 +  return ((PyObject *)rc);
 +}
 +
  static PyObject *kxvikmeth_mix(PyObject *me, PyObject *arg)
  {
    kxvik_pyobj *k = (kxvik_pyobj *)me;
@@@ -2724,7 -1644,7 +2723,7 @@@ static PyObject *kxvikmeth_extract(PyOb
    unsigned i;
    unsigned n;
  
-   if (!PyArg_ParseTuple(arg, "O&:mix", convuint, &n)) goto end;
+   if (!PyArg_ParseTuple(arg, "O&:extract", convuint, &n)) goto end;
    if (n > 200) VALERR("out of range");
    rc = bytestring_pywrap(0, n);
    q = (octet *)PyString_AS_STRING(rc);
@@@ -2754,10 -1674,14 +2753,14 @@@ static int kxvikset_nround(PyObject *me
  {
    kxvik_pyobj *k = (kxvik_pyobj *)me;
    unsigned n;
+   int rc = -1;
  
-   if (!convuint(val, &n)) return (-1);
+   if (!val) NIERR("__del__");
+   if (!convuint(val, &n)) goto end;
    k->n = n;
-   return (0);
+   rc = 0;
+ end:
+   return (rc);
  }
  
  static PyGetSetDef kxvik_pygetset[] = {
  
  static PyMethodDef kxvik_pymethods[] = {
  #define METHNAME(func) kxvikmeth_##func
 +  METH        (copy,                  "KECCAK.copy() -> KECCAK'")
    METH        (mix,                   "KECCAK.mix(DATA)")
    METH        (extract,               "KECCAK.extract(NOCTETS)")
    METH        (step,                  "KECCAK.step()")
@@@ -2802,7 -1725,7 +2805,7 @@@ static PyTypeObject kxvik_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Keccak-p[1600, n] state.",
 +"Keccak1600([nround = 24]): Keccak-p[1600, n] state.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -2845,9 -1768,9 +2848,9 @@@ static PyObject *shake_dopynew(void (*i
    shake_pyobj *rc = 0;
    char *p = 0, *f = 0;
    Py_ssize_t psz = 0, fsz = 0;
 -  char *kwlist[] = { "perso", "func", 0 };
 +  static const char *const kwlist[] = { "perso", "func", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#s#:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#s#:new", KWLIST,
                                   &p, &psz, &f, &fsz))
      goto end;
    rc = (shake_pyobj *)ty->tp_alloc(ty, 0);
@@@ -2888,10 -1811,12 +2891,10 @@@ static PyObject *shakemeth_hash(PyObjec
    {                                                                   \
      uint##n x;                                                                \
      octet b[SZ_##W];                                                  \
 -    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end;        \
 -    if (shake_check(me, 0)) goto end;                                 \
 +    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
 +    if (shake_check(me, 0)) return (0);                                       \
      STORE##W(b, x); shake_hash(SHAKE_H(me), b, sizeof(b));            \
      RETURN_ME;                                                                \
 -  end:                                                                        \
 -    return (0);                                                               \
    }
  DOUINTCONV(SHAKEMETH_HASHU_)
  
@@@ -2954,7 -1879,7 +2957,7 @@@ static PyObject *shakemeth_copy(PyObjec
    rc->h = *SHAKE_H(me);
    rc->st = SHAKE_ST(me);
  end:
-   return ((PyObject *)me);
+   return ((PyObject *)rc);
  }
  
  static PyObject *shakemeth_get(PyObject *me, PyObject *arg)
@@@ -3097,7 -2022,7 +3100,7 @@@ static PyTypeObject shake128_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"SHAKE128/cSHAKE128 XOF.",
 +"Shake128([perso = STR], [func = STR]): SHAKE128/cSHAKE128 XOF.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -3145,7 -2070,7 +3148,7 @@@ static PyTypeObject shake256_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"SHAKE256/cSHAKE256 XOF.",
 +"Shake256([perso = STR], [func = STR]): SHAKE256/cSHAKE256 XOF.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -3227,13 -2152,13 +3230,13 @@@ typedef struct prp 
  
  static PyObject *gprp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "key", 0 };
 +  static const char *const kwlist[] = { "key", 0 };
    char *k;
    Py_ssize_t sz;
    const prpinfo *prp = GCPRP_PRP(ty);
    PyObject *me;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
      goto end;
    if (keysz(sz, prp->keysz) != sz) VALERR("bad key length");
    me = (PyObject *)ty->tp_alloc(ty, 0);
@@@ -3456,14 -2381,6 +3459,14 @@@ void algorithms_pyinit(void
    INITTYPE(keyszset, keysz);
    INITTYPE(gccipher, type);
    INITTYPE(gcipher, root);
 +  INITTYPE(gcaead, type);
 +  INITTYPE(gaeadkey, root);
 +  INITTYPE(gcaeadaad, type);
 +  INITTYPE(gaeadaad, root);
 +  INITTYPE(gcaeadenc, type);
 +  INITTYPE(gaeadenc, root);
 +  INITTYPE(gcaeaddec, type);
 +  INITTYPE(gaeaddec, root);
    INITTYPE(gchash, type);
    INITTYPE(ghash, root);
    INITTYPE(gcmac, type);
  }
  
  GEN(gcciphers, cipher)
 +GEN(gcaeads, aead)
  GEN(gchashes, hash)
  GEN(gcmacs, mac)
  #define gcprp prpinfo
@@@ -3498,15 -2414,6 +3501,15 @@@ void algorithms_pyinsert(PyObject *mod
    INSERT("GCCipher", gccipher_pytype);
    INSERT("GCipher", gcipher_pytype);
    INSERT("gcciphers", gcciphers());
 +  INSERT("GCAEAD", gcaead_pytype);
 +  INSERT("GAEKey", gaeadkey_pytype);
 +  INSERT("GAEAADClass", gcaeadaad_pytype);
 +  INSERT("GAEAAD", gaeadaad_pytype);
 +  INSERT("GAEEncClass", gcaeadenc_pytype);
 +  INSERT("GAEEnc", gaeadenc_pytype);
 +  INSERT("GAEDecClass", gcaeaddec_pytype);
 +  INSERT("GAEDec", gaeaddec_pytype);
 +  INSERT("gcaeads", gcaeads());
    INSERT("GCHash", gchash_pytype);
    INSERT("GHash", ghash_pytype);
    INSERT("gchashes", d = gchashes());
diff --combined algorithms.py
index 4b65ce9b837a1ede0f97a016aea577906307489b,46abccbc7b8942217e61c8671f7bcc577cefb466..5730c4567804a200a7f93b11aa31cee17f43c7be
@@@ -25,8 -25,6 +25,8 @@@ serpent noekeo
  '''.split()
  pmodes = '''
  ecb cbc cfb ofb counter
 +cmac pmac1
 +ccm eax gcm ocb1 ocb3
  '''.split()
  streamciphers = '''
  rc4 seal
@@@ -39,7 -37,7 +39,7 @@@ chacha20 chacha12 chacha
  chacha20-ietf chacha12-ietf chacha8-ietf
  xchacha20 xchacha12 xchacha8
  '''.split()
- streamciphers += map(lambda s: s.translate(None, '/'), latindances)
+ streamciphers += map(lambda s: s.replace('/', ''), latindances)
  hashes = '''
  md2 md4 md5 tiger has160
  sha sha224 sha256 sha512/224 sha512/256 sha384 sha512
@@@ -96,7 -94,7 +96,7 @@@ for i in latindances
    if i.endswith('-ietf'): root += '_ietf'
    print ('\t_("%(name)s", %(root)s_keysz, %(id)s_rand, ' +
           'RNG_LATIN, %(ROOT)s_NONCESZ) \\') % \
-       {'name': i, 'id': i.translate(None, '/').replace('-', '_'),
+       {'name': i, 'id': i.replace('/', '').replace('-', '_'),
         'root': root, 'ROOT': root.upper()}
  for i in [128, 256]:
    print ('\t_("shake%(w)d", shake%(w)d_keysz, cshake%(w)d_rand, ' +
diff --combined buffer.c
index b88232b158dca1e21a32f446364178114c17d3fe,ac3e56be66e130bee408eddd7b7267b8a9eca88c..5b7bb64692a4530d9ef05671185351001096942f
+++ b/buffer.c
@@@ -55,9 -55,9 +55,9 @@@ static PyObject *rbuf_pynew(PyTypeObjec
    char *p, *q;
    Py_ssize_t n;
    buf_pyobj *me = 0;
 -  static char *kwlist[] = { "data", 0 };
 +  static const char *const kwlist[] = { "data", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &n))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &p, &n))
      goto end;
    q = xmalloc(n);
    memcpy(q, p, n);
@@@ -112,7 -112,8 +112,8 @@@ end
      uint##n x;                                                                \
      if (!PyArg_ParseTuple(arg, ":getu" #w)) goto end;                 \
      if (buf_getu##w(BUF_B(me), &x)) BUFERR();                         \
-     return (getulong(x));                                             \
+     if (MASK##W <= ULONG_MAX) return (getulong(x));                   \
+     else { kludge64 y; ASSIGN64(y, x); return (getk64(y)); }          \
    end:                                                                        \
      return (0);                                                               \
    }
@@@ -171,9 -172,9 +172,9 @@@ end
  static PyObject *rbmeth_getecpt(PyObject *me, PyObject *arg, PyObject *kw)
  {
    PyObject *cobj = Py_None;
 -  static char *kwlist[] = { "curve", 0 };
 +  static const char *const kwlist[] = { "curve", 0 };
    ec pt = EC_INIT;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", kwlist, &cobj))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", KWLIST, &cobj))
      goto end;
    if (cobj == Py_None) cobj = (PyObject *)ecpt_pytype;
    if (!PyType_Check(cobj) ||
@@@ -261,14 -262,14 +262,14 @@@ static PyMethodDef rbuf_pymethods[] = 
      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")
+     METH(getblk##w, "RBUF.getblk" #w "() -> BYTES")
    BUF_DOSUFFIXES(RBMETH_DECL_GETBLK_)
  #define RBMETH_DECL_GETBUF_(n, W, w)                                  \
-     METH(getbuf##w, "RBUF.getbuf" #w "() -> INT")
+     METH(getbuf##w, "RBUF.getbuf" #w "() -> RBUF'")
    BUF_DOSUFFIXES(RBMETH_DECL_GETBUF_)
    METH        (getmp,                 "RBUF.getmp() -> X")
    METH        (getgf,                 "RBUF.getgf() -> X")
-   KWMETH(getecpt,             "RBUF.getecpt(curve = None) -> P")
+   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")
@@@ -308,7 -309,7 +309,7 @@@ static PyTypeObject rbuf_pytype_skel = 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "A read buffer.",
 +"ReadBuffer(STR): a read buffer.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -354,9 -355,9 +355,9 @@@ static PyObject *wbuf_pynew(PyTypeObjec
    char *p;
    size_t n = 64;
    buf_pyobj *me = 0;
 -  static char *kwlist[] = { "size", 0 };
 +  static const char *const kwlist[] = { "size", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
                                   convszt, &n))
      goto end;
    me = (buf_pyobj *)ty->tp_alloc(ty, 0);
@@@ -405,16 -406,20 +406,20 @@@ static PyObject *wbmeth_put(PyObject *m
    }
  DOUINTCONV(WBMETH_PUTU_)
  
+ #define MASKz 0
  #define SZ_z 1
  #define WBMETH_PUTBLK_(n, W, w)                                               \
    static PyObject *wbmeth_putblk##w(PyObject *me, PyObject *arg)      \
    {                                                                   \
      char *p;                                                          \
      Py_ssize_t sz;                                                    \
-     if (!PyArg_ParseTuple(arg, "s#:putblk" #w, &p, &sz)) return (0);  \
+     if (!PyArg_ParseTuple(arg, "s#:putblk" #w, &p, &sz)) goto end;    \
+     if (MASK##W && sz > MASK##W) VALERR("too large");                 \
      ensure(me, sz + SZ_##n);                                          \
      buf_putmem##w(BUF_B(me), p, sz); assert(BOK(BUF_B(me)));          \
      RETURN_ME;                                                                \
+   end:                                                                        \
+     return (0);                                                               \
    }
  BUF_DOSUFFIXES(WBMETH_PUTBLK_)
  
@@@ -441,8 -446,7 +446,7 @@@ static PyObject *wbmeth_putecpt(PyObjec
  {
    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));
+   ensure(me, EC_ATINF(&pt) ? 2 : 6 + mp_octets(pt.x) + mp_octets(pt.y));
    buf_putec(BUF_B(me), &pt); assert(BOK(BUF_B(me)));
    EC_DESTROY(&pt);
    RETURN_ME;
@@@ -482,13 -486,9 +486,13 @@@ static PyObject *wbmeth_putgeraw(PyObje
  static PyObject *wbget_size(PyObject *me, void *hunoz)
    { return (PyInt_FromLong(BLEN(BUF_B(me)))); }
  
 +static PyObject *wbget_contents(PyObject *me, void *hunoz)
 +  { return (bytestring_pywrap(BBASE(BUF_B(me)), BLEN(BUF_B(me)))); }
 +
  static PyGetSetDef wbuf_pygetset[] = {
  #define GETSETNAME(op, name) wb##op##_##name
    GET (size,                  "WBUF.size -> SIZE")
 +  GET (contents,              "WBUF.contents -> STR")
  #undef GETSETNAME
    { 0 }
  };
@@@ -505,7 -505,7 +509,7 @@@ static PyMethodDef wbuf_pymethods[] = 
    BUF_DOSUFFIXES(WBMETH_DECL_PUTBLK_)
    METH        (putmp,                 "WBUF.putmp(X)")
    METH        (putgf,                 "WBUF.putgf(X)")
-   KWMETH(putecpt,             "WBUF.putecpt(P)")
+   METH        (putecpt,               "WBUF.putecpt(P)")
    METH        (putecptraw,            "WBUF.putecptraw(P)")
    METH        (putge,                 "WBUF.putge(X)")
    METH        (putgeraw,              "WBUF.putgeraw(X)")
@@@ -545,7 -545,7 +549,7 @@@ static PyTypeObject wbuf_pytype_skel = 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "A write buffer.",
 +"WriteBuffer([size = ?]): a write buffer.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
diff --combined catacomb-python.h
index df29099b2d85ad343f90b65fc68303adbccd5916,ad27461008a58b811aa48270aaf3b02c57451bdd..6c1ae2139030fae290f9c1b72efd290cded32da9
@@@ -61,7 -61,6 +61,7 @@@
  #include <catacomb/blkc.h>
  
  #include <catacomb/gcipher.h>
 +#include <catacomb/gaead.h>
  #include <catacomb/ghash.h>
  #include <catacomb/gmac.h>
  #include <catacomb/md5.h>
    goto end;                                                           \
  } while (0)
  #define VALERR(str) EXCERR(PyExc_ValueError, str)
+ #define OVFERR(str) EXCERR(PyExc_OverflowError, str)
  #define TYERR(str) EXCERR(PyExc_TypeError, str)
 +#define IXERR(str) EXCERR(PyExc_IndexError, str)
  #define ZDIVERR(str) EXCERR(PyExc_ZeroDivisionError, str)
  #define SYSERR(str) EXCERR(PyExc_SystemError, str)
  #define NIERR(str) EXCERR(PyExc_NotImplementedError, str)
    PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);                        \
    goto end;                                                           \
  } while (0)
- #define PGENERR do { pgenerr(); goto end; } while (0)
+ #define PGENERR(exc) do { pgenerr(exc); goto end; } while (0)
  
  #define CONVFUNC(ty, cty, ext)                                                \
    int conv##ty(PyObject *o, void *p)                                  \
  } while (0)
  #define INITTYPE(ty, base) INITTYPE_META(ty, base, type)
  
- #define INSERT(name, ob) do {                                         \
+ extern PyObject *home_module;
+ #define INSERT(name, ob) do { \
    PyObject *_o = (PyObject *)(ob);                                    \
    Py_INCREF(_o);                                                      \
    PyModule_AddObject(mod, name, _o);                                  \
@@@ -233,9 -234,8 +236,10 @@@ MODULES(DO
      return (d);                                                               \
    }
  
- struct nameval { const char *name; unsigned long value; };
 +#define KWLIST (/*unconst*/ char **)kwlist
 +
+ struct nameval { const char *name; unsigned f; unsigned long value; };
+ #define CF_SIGNED 1u
  extern void setconstants(PyObject *, const struct nameval *);
  
  extern PyObject *mexp_common(PyObject *, PyObject *, size_t,
@@@ -259,7 -259,34 +263,34 @@@ extern PyObject *getulong(unsigned long
  extern PyObject *getk64(kludge64);
  extern void *newtype(PyTypeObject *, const PyTypeObject *, const char *);
  
+ struct excinfo { PyObject *ty, *val, *tb; };
+ #define EXCINFO_INIT { 0, 0, 0 }
  extern PyObject *mkexc(PyObject *, PyObject *, const char *, PyMethodDef *);
+ #define INIT_EXCINFO(exc) do {                                                \
+   struct excinfo *_exc = (exc); _exc->ty = _exc->val = _exc->tb = 0;  \
+ } while (0)
+ #define RELEASE_EXCINFO(exc) do {                                     \
+   struct excinfo *_exc = (exc);                                               \
+   Py_XDECREF(_exc->ty);        _exc->ty  = 0;                                 \
+   Py_XDECREF(_exc->val); _exc->val = 0;                                       \
+   Py_XDECREF(_exc->tb);        _exc->tb  = 0;                                 \
+ } while (0)
+ #define STASH_EXCINFO(exc) do {                                               \
+   struct excinfo *_exc = (exc);                                               \
+   PyErr_Fetch(&_exc->ty, &_exc->val, &_exc->tb);                      \
+   PyErr_NormalizeException(&_exc->ty, &_exc->val, &_exc->tb);         \
+ } while (0)
+ #define RESTORE_EXCINFO(exc) do {                                     \
+   struct excinfo *_exc = (exc);                                               \
+   PyErr_Restore(_exc->ty, _exc->val, _exc->tb);                               \
+   _exc->ty = _exc->val = _exc->tb = 0;                                        \
+ } while (0)
+ extern void report_lost_exception(struct excinfo *, const char *, ...);
+ extern void report_lost_exception_v(struct excinfo *, const char *, va_list);
+ extern void stash_exception(struct excinfo *, const char *, ...);
+ extern void restore_exception(struct excinfo *, const char *, ...);
  extern void typeready(PyTypeObject *);
  extern PyTypeObject *inittype(PyTypeObject *, PyTypeObject *);
  extern void addmethods(const PyMethodDef *);
@@@ -328,6 -355,7 +359,7 @@@ extern mp *getgf(PyObject *)
  extern int convgf(PyObject *, void *);
  extern PyObject *mp_pywrap(mp *);
  extern PyObject *gf_pywrap(mp *);
+ extern long mphash(mp *);
  extern mp *mp_frompyobject(PyObject *, int);
  extern PyObject *mp_topystring(mp *, int,
                               const char *, const char *, const char *);
@@@ -346,7 -374,6 +378,6 @@@ extern PyTypeObject *fe_pytype
  #define FE_FOBJ(o) ((PyObject *)(o)->ob_type)
  #define FE_X(o) (((fe_pyobj *)(o))->x)
  extern PyObject *fe_pywrap(PyObject *, mp *);
- extern mp *getfe(field *, PyObject *);
  
  typedef struct fe_pyobj {
    PyObject_HEAD
@@@ -504,130 -531,24 +535,130 @@@ typedef struct gccipher_pyobj 
  extern PyTypeObject *gccipher_pytype;
  #define GCCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gccipher_pytype)
  #define GCCIPHER_CC(o) (((gccipher_pyobj *)(o))->cc)
 -#define GCCIPHER_F(o) (((gccipher_pyobj *)(o))->f)
  extern PyObject *gccipher_pywrap(gccipher *);
  extern int convgccipher(PyObject *, void *);
 -extern int convgcipher(PyObject *, void *);
  
  typedef struct gcipher_pyobj {
    PyObject_HEAD
 -  unsigned f;
    gcipher *c;
  } gcipher_pyobj;
  
  extern PyTypeObject *gcipher_pytype;
  #define GCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gcipher_pytype)
  #define GCIPHER_C(o) (((gcipher_pyobj *)(o))->c)
 -#define GCIPHER_F(o) (((gcipher_pyobj *)(o))->f)
 -extern PyObject *gcipher_pywrap(PyObject *, gcipher *, unsigned);
 +extern PyObject *gcipher_pywrap(PyObject *, gcipher *);
  extern int convgcipher(PyObject *, void *);
  
 +typedef struct gcaead_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead *aec;
 +  struct gcaeadaad_pyobj *aad;
 +  struct gcaeadenc_pyobj *enc;
 +  struct gcaeaddec_pyobj *dec;
 +} gcaead_pyobj;
 +
 +extern PyTypeObject *gcaead_pytype;
 +#define GCAEAD_PYCHECK(o) PyObject_TypeCheck((o), gcaead_pytype)
 +#define GCAEAD_AEC(o) (((gcaead_pyobj *)(o))->aec)
 +#define GCAEAD_AAD(o) (((gcaead_pyobj *)(o))->aad)
 +#define GCAEAD_ENC(o) (((gcaead_pyobj *)(o))->enc)
 +#define GCAEAD_DEC(o) (((gcaead_pyobj *)(o))->dec)
 +extern PyObject *gcaead_pywrap(gcaead *);
 +extern int convgcaead(PyObject *, void *);
 +
 +typedef struct gaeadkey_pyobj {
 +  PyObject_HEAD
 +  gaead_key *k;
 +} gaeadkey_pyobj;
 +
 +extern PyTypeObject *gaeadkey_pytype;
 +#define GAEADKEY_PYCHECK(o) PyObject_TypeCheck((o), gaeadkey_pytype)
 +#define GAEADKEY_K(o) (((gaeadkey_pyobj *)(o))->k)
 +extern PyObject *gaeadkey_pywrap(PyObject *, gaead_key *);
 +extern int convgaeadkey(PyObject *, void *);
 +
 +typedef struct gcaeadaad_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead_pyobj *key;
 +} gcaeadaad_pyobj;
 +#define GCAEADAAD_KEY(o) (((gcaeadaad_pyobj *)(o))->key)
 +extern PyTypeObject *gcaeadaad_pytype;
 +
 +typedef struct gaeadaad_pyobj {
 +  PyObject_HEAD
 +  gaead_aad *a;
 +  unsigned f;
 +#define AEADF_DEAD 32768u
 +  size_t hsz, hlen;
 +} gaeadaad_pyobj;
 +
 +extern PyTypeObject *gaeadaad_pytype;
 +#define GAEADAAD_PYCHECK(o) PyObject_TypeCheck((o), gaeadaad_pytype)
 +#define GAEADAAD_A(o) (((gaeadaad_pyobj *)(o))->a)
 +#define GAEADAAD_F(o) (((gaeadaad_pyobj *)(o))->f)
 +#define GAEADAAD_HSZ(o) (((gaeadaad_pyobj *)(o))->hsz)
 +#define GAEADAAD_HLEN(o) (((gaeadaad_pyobj *)(o))->hlen)
 +extern PyObject *gaeadaad_pywrap(PyObject *, gaead_aad *, unsigned, size_t);
 +extern int convgaeadaad(PyObject *, void *);
 +
 +typedef struct gcaeadenc_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead_pyobj *key;
 +} gcaeadenc_pyobj;
 +#define GCAEADENC_KEY(o) (((gcaeadenc_pyobj *)(o))->key)
 +extern PyTypeObject *gcaeadenc_pytype;
 +
 +typedef struct gaeadenc_pyobj {
 +  PyObject_HEAD
 +  gaead_enc *e;
 +  gaeadaad_pyobj *aad;
 +  unsigned f;
 +  size_t hsz, msz, tsz;
 +  size_t mlen;
 +} gaeadenc_pyobj;
 +
 +extern PyTypeObject *gaeadenc_pytype;
 +#define GAEADENC_PYCHECK(o) PyObject_TypeCheck((o), gaeadenc_pytype)
 +#define GAEADENC_AAD(o) (((gaeadenc_pyobj *)(o))->aad)
 +#define GAEADENC_E(o) (((gaeadenc_pyobj *)(o))->e)
 +#define GAEADENC_F(o) (((gaeadenc_pyobj *)(o))->f)
 +#define GAEADENC_HSZ(o) (((gaeadenc_pyobj *)(o))->hsz)
 +#define GAEADENC_MSZ(o) (((gaeadenc_pyobj *)(o))->msz)
 +#define GAEADENC_TSZ(o) (((gaeadenc_pyobj *)(o))->tsz)
 +#define GAEADENC_MLEN(o) (((gaeadenc_pyobj *)(o))->mlen)
 +extern PyObject *gaeadenc_pywrap(PyObject *, gaead_enc *, unsigned,
 +                               size_t, size_t, size_t);
 +extern int convgaeadenc(PyObject *, void *);
 +
 +typedef struct gcaeaddec_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead_pyobj *key;
 +} gcaeaddec_pyobj;
 +#define GCAEADDEC_KEY(o) (((gcaeaddec_pyobj *)(o))->key)
 +extern PyTypeObject *gcaeaddec_pytype;
 +
 +typedef struct gaeaddec_pyobj {
 +  PyObject_HEAD
 +  gaead_dec *d;
 +  gaeadaad_pyobj *aad;
 +  unsigned f;
 +  size_t hsz, csz, tsz;
 +  size_t clen;
 +} gaeaddec_pyobj;
 +
 +extern PyTypeObject *gaeaddec_pytype;
 +#define GAEADDEC_PYCHECK(o) PyObject_TypeCheck((o), gaeaddec_pytype)
 +#define GAEADDEC_AAD(o) (((gaeaddec_pyobj *)(o))->aad)
 +#define GAEADDEC_D(o) (((gaeaddec_pyobj *)(o))->d)
 +#define GAEADDEC_F(o) (((gaeaddec_pyobj *)(o))->f)
 +#define GAEADDEC_HSZ(o) (((gaeaddec_pyobj *)(o))->hsz)
 +#define GAEADDEC_CSZ(o) (((gaeaddec_pyobj *)(o))->csz)
 +#define GAEADDEC_TSZ(o) (((gaeaddec_pyobj *)(o))->tsz)
 +#define GAEADDEC_CLEN(o) (((gaeaddec_pyobj *)(o))->clen)
 +extern PyObject *gaeaddec_pywrap(PyObject *, gaead_dec *, unsigned,
 +                               size_t, size_t, size_t);
 +extern int convgaeaddec(PyObject *, void *);
 +
  typedef struct gchash_pyobj {
    PyHeapTypeObject ty;
    gchash *ch;
  extern PyTypeObject *gchash_pytype;
  #define GCHASH_PYCHECK(o) PyObject_TypeCheck((o), gchash_pytype)
  #define GCHASH_CH(o) (((gchash_pyobj *)(o))->ch)
 -#define GCHASH_F(o) (((gchash_pyobj *)(o))->f)
  extern PyObject *gchash_pywrap(gchash *);
  extern int convgchash(PyObject *, void *);
  
  typedef struct ghash_pyobj {
    PyObject_HEAD
 -  unsigned f;
    ghash *h;
  } ghash_pyobj;
  
@@@ -648,7 -571,8 +679,7 @@@ extern PyTypeObject *ghash_pytype, *gmh
  extern PyObject *sha_pyobj, *has160_pyobj;
  #define GHASH_PYCHECK(o) PyObject_TypeCheck((o), ghash_pytype)
  #define GHASH_H(o) (((ghash_pyobj *)(o))->h)
 -#define GHASH_F(o) (((ghash_pyobj *)(o))->f)
 -extern PyObject *ghash_pywrap(PyObject *, ghash *, unsigned);
 +extern PyObject *ghash_pywrap(PyObject *, ghash *);
  extern int convghash(PyObject *, void *);
  extern int convgmhash(PyObject *, void *);
  
@@@ -666,6 -590,7 +697,6 @@@ extern int convgcmac(PyObject *, void *
  
  typedef struct gmac_pyobj {
    PyHeapTypeObject ty;
 -  unsigned f;
    gmac *m;
  } gmac_pyobj;
  
@@@ -673,7 -598,7 +704,7 @@@ extern PyTypeObject *gmac_pytype
  #define GMAC_PYCHECK(o) PyObject_TypeCheck((o), gmac_pytype)
  #define GMAC_M(o) (((gmac_pyobj *)(o))->m)
  #define GMAC_F(o) (((gmac_pyobj *)(o))->f)
 -extern PyObject *gmac_pywrap(PyObject *, gmac *, unsigned);
 +extern PyObject *gmac_pywrap(PyObject *, gmac *);
  extern int convgmac(PyObject *, void *);
  
  /*----- Key generation ----------------------------------------------------*/
@@@ -700,9 -625,15 +731,15 @@@ extern PyTypeObject *pgev_pytype
  #define PGEV_PYCHECK(o) PyObject_TypeCheck(o, pgev_pytype)
  #define PGEV_PG(o) (&((pgev_pyobj *)(o))->pg)
  
+ typedef struct pypgev {
+   pgev ev;
+   PyObject *obj;
+   struct excinfo *exc;
+ } pypgev;
  extern int convpgev(PyObject *, void *);
- extern void droppgev(pgev *);
- extern void pgenerr(void);
+ extern void droppgev(pypgev *);
+ extern void pgenerr(struct excinfo *exc);
  
  /*----- That's all, folks -------------------------------------------------*/
  
diff --combined catacomb.c
index b596b5ec062c5b646f442880b9fe19bf799eac17,e24e5ec9f47e033f27129b1b002da90a2b68c273..e9c951385ae4f23f1b3ec9afcc75aa9dfde5a904
  /*----- Main code ---------------------------------------------------------*/
  
  static const struct nameval consts[] = {
- #define C(x) { #x, x }
+ #define CF(f, x) { #x, f, x }
+ #define C(x) { #x, (x) >= 0 ? 0 : CF_SIGNED, x }
    C(FTY_PRIME), C(FTY_BINARY),
    C(PGEN_PASS), C(PGEN_FAIL), C(PGEN_BEGIN), C(PGEN_TRY), C(PGEN_DONE),
    C(PGEN_ABORT),
    C(MPW_MAX),
+   C(RAND_IBITS),
    C(PMODE_READ), C(PMODE_VERIFY),
    C(KOPEN_READ), C(KOPEN_WRITE), C(KOPEN_NOFILE),
-   C(KEXP_FOREVER), C(KEXP_EXPIRE),
+   CF(0, KEXP_FOREVER), CF(0, 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(ED25519_KEYSZ), C(ED25519_PUBSZ), C(ED25519_SIGSZ),
      C(ED25519_MAXPERSOSZ),
    C(ED448_KEYSZ), C(ED448_PUBSZ), C(ED448_SIGSZ), C(ED448_MAXPERSOSZ),
 +  C(AEADF_PCHSZ), C(AEADF_PCMSZ), C(AEADF_PCTSZ),
 +  C(AEADF_AADNDEP), C(AEADF_AADFIRST), C(AEADF_NOAAD),
  #define ENTRY(tag, val, str) C(KERR_##tag),
    KEY_ERRORS(ENTRY)
  #undef ENTRY
  #undef C
+ #undef CF
    { 0 }
  };
  
@@@ -75,7 -76,8 +78,8 @@@ PyObject *mexp_common(PyObject *me, PyO
      arg = PyTuple_GetItem(arg, 0);
    Py_INCREF(arg);
    if (!PySequence_Check(arg)) TYERR("not a sequence");
-   n = PySequence_Size(arg); if (!n) { z = id(me); goto end; }
+   n = PySequence_Size(arg); if (n < 0) goto end;
+   if (!n) { z = id(me); goto end; }
    x = PySequence_GetItem(arg, 0);
    if (PySequence_Check(x))
      flat = 0;
diff --combined catacomb/__init__.py
index 8038815a66fb00ce8519b481c8be3c01176b758d,24265acb3398ebf4c2209e0224431a1aacfe4168..9178475b2d6faf4d3153e064e5f04f94633ea0f7
@@@ -27,7 -27,8 +27,8 @@@ from __future__ import with_statemen
  
  from binascii import hexlify as _hexify, unhexlify as _unhexify
  from contextlib import contextmanager as _ctxmgr
- import DLFCN as _dlfcn
+ try: import DLFCN as _dlfcn
+ except ImportError: _dlfcn = None
  import os as _os
  from struct import pack as _pack
  import sys as _sys
@@@ -67,6 -68,15 +68,15 @@@ del _dlflags, _odlflag
  ## For the benefit of the default keyreporter, we need the program name.
  _base._ego(_sys.argv[0])
  
+ ## Register our module.
+ _base._set_home_module(_sys.modules[__name__])
+ def default_lostexchook(why, ty, val, tb):
+   """`catacomb.lostexchook(WHY, TY, VAL, TB)' reports lost exceptions."""
+   _sys.stderr.write("\n\n!!! LOST EXCEPTION: %s\n" % why)
+   _sys.excepthook(ty, val, tb)
+   _sys.stderr.write("\n")
+ lostexchook = default_lostexchook
  ## How to fix a name back into the right identifier.  Alas, the rules are not
  ## consistent.
  def _fixname(name):
@@@ -75,7 -85,7 +85,7 @@@
    name = name.replace('-', '_')
  
    ## But slashes might become underscores or just vanish.
-   if name.startswith('salsa20'): name = name.translate(None, '/')
+   if name.startswith('salsa20'): name = name.replace('/', '')
    else: name = name.replace('/', '_')
  
    ## Done.
@@@ -102,7 -112,7 +112,7 @@@ def _init()
      for j in b:
        if j[:plen] == pre:
          setattr(c, j[plen:], classmethod(b[j]))
 -  for i in [gcciphers, gchashes, gcmacs, gcprps]:
 +  for i in [gcciphers, gcaeads, gchashes, gcmacs, gcprps]:
      for c in i.itervalues():
        d[_fixname(c.name)] = c
    for c in gccrands.itervalues():
@@@ -183,27 -193,6 +193,27 @@@ _augment(ByteString, _tmp
  ByteString.__hash__ = str.__hash__
  bytes = ByteString.fromhex
  
 +###--------------------------------------------------------------------------
 +### Symmetric encryption.
 +
 +class _tmp:
 +  def encrypt(me, n, m, tsz = None, h = ByteString('')):
 +    if tsz is None: tsz = me.__class__.tagsz.default
 +    e = me.enc(n, len(h), len(m), tsz)
 +    if not len(h): a = None
 +    else: a = e.aad().hash(h)
 +    c0 = e.encrypt(m)
 +    c1, t = e.done(aad = a)
 +    return c0 + c1, t
 +  def decrypt(me, n, c, t, h = ByteString('')):
 +    d = me.dec(n, len(h), len(c), len(t))
 +    if not len(h): a = None
 +    else: a = d.aad().hash(h)
 +    m = d.decrypt(c)
 +    m += d.done(t, aad = a)
 +    return m
 +_augment(GAEKey, _tmp)
 +
  ###--------------------------------------------------------------------------
  ### Hashing.
  
@@@ -217,15 -206,31 +227,31 @@@ _augment(Poly1305Hash, _tmp
  class _HashBase (object):
    ## The standard hash methods.  Assume that `hash' is defined and returns
    ## the receiver.
-   def hashu8(me, n): return me.hash(_pack('B', n))
-   def hashu16l(me, n): return me.hash(_pack('<H', n))
-   def hashu16b(me, n): return me.hash(_pack('>H', n))
+   def _check_range(me, n, max):
+     if not (0 <= n <= max): raise OverflowError("out of range")
+   def hashu8(me, n):
+     me._check_range(n, 0xff)
+     return me.hash(_pack('B', n))
+   def hashu16l(me, n):
+     me._check_range(n, 0xffff)
+     return me.hash(_pack('<H', n))
+   def hashu16b(me, n):
+     me._check_range(n, 0xffff)
+     return me.hash(_pack('>H', n))
    hashu16 = hashu16b
-   def hashu32l(me, n): return me.hash(_pack('<L', n))
-   def hashu32b(me, n): return me.hash(_pack('>L', n))
+   def hashu32l(me, n):
+     me._check_range(n, 0xffffffff)
+     return me.hash(_pack('<L', n))
+   def hashu32b(me, n):
+     me._check_range(n, 0xffffffff)
+     return me.hash(_pack('>L', n))
    hashu32 = hashu32b
-   def hashu64l(me, n): return me.hash(_pack('<Q', n))
-   def hashu64b(me, n): return me.hash(_pack('>Q', n))
+   def hashu64l(me, n):
+     me._check_range(n, 0xffffffffffffffff)
+     return me.hash(_pack('<Q', n))
+   def hashu64b(me, n):
+     me._check_range(n, 0xffffffffffffffff)
+     return me.hash(_pack('>Q', n))
    hashu64 = hashu64b
    def hashbuf8(me, s): return me.hashu8(len(s)).hash(s)
    def hashbuf16l(me, s): return me.hashu16l(len(s)).hash(s)
@@@ -248,8 -253,8 +274,8 @@@ class _ShakeBase (_HashBase)
      me._h = me._SHAKE(perso = perso, func = me._FUNC)
  
    ## Delegate methods...
-   def copy(me): new = me.__class__(); new._copy(me)
-   def _copy(me, other): me._h = other._h
+   def copy(me): new = me.__class__._bare_new(); new._copy(me); return new
+   def _copy(me, other): me._h = other._h.copy()
    def hash(me, m): me._h.hash(m); return me
    def xof(me): me._h.xof(); return me
    def get(me, n): return me._h.get(n)
    def buffered(me): return me._h.buffered
    @property
    def rate(me): return me._h.rate
+   @classmethod
+   def _bare_new(cls): return cls()
  
  class _tmp:
    def check(me, h):
      me.bytepad_after()
  _augment(Shake, _tmp)
  _augment(_ShakeBase, _tmp)
 -Shake._Z = _ShakeBase._Z = ByteString(200*'\0')
 +Shake._Z = _ShakeBase._Z = ByteString.zero(200)
  
  class KMAC (_ShakeBase):
    _FUNC = 'KMAC'
    def xof(me):
      me.rightenc(0)
      return super(KMAC, me).xof()
+   @classmethod
+   def _bare_new(cls): return cls("")
  
  class KMAC128 (KMAC): _SHAKE = Shake128; _TAGSZ = 16
  class KMAC256 (KMAC): _SHAKE = Shake256; _TAGSZ = 32
  ### NaCl `secretbox'.
  
  def secret_box(k, n, m):
 -  E = xsalsa20(k).setiv(n)
 -  r = E.enczero(poly1305.keysz.default)
 -  s = E.enczero(poly1305.masksz)
 -  y = E.encrypt(m)
 -  t = poly1305(r)(s).hash(y).done()
 -  return ByteString(t + y)
 +  y, t = salsa20_naclbox(k).encrypt(n, m)
 +  return t + y
  
  def secret_unbox(k, n, c):
 -  E = xsalsa20(k).setiv(n)
 -  r = E.enczero(poly1305.keysz.default)
 -  s = E.enczero(poly1305.masksz)
 -  y = c[poly1305.tagsz:]
 -  if not poly1305(r)(s).hash(y).check(c[0:poly1305.tagsz]):
 -    raise ValueError, 'decryption failed'
 -  return E.decrypt(c[poly1305.tagsz:])
 +  tsz = poly1305.tagsz
 +  return salsa20_naclbox(k).decrypt(n, c[tsz:], c[0:tsz])
  
  ###--------------------------------------------------------------------------
  ### Multiprecision integers and binary polynomials.
@@@ -353,15 -371,18 +383,18 @@@ class BaseRat (object)
    def __mul__(me, you):
      n, d = _split_rat(you)
      return type(me)(me._n*n, me._d*d)
-   def __div__(me, you):
+   __rmul__ = __mul__
+   def __truediv__(me, you):
      n, d = _split_rat(you)
      return type(me)(me._n*d, me._d*n)
-   def __rdiv__(me, you):
+   def __rtruediv__(me, you):
      n, d = _split_rat(you)
      return type(me)(me._d*n, me._n*d)
+   __div__ = __truediv__
+   __rdiv__ = __rtruediv__
    def __cmp__(me, you):
      n, d = _split_rat(you)
-     return type(me)(me._n*d, n*me._d)
+     return cmp(me._n*d, n*me._d)
    def __rcmp__(me, you):
      n, d = _split_rat(you)
      return cmp(n*me._d, me._n*d)
@@@ -381,8 -402,10 +414,10 @@@ class _tmp
    def mont(x): return MPMont(x)
    def barrett(x): return MPBarrett(x)
    def reduce(x): return MPReduce(x)
-   def __div__(me, you): return IntRat(me, you)
-   def __rdiv__(me, you): return IntRat(you, me)
+   def __truediv__(me, you): return IntRat(me, you)
+   def __rtruediv__(me, you): return IntRat(you, me)
+   __div__ = __truediv__
+   __rdiv__ = __rtruediv__
    _repr_pretty_ = _pp_str
  _augment(MP, _tmp)
  
@@@ -393,8 -416,10 +428,10 @@@ class _tmp
    def halftrace(x, y): return x.reduce().halftrace(y)
    def modsqrt(x, y): return x.reduce().sqrt(y)
    def quadsolve(x, y): return x.reduce().quadsolve(y)
-   def __div__(me, you): return GFRat(me, you)
-   def __rdiv__(me, you): return GFRat(you, me)
+   def __truediv__(me, you): return GFRat(me, you)
+   def __rtruediv__(me, you): return GFRat(you, me)
+   __div__ = __truediv__
+   __rdiv__ = __rtruediv__
    _repr_pretty_ = _pp_str
  _augment(GF, _tmp)
  
@@@ -561,7 -586,6 +598,7 @@@ class _tmp
    def __repr__(me): return '%s(%d)' % (_clsname(me), me.default)
    def check(me, sz): return True
    def best(me, sz): return sz
 +  def pad(me, sz): return sz
  _augment(KeySZAny, _tmp)
  
  class _tmp:
        pp.pretty(me.max); pp.text(','); pp.breakable()
        pp.pretty(me.mod)
      pp.end_group(ind, ')')
 -  def check(me, sz): return me.min <= sz <= me.max and sz % me.mod == 0
 +  def check(me, sz): return me.min <= sz <= me.max and sz%me.mod == 0
    def best(me, sz):
      if sz < me.min: raise ValueError, 'key too small'
      elif sz > me.max: return me.max
 -    else: return sz - (sz % me.mod)
 +    else: return sz - sz%me.mod
 +  def pad(me, sz):
 +    if sz > me.max: raise ValueError, 'key too large'
 +    elif sz < me.min: return me.min
 +    else: sz += me.mod; return sz - sz%me.mod
  _augment(KeySZRange, _tmp)
  
  class _tmp:
        if found < i <= sz: found = i
      if found < 0: raise ValueError, 'key too small'
      return found
 +  def pad(me, sz):
 +    found = -1
 +    for i in me.set:
 +      if sz <= i and (found == -1 or i < found): found = i
 +    if found < 0: raise ValueError, 'key too large'
 +    return found
  _augment(KeySZSet, _tmp)
  
  ###--------------------------------------------------------------------------
@@@ -852,23 -866,21 +889,23 @@@ _augment(RSAPriv, _tmp
  ### DSA and related schemes.
  
  class _tmp:
 -  def __repr__(me): return '%s(G = %r, p = %r)' % (_clsname(me), me.G, me.p)
 +  def __repr__(me): return '%s(G = %r, p = %r, hash = %r)' % \
 +        (_clsname(me), me.G, me.p, me.hash)
    def _repr_pretty_(me, pp, cyclep):
      ind = _pp_bgroup_tyname(pp, me)
      if cyclep:
        pp.text('...')
      else:
        _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
 -      _pp_kv(pp, 'p', me.p)
 +      _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'hash', me.hash)
      pp.end_group(ind, ')')
  _augment(DSAPub, _tmp)
  _augment(KCDSAPub, _tmp)
  
  class _tmp:
 -  def __repr__(me): return '%s(G = %r, u = %s, p = %r)' % \
 -      (_clsname(me), me.G, _repr_secret(me.u), me.p)
 +  def __repr__(me): return '%s(G = %r, u = %s, p = %r, hash = %r)' % \
 +      (_clsname(me), me.G, _repr_secret(me.u), me.p, me.hash)
    def _repr_pretty_(me, pp, cyclep):
      ind = _pp_bgroup_tyname(pp, me)
      if cyclep:
      else:
        _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
        _pp_kv(pp, 'u', me.u, True); pp.text(','); pp.breakable()
 -      _pp_kv(pp, 'p', me.p)
 +      _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'hash', me.hash)
      pp.end_group(ind, ')')
  _augment(DSAPriv, _tmp)
  _augment(KCDSAPriv, _tmp)
diff --combined debian/changelog
index 866bc9073730367c42761e88f2d4b58ea2ede9c4,ca06b5c968208f1a6b0528e00ac762c73209a6de..039886401cd8b6d44e3352a4957a9d21d11ab3c3
@@@ -1,26 -1,9 +1,32 @@@
 +catacomb-python (1.3.0.1) experimental; urgency=medium
 +
 +  * Fix required Catacomb version in `setup.py' script.  Only affects the
 +    source package.
 +
 + -- Mark Wooding <mdw@distorted.org.uk>  Sun, 22 Sep 2019 01:21:28 +0100
 +
 +catacomb-python (1.3.0) experimental; urgency=medium
 +
 +  * catacomb: Bindings for new blockcipher-based MACs, and AEAD schemes.
 +  * catacomb: Invalidate `grand' objects passed into Python through prime-
 +    generation events.
 +  * catacomb: Improve class docstrings.  (They're still extremely terse.)
 +  * catacomb: Add missing `copy' methods on hash and Keccak objects.
 +  * catacomb: Add `WriteBuffer.contents' as a more convenient way to
 +    extract the contents than coercing to `str' or `ByteString'.
 +  * catacomb: Set `RTLD_DEEPBIND' while loading the native module to work
 +    around #868366.
 +  * pock: New program for generating efficiently verifiable prime numbers,
 +    and for verifying their certificates.
 +
 + -- Mark Wooding <mdw@distorted.org.uk>  Sat, 21 Sep 2019 23:00:25 +0100
 +
+ catacomb-python (1.2.1.1) experimental; urgency=medium
+   * Fixing to build against Debian `stretch'.
+  -- Mark Wooding <mdw@distorted.org.uk>  Mon, 24 Dec 2018 15:21:08 +0000
  catacomb-python (1.2.1) experimental; urgency=low
  
    * Fix use-after-free bug in ECPt hashing causing hash instability.
diff --combined debian/control
index 24197f41ca442d1cee1652855f696ee092ebfef0,61f54a845c44fa38f1d9b1ac478c04a958463480..1bde721196a09361b89f26a041899fb94205e1c4
@@@ -3,9 -3,9 +3,9 @@@ Section: pytho
  Priority: extra
  XS-Python-Version: >= 2.6, << 2.8
  Maintainer: Mark Wooding <mdw@distorted.org.uk>
- Build-Depends: debhelper (>= 9), pkg-config,
+ Build-Depends: debhelper (>= 9), dh-python, pkg-config,
        python (>= 2.6.6-3~), python-all-dev,
 -      mlib-dev (>= 2.2.2.1), catacomb-dev (>= 2.4.0)
 +      mlib-dev (>= 2.2.2.1), catacomb-dev (>= 2.5.0)
  Standards-Version: 3.8.0
  
  Package: python-catacomb
diff --combined ec.c
index f7d61814b5cf5f3d7e14fbe4e2979ee52dae8ff8,361bbf5288674c07033c06460804480696c7857a..184dc9ecd47ff83d3b6acb85f5b9b61d59caa46b
--- 1/ec.c
--- 2/ec.c
+++ b/ec.c
@@@ -188,38 -188,31 +188,31 @@@ static PyObject *ecpt_pymul(PyObject *x
    if (ECPT_PYCHECK(x)) { PyObject *t; t = x; x = y; y = t; }
    if (!ECPT_PYCHECK(y) || (xx = tomp(x)) == 0) RETURN_NOTIMPL;
    ec_imul(ECPT_C(y), &zz, ECPT_P(y), xx);
+   MP_DROP(xx);
    return (ecpt_pywrap(ECPT_COBJ(y), &zz));
  }
  
  static long ecpt_pyhash(PyObject *me)
  {
    uint32 h;
-   buf b;
    ec p = EC_INIT;
-   size_t sz = 2*ECPT_C(me)->f->noctets + 1;
-   octet *q = xmalloc(sz);
  
-   h = 0xe0fdd039 + ECPT_C(me)->f->ops->ty;
-   buf_init(&b, q, sz);
-   EC_OUT(ECPT_C(me), &p, ECPT_P(me));
-   ec_putraw(ECPT_C(me), &b, &p);
+   getecptout(&p, me);
+   if (EC_ATINF(&p)) h = 0x81d81a94;
+   else h = 0xe0fdd039 ^ (2*mphash(p.x)) ^ (3*mphash(p.y));
    EC_DESTROY(&p);
-   h = unihash_hash(&unihash_global, h, BBASE(&b), BLEN(&b));
-   xfree(q);
-   return (h % LONG_MAX);
+   return (h%LONG_MAX);
  }
  
  static PyObject *ecpt_pyrichcompare(PyObject *x, PyObject *y, int op)
  {
-   ec_curve *c;
-   PyObject *cobj;
    ec p = EC_INIT, q = EC_INIT;
    int b;
    PyObject *rc = 0;
  
-   if (ecbinop(x, y, &c, &cobj, &p, &q)) RETURN_NOTIMPL;
-   EC_OUT(c, &p, &p);
-   EC_OUT(c, &q, &q);
+   if (!ECPT_PYCHECK(y)) RETURN_NOTIMPL;
+   getecptout(&p, x);
+   getecptout(&q, y);
    switch (op) {
      case Py_EQ: b = EC_EQ(&p, &q); break;
      case Py_NE: b = !EC_EQ(&p, &q); break;
@@@ -259,7 -252,7 +252,7 @@@ static PyObject *epmeth_tobuf(PyObject 
    if (EC_ATINF(&p))
      n = 2;
    else
-     n = mp_octets(p.x) + mp_octets(p.y) + 4;
+     n = mp_octets(p.x) + mp_octets(p.y) + 6;
    rc = bytestring_pywrap(0, n);
    buf_init(&b, PyString_AS_STRING(rc), n);
    buf_putec(&b, &p);
@@@ -297,11 -290,12 +290,12 @@@ static PyObject *epmeth_ec2osp(PyObjec
    char *p;
    ec_curve *c = ECPT_C(me);
    ec pp = EC_INIT;
-   int f = EC_EXPLY;
+   unsigned f = EC_EXPLY;
    int len;
 -  char *kwlist[] = { "flags", 0 };
 +  static const char *const kwlist[] = { "flags", 0 };
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:ectosp", KWLIST, &f))
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:ec2osp", kwlist,
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:ec2osp", KWLIST,
+                                  convuint, &f))
      return (0);
    len = c->f->noctets * 2 + 1;
    rc = bytestring_pywrap(0, len);
@@@ -419,9 -413,9 +413,9 @@@ static int ecptxl_3(ec_curve *c, ec *p
  
    if (!x || !y || !z) TYERR("missing argument");
    if (!c) VALERR("internal form with no curve!");
-   if ((p->x == coord_in(c->f, x)) == 0 ||
-       (p->y == coord_in(c->f, y)) == 0 ||
-       (z != Py_None && (p->z = coord_in(c->f, z))) == 0)
+   if ((p->x = coord_in(c->f, x)) == 0 ||
+       (p->y = coord_in(c->f, y)) == 0 ||
+       (z != Py_None && (p->z = coord_in(c->f, z)) == 0))
      goto end;
    if (!p->z) p->z = MP_COPY(c->f->one); /* just in case */
    rc = 0;
@@@ -459,7 -453,7 +453,7 @@@ static int ecptxl_1(ec_curve *c, ec *p
      getecptout(p, x);
      goto fix;
    } else if (PyString_Check(x)) {
-     if (PyObject_AsReadBuffer(x, &q, 0))
+     if (PyObject_AsReadBuffer(x, &q, &n))
        goto end;
      qd.p = q;
      qd.e = 0;
      if (!EC_FIND(c, p, xx)) VALERR("not on the curve");
    } else if (PySequence_Check(x)) {
      t = x; x = 0;
-     n = PySequence_Size(t);
+     n = PySequence_Size(t); if (n < 0) goto end;
      if (n != 2 && (n != 3 || !c))
        TYERR("want sequence of two or three items");
      if ((x = PySequence_GetItem(t, 0)) == 0 ||
        (n == 3 && (z = PySequence_GetItem(t, 2)) == 0))
        goto end;
      rc = (n == 2) ? ecptxl_2(c, p, x, y) : ecptxl_3(c, p, x, y, z);
+     goto end;
    } else
      TYERR("can't convert to curve point");
    goto ok;
@@@ -507,14 -502,14 +502,14 @@@ static PyObject *ecptnc_pynew(PyTypeObj
  {
    PyObject *x = 0, *y = 0, *z = 0;
    ec p = EC_INIT;
 -  char *kwlist[] = { "x", "y", 0 };
 +  static const char *const kwlist[] = { "x", "y", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:new", kwlist, &x, &y) ||
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:new", KWLIST, &x, &y) ||
        ecptxl(0, &p, x, y, z))
      goto end;
    return (ecpt_pywrapout(ty, &p));
  end:
-   EC_DESTROY(&p);
+   mp_drop(p.x); mp_drop(p.y); mp_drop(p.z);
    return (0);
  }
  
@@@ -548,15 -543,15 +543,15 @@@ static PyObject *ecpt_pynew(PyTypeObjec
  {
    PyObject *x = 0, *y = 0, *z = 0;
    ec p = EC_INIT;
 -  char *kwlist[] = { "x", "y", "z", 0 };
 +  static const char *const kwlist[] = { "x", "y", "z", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OOO:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OOO:new", KWLIST,
                                   &x, &y, &z) ||
        ecptxl(ECCURVE_C(ty), &p, x, y, z))
      goto end;
    return (ecpt_pywrap((PyObject *)ty, &p));
  end:
-   EC_DESTROY(&p);
+   mp_drop(p.x); mp_drop(p.y); mp_drop(p.z);
    return (0);
  }
  
@@@ -645,9 -640,7 +640,9 @@@ static PyTypeObject ecpt_pytype_skel = 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Elliptic curve points, not associated with any curve.",
 +"ECPt([X, [Y]]): elliptic curve points, not associated with any curve.\n\
 +  X alone may be None, an existing point, a string 'X, Y', an\n\
 +  x-coordinate, or a pair (X, Y); X and Y should be a coordinate pair.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -789,10 -782,14 +784,14 @@@ static PyTypeObject ecptcurve_pytype_sk
  
  static PyObject *eccurve_pyrichcompare(PyObject *x, PyObject *y, int op)
  {
-   int b = ec_samep(ECCURVE_C(x), ECCURVE_C(y));
+   int b;
+   assert(ECCURVE_PYCHECK(x));
+   if (!ECCURVE_PYCHECK(y)) RETURN_NOTIMPL;
+   b = ec_samep(ECCURVE_C(x), ECCURVE_C(y));
    switch (op) {
      case Py_EQ: break;
-     case Py_NE: b = !b;
+     case Py_NE: b = !b; break;
      default: TYERR("can't order elliptic curves");
    }
    return (getbool(b));
@@@ -863,12 -860,12 +862,12 @@@ static PyObject *meth__ECPtCurve_os2ecp
    buf b;
    PyObject *rc = 0;
    ec_curve *cc;
-   int f = EC_XONLY | EC_LSB | EC_SORT | EC_EXPLY;
+   unsigned f = EC_XONLY | EC_LSB | EC_SORT | EC_EXPLY;
    ec pp = EC_INIT;
-   static const char *const kwlist[] = { "buf", "flags", 0 };
 -  char *kwlist[] = { "class", "buf", "flags", 0 };
++  static const char *const kwlist[] = { "class", "buf", "flags", 0 };
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|f:os2ecp", KWLIST,
-                                  &me, &p, &len, &f))
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|O&:os2ecp", kwlist,
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|O&:os2ecp", KWLIST,
+                                  &me, &p, &len, convuint, &f))
      return (0);
    buf_init(&b, p, len);
    cc = ECCURVE_C(me);
@@@ -943,11 -940,11 +942,11 @@@ end
  
  static PyObject *ecmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "rng", 0 };
 +  static const char *const kwlist[] = { "rng", 0 };
    grand *r = &rand_global;
    ec p = EC_INIT;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
                                   convgrand, &r))
      return (0);
    ec_rand(ECCURVE_C(me), &p, r);
@@@ -1003,10 -1000,10 +1002,10 @@@ static PyObject *eccurve_pynew(PyTypeOb
  {
    PyObject *fobj;
    PyObject *cobj = 0;
 -  char *kwlist[] = { "field", "a", "b", 0 };
 +  static const char *const kwlist[] = { "field", "a", "b", 0 };
    mp *aa = 0, *bb = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&O&", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&O&", KWLIST,
                                   field_pytype, &fobj,
                                   convmp, &aa, convmp, &bb))
      goto end;
@@@ -1067,7 -1064,7 +1066,7 @@@ static PyMethodDef eccurve_pymethods[] 
    METH        (mmul,          "\
  E.mmul([(P0, N0), (P1, N1), ...]) = N0 P0 + N1 P1 + ...")
    METH        (find,          "E.find(X) -> P")
-   KWMETH(rand,                "E.rand(rng = rand) ->P")
+   KWMETH(rand,                "E.rand([rng = rand]) -> P")
  #undef METHNAME
    { 0 }
  };
@@@ -1097,7 -1094,7 +1096,7 @@@ static PyTypeObject eccurve_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "An elliptic curve.  Abstract class.",
 +"An elliptic curve.  Abstract class.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1151,8 -1148,7 +1150,8 @@@ static PyTypeObject ecprimecurve_pytype
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "An elliptic curve over a prime field.  Use ecprimeprojcurve.",
 +"ECPrimeCurve(FIELD, A, B): an elliptic curve over a prime field.\n\
 +  Use ECPrimeProjCurve instead.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1206,8 -1202,7 +1205,8 @@@ static PyTypeObject ecprimeprojcurve_py
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "An elliptic curve over a prime field, using projective coordinates.",
 +"ECPrimeProjCurve(FIELD, A, B): an elliptic curve over a prime field\n\
 +  using projective coordinates.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1261,8 -1256,7 +1260,8 @@@ static PyTypeObject ecbincurve_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "An elliptic curve over a binary field.  Use ecbinprojcurve.",
 +"ECBinCurve(FIELD, A, B): an elliptic curve over a binary field.\n\
 +  Use ECBinProjCurve instead.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1316,8 -1310,7 +1315,8 @@@ static PyTypeObject ecbinprojcurve_pyty
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "An elliptic curve over a binary field, using projective coordinates.",
 +"ECBinProjCurve(FIELD, A, B): an elliptic curve over a binary field,\n\
 +  using projective coordinates.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1378,10 -1371,10 +1377,10 @@@ static PyObject *ecinfo_pynew(PyTypeObj
  {
    ec_info ei = { 0 };
    PyObject *e, *g;
 -  char *kwlist[] = { "curve", "G", "r", "h", 0 };
 +  static const char *const kwlist[] = { "curve", "G", "r", "h", 0 };
    ecinfo_pyobj *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O&O&:new", KWLIST,
                                   eccurve_pytype, &e, ecpt_pytype, &g,
                                   convmp, &ei.r, convmp, &ei.h))
      goto end;
  
  static PyObject *eimeth_check(PyObject *me, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "rng", 0 };
 +  static const char *const kwlist[] = { "rng", 0 };
    grand *r = &rand_global;
    const char *p;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:check", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:check", KWLIST,
                                   convgrand, &r))
      goto end;
    if ((p = ec_checkinfo(ECINFO_EI(me), r)) != 0)
@@@ -1492,7 -1485,7 +1491,7 @@@ static PyGetSetDef ecinfo_pygetset[] = 
  
  static PyMethodDef ecinfo_pymethods[] = {
  #define METHNAME(name) eimeth_##name
-   KWMETH(check,               "I.check() -> None")
+   KWMETH(check,               "I.check([rng = rand]) -> None")
  #undef METHNAME
    { 0 }
  };
@@@ -1522,7 -1515,7 +1521,7 @@@ static PyTypeObject ecinfo_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Elliptic curve domain parameters.",
 +"ECInfo(CURVE, G, R, H): elliptic curve domain parameters.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
diff --combined field.c
index f2a0d2f855282e23dadbb3fddeed97045c674249,224363dfce270e4d63c95eb842a4f89c6bec5632..c23f3119ce6af8199e1a67377b91b9130fa22895
+++ b/field.c
@@@ -42,9 -42,9 +42,9 @@@ static PyObject *fe_pynew(PyTypeObject 
  {
    PyObject *x;
    mp *z;
 -  char *kwlist[] = { "x", 0 };
 +  static const char *const kwlist[] = { "x", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", kwlist, &x))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", KWLIST, &x))
      return (0);
    if (FE_PYCHECK(x) && FE_F(x) == FIELD_F(ty)) RETURN_OBJ(x);
    if ((z = getmp(x)) == 0) return (0);
@@@ -126,16 -126,6 +126,6 @@@ static mp *tofe(field *f, PyObject *o
    return (y);
  }
  
- mp *getfe(field *f, PyObject *o)
- {
-   mp *x = 0;
-   if ((x = tofe(f, o)) == 0) {
-     PyErr_Format(PyExc_TypeError, "can't convert %.100s to fe",
-                o->ob_type->tp_name);
-   }
-   return (x);
- }
  /*----- Field elements ----------------------------------------------------*/
  
  static int febinop(PyObject *x, PyObject *y,
@@@ -229,15 -219,7 +219,7 @@@ end
  }
  
  static long fe_pyhash(PyObject *me)
- {
-   size_t sz = FE_F(me)->noctets;
-   uint32 h = 0xe0c127ca + FE_F(me)->ops->ty;
-   octet *p = xmalloc(sz);
-   mp_storeb(FE_X(me), p, sz);
-   h = unihash_hash(&unihash_global, h, p, sz);
-   xfree(p);
-   return (h % LONG_MAX);
- }
+   { return (mphash(FE_X(me))); }
  
  static int fe_pycoerce(PyObject **x, PyObject **y)
  {
@@@ -282,7 -264,7 +264,7 @@@ static PyObject *fe_pylong(PyObject *x
  #define BASEOP(name, radix, pre)                                      \
    static PyObject *fe_py##name(PyObject *x) {                         \
      mp *xx = F_OUT(FE_F(x), MP_NEW, FE_X(x));                         \
-     PyObject *rc = mp_topystring(FE_X(x), radix, 0, pre, 0);          \
+     PyObject *rc = mp_topystring(xx, radix, 0, pre, 0);                       \
      MP_DROP(xx);                                                      \
      return (rc);                                                      \
    }
@@@ -345,8 -327,8 +327,8 @@@ static PyObject *feget__value(PyObject 
  static PyGetSetDef fe_pygetset[] = {
  #define GETSETNAME(op, name) fe##op##_##name
    GET (field,         "X.field -> field containing X")
-   GET (value,         "X.value -> `natural' integer representation of X")
-   GET (_value,        "X._value -> internal integer representation of X")
+   GET (value,         "X.value -> `natural' MP/GF representation of X")
+   GET (_value,        "X._value -> internal MP/GF representation of X")
  #undef GETSETNAME
    { 0 }
  };
@@@ -474,10 -456,10 +456,10 @@@ end
  
  static PyObject *fmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "rng", 0 };
 +  static const char *const kwlist[] = { "rng", 0 };
    grand *r = &rand_global;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
                                   convgrand, &r))
      return (0);
    return (fe_pywrap(me, F_RAND(FIELD_F(me), MP_NEW, r)));
@@@ -533,7 -515,7 +515,7 @@@ static PyGetSetDef field_pygetset[] = 
  static PyMethodDef field_pymethods[] = {
  #define METHNAME(name) fmeth_##name
    METH        (_adopt,        "F._adopt(X) -> FE")
-   KWMETH(rand,                "F.rand(rng = rand) -> FE, uniformly distributed")
+   KWMETH(rand,                "F.rand([rng = rand]) -> FE, uniformly distributed")
  #undef METHNAME
    { 0 }
  };
@@@ -593,9 -575,9 +575,9 @@@ static PyObject *primefield_pynew(PyTyp
  {
    mp *xx = 0;
    field *f;
 -  char *kwlist[] = { "p", 0 };
 +  static const char *const kwlist[] = { "p", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", KWLIST,
                                   convmp, &xx))
      goto end;
    if ((f = field_prime(xx)) == 0)
@@@ -641,7 -623,7 +623,7 @@@ static PyTypeObject primefield_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Prime fields.",
 +"PrimeField(P): prime fields.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -669,10 -651,10 +651,10 @@@ static PyObject *niceprimefield_pynew(P
  {
    mp *xx = 0;
    field *f;
 -  char *kwlist[] = { "p", 0 };
 +  static const char *const kwlist[] = { "p", 0 };
  
    if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:niceprimefield",
 -                                 kwlist, convmp, &xx))
 +                                 KWLIST, convmp, &xx))
      goto end;
    if ((f = field_niceprime(xx)) == 0)
      VALERR("bad prime for niceprimefield");
@@@ -708,7 -690,7 +690,7 @@@ static PyTypeObject niceprimefield_pyty
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Nice prime fields.",
 +"NicePrimeField(P): prime field using Solinas reduction.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -768,7 -750,7 +750,7 @@@ static PyTypeObject binfield_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Binary fields.  Abstract class.",
 +"Binary fields.  Abstract class.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -796,9 -778,9 +778,9 @@@ static PyObject *binpolyfield_pynew(PyT
  {
    mp *xx = 0;
    field *f;
 -  char *kwlist[] = { "p", 0 };
 +  static const char *const kwlist[] = { "p", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", KWLIST,
                                   convgf, &xx))
      goto end;
    if ((f = field_binpoly(xx)) == 0) VALERR("bad poly for binpolyfield");
@@@ -809,8 -791,11 +791,11 @@@ end
    return (0);
  }
  
+ static PyObject *bfget_p(PyObject *me, void *hunoz)
+   { return (gf_pywrap(MP_COPY(FIELD_F(me)->m))); }
  static PyGetSetDef binpolyfield_pygetset[] = {
- #define GETSETNAME(op, name) pf##op##_##name
+ #define GETSETNAME(op, name) bf##op##_##name
    GET (p,             "F.p -> field polynomial")
  #undef GETSETNAME
    { 0 }
@@@ -841,7 -826,7 +826,7 @@@ static PyTypeObject binpolyfield_pytype
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Binary fields with polynomial basis representation.",
 +"BinPolyField(P): binary fields with polynomial basis representation.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -869,10 -854,10 +854,10 @@@ static PyObject *binnormfield_pynew(PyT
  {
    mp *xx = 0, *yy = 0;
    field *f;
 -  char *kwlist[] = { "p", "beta", 0 };
 +  static const char *const kwlist[] = { "p", "beta", 0 };
  
    if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:binnormfield",
 -                                 kwlist, convgf, &xx, convgf, &yy))
 +                                 KWLIST, convgf, &xx, convgf, &yy))
      goto end;
    if ((f = field_binnorm(xx, yy)) == 0) VALERR("bad args for binnormfield");
    MP_DROP(xx); MP_DROP(yy);
@@@ -889,7 -874,7 +874,7 @@@ static PyObject *bnfget_beta(PyObject *
  }
  
  static PyGetSetDef binnormfield_pygetset[] = {
- #define GETSETNAME(op, name) pf##op##_##name
+ #define GETSETNAME(op, name) bf##op##_##name
    GET (p,             "F.p -> field polynomial")
  #undef GETSETNAME
  #define GETSETNAME(op, name) bnf##op##_##name
@@@ -923,7 -908,7 +908,7 @@@ static PyTypeObject binnormfield_pytype
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Binary fields with normal basis representation.",
 +"BinNormField(P, BETA): binary fields with normal basis representation.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
diff --combined group.c
index b5a1815ff459a14c6fc44c10027f6937ff74ea47,e00e3fb63748562c80c1792adc91626ad6aca07c..3d290705dac8766e1a4052f03d252fcea543dd24
+++ b/group.c
@@@ -40,11 -40,11 +40,11 @@@ PyObject *fginfo_pywrap(gprime_param *d
  static PyObject *fginfo_pynew(PyTypeObject *ty,
                              PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "p", "r", "g", 0 };
 +  static const char *const kwlist[] = { "p", "r", "g", 0 };
    gprime_param dp = { 0 };
    fginfo_pyobj *z = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&:new", KWLIST,
                                   convmp, &dp.p,
                                   convmp, &dp.q,
                                   convmp, &dp.g))
@@@ -92,18 -92,20 +92,20 @@@ static PyObject *meth__DHInfo_generate(
    unsigned ql = 0, pl;
    unsigned steps = 0;
    grand *r = &rand_global;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
 -  char *kwlist[] =
 +  static const char *const kwlist[] =
      { "class", "pbits", "qbits", "event", "rng", "nsteps", 0 };
    PyObject *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &steps))
      goto end;
-   if (dh_gen(&dp, ql, pl, steps, r, evt.proc, evt.ctx))
-     PGENERR;
+   if (dh_gen(&dp, ql, pl, steps, r, evt.ev.proc, evt.ev.ctx))
+     PGENERR(&exc);
    rc = fginfo_pywrap(&dp, dhinfo_pytype);
  end:
    droppgev(&evt);
@@@ -117,19 -119,19 +119,21 @@@ static PyObject *meth__DHInfo_genlimlee
    unsigned ql, pl;
    unsigned steps = 0;
    grand *r = &rand_global;
-   pgev oe = { 0 }, ie = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev oe = { { 0 } }, ie = { { 0 } };
    int subgroupp = 1;
    unsigned f = 0;
 -  char *kwlist[] = { "class", "pbits", "qbits", "event", "ievent",
 -                   "rng", "nsteps", "subgroupp", 0 };
 +  static const char *const kwlist[] = {
 +    "class", "pbits", "qbits", "event", "ievent",
 +    "rng", "nsteps", "subgroupp", 0
 +  };
    size_t i, nf;
    mp **v = 0;
    PyObject *rc = 0, *vec = 0;
  
+   oe.exc = ie.exc = &exc;
    if (!PyArg_ParseTupleAndKeywords(arg, kw,
 -                                 "OO&O&|O&O&O&O&O&:genlimlee", kwlist,
 +                                 "OO&O&|O&O&O&O&O&:genlimlee", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   convpgev, &oe, convpgev, &ie,
                                   convgrand, &r, convuint, &steps,
      goto end;
    if (subgroupp) f |= DH_SUBGROUP;
    if (dh_limlee(&dp, ql, pl, f, steps, r,
-               oe.proc, oe.ctx, ie.proc, ie.ctx, &nf, &v))
-     PGENERR;
+               oe.ev.proc, oe.ev.ctx, ie.ev.proc, ie.ev.ctx, &nf, &v))
+     PGENERR(&exc);
    vec = PyList_New(nf);
    for (i = 0; i < nf; i++)
      PyList_SetItem(vec, i, mp_pywrap(v[i]));
@@@ -156,19 -158,21 +160,21 @@@ static PyObject *meth__DHInfo_genkcdsa(
    unsigned ql, pl;
    unsigned steps = 0;
    grand *r = &rand_global;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
 -  char *kwlist[] = { "class", "pbits", "qbits",
 -                   "event", "rng", "nsteps", 0 };
 +  static const char *const kwlist[] =
 +    { "class", "pbits", "qbits", "event", "rng", "nsteps", 0 };
    mp *v = MP_NEW;
    PyObject *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|O&O&O&:genkcdsa", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|O&O&O&:genkcdsa", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &steps))
      goto end;
-   if (dh_kcdsagen(&dp, ql, pl, 0, steps, r, evt.proc, evt.ctx))
-     PGENERR;
+   if (dh_kcdsagen(&dp, ql, pl, 0, steps, r, evt.ev.proc, evt.ev.ctx))
+     PGENERR(&exc);
    mp_div(&v, 0, dp.p, dp.q);
    v = mp_lsr(v, v, 1);
    rc = Py_BuildValue("(NN)", fginfo_pywrap(&dp, dhinfo_pytype),
@@@ -187,18 -191,20 +193,20 @@@ static PyObject *meth__DHInfo_gendsa(Py
    dsa_seed ds;
    char *k;
    Py_ssize_t ksz;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
 -  char *kwlist[] =
 +  static const char *const kwlist[] =
      { "class", "pbits", "qbits", "seed", "event", "nsteps", 0 };
    PyObject *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&s#|O&O&:gendsa", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&s#|O&O&:gendsa", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   &k, &ksz, convpgev, &evt,
                                   convuint, &steps))
      goto end;
-   if (dsa_gen(&dp, ql, pl, steps, k, ksz, &ds, evt.proc, evt.ctx))
-     PGENERR;
+   if (dsa_gen(&dp, ql, pl, steps, k, ksz, &ds, evt.ev.proc, evt.ev.ctx))
+     PGENERR(&exc);
    rc = Py_BuildValue("(NNl)", fginfo_pywrap(&dp, dhinfo_pytype),
                     bytestring_pywrap(ds.p, ds.sz), (long)ds.count);
    xfree(ds.p);
@@@ -327,7 -333,7 +335,7 @@@ static PyTypeObject fginfo_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Abstract base class for field-group information objects.",
 +"Abstract base class for field-group information objects.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -375,7 -381,7 +383,7 @@@ static PyTypeObject dhinfo_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Standard (integer) Diffie-Hellman group information.",
 +"DHInfo(P, R, G): standard (integer) Diffie-Hellman group information.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -423,7 -429,7 +431,7 @@@ static PyTypeObject bindhinfo_pytype_sk
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Binary-field Diffie-Hellman group information.",
 +"BinDHInfo(P, R, G): binary-field Diffie-Hellman group information.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -495,7 -501,7 +503,7 @@@ PyObject *ge_pywrap(PyObject *gobj, ge 
  
  static PyObject *ge_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "x", 0 };
 +  static const char *const kwlist[] = { "x", 0 };
    PyObject *x;
    group *g;
    ec p = EC_INIT;
    mptext_stringctx sc;
  
    g = GROUP_G(ty);
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &x)) goto end;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &x)) goto end;
    xx = G_CREATE(g);
    if (ECPT_PYCHECK(x)) {
      getecptout(&p, x);
@@@ -718,14 -724,14 +726,14 @@@ end
  
  static PyObject *gemeth_toec(PyObject *me, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "curve", 0 };
 +  static const char *const kwlist[] = { "curve", 0 };
    PyTypeObject *cty = 0;
    PyObject *rc = 0;
    group *g;
    ec_curve *c;
    ec p = EC_INIT;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:toec", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:toec", KWLIST,
                                   &cty)) goto end;
    g = GROUP_G(GE_GOBJ(me));
    if (cty) {
@@@ -802,11 -808,11 +810,11 @@@ static PyObject *gmeth_mexp(PyObject *m
  
  static PyObject *gmeth_checkgroup(PyObject *me, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "rng", 0 };
 +  static const char *const kwlist[] = { "rng", 0 };
    grand *r = &rand_global;
    const char *p;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:checkgroup", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:checkgroup", KWLIST,
                                   convgrand, &r))
      goto end;
    if ((p = G_CHECK(GROUP_G(me), r)) != 0)
@@@ -966,7 -972,7 +974,7 @@@ static PyMethodDef ge_pymethods[] = 
    METH        (check,         "X.check() -> check X really belongs to its group")
    METH        (toint,         "X.toint() -> X converted to an integer")
    KWMETH(toec,                "\
- X.toec(curve = ecpt) -> X converted to elliptic curve point")
+ X.toec([curve = ECPt]) -> X converted to elliptic curve point")
    METH        (tobuf,         "X.tobuf() -> X in buffer representation")
    METH        (toraw,         "X.toraw() -> X in raw representation")
  #undef METHNAME
@@@ -1081,7 -1087,7 +1089,7 @@@ static PyMethodDef group_pymethods[] = 
  #define METHNAME(name) gmeth_##name
    METH        (mexp,          "\
  G.mexp([(X0, N0), (X1, N1), ...]) -> X0^N0 X1^N1 ...")
-   KWMETH(checkgroup,  "G.checkgroup(rand = random): check group is good")
+   KWMETH(checkgroup,  "G.checkgroup([rng = rand]): check group is good")
  #undef METHNAME
    { 0 }
  };
@@@ -1155,9 -1161,9 +1163,9 @@@ static PyObject *primegroup_pynew(PyTyp
                                  PyObject *arg, PyObject *kw)
  {
    PyObject *i;
 -  char *kwlist[] = { "info", 0 };
 +  static const char *const kwlist[] = { "info", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
                                   dhinfo_pytype, &i))
      return (0);
    return (group_dopywrap(ty, group_prime(FGINFO_DP(i))));
@@@ -1188,7 -1194,7 +1196,7 @@@ static PyTypeObject primegroup_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Subgroups of prime fields.",
 +"PrimeGroup(INFO): subgroups of prime fields.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1232,9 -1238,9 +1240,9 @@@ static PyObject *bingroup_pynew(PyTypeO
                                PyObject *arg, PyObject *kw)
  {
    PyObject *i;
 -  char *kwlist[] = { "info", 0 };
 +  static const char *const kwlist[] = { "info", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
                                   bindhinfo_pytype, &i))
      return (0);
    return (group_dopywrap(ty, group_binary(FGINFO_DP(i))));
@@@ -1265,7 -1271,7 +1273,7 @@@ static PyTypeObject bingroup_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Subgroups of binary fields.",
 +"BinGroup(INFO): subgroups of binary fields.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1309,9 -1315,9 +1317,9 @@@ static PyObject *ecgroup_pynew(PyTypeOb
  {
    PyObject *i;
    ec_info ei;
 -  char *kwlist[] = { "info", 0 };
 +  static const char *const kwlist[] = { "info", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
                                   ecinfo_pytype, &i))
      return (0);
    ecinfo_copy(&ei, ECINFO_EI(i));
@@@ -1343,7 -1349,7 +1351,7 @@@ static PyTypeObject ecgroup_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Elliptic curve groups.",
 +"ECGroup(INFO): elliptic curve groups.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1379,16 -1385,16 +1387,16 @@@ static PyMethodDef methods[] = 
    METH        (_DHInfo__groupn,       0)
    METH        (_BinDHInfo__groupn,    0)
    KWMETH(_DHInfo_generate,    "\
- generate(PBITS, [qbits = 0, event = pgen_nullev,\n\
-        rng = rand, nsteps = 0]) -> D")
+ generate(PBITS, [qbits = 0], [event = pgen_nullev],\n\
+        [rng = rand], [nsteps = 0]) -> D")
    KWMETH(_DHInfo_genlimlee,   "\
- genlimlee(PBITS, QBITS, [event = pgen_nullev, ievent = pgen_nullev,\n\
-         rng = rand, nsteps = 0, subgroupp = True]) -> (D, [Q, ...])")
+ genlimlee(PBITS, QBITS, [event = pgen_nullev], [ievent = pgen_nullev],\n\
+         [rng = rand], [nsteps = 0], [subgroupp = True]) -> (D, [Q, ...])")
    KWMETH(_DHInfo_gendsa,      "\
- gendsa(PBITS, QBITS, SEED, [event = pgen_nullevnsteps = 0])\n\
+ gendsa(PBITS, QBITS, SEED, [event = pgen_nullev], [nsteps = 0])\n\
    -> (D, SEED, COUNT)")
    KWMETH(_DHInfo_genkcdsa,    "\
- gendsa(PBITS, QBITS, [event = pgen_nullev, rng = rand, nsteps = 0])\n\
+ gendsa(PBITS, QBITS, [event = pgen_nullev], [rng = rand], [nsteps = 0])\n\
    -> (D, V)")
  #undef METHNAME
    { 0 }
diff --combined key.c
index 1700259d02946d918787ac74398ee840ab2fc21b,99c25aba5d1f688365c12c2065001be0372abf9a..87462ab59b76e77deac3f2d98ff0ac0328b67cf0
--- 1/key.c
--- 2/key.c
+++ b/key.c
@@@ -36,29 -36,31 +36,31 @@@ static PyObject *keyfilebrokenexc
  
  static PyObject *kxmeth___init__(PyObject *me, PyObject *arg)
  {
-   int err;
+   long err;
    PyObject *x = 0;
+   Py_ssize_t n;
  
-   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;
+   n = PyTuple_GET_SIZE(arg);
+   if (n < 2) TYERR("__init__() takes at least two arguments");
+   me = PyTuple_GET_ITEM(arg, 0);
+   err = PyInt_AsLong(PyTuple_GET_ITEM(arg, 1));
+   if (err == -1 && PyErr_Occurred()) goto end;
+   if (INT_MIN > err || err > INT_MAX) OVFERR("error code out of range");
+   x = PyInt_FromLong(err); if (!x) goto end;
+   if (PyObject_SetAttrString(me, "err", x)) goto end;
    Py_DECREF(x); x = 0;
-   if ((x = PyString_FromString(key_strerror(err))) == 0 ||
-       PyObject_SetAttrString(me, "errstring", x))
-     goto fail;
+   x = PyString_FromString(key_strerror(err)); if (!x) goto end;
+   if (PyObject_SetAttrString(me, "errstring", x)) goto end;
    Py_DECREF(x); x = 0;
-   if ((x = PySequence_GetSlice(arg, 1, PySequence_Size(arg))) == 0 ||
-       PyObject_SetAttrString(me, "args", x))
-     goto fail;
+   x = PyTuple_GetSlice(arg, 1, n); if (!x) goto end;
+   if (PyObject_SetAttrString(me, "args", x)) goto end;
    Py_DECREF(x); x = 0;
    RETURN_NONE;
  
fail:
end:
    Py_XDECREF(x);
    return (0);
  }
@@@ -104,7 -106,7 +106,7 @@@ static PyMethodDef keyexc_pymethods[] 
  
  static void keyexc_raise(int err)
  {
-   PyObject *arg = Py_BuildValue("(is)", err, key_strerror(err));
+   PyObject *arg = Py_BuildValue("(i)", err);
    if (arg) PyErr_SetObject(keyexc, arg);
    Py_XDECREF(arg);
  }
@@@ -229,8 -231,8 +231,8 @@@ static int convfilter(PyObject *x, voi
        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))
+     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;
@@@ -351,10 -353,10 +353,10 @@@ static PyObject *kdmeth_split(PyObject 
  static PyObject *kdmeth_copy(PyObject *me, PyObject *arg, PyObject *kw)
  {
    key_filter f = { 0, 0 };
 -  static char *kwlist[] = { "filter", 0 };
 +  static const char *const kwlist[] = { "filter", 0 };
    key_data *kd;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:copy", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:copy", KWLIST,
                                   convfilter, &f))
      return (0);
    if ((kd = key_copydata(KEYDATA_KD(me), &f)) == 0)
@@@ -368,9 -370,9 +370,9 @@@ static PyObject *kdmeth_write(PyObject 
    key_filter f = { 0, 0 };
    dstr d = DSTR_INIT;
    PyObject *rc = 0;
 -  static char *kwlist[] = { "filter", 0 };
 +  static const char *const kwlist[] = { "filter", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:write", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:write", KWLIST,
                                   convfilter, &f))
      return (0);
    key_write(KEYDATA_KD(me), &d, &f);
@@@ -384,9 -386,9 +386,9 @@@ static PyObject *kdmeth_encode(PyObjec
    key_filter f = { 0, 0 };
    dstr d = DSTR_INIT;
    PyObject *rc = 0;
 -  static char *kwlist[] = { "filter", 0 };
 +  static const char *const kwlist[] = { "filter", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:encode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:encode", KWLIST,
                                   convfilter, &f))
      return (0);
    key_encode(KEYDATA_KD(me), &d, &f);
@@@ -465,9 -467,9 +467,9 @@@ 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")
-   KWMETH(copy,                        "KD.copy(filter = <any>) -> KD")
+   KWMETH(write,                       "KD.write([filter = <any>]) -> STRING")
+   KWMETH(encode,              "KD.encode([filter = <any>]) -> BYTES")
+   KWMETH(copy,                        "KD.copy([filter = <any>]) -> KD")
    METH        (plock,                 "KD.plock(TAG) -> ENCRYPTED-KD")
    METH        (lock,                  "KD.lock(KEY) -> ENCRYPTED-KD")
  #undef METHNAME
@@@ -536,9 -538,9 +538,9 @@@ static PyObject *keydatabin_pynew(PyTyp
    Py_ssize_t n;
    unsigned f = 0;
    keydata_pyobj *me = 0;
 -  static char *kwlist[] = { "key", "flags", 0 };
 +  static const char *const kwlist[] = { "key", "flags", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", KWLIST,
                                   &p, &n, convflags, &f))
      goto end;
    me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@@ -583,7 -585,7 +585,7 @@@ static PyTypeObject keydatabin_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key data for binary keys.",
 +"KeyDataBinary(KEY, [flags = 0]): key data for binary keys.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -613,9 -615,9 +615,9 @@@ static PyObject *keydataenc_pynew(PyTyp
    Py_ssize_t n;
    unsigned f = 0;
    keydata_pyobj *me = 0;
 -  static char *kwlist[] = { "key", "flags", 0 };
 +  static const char *const kwlist[] = { "key", "flags", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", KWLIST,
                                   &p, &n, convflags, &f))
      goto end;
    me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@@ -720,7 -722,7 +722,7 @@@ static PyTypeObject keydataenc_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key data for encrypted keys.",
 +"KeyDataEncrypted(KEY, [flags = 0]): key data for encrypted keys.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -749,9 -751,9 +751,9 @@@ static PyObject *keydatamp_pynew(PyType
    mp *x = 0;
    unsigned f = 0;
    keydata_pyobj *me = 0;
 -  static char *kwlist[] = { "key", "flags", 0 };
 +  static const char *const kwlist[] = { "key", "flags", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
                                   convmp, &x, convflags, &f))
      goto end;
    me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@@ -796,7 -798,7 +798,7 @@@ static PyTypeObject keydatamp_pytype_sk
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key data for large-integer keys.",
 +"KeyDataMP(KEY, [flags = 0]): key data for large-integer keys.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -825,9 -827,9 +827,9 @@@ static PyObject *keydatastr_pynew(PyTyp
    char *p;
    unsigned f = 0;
    keydata_pyobj *me = 0;
 -  static char *kwlist[] = { "key", "flags", 0 };
 +  static const char *const kwlist[] = { "key", "flags", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:new", KWLIST,
                                   &p, convflags, &f))
      goto end;
    me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@@ -871,7 -873,7 +873,7 @@@ static PyTypeObject keydatastr_pytype_s
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key data for string keys.",
 +"KeyDataString(KEY, [flags = 0]): key data for string keys.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -900,9 -902,9 +902,9 @@@ static PyObject *keydataec_pynew(PyType
    ec x = EC_INIT;
    unsigned f = 0;
    keydata_pyobj *me = 0;
 -  static char *kwlist[] = { "key", "flags", 0 };
 +  static const char *const kwlist[] = { "key", "flags", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
                                   convecpt, &x, convflags, &f))
      goto end;
    me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@@ -951,7 -953,7 +953,7 @@@ static PyTypeObject keydataec_pytype_sk
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key data for elliptic-curve keys.",
 +"KeyDataECPt(KEY, [flags = 0]): key data for elliptic-curve keys.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1053,10 -1055,10 +1055,10 @@@ static PyObject *keydatastruct_pynew(Py
    char *p;
    keydata_pyobj *me = 0;
    key_data *kd = 0;
 -  static char *kwlist[] = { "subkeys", 0 };
 +  static const char *const kwlist[] = { "subkeys", 0 };
  
    Py_XINCREF(arg); Py_XINCREF(kw);
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", kwlist, &sub))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", KWLIST, &sub))
      goto end;
    kd = key_newstruct();
    if (sub) {
@@@ -1157,7 -1159,7 +1159,7 @@@ static PyTypeObject keydatastruct_pytyp
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key data for structured keys.",
 +"KeyDataStructured([subkeys = []]): key data for structured keys.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1386,12 -1388,11 +1388,12 @@@ static PyObject *key_pynew(PyTypeObjec
    uint32 id;
    char *type;
    unsigned long exptime = KEXP_FOREVER;
 -  static char *kwlist[] = { "keyfile", "id", "type", "exptime", 0 };
 +  static const char *const kwlist[] =
 +    { "keyfile", "id", "type", "exptime", 0 };
    key *k;
    int err;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&s|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&s|O&:new", KWLIST,
                                   keyfile_pytype, &kfobj, convu32, &id,
                                   &type, convulong, &exptime))
      goto end;
@@@ -1449,9 -1450,9 +1451,9 @@@ static PyObject *kmeth_extract(PyObjec
    PyObject *nameobj;
    char *name;
    FILE *fp;
 -  static char *kwlist[] = { "file", "filter", 0 };
 +  static const char *const kwlist[] = { "file", "filter", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:extract", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:extract", KWLIST,
                                   &PyFile_Type, &file,
                                   convfilter, &f) ||
        (fp = PyFile_AsFile(file)) == 0 ||
@@@ -1470,9 -1471,9 +1472,9 @@@ static PyObject *kmeth_fingerprint(PyOb
  {
    ghash *h;
    key_filter f = { KF_NONSECRET, KF_NONSECRET };
 -  static char *kwlist[] = { "hash", "filter", 0 };
 +  static const char *const kwlist[] = { "hash", "filter", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:fingerprint", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:fingerprint", KWLIST,
                                   convghash, &h, convfilter, &f))
      return (0);
    return (getbool(key_fingerprint(KEY_K(me), h, &f)));
@@@ -1498,6 -1499,7 +1500,7 @@@ static int kset_exptime(PyObject *me, P
    key *k = KEY_K(me);
    unsigned long et;
  
+   if (!x) NIERR("__del__");
    if (!convulong(x, &et))
      goto end;
    if (!(KEY_KF(me)->f & KF_WRITE))
@@@ -1514,6 -1516,7 +1517,7 @@@ static int kset_deltime(PyObject *me, P
    key *k = KEY_K(me);
    unsigned long dt;
  
+   if (!x) NIERR("__del__");
    if (!convulong(x, &dt))
      goto end;
    if (dt == KEXP_FOREVER && k->exp != KEXP_FOREVER)
@@@ -1598,8 -1601,8 +1602,8 @@@ static PyMethodDef key_pymethods[] = 
    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')")
+   KWMETH(extract,     "KEY.extract(FILE, [filter = <any>])")
+   KWMETH(fingerprint, "KEY.fingerprint(HASH, [filter = '-secret'])")
  #undef METHNAME
    { 0 }
  };
@@@ -1646,7 -1649,7 +1650,7 @@@ static PyTypeObject key_pytype_skel = 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Key object.",
 +"Key(KF, ID, TYPE, [exptime = KEXP_FOREVER]): key object.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1774,10 -1777,10 +1778,10 @@@ static PyObject *keyfile_pynew(PyTypeOb
    char *file = 0;
    unsigned how = KOPEN_READ;
    keyfile_pyobj *rc = 0;
 -  static char *kwlist[] = { "file", "how", "report", 0 };
 +  static const char *const kwlist[] = { "file", "how", "report", 0 };
  
    Py_XINCREF(arg); Py_XINCREF(kw);
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", KWLIST,
                                   &file, &how, &ri.func))
      goto end;
    if (ri.func && !PyCallable_Check(ri.func))
@@@ -1833,10 -1836,10 +1837,10 @@@ static PyObject *kfmeth_merge(PyObject 
    PyObject *x = 0;
    FILE *fp = 0;
    int rc;
 -  static char *kwlist[] = { "file", "report", 0 };
 +  static const char *const kwlist[] = { "file", "report", 0 };
  
    Py_XINCREF(arg); Py_XINCREF(kw);
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O:merge", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O:merge", KWLIST,
                                   &PyFile_Type, &x, &ri.func))
      goto end;
    if (ri.func && !PyCallable_Check(ri.func))
@@@ -1932,11 -1935,11 +1936,11 @@@ static PyObject *kfmeth_newkey(PyObjec
    uint32 id;
    char *type;
    long exptime = KEXP_FOREVER;
 -  static char *kwlist[] = { "id", "type", "exptime", 0 };
 +  static const char *const kwlist[] = { "id", "type", "exptime", 0 };
    key *k;
    int err;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s|l:newkey", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s|l:newkey", KWLIST,
                                   convu32, &id, &type, &exptime))
      goto end;
    if ((err = key_new(KEYFILE_KF(me), id, type, exptime, &k)) != 0)
@@@ -1954,9 -1957,9 +1958,9 @@@ static PyObject *kfmeth_qtag(PyObject *
    char *tag;
    dstr d = DSTR_INIT;
    PyObject *rc = 0;
 -  static char *kwlist[] = { "tag", "new", 0 };
 +  static const char *const kwlist[] = { "tag", "new", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O!:qtag", kwlist,
 +  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))
@@@ -1989,12 -1992,13 +1993,13 @@@ static PyObject *kfget_filep(PyObject *
  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")
+   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")
+   KWMETH(qtag,                "KF.qtag(TAG, [new = KD]) -> FULLTAG, KEY, OLDKD")
    GMAP_ROMETHODS
  #undef METHNAME
    { 0 }
@@@ -2041,8 -2045,7 +2046,8 @@@ static PyTypeObject keyfile_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Keyring file.",
 +"KeyFile(FILE, [how = KOPEN_READ], [report = ?]): Keyring file.\n\
 +   calls REPORT(FILE, LINE, MSG) on problems",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
diff --combined mp.c
index e0f72d4b32ac732d6dff4624253a6c52af3a040e,ab15d3fd10e115c476f1623e1eb162c37bb1d68f..1448bc237c2c680253f6fa4c5498c183ca2930c1
--- 1/mp.c
--- 2/mp.c
+++ b/mp.c
@@@ -211,6 -211,7 +211,7 @@@ mp *tomp(PyObject *o
      return (MP_COPY(PFILT_F(o)->m));
    else if (ECPT_PYCHECK(o)) {
      ec p = EC_INIT;
+     if (EC_ATINF(ECPT_P(o))) return (0);
      getecptout(&p, o);
      x = MP_COPY(p.x);
      EC_DESTROY(&p);
@@@ -527,9 -528,9 +528,9 @@@ static PyObject *mp_pynew(PyTypeObject 
    mp *z;
    mp_pyobj *zz = 0;
    int radix = 0;
 -  char *kwlist[] = { "x", "radix", 0 };
 +  static const char *const kwlist[] = { "x", "radix", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", kwlist, &x, &radix))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", KWLIST, &x, &radix))
      goto end;
    if (MP_PYCHECK(x)) RETURN_OBJ(x);
    if (!good_radix_p(radix, 1)) VALERR("bad radix");
@@@ -544,13 -545,15 +545,15 @@@ end
    return ((PyObject *)zz);
  }
  
static long mp_pyhash(PyObject *me)
long mphash(mp *x)
  {
-   long h;
-   PyObject *l = mp_topylong(MP_X(me)); h = PyObject_Hash(l);
+   PyObject *l = mp_topylong(x);
+   long h = PyObject_Hash(l);
    Py_DECREF(l); return (h);
  }
  
+ static long mp_pyhash(PyObject *me) { return (mphash(MP_X(me))); }
  static PyObject *mpmeth_jacobi(PyObject *me, PyObject *arg)
  {
    mp *y = 0;
@@@ -659,8 -662,8 +662,8 @@@ end
  static PyObject *mpmeth_tostring(PyObject *me, PyObject *arg, PyObject *kw)
  {
    int radix = 10;
 -  char *kwlist[] = { "radix", 0 };
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", kwlist, &radix))
 +  static const char *const kwlist[] = { "radix", 0 };
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", KWLIST, &radix))
      goto end;
    if (!good_radix_p(radix, 0)) VALERR("bad radix");
    return (mp_topystring(MP_X(me), radix, 0, 0, 0));
@@@ -700,11 -703,11 +703,11 @@@ end
                                 PyObject *arg, PyObject *kw)           \
    {                                                                   \
      long len = -1;                                                    \
 -    char *kwlist[] = { "len", 0 };                                    \
 +    static const char *const kwlist[] = { "len", 0 };                                 \
      PyObject *rc = 0;                                                 \
                                                                        \
      if (!PyArg_ParseTupleAndKeywords(arg, kw, "|l:" #name,            \
 -                                  kwlist, &len))                      \
 +                                  KWLIST, &len))                      \
        goto end;                                                               \
      if (len < 0) {                                                    \
        len = mp_octets##c(MP_X(me));                                   \
@@@ -763,10 -766,10 +766,10 @@@ static PyObject *mpmeth_tobuf(PyObject 
  static PyObject *mpmeth_primep(PyObject *me, PyObject *arg, PyObject *kw)
  {
    grand *r = &rand_global;
 -  char *kwlist[] = { "rng", 0 };
 +  static const char *const kwlist[] = { "rng", 0 };
    PyObject *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", kwlist, convgrand, &r))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", KWLIST, convgrand, &r))
      goto end;
    rc = getbool(pgen_primep(MP_X(me), r));
  end:
@@@ -793,7 -796,7 +796,7 @@@ static PyGetSetDef mp_pygetset[] = 
  
  static PyMethodDef mp_pymethods[] = {
  #define METHNAME(func) mpmeth_##func
-   METH        (jacobi,        "X.jacobi(Y) -> Jacobi symbol (Y/X) (NB inversion!)")
+   METH        (jacobi,        "X.jacobi(Y) -> Jacobi symbol (Y|X) (NB inversion!)")
    METH        (setbit,        "X.setbit(N) -> X with bit N set")
    METH        (clearbit,      "X.clearbit(N) -> X with bit N clear")
    METH        (testbit,       "X.testbit(N) -> true/false if bit N set/clear in X")
    METH        (modsqrt,       "X.modsqrt(Y) -> square root of Y mod X, if X prime")
    METH        (leastcongruent,
         "X.leastcongruent(B, M) -> smallest Z >= B with Z == X (mod M)")
-   KWMETH(primep,      "X.primep(rng = rand) -> true/false if X is prime")
-   KWMETH(tostring,    "X.tostring(radix = 10) -> STR")
-   KWMETH(storel,      "X.storel(len = -1) -> little-endian bytes")
-   KWMETH(storeb,      "X.storeb(len = -1) -> big-endian bytes")
+   KWMETH(primep,      "X.primep([rng = rand]) -> true/false if X is prime")
+   KWMETH(tostring,    "X.tostring([radix = 10]) -> STR")
+   KWMETH(storel,      "X.storel([len = -1]) -> little-endian bytes")
+   KWMETH(storeb,      "X.storeb([len = -1]) -> big-endian bytes")
    KWMETH(storel2c,
-        "X.storel2c(len = -1) -> little-endian bytes, two's complement")
+        "X.storel2c([len = -1]) -> little-endian bytes, two's complement")
    KWMETH(storeb2c,
-        "X.storeb2c(len = -1) -> big-endian bytes, two's complement")
+        "X.storeb2c([len = -1]) -> big-endian bytes, two's complement")
    METH        (tobuf,         "X.tobuf() -> buffer format")
  #undef METHNAME
    { 0 }
@@@ -890,15 -893,19 +893,19 @@@ static PyTypeObject mp_pytype_skel = 
  
    /* @tp_doc@ */
  "Multiprecision integers, similar to `long' but more efficient and\n\
- versatile.  Support all the standard arithmetic operations.\n\
+ versatile.  Support all the standard arithmetic operations, with\n\
+ implicit conversions from `PrimeFilter', and other objects which\n\
+ convert to `long'.\n\
  \n\
- Constructor mp(X, [radix = R]) attempts to convert X to an `mp'.  If\n\
+ Constructor MP(X, [radix = R]) attempts to convert X to an `MP'.  If\n\
  X is a string, it's read in radix-R form, or we look for a prefix\n\
- if R = 0.  Other acceptable things are ints and longs.\n\
+ if R = 0.  Other acceptable things are field elements, elliptic curve\n\
+ points, group elements, Python `int' and `long' objects, and anything\n\
+ with an integer conversion.\n\
  \n\
  Notes:\n\
  \n\
 -  * Use `//' for division.  MPs don't have `/' division.",
 +  * Use `//' for integer division.  `/' gives exact rational division.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -930,10 -937,10 +937,10 @@@ static PyObject *meth__MP_fromstring(Py
    PyObject *z = 0;
    mp *zz;
    mptext_stringctx sc;
 -  char *kwlist[] = { "class", "x", "radix", 0 };
 +  static const char *const kwlist[] = { "class", "x", "radix", 0 };
  
    if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
 -                                 kwlist, &me, &p, &len, &r))
 +                                 KWLIST, &me, &p, &len, &r))
      goto end;
    if (!good_radix_p(r, 1)) VALERR("bad radix");
    sc.buf = p; sc.lim = p + len;
@@@ -1101,7 -1108,7 +1108,7 @@@ static PyTypeObject *mpmul_pytype, mpmu
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"An object for multiplying many small integers.",
 +"MPMul(N_0, N_1, ....): an object for multiplying many small integers.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1294,10 -1301,10 +1301,10 @@@ static void mpmont_pydealloc(PyObject *
  static PyObject *mpmont_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
    mpmont_pyobj *mm = 0;
 -  char *kwlist[] = { "m", 0 };
 +  static const char *const kwlist[] = { "m", 0 };
    mp *xx = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
      goto end;
    if (!MP_POSP(xx) || !MP_ODDP(xx)) VALERR("m must be positive and odd");
    mm = (mpmont_pyobj *)ty->tp_alloc(ty, 0);
@@@ -1368,7 -1375,7 +1375,7 @@@ static PyTypeObject *mpmont_pytype, mpm
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"A Montgomery reduction context.",
 +"MPMont(N): a Montgomery reduction context.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1448,10 -1455,10 +1455,10 @@@ static PyObject *mpbarrett_pynew(PyType
                                 PyObject *arg, PyObject *kw)
  {
    mpbarrett_pyobj *mb = 0;
 -  char *kwlist[] = { "m", 0 };
 +  static const char *const kwlist[] = { "m", 0 };
    mp *xx = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
      goto end;
    if (!MP_POSP(xx)) VALERR("m must be positive");
    mb = (mpbarrett_pyobj *)ty->tp_alloc(ty, 0);
@@@ -1507,7 -1514,7 +1514,7 @@@ static PyTypeObject *mpbarrett_pytype, 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"A Barrett reduction context.",
 +"MPBarrett(N): a Barrett reduction context.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1578,10 -1585,10 +1585,10 @@@ static PyObject *mpreduce_pynew(PyTypeO
  {
    mpreduce_pyobj *mr = 0;
    mpreduce r;
 -  char *kwlist[] = { "m", 0 };
 +  static const char *const kwlist[] = { "m", 0 };
    mp *xx = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
      goto end;
    if (!MP_POSP(xx)) VALERR("m must be positive");
    if (mpreduce_create(&r, xx)) VALERR("bad modulus (must be 2^k - ...)");
@@@ -1635,7 -1642,7 +1642,7 @@@ static PyTypeObject *mpreduce_pytype, m
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"A reduction context for reduction modulo primes of special form.",
 +"MPReduce(N): a reduction context for reduction modulo Solinas primes.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1673,7 -1680,7 +1680,7 @@@ static PyObject *mcmeth_solve(PyObject 
    PyObject *q = 0, *x, *z = 0;
    mp *xx;
    mp **v = 0;
-   int i = 0, n = c->k;
+   Py_ssize_t i = 0, n = c->k;
  
    Py_INCREF(me);
    if (PyTuple_Size(arg) == n)
      goto end;
    Py_INCREF(q);
    if (!PySequence_Check(q)) TYERR("want a sequence of residues");
-   if (PySequence_Size(q) != n) VALERR("residue count mismatch");
+   i = PySequence_Size(q); if (i < 0) goto end;
+   if (i != n) VALERR("residue count mismatch");
    v = xmalloc(n * sizeof(*v));
    for (i = 0; i < n; i++) {
      if ((x = PySequence_GetItem(q, i)) == 0) goto end;
@@@ -1712,30 -1720,41 +1720,41 @@@ static void mpcrt_pydealloc(PyObject *m
  static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
    mpcrt_mod *v = 0;
-   int n, i = 0;
+   Py_ssize_t n, i = 0, j;
 -  char *kwlist[] = { "mv", 0 };
 +  static const char *const kwlist[] = { "mv", 0 };
    PyObject *q = 0, *x;
-   mp *xx;
+   mp *xx = MP_NEW, *y = MP_NEW, *g = MP_NEW;
+   mpmul mm;
    mpcrt_pyobj *c = 0;
  
    if (PyTuple_Size(arg) > 1)
      q = arg;
 -  else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &q))
 +  else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &q))
      goto end;
    Py_INCREF(q);
    if (!PySequence_Check(q)) TYERR("want a sequence of moduli");
-   n = PySequence_Size(q);
-   if (PyErr_Occurred()) goto end;
+   n = PySequence_Size(q); if (n < 0) goto end;
    if (!n) VALERR("want at least one modulus");
    v = xmalloc(n * sizeof(*v));
    for (i = 0; i < n; i++) {
      if ((x = PySequence_GetItem(q, i)) == 0) goto end;
      xx = getmp(x); Py_DECREF(x); if (!xx) goto end;
-     v[i].m = xx; v[i].n = 0; v[i].ni = 0; v[i].nni = 0;
+     if (MP_CMP(xx, <=, MP_ZERO)) VALERR("moduli must be positive");
+     v[i].m = xx; v[i].n = 0; v[i].ni = 0; v[i].nni = 0; xx = MP_NEW;
    }
+   mpmul_init(&mm);
+   for (j = 0; j < i; j++) mpmul_add(&mm, v[j].m);
+   xx = mpmul_done(&mm);
+   for (j = 0; j < i; j++) {
+     mp_div(&y, 0, xx, v[j].m);
+     mp_gcd(&g, 0, 0, y, v[j].m);
+     if (!MP_EQ(g, MP_ONE)) VALERR("moduli must be pairwise coprime");
+   }
    c = (mpcrt_pyobj *)ty->tp_alloc(ty, 0);
    mpcrt_create(&c->c, v, n, 0);
    Py_DECREF(q);
+   mp_drop(xx); mp_drop(y); mp_drop(g);
    return ((PyObject *)c);
  
  end:
      xfree(v);
    }
    Py_XDECREF(q);
+   mp_drop(xx); mp_drop(y); mp_drop(g);
    return (0);
  }
  
@@@ -1804,7 -1824,7 +1824,7 @@@ static PyTypeObject *mpcrt_pytype, mpcr
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"A context for the solution of Chinese Remainder Theorem problems.",
 +"MPCRT(SEQ): a context for solving Chinese Remainder Theorem problems.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1864,9 -1884,9 +1884,9 @@@ static PyObject *gf_pynew(PyTypeObject 
    mp *z;
    mp_pyobj *zz = 0;
    int radix = 0;
 -  char *kwlist[] = { "x", "radix", 0 };
 +  static const char *const kwlist[] = { "x", "radix", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", kwlist, &x, &radix))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", KWLIST, &x, &radix))
      goto end;
    if (GF_PYCHECK(x)) RETURN_OBJ(x);
    if (!good_radix_p(radix, 1)) VALERR("radix out of range");
@@@ -1885,15 -1905,6 +1905,6 @@@ end
    return ((PyObject *)zz);
  }
  
- static long gf_pyhash(PyObject *me)
- {
-   long i = mp_tolong(MP_X(me));
-   i ^= 0xc7ecd67c; /* random perturbance */
-   if (i == -1)
-     i = -2;
-   return (i);
- }
  static PyObject *gf_pyexp(PyObject *x, PyObject *y, PyObject *z)
  {
    mp *xx = 0, *yy = 0, *zz = 0;
@@@ -2006,13 -2017,13 +2017,13 @@@ static PyMethodDef gf_pymethods[] = 
    METH        (irreduciblep,  "X.irreduciblep() -> true/false")
  #undef METHNAME
  #define METHNAME(func) mpmeth_##func
-   KWMETH(tostring,    "X.tostring(radix = 10) -> STR")
-   KWMETH(storel,      "X.storel(len = -1) -> little-endian bytes")
-   KWMETH(storeb,      "X.storeb(len = -1) -> big-endian bytes")
+   KWMETH(tostring,    "X.tostring([radix = 10]) -> STR")
+   KWMETH(storel,      "X.storel([len = -1]) -> little-endian bytes")
+   KWMETH(storeb,      "X.storeb([len = -1]) -> big-endian bytes")
    KWMETH(storel2c,
-        "X.storel2c(len = -1) -> little-endian bytes, two's complement")
+        "X.storel2c([len = -1]) -> little-endian bytes, two's complement")
    KWMETH(storeb2c,
-        "X.storeb2c(len = -1) -> big-endian bytes, two's complement")
+        "X.storeb2c([len = -1]) -> big-endian bytes, two's complement")
    METH        (tobuf,         "X.tobuf() -> buffer format")
  #undef METHNAME
    { 0 }
@@@ -2076,7 -2087,7 +2087,7 @@@ static PyTypeObject gf_pytype_skel = 
    &gf_pynumber,                               /* @tp_as_number@ */
    0,                                  /* @tp_as_sequence@ */
    0,                                  /* @tp_as_mapping@ */
-   gf_pyhash,                          /* @tp_hash@ */
+   mp_pyhash,                          /* @tp_hash@ */
    0,                                  /* @tp_call@ */
    mp_pyhex,                           /* @tp_str@ */
    0,                                  /* @tp_getattro@ */
  "Binary polynomials.  Support almost all the standard arithmetic\n\
  operations.\n\
  \n\
- Constructor gf(X, radix = R) attempts to convert X to a `gf'.  If\n\
+ Constructor GF(X, [radix = R]) attempts to convert X to a `GF'.  If\n\
  X is a string, it's read in radix-R form, or we look for a prefix\n\
- if R = 0.  Other acceptable things are ints and longs.\n\
+ if R = 0.  Other acceptable things are field elements, elliptic curve\n\
+ points, group elements, Python `int' and `long' objects, and anything\n\
+ with an integer conversion.\n\
  \n\
  The name is hopelessly wrong from a technical point of view, but\n\
  but it's much easier to type than `p2' or `c2' or whatever.\n\
  \n\
  Notes:\n\
  \n\
 -  * Use `//' for division.  GFs don't have `/' division.",
 +  * Use `//' for Euclidean division.  `/' gives exact rational division.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -2131,10 -2144,10 +2144,10 @@@ static PyObject *meth__GF_fromstring(Py
    PyObject *z = 0;
    mp *zz;
    mptext_stringctx sc;
 -  char *kwlist[] = { "class", "x", "radix", 0 };
 +  static const char *const kwlist[] = { "class", "x", "radix", 0 };
  
    if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
 -                                 kwlist, &me, &p, &len, &r))
 +                                 KWLIST, &me, &p, &len, &r))
      goto end;
    if (!good_radix_p(r, 1)) VALERR("bad radix");
    sc.buf = p; sc.lim = p + len;
@@@ -2249,10 -2262,10 +2262,10 @@@ static PyObject *gfreduce_pynew(PyTypeO
  {
    gfreduce_pyobj *mr = 0;
    gfreduce r;
 -  char *kwlist[] = { "m", 0 };
 +  static const char *const kwlist[] = { "m", 0 };
    mp *xx = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convgf, &xx))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convgf, &xx))
      goto end;
    if (MP_ZEROP(xx)) ZDIVERR("modulus is zero!");
    gfreduce_create(&r, xx);
@@@ -2310,7 -2323,7 +2323,7 @@@ static PyTypeObject *gfreduce_pytype, g
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"A reduction context for reduction modulo sparse irreducible polynomials.",
 +"GFReduce(N): a context for reduction modulo sparse polynomials.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -2351,14 -2364,15 +2364,15 @@@ static PyObject *gfn_pynew(PyTypeObjec
  {
    mp *p = 0, *beta = 0;
    gfn_pyobj *gg = 0;
 -  char *kwlist[] = { "p", "beta", 0 };
 +  static const char *const kwlist[] = { "p", "beta", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   convgf, &p, convgf, &beta))
      goto end;
    gg = PyObject_New(gfn_pyobj, ty);
+   gg->p = 0;
    if (gfn_create(p, beta, &gg->ntop, &gg->pton)) {
-     FREEOBJ(gg);
+     Py_DECREF(gg);
      gg = 0;
      VALERR("can't invert transformation matrix");
    }
@@@ -2390,7 -2404,7 +2404,7 @@@ static PyObject *gfnget_beta(PyObject *
    end:                                                                        \
      mp_drop(xx);                                                      \
      if (!z) return (0);                                                       \
-     return (mp_pywrap(z));                                            \
+     return (gf_pywrap(z));                                            \
    }
  XFORMOP(pton, PTON)
  XFORMOP(ntop, NTOP)
  
  static void gfn_pydealloc(PyObject *me)
  {
-   gfn_destroy(GFN_PTON(me));
-   gfn_destroy(GFN_NTOP(me));
+   if (GFN_P(me)) {
+     MP_DROP(GFN_P(me));
+     gfn_destroy(GFN_PTON(me));
+     gfn_destroy(GFN_NTOP(me));
+   }
    FREEOBJ(me);
  }
  
@@@ -2444,8 -2461,8 +2461,8 @@@ static PyTypeObject gfn_pytype_skel = 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"An object for transforming elements of binary fields between polynomial\n\
 -and normal basis representations.",
 +"GFN(P, BETA): an object for transforming elements of binary fields\n\
 +  between polynomial and normal basis representations.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
  static PyMethodDef methods[] = {
  #define METHNAME(func) meth_##func
    KWMETH(_MP_fromstring,      "\
- fromstring(STR, radix = 0) -> (X, REST)\n\
+ fromstring(STR, [radix = 0]) -> (X, REST)\n\
  \n\
  Parse STR as a large integer, according to radix.  If radix is zero,\n\
  read a prefix from STR to decide radix: allow `0' for octal, `0x' for hex\n\
  or `R_' for other radix R.")
    KWMETH(_GF_fromstring,      "\
- fromstring(STR, radix = 0) -> (X, REST)\n\
+ fromstring(STR, [radix = 0]) -> (X, REST)\n\
  \n\
  Parse STR as a binary polynomial, according to radix.  If radix is zero,\n\
  read a prefix from STR to decide radix: allow `0' for octal, `0x' for hex\n\
diff --combined pgen.c
index 18b0c2688f0385474e17d3cd7eb29d2566babcdb,94603100b23c03e7f78a8e06cceab018676922ef..9f9e1a2199b17b36ff85175784def45fce77c0dd
--- 1/pgen.c
--- 2/pgen.c
+++ b/pgen.c
@@@ -57,10 -57,10 +57,10 @@@ end
  
  static PyObject *pfilt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "x", 0 };
 +  static const char *const kwlist[] = { "x", 0 };
    PyObject *xobj;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &xobj))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &xobj))
      return (0);
    return (pfilt_pymake(ty, xobj));
  }
@@@ -219,7 -219,7 +219,7 @@@ static PyTypeObject pfilt_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Small-primes filter.",
 +"PrimeFilter(X): small-primes filter.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -256,9 -256,9 +256,9 @@@ static PyObject *rabin_pynew(PyTypeObje
  {
    mp *x = 0;
    rabin_pyobj *o = 0;
 -  char *kwlist[] = { "x", 0 };
 +  static const char *const kwlist[] = { "x", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &x))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &x))
      goto end;
    if (!MP_POSP(x) || MP_EVENP(x)) VALERR("must be positive and odd");
    o = (rabin_pyobj *)ty->tp_alloc(ty, 0);
@@@ -352,7 -352,7 +352,7 @@@ static PyTypeObject rabin_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Rabin-Miller strong primality test.",
 +"RabinMiller(X): Rabin-Miller strong primality test.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -443,6 -443,7 +443,7 @@@ static int peset_x(PyObject *me, PyObje
    mp *x = 0;
    pgen_event *ev = PGEVENT_EV(me);
    int rc = -1;
+   if (!xobj) NIERR("__del__");
    PGEVENT_CHECK(me);
    if ((x = getmp(xobj)) == 0) goto end;
    mp_drop(ev->m);
@@@ -543,20 -544,19 +544,19 @@@ static PyTypeObject *pgtest_pytype
  
  static int pgev_python(int rq, pgen_event *ev, void *p)
  {
-   PyObject *py = p;
+   pypgev *pg = p;
    PyObject *pyev = 0;
    PyObject *rc = 0;
    int st = PGEN_ABORT;
    long l;
 -  char *meth[] = {
 -    "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass"
 -  };
 +  static const char *const meth[] =
 +    { "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass" };
  
-   Py_INCREF(py);
    rq++;
    if (rq > N(meth)) SYSERR("event code out of range");
    pyev = pgevent_pywrap(ev);
-   if ((rc = PyObject_CallMethod(py, (/*unconst*/ char *)meth[rq],
 -  if ((rc = PyObject_CallMethod(pg->obj, meth[rq], "(O)", pyev)) == 0)
++  if ((rc = PyObject_CallMethod(pg->obj, (/*unconst*/ char *)meth[rq],
 +                              "(O)", pyev)) == 0)
      goto end;
    if (rc == Py_None)
      st = PGEN_TRY;
    else
      st = l;
  end:
+   if (PyErr_Occurred())
+     stash_exception(pg->exc, "exception from `pgen' handler");
    if (pyev) {
      pgevent_kill(pyev);
      Py_DECREF(pyev);
    }
    Py_XDECREF(rc);
-   Py_DECREF(py);
    return (st);
  }
  
@@@ -587,24 -588,22 +588,22 @@@ static PyObject *pgev_pywrap(const pge
  
  int convpgev(PyObject *o, void *p)
  {
-   pgev *pg = p;
+   pypgev *pg = p;
  
    if (PGEV_PYCHECK(o))
-     *pg = *PGEV_PG(o);
+     pg->ev = *PGEV_PG(o);
    else {
-     pg->proc = pgev_python;
-     pg->ctx = o;
-     Py_INCREF(o);
+     pg->ev.proc = pgev_python;
+     pg->ev.ctx = pg;
+     pg->obj = o; Py_INCREF(o);
    }
    return (1);
  }
  
- void droppgev(pgev *p)
+ void droppgev(pypgev *pg)
  {
-   if (p->proc == pgev_python) {
-     PyObject *py = p->ctx;
-     Py_DECREF(py);
-   }
+   if (pg->ev.proc == pgev_python)
+     { assert(pg->ev.ctx == pg); Py_DECREF(pg->obj); }
  }
  
  static PyObject *pgmeth_common(PyObject *me, PyObject *arg, int rq)
@@@ -635,12 -634,12 +634,12 @@@ static PyObject *pgev_stdev(pgen_proc *
  
  static PyMethodDef pgev_pymethods[] = {
  #define METHNAME(name) pgmeth_##name
-   METH        (pg_abort,      "E.pg_abort() -> PGRC -- prime generation aborted")
-   METH        (pg_done,       "E.pg_done() -> PGRC -- prime generation finished")
-   METH        (pg_begin,      "E.pg_begin() -> PGRC -- commence stepping/testing")
-   METH        (pg_try,        "E.pg_try() -> PGRC -- found new candidate")
-   METH        (pg_pass,       "E.pg_pass() -> PGRC -- passed primality test")
-   METH        (pg_fail,       "E.pg_fail() -> PGRC -- failed primality test")
+   METH        (pg_abort,      "E.pg_abort(EV) -> PGRC -- prime generation aborted")
+   METH        (pg_done,       "E.pg_done(EV) -> PGRC -- prime generation finished")
+   METH        (pg_begin,     "E.pg_begin(EV) -> PGRC -- commence stepping/testing")
+   METH        (pg_try,        "E.pg_try(EV) -> PGRC -- found new candidate")
+   METH        (pg_pass,       "E.pg_pass(EV) -> PGRC -- passed primality test")
+   METH        (pg_fail,       "E.pg_fail(EV) -> PGRC -- failed primality test")
  #undef METHNAME
    { 0 }
  };
@@@ -697,9 -696,9 +696,9 @@@ static PyObject *pgstep_pynew(PyTypeObj
  {
    mpw s;
    pgstep_pyobj *rc = 0;
 -  char *kwlist[] = { "step", 0 };
 +  static const char *const kwlist[] = { "step", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmpw, &s))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmpw, &s))
      goto end;
    rc = (pgstep_pyobj *)ty->tp_alloc(ty, 0);
    rc->f.step = s;
@@@ -744,7 -743,7 +743,7 @@@ static PyTypeObject pgstep_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -  "Simple prime-number stepper with small-factors filter.",
 +"PrimeGenStepper(STEP): simple stepper with small-factors filter.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -771,9 -770,9 +770,9 @@@ static PyObject *pgjump_pynew(PyTypeObj
  {
    PyObject *o, *fobj;
    pgjump_pyobj *rc = 0;
 -  char *kwlist[] = { "jump", 0 };
 +  static const char *const kwlist[] = { "jump", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &o) ||
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &o) ||
        (fobj = pfilt_pymake(pfilt_pytype, o)) == 0)
      goto end;
    rc = (pgjump_pyobj *)ty->tp_alloc(ty, 0);
@@@ -826,7 -825,7 +825,7 @@@ static PyTypeObject pgjump_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Stepper for larger steps, with small-factors filter.",
 +"PrimeGenJumper(JUMP): stepper for larger steps with small-factors filter.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
  static PyObject *pgtest_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
    pgtest_pyobj *rc = 0;
 -  char *kwlist[] = { 0 };
 +  static const char *const kwlist[] = { 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist)) goto end;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
    rc = (pgtest_pyobj *)ty->tp_alloc(ty, 0);
    rc->pg.proc = pgen_test;
    rc->pg.ctx = &rc->r;
@@@ -887,7 -886,7 +886,7 @@@ static PyTypeObject pgtest_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Rabin-Miller tester.",
 +"PrimeGenTester(): Rabin-Miller tester.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
  
  /*----- Prime generation functions ----------------------------------------*/
  
- void pgenerr(void)
+ void pgenerr(struct excinfo *exc)
  {
-   if (!PyErr_Occurred())
-     PyErr_SetString(PyExc_ValueError, "prime generation failed");
+   if (exc->ty) RESTORE_EXCINFO(exc);
+   else PyErr_SetString(PyExc_ValueError, "prime generation failed");
  }
  
  static PyObject *meth_pgen(PyObject *me, PyObject *arg, PyObject *kw)
    char *p = "p";
    pgen_filterctx fc = { 2 };
    rabin tc;
-   pgev step = { 0 }, test = { 0 }, evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev step = { { 0 } }, test = { { 0 } }, evt = { { 0 } };
    unsigned nsteps = 0, ntests = 0;
 -  char *kwlist[] = { "start", "name", "stepper", "tester", "event",
 -                   "nsteps", "ntests", 0 };
 +  static const char *const kwlist[] =
 +    { "start", "name", "stepper", "tester", "event", "nsteps", "ntests", 0 };
  
-   step.proc = pgen_filter; step.ctx = &fc;
-   test.proc = pgen_test; test.ctx = &tc;
+   step.exc = &exc; step.ev.proc = pgen_filter; step.ev.ctx = &fc;
+   test.exc = &exc; test.ev.proc = pgen_test; test.ev.ctx = &tc;
+   evt.exc = &exc;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&O&O&:pgen", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&O&O&:pgen", KWLIST,
                                   convmp, &x, &p, convpgev, &step,
                                   convpgev, &test, convpgev, &evt,
                                   convuint, &nsteps, convuint, &ntests))
      goto end;
    if (!ntests) ntests = rabin_iters(mp_bits(x));
-   if ((r = pgen(p, MP_NEW, x, evt.proc, evt.ctx,
-               nsteps, step.proc, step.ctx,
-               ntests, test.proc, test.ctx)) == 0)
-     PGENERR;
-   if (PyErr_Occurred()) goto end;
-   rc = mp_pywrap(r);
-   r = 0;
+   if ((r = pgen(p, MP_NEW, x, evt.ev.proc, evt.ev.ctx,
+               nsteps, step.ev.proc, step.ev.ctx,
+               ntests, test.ev.proc, test.ev.ctx)) == 0)
+     PGENERR(&exc);
+   rc = mp_pywrap(r); r = 0;
  end:
    mp_drop(r); mp_drop(x);
    droppgev(&step); droppgev(&test); droppgev(&evt);
@@@ -961,19 -960,20 +960,21 @@@ static PyObject *meth_strongprime_setup
    unsigned nbits;
    char *name = "p";
    unsigned n = 0;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
    PyObject *rc = 0;
 -  char *kwlist[] = { "nbits", "name", "event", "rng", "nsteps", 0 };
 +  static const char *const kwlist[] =
 +    { "nbits", "name", "event", "rng", "nsteps", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
                                   convuint, &nbits, &name,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &n))
      goto end;
    if ((x = strongprime_setup(name, MP_NEW, &f, nbits,
-                            r, n, evt.proc, evt.ctx)) == 0)
-     PGENERR;
+                            r, n, evt.ev.proc, evt.ev.ctx)) == 0)
+     PGENERR(&exc);
    rc = Py_BuildValue("(NN)", mp_pywrap(x), pfilt_pywrap(&f));
    x = 0;
  end:
@@@ -989,19 -989,20 +990,21 @@@ static PyObject *meth_strongprime(PyObj
    unsigned nbits;
    char *name = "p";
    unsigned n = 0;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
    PyObject *rc = 0;
 -  char *kwlist[] = { "nbits", "name", "event", "rng", "nsteps", 0 };
 +  static const char *const kwlist[] =
 +    { "nbits", "name", "event", "rng", "nsteps", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
                                   convuint, &nbits, &name,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &n))
      goto end;
    if ((x = strongprime(name, MP_NEW, nbits,
-                      r, n, evt.proc, evt.ctx)) == 0)
-     PGENERR;
+                      r, n, evt.ev.proc, evt.ev.ctx)) == 0)
+     PGENERR(&exc);
    rc = mp_pywrap(x);
    x = 0;
  end:
  static PyObject *meth_limlee(PyObject *me, PyObject *arg, PyObject *kw)
  {
    char *p = "p";
-   pgev ie = { 0 }, oe = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev ie = { { 0 } }, oe = { { 0 } };
    unsigned ql, pl;
    grand *r = &rand_global;
    unsigned on = 0;
    size_t i, nf = 0;
    PyObject *rc = 0, *vec;
 -  char *kwlist[] = { "pbits", "qbits", "name", "event", "ievent",
 -                   "rng", "nsteps", 0 };
 +  static const char *const kwlist[] =
 +    { "pbits", "qbits", "name", "event", "ievent", "rng", "nsteps", 0 };
    mp *x = 0, **v = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|sO&O&O&O&:limlee", kwlist,
+   ie.exc = oe.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|sO&O&O&O&:limlee", KWLIST,
                                   convuint, &pl, convuint, &ql,
                                   &p, convpgev, &oe, convpgev, &ie,
                                   convgrand, &r, convuint, &on))
      goto end;
    if ((x = limlee(p, MP_NEW, MP_NEW, ql, pl, r, on,
-                 oe.proc, oe.ctx, ie.proc, ie.ctx, &nf, &v)) == 0)
-     PGENERR;
+                 oe.ev.proc, oe.ev.ctx, ie.ev.proc, ie.ev.ctx,
+                 &nf, &v)) == 0)
+     PGENERR(&exc);;
    vec = PyList_New(nf);
    for (i = 0; i < nf; i++)
      PyList_SetItem(vec, i, mp_pywrap(v[i]));
@@@ -1048,18 -1052,18 +1054,18 @@@ static PyMethodDef methods[] = 
    METH        (_PrimeFilter_smallfactor,      "smallfactor(X) -> PGRC")
    METH        (_RabinMiller_iters,            "iters(NBITS) -> NITERS")
    KWMETH(pgen,                                "\
- pgen(START, [name = 'p', stepper = PrimeGenStepper(2),\n\
-      tester = PrimeGenTester(), event = pgen_nullev,\n\
-      nsteps = 0, ntests = RabinMiller.iters(START.nbits)]) -> P")
+ pgen(START, [name = 'p'[, [stepper = PrimeGenStepper(2)],\n\
+      [tester = PrimeGenTester()], [event = pgen_nullev],\n\
+      [nsteps = 0], [ntests = RabinMiller.iters(START.nbits)]) -> P")
    KWMETH(strongprime_setup,           "\
- strongprime_setup(NBITS, [name = 'p', event = pgen_nullev,\n\
-                 rng = rand, nsteps = 0]) -> (START, JUMP)")
+ strongprime_setup(NBITS, [name = 'p'], [event = pgen_nullev],\n\
+                 [rng = rand], [nsteps = 0]) -> (START, JUMP)")
    KWMETH(strongprime,                 "\
- strongprime(NBITS, [name = 'p', event = pgen_nullev,\n\
-           rng = rand, nsteps = 0]) -> P")
+ strongprime(NBITS, [name = 'p'], [event = pgen_nullev],\n\
+           [rng = rand], [nsteps = 0]) -> P")
    KWMETH(limlee,                      "\
- limlee(PBITS, QBITS, [name = 'p', event = pgen_nullev,\n\
-        ievent = pgen_nullev, rng = rand, nsteps = 0]) -> (P, [Q, ...])")
+ limlee(PBITS, QBITS, [name = 'p'], [event = pgen_nullev],\n\
+        [ievent = pgen_nullev], [rng = rand], [nsteps = 0]) -> (P, [Q, ...])")
  #undef METHNAME
    { 0 }
  };
diff --combined pubkey.c
index 9c43ca723db0fd3e66bac162ec1c19822bbda098,0abb011b82d705329767b108874ee05aa5bbdcd1..5680429c1a410cd81ee5724078a9b8abcf5b99be
+++ b/pubkey.c
@@@ -96,9 -96,9 +96,9 @@@ static PyObject *dsapub_pynew(PyTypeObj
  {
    PyObject *G, *p, *rng = rand_pyobj, *hash = sha_pyobj;
    PyObject *rc = 0;
 -  char *kwlist[] = { "G", "p", "hash", "rng", 0 };
 +  static const char *const kwlist[] = { "G", "p", "hash", "rng", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   ge_pytype, &p,
                                   gchash_pytype, &hash,
@@@ -112,7 -112,7 +112,7 @@@ end
  static PyObject *dsameth_beginhash(PyObject *me, PyObject *arg)
  {
    if (!PyArg_ParseTuple(arg, ":beginhash")) return (0);
 -  return (ghash_pywrap(DSA_HASH(me), gdsa_beginhash(DSA_D(me)), f_freeme));
 +  return (ghash_pywrap(DSA_HASH(me), gdsa_beginhash(DSA_D(me))));
  }
  
  static PyObject *dsameth_endhash(PyObject *me, PyObject *arg)
@@@ -135,9 -135,9 +135,9 @@@ static PyObject *dsameth_sign(PyObject 
    Py_ssize_t n;
    mp *k = 0;
    PyObject *rc = 0;
 -  char *kwlist[] = { "msg", "k", 0 };
 +  static const char *const kwlist[] = { "msg", "k", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", KWLIST,
                                   &p, &n, convmp, &k))
      goto end;
    if (n != DSA_D(me)->h->hashsz)
@@@ -175,9 -175,9 +175,9 @@@ static PyObject *dsapriv_pynew(PyTypeOb
  {
    PyObject *G, *p = 0, *u, *rng = rand_pyobj, *hash = sha_pyobj;
    PyObject *rc = 0;
 -  char *kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
 +  static const char *const kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   &u,
                                   ge_pytype, &p,
@@@ -200,7 -200,7 +200,7 @@@ static PyMethodDef dsapub_pymethods[] 
  
  static PyMethodDef dsapriv_pymethods[] = {
  #define METHNAME(name) dsameth_##name
-   KWMETH(sign,                        "D.sign(MSG, k = K) -> R, S")
+   KWMETH(sign,                        "D.sign(MSG, [k = K]) -> R, S")
  #undef METHNAME
    { 0 }
  };
@@@ -247,7 -247,7 +247,7 @@@ static PyTypeObject dsapub_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"DSA public key information.",
 +"DSAPub(GROUP, P, [hash = sha], [rng = rand]): DSA public key.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -295,7 -295,7 +295,7 @@@ static PyTypeObject dsapriv_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"DSA private key information.",
 +"DSAPriv(GROUP, U, [p = u G], [hash = sha], [rng = rand]): DSA private key.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -323,9 -323,9 +323,9 @@@ static PyObject *kcdsapub_pynew(PyTypeO
  {
    PyObject *G, *p, *rng = rand_pyobj, *hash = has160_pyobj;
    PyObject *rc = 0;
 -  char *kwlist[] = { "G", "p", "hash", "rng", 0 };
 +  static const char *const kwlist[] = { "G", "p", "hash", "rng", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   ge_pytype, &p,
                                   gchash_pytype, &hash,
@@@ -348,9 -348,9 +348,9 @@@ static PyObject *kcdsapriv_pynew(PyType
  {
    PyObject *G, *u, *p = 0, *rng = rand_pyobj, *hash = has160_pyobj;
    PyObject *rc = 0;
 -  char *kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
 +  static const char *const kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   &u,
                                   ge_pytype, &p,
@@@ -366,7 -366,7 +366,7 @@@ end
  static PyObject *kcdsameth_beginhash(PyObject *me, PyObject *arg)
  {
    if (!PyArg_ParseTuple(arg, ":beginhash")) return (0);
 -  return (ghash_pywrap(DSA_HASH(me), gkcdsa_beginhash(DSA_D(me)), f_freeme));
 +  return (ghash_pywrap(DSA_HASH(me), gkcdsa_beginhash(DSA_D(me))));
  }
  
  static PyObject *kcdsameth_endhash(PyObject *me, PyObject *arg)
@@@ -389,9 -389,9 +389,9 @@@ static PyObject *kcdsameth_sign(PyObjec
    Py_ssize_t n;
    mp *k = 0;
    PyObject *r = 0, *rc = 0;
 -  char *kwlist[] = { "msg", "k", 0 };
 +  static const char *const kwlist[] = { "msg", "k", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", KWLIST,
                                   &p, &n, convmp, &k))
      goto end;
    if (n != DSA_D(me)->h->hashsz)
@@@ -437,7 -437,7 +437,7 @@@ static PyMethodDef kcdsapub_pymethods[
  
  static PyMethodDef kcdsapriv_pymethods[] = {
  #define METHNAME(name) kcdsameth_##name
-   KWMETH(sign,                        "D.sign(MSG, k = K) -> R, S")
+   KWMETH(sign,                        "D.sign(MSG, [k = K]) -> R, S")
  #undef METHNAME
    { 0 }
  };
@@@ -467,7 -467,7 +467,7 @@@ static PyTypeObject kcdsapub_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"KCDSA public key information.",
 +"KCDSAPub(GROUP, P, [hash = sha], [rng = rand]): KCDSA public key.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -515,7 -515,7 +515,7 @@@ static PyTypeObject kcdsapriv_pytype_sk
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"KCDSA private key information.",
 +"KCDSAPriv(GROUP, U, [p = u G], [hash = sha], [rng = rand]): KCDSA private key.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -569,9 -569,9 +569,9 @@@ static PyObject *rsapub_pynew(PyTypeObj
  {
    rsa_pub rp = { 0 };
    rsapub_pyobj *o;
 -  char *kwlist[] = { "n", "e", 0 };
 +  static const char *const kwlist[] = { "n", "e", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   convmp, &rp.n, convmp, &rp.e))
      goto end;
    if (!MP_ODDP(rp.n)) VALERR("RSA modulus must be even");
@@@ -636,10 -636,10 +636,10 @@@ static PyObject *rsapriv_pynew(PyTypeOb
  {
    rsa_priv rp = { 0 };
    PyObject *rng = Py_None;
 -  char *kwlist[] =
 +  static const char *const kwlist[] =
      { "n", "e", "d", "p", "q", "dp", "dq", "q_inv", "rng", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&O&O&O&O&O:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&O&O&O&O&O:new", KWLIST,
                                   convmp, &rp.n, convmp, &rp.e,
                                   convmp, &rp.d,
                                   convmp, &rp.p, convmp, &rp.q,
      goto end;
    if ((rp.n && !MP_ODDP(rp.n)) ||
        (rp.p && !MP_ODDP(rp.p)) ||
-       (rp.p && !MP_ODDP(rp.q)))
+       (rp.q && !MP_ODDP(rp.q)))
      VALERR("RSA modulus and factors must be odd");
    if (rsa_recover(&rp)) VALERR("couldn't construct private key");
    if (rng != Py_None && !GRAND_PYCHECK(rng))
@@@ -711,9 -711,9 +711,9 @@@ static PyObject *rsameth_privop(PyObjec
    PyObject *rng = RSA_RNG(me);
    mp *x = 0;
    PyObject *rc = 0;
 -  char *kwlist[] = { "x", "rng", 0 };
 +  static const char *const kwlist[] = { "x", "rng", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:privop", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:privop", KWLIST,
                                   convmp, &x, &rng))
      goto end;
    if (rng != Py_None && !GRAND_PYCHECK(rng))
@@@ -733,20 -733,21 +733,22 @@@ static PyObject *meth__RSAPriv_generate
    unsigned n = 0;
    rsa_priv rp;
    mp *e = 0;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
 -  char *kwlist[] = { "class", "nbits", "event", "rng", "nsteps", "e", 0 };
 +  static const char *const kwlist[] =
 +    { "class", "nbits", "event", "rng", "nsteps", "e", 0 };
    PyObject *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", KWLIST,
                                   &me, convuint, &nbits, convpgev, &evt,
                                   convgrand, &r, convuint, &n,
                                   convmp, &e))
      goto end;
    if (e) MP_COPY(e);
    else e = mp_fromulong(MP_NEW, 65537);
-   if (rsa_gen_e(&rp, nbits, e, r, n, evt.proc, evt.ctx))
-     PGENERR;
+   if (rsa_gen_e(&rp, nbits, e, r, n, evt.ev.proc, evt.ev.ctx))
+     PGENERR(&exc);
    rc = rsapriv_pywrap(&rp);
  end:
    droppgev(&evt);
@@@ -784,7 -785,7 +786,7 @@@ static PyGetSetDef rsapriv_pygetset[] 
  
  static PyMethodDef rsapriv_pymethods[] = {
  #define METHNAME(name) rsameth_##name
-   KWMETH(privop,              "R.privop(X, rng = None) -> X^D (mod N)")
+   KWMETH(privop,              "R.privop(X, [rng = None]) -> X^D (mod N)")
  #undef METHNAME
    { 0 }
  };
@@@ -814,7 -815,7 +816,7 @@@ static PyTypeObject rsapub_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"RSA public key information.",
 +"RSAPub(N, E): RSA public key.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -862,8 -863,7 +864,8 @@@ static PyTypeObject rsapriv_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"RSA private key information.",
 +"RSAPriv(..., [rng = rand]): RSA private key.\n\
 +  Keywords: n, e, d, p, q, dp, dq, q_inv; must provide enough",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -899,10 -899,10 +901,10 @@@ static PyObject *meth__p1crypt_encode(P
    octet *b = 0;
    size_t sz;
    mp *x;
 -  char *kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
 +  static const char *const kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
  
    p1.r = &rand_global; ep = 0; epsz = 0;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
      goto end;
@@@ -929,10 -929,10 +931,10 @@@ static PyObject *meth__p1crypt_decode(P
    octet *b = 0;
    size_t sz;
    mp *x = 0;
 -  char *kwlist[] = { "ct", "nbits", "ep", "rng", 0 };
 +  static const char *const kwlist[] = { "ct", "nbits", "ep", "rng", 0 };
  
    p1.r = &rand_global; ep = 0; epsz = 0;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|s#O&:decode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|s#O&:decode", KWLIST,
                                   convmp, &x, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
      goto end;
@@@ -960,10 -960,10 +962,10 @@@ static PyObject *meth__p1sig_encode(PyO
    octet *b = 0;
    size_t sz;
    mp *x;
 -  char *kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
 +  static const char *const kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
  
    p1.r = &rand_global; ep = 0; epsz = 0;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
      goto end;
@@@ -991,11 -991,10 +993,11 @@@ static PyObject *meth__p1sig_decode(PyO
    octet *b = 0;
    size_t sz;
    mp *x = 0;
 -  char *kwlist[] = { "msg", "sig", "nbits", "ep", "rng", 0 };
 +  static const char *const kwlist[] =
 +    { "msg", "sig", "nbits", "ep", "rng", 0 };
  
    p1.r = &rand_global; ep = 0; epsz = 0;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|s#O&:decode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|s#O&:decode", KWLIST,
                                   &hukairz, convmp, &x, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
      goto end;
@@@ -1023,11 -1022,10 +1025,11 @@@ static PyObject *meth__oaep_encode(PyOb
    octet *b = 0;
    size_t sz;
    mp *x;
 -  char *kwlist[] = { "msg", "nbits", "mgf", "hash", "ep", "rng", 0 };
 +  static const char *const kwlist[] =
 +    { "msg", "nbits", "mgf", "hash", "ep", "rng", 0 };
  
    o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha; ep = 0; epsz = 0;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&s#O&:encode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&s#O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   convgccipher, &o.cc,
                                   convgchash, &o.ch,
@@@ -1057,11 -1055,10 +1059,11 @@@ static PyObject *meth__oaep_decode(PyOb
    octet *b = 0;
    size_t sz;
    mp *x = 0;
 -  char *kwlist[] = { "ct", "nbits", "mgf", "hash", "ep", "rng", 0 };
 +  static const char *const kwlist[] =
 +    { "ct", "nbits", "mgf", "hash", "ep", "rng", 0 };
  
    o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha; ep = 0; epsz = 0;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&s#O&:decode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&s#O&:decode", KWLIST,
                                   convmp, &x, convulong, &nbits,
                                   convgccipher, &o.cc,
                                   convgchash, &o.ch,
@@@ -1092,11 -1089,10 +1094,11 @@@ static PyObject *meth__pss_encode(PyObj
    octet *b = 0;
    size_t sz;
    mp *x = 0;
 -  char *kwlist[] = { "msg", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
 +  static const char *const kwlist[] =
 +    { "msg", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
  
    p.cc = &sha_mgf; p.ch = &sha; p.r = &rand_global; p.ssz = (size_t)-1;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&O&O&:encode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&O&O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   convgccipher, &p.cc,
                                   convgchash, &p.ch,
@@@ -1126,11 -1122,11 +1128,11 @@@ static PyObject *meth__pss_decode(PyObj
    size_t sz;
    int n;
    mp *x = 0;
 -  char *kwlist[] =
 +  static const char *const kwlist[] =
      { "msg", "sig", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
  
    p.cc = &sha_mgf; p.ch = &sha; p.r = &rand_global; p.ssz = (size_t)-1;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&O&|O&O&O&O&:decode", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&O&|O&O&O&O&:decode", KWLIST,
                                   &m, &msz, convmp, &x, convulong, &nbits,
                                   convgccipher, &p.cc,
                                   convgchash, &p.ch,
@@@ -1206,11 -1202,10 +1208,11 @@@ XDHS(DEFXDH
      int ph = phdflt;                                                  \
      PyObject *rc = 0;                                                 \
      octet pp[ED##_PUBSZ];                                             \
 -    char *kwlist[] = { "key", "msg", "pub", "perso", "phflag", 0 };   \
 +    static const char *const kwlist[] =                                       \
 +      { "key", "msg", "pub", "perso", "phflag", 0 };                  \
      if (!PyArg_ParseTupleAndKeywords(arg, kw,                         \
                                     "s#s#|s#s#O&:" #ed "_sign",        \
 -                                   kwlist,                            \
 +                                   KWLIST,                            \
                                     &k, &ksz, &m, &msz, &p, &psz,      \
                                     &c, &csz, convbool, &ph))          \
        goto end;                                                               \
      Py_ssize_t psz, csz = 0, msz, ssz;                                        \
      int ph = phdflt;                                                  \
      PyObject *rc = 0;                                                 \
 -    char *kwlist[] = { "pub", "msg", "sig", "perso", "phflag", 0 };   \
 +    static const char *const kwlist[] =                                       \
 +      { "pub", "msg", "sig", "perso", "phflag", 0 };                  \
      if (!PyArg_ParseTupleAndKeywords(arg, kw,                         \
                                     "s#s#s#|s#O&:" #ed "_verify",      \
 -                                   kwlist,                            \
 +                                   KWLIST,                            \
                                     &p, &psz, &m, &msz, &s, &ssz,      \
                                     &c, &csz, convbool, &ph))          \
        goto end;                                                               \
@@@ -1269,7 -1263,7 +1271,7 @@@ static PyMethodDef methods[] = 
    KWMETH(_pss_encode,                 0)
    KWMETH(_pss_decode,                 0)
    KWMETH(_RSAPriv_generate,           "\
- generate(NBITS, [event = pgen_nullev, rng = rand, nsteps = 0]) -> R")
+ generate(NBITS, [event = pgen_nullev], [rng = rand], [nsteps = 0]) -> R")
  #define DEFMETH(X, x)                                                 \
    METH  (x,                           "\
  " #x "(KEY, PUBLIC) -> SHARED")
    METH  (ed##_pubkey,                 "\
  " #ed "_pubkey(KEY) -> PUBLIC")                                               \
    KWMETH(ed##_sign,                   "\
- " #ed "_sign(KEY, MSG, [pub = PUBLIC, "                                       \
-        "perso = STRING, phflag = BOOL]) -> SIG")                      \
+ " #ed "_sign(KEY, MSG, [pub = PUBLIC], "                              \
+        "[perso = STRING], [phflag = BOOL]) -> SIG")                   \
    KWMETH(ed##_verify,                 "\
  " #ed "_verify(PUBLIC, MSG, SIG, "                                    \
-        "[perso = STRINGphflag = BOOL]) -> BOOL")
+        "[perso = STRING], [phflag = BOOL]) -> BOOL")
    EDDSAS(DEFMETH)
  #undef DEFMETH
  #undef METHNAME
diff --combined rand.c
index 9afb271c86b26e17bf02e0ff5c6b01d4cf4419c8,1203f76c56d40a674f6ab84ffaba0f355d375053..d57795a86744eb95aae58f07e71787dbcf964bef
--- 1/rand.c
--- 2/rand.c
+++ b/rand.c
@@@ -124,9 -124,9 +124,9 @@@ static PyObject *grmeth_mp(PyObject *me
  {
    size_t l;
    mpw o = 0;
 -  char *kwlist[] = { "bits", "or", 0 };
 +  static const char *const kwlist[] = { "bits", "or", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", KWLIST,
                                   convszt, &l, convmpw, &o))
      goto end;
    if (grand_check(me)) return (0);
@@@ -214,10 -214,10 +214,10 @@@ end
  
  static PyObject *grmeth_seedrand(PyObject *me, PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { "rng", 0 };
 +  static const char *const kwlist[] = { "rng", 0 };
    grand *r = GRAND_R(me);
    grand *rr = &rand_global;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", KWLIST,
                                   convgrand, &rr) ||
        grand_check(me) || checkop(r, GRAND_SEEDRAND, "seedrand"))
      goto end;
@@@ -332,8 -332,8 +332,8 @@@ static PyTypeObject grand_pytype_skel 
  static PyObject *lcrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw)
  {
    uint32 n = 0;
 -  char *kwlist[] = { "seed", 0 };
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist, convu32, &n))
 +  static const char *const kwlist[] = { "seed", 0 };
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convu32, &n))
      return (0);
    return (grand_dopywrap(lcrand_pytype, lcrand_create(n), f_freeme));
  }
@@@ -363,7 -363,7 +363,7 @@@ static PyTypeObject lcrand_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Linear congruential generator.",
 +"LCRand([seed = 0]): linear congruential generator.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
  static PyObject *fibrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw)
  {
    uint32 n = 0;
 -  char *kwlist[] = { "seed", 0 };
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist, convu32, &n))
 +  static const char *const kwlist[] = { "seed", 0 };
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convu32, &n))
      return (0);
    return (grand_dopywrap(fibrand_pytype, fibrand_create(n), f_freeme));
  }
@@@ -420,7 -420,7 +420,7 @@@ static PyTypeObject fibrand_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Fibonacci generator.",
 +"FibRand([seed = 0]): Fibonacci generator.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -503,10 -503,10 +503,10 @@@ static PyObject *trmeth_timer(PyObject 
  static PyObject *truerand_pynew(PyTypeObject *ty,
                                PyObject *arg, PyObject *kw)
  {
 -  char *kwlist[] = { 0 };
 +  static const char *const kwlist[] = { 0 };
    grand *r;
    PyObject *rc = 0;
-   if (PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist)) goto end;
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
    r = rand_create();
    r->ops->misc(r, RAND_NOISESRC, &noise_source);
    r->ops->misc(r, RAND_SEED, 160);
@@@ -565,7 -565,7 +565,7 @@@ static PyTypeObject truerand_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"True random number source.",
 +"TrueRand(): true random number source.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -639,11 -639,11 +639,11 @@@ static const gccrand_info *const gcrand
  static PyObject *gcrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
    const gccrand_info *info = GCCRAND_INFO(ty);
 -  static char *kwlist[] = { "key", 0 };
 +  static const char *const kwlist[] = { "key", 0 };
    char *k;
    Py_ssize_t n;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &n))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &n))
      goto end;
    if (keysz(n, info->keysz) != n) VALERR("bad key length");
    return (grand_dopywrap(ty, info->func(k, n), f_freeme));
@@@ -655,11 -655,11 +655,11 @@@ static PyObject *gcirand_pynew(PyTypeOb
  {
    const gccrand_info *info = GCCRAND_INFO(ty);
    uint32 i = 0;
 -  static char *kwlist[] = { "key", "i", 0 };
 +  static const char *const kwlist[] = { "key", "i", 0 };
    char *k;
    Py_ssize_t n;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&:new", KWLIST,
                                   &k, &n, convu32, &i))
      goto end;
    if (keysz(n, info->keysz) != n) VALERR("bad key length");
@@@ -673,11 -673,11 +673,11 @@@ end
  static PyObject *gcnrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
  {
    const gccrand_info *info = GCCRAND_INFO(ty);
 -  static char *kwlist[] = { "key", "nonce", 0 };
 +  static const char *const kwlist[] = { "key", "nonce", 0 };
    char *k, *n;
    Py_ssize_t ksz, nsz;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#:new", KWLIST,
                                   &k, &ksz, &n, &nsz))
      goto end;
    if (keysz(ksz, info->keysz) != ksz) VALERR("bad key length");
@@@ -693,18 -693,15 +693,18 @@@ static PyObject *gcshakyrand_pynew(PyTy
                                   PyObject *arg, PyObject *kw)
  {
    const gccrand_info *info = GCCRAND_INFO(ty);
 -  static char *kwlist_shake[] = { "key", "func", "perso", 0 };
 -  static char *kwlist_func[] = { "key", "perso", 0 };
 +  static const char
 +    *const kwlist_shake[] = { "key", "func", "perso", 0 },
 +    *const kwlist_func[] = { "key", "perso", 0 };
    char *k, *f = 0, *p = 0;
    Py_ssize_t ksz, fsz = 0, psz = 0;
  
    if ((info->f&RNGF_MASK) == RNG_SHAKE
 -      ? !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#s#:new", kwlist_shake,
 +      ? !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#s#:new",
 +                                     (/*unconst*/ char **)kwlist_shake,
                                       &k, &ksz, &f, &fsz, &p, &psz)
 -      : !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#:new", kwlist_func,
 +      : !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#:new",
 +                                     (/*unconst*/ char **)kwlist_func,
                                       &k, &ksz, &p, &psz))
      goto end;
    if (keysz(ksz, info->keysz) != ksz) VALERR("bad key length");
@@@ -941,9 -938,9 +941,9 @@@ static PyObject *sslprf_pynew(PyTypeObj
    int ksz, ssz;
    const gchash *hco = &md5, *hci = &sha;
    PyObject *rc = 0;
 -  char *kwlist[] = { "key", "seed", "ohash", "ihash", 0 };
 +  static const char *const kwlist[] = { "key", "seed", "ohash", "ihash", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", KWLIST,
                                   &k, &ksz, &s, &ssz,
                                   convgchash, &hco, convgchash, &hci))
      goto end;
@@@ -958,9 -955,9 +958,9 @@@ static PyObject *tlsdx_pynew(PyTypeObje
    int ksz, ssz;
    const gcmac *mc = &sha_hmac;
    PyObject *rc = 0;
 -  char *kwlist[] = { "key", "seed", "mac", 0 };
 +  static const char *const kwlist[] = { "key", "seed", "mac", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&:new", KWLIST,
                                   &k, &ksz, &s, &ssz,
                                   convgcmac, &mc))
      goto end;
@@@ -975,9 -972,9 +975,9 @@@ static PyObject *tlsprf_pynew(PyTypeObj
    int ksz, ssz;
    const gcmac *mcl = &md5_hmac, *mcr = &sha_hmac;
    PyObject *rc = 0;
 -  char *kwlist[] = { "key", "seed", "lmac", "rmac", 0 };
 +  static const char *const kwlist[] = { "key", "seed", "lmac", "rmac", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", KWLIST,
                                   &k, &ksz, &s, &ssz,
                                   convgcmac, &mcl, convgcmac, &mcr))
      goto end;
@@@ -1011,8 -1008,7 +1011,8 @@@ static PyTypeObject sslprf_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Random number generator for SSL master secret.",
 +"SSLRand(KEY, SEED, [ohash = md5], [ihash = sha]):\n\
 +  RNG for SSL master secret.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1060,8 -1056,7 +1060,8 @@@ static PyTypeObject tlsdx_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"TLS data expansion function.",
 +"TLSDataExpansion(KEY, SEED, [mac = sha_hmac]):\n\
 +  TLS data expansion function.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1109,8 -1104,7 +1109,8 @@@ static PyTypeObject tlsprf_pytype_skel 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"TLS pseudorandom function.",
 +"TLSPRF(KEY, SEED, [lmac = md5_hmac], [rmac = sha_hmac]):\n\
 +  TLS pseudorandom function.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1140,9 -1134,9 +1140,9 @@@ static PyObject *dsarand_pynew(PyTypeOb
    char *p;
    int sz;
    PyObject *rc = 0;
 -  char *kwlist[] = { "seed", 0 };
 +  static const char *const kwlist[] = { "seed", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &sz))
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &p, &sz))
      goto end;
    rc = grand_dopywrap(ty, dsarand_create(p, sz), f_freeme);
  end:
@@@ -1190,7 -1184,7 +1190,7 @@@ static PyTypeObject dsarand_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Pseudorandom number generator for constructing DSA parameters.",
 +"DSARand(SEED): pseudorandom number generator for DSA parameters.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1219,9 -1213,9 +1219,9 @@@ static PyObject *bbs_pynew(PyTypeObjec
  {
    mp *n = 0, *x = MP_TWO;
    PyObject *rc = 0;
 -  char *kwlist[] = { "n", "x", 0 };
 +  static const char *const kwlist[] = { "n", "x", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
                                   convmp, &n, convmp, &x))
      goto end;
    rc = grand_dopywrap(ty, bbs_rand(n, x), f_freeme);
@@@ -1267,9 -1261,8 +1267,9 @@@ static PyObject *bbsget_x(PyObject *me
  
  static int bbsset_x(PyObject *me, PyObject *val, void *hunoz)
  {
-   mp *x = 0; grand *r = GRAND_R(me); int rc = -1; if (!x) NIERR("__del__");
+   mp *x = 0; grand *r = GRAND_R(me); int rc = -1; if (!val) NIERR("__del__");
 -  if ((x = getmp(val)) == 0) goto end; r->ops->misc(r, BBS_SET, x); rc = 0;
 +  if ((x = getmp(val)) == 0) goto end;
 +  r->ops->misc(r, BBS_SET, x); rc = 0;
    end: mp_drop(x); return (rc);
  }
  
@@@ -1322,7 -1315,7 +1322,7 @@@ static PyTypeObject bbs_pytype_skel = 
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Blum-Blum-Shub strong pseudorandom number generator.",
 +"BlumBlumShub(N, [x = 2]): Blum-Blum-Shub pseudorandom number generator.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -1357,9 -1350,9 +1357,9 @@@ static PyObject *bbspriv_pynew(PyTypeOb
  {
    mp *p = 0, *q = 0, *n = 0, *x = MP_TWO;
    bbspriv_pyobj *rc = 0;
 -  char *kwlist[] = { "n", "p", "q", "seed", 0 };
 +  static const char *const kwlist[] = { "n", "p", "q", "seed", 0 };
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&:new", kwlist,
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&:new", KWLIST,
                                   convmp, &n, convmp, &p, convmp, &q,
                                   convmp, &x))
      goto end;
@@@ -1383,19 -1376,20 +1383,21 @@@ static PyObject *meth__BBSPriv_generate
  {
    bbs_priv bp = { 0 };
    mp *x = MP_TWO;
-   pgev evt = { 0 };
+   struct excinfo exc = EXCINFO_INIT;
+   pypgev evt = { { 0 } };
    unsigned nbits, n = 0;
    grand *r = &rand_global;
 -  char *kwlist[] = { "class", "nbits", "event", "rng", "nsteps", "seed", 0 };
 +  static const char *const kwlist[] =
 +    { "class", "nbits", "event", "rng", "nsteps", "seed", 0 };
    bbspriv_pyobj *rc = 0;
  
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
+   evt.exc = &exc;
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", KWLIST,
                                   &me, convuint, &nbits, convpgev, &evt,
                                   convgrand, &r, convuint, &n, convmp, &x))
      goto end;
-   if (bbs_gen(&bp, nbits, r, n, evt.proc, evt.ctx))
-     VALERR("prime genration failed");
+   if (bbs_gen(&bp, nbits, r, n, evt.ev.proc, evt.ev.ctx))
+     PGENERR(&exc);
    rc = PyObject_New(bbspriv_pyobj, bbspriv_pytype);
    rc->gr.r = bbs_rand(bp.n, x);
    rc->gr.f = f_freeme;
@@@ -1481,8 -1475,7 +1483,8 @@@ static PyTypeObject bbspriv_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
 -"Blum-Blum-Shub strong pseudorandom generator, with private key.",
 +"BBSPriv(..., seed = 2]): Blum-Blum-Shub, with private key.\n\
 +  Keywords: n, p, q; must provide at least two",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
  static PyMethodDef methods[] = {
  #define METHNAME(name) meth_##name
    KWMETH(_BBSPriv_generate,           "\
- generate(NBITS, [event = pgen_nullev, rng = rand, nsteps = 0, seed = 2])")
+ generate(NBITS, [event = pgen_nullev], [rng = rand],\n\
+        [nsteps = 0], [seed = 2]) -> R")
  #undef METHNAME
    { 0 }
  };
diff --combined setup.py
index efa922b899ea3f731901dcd5714ae009567168c1,80a154772a0504041c2e2793d3588c25d69e03d0..d7781775a5d3e8f63594f13bf38f8a6183040421
+++ b/setup.py
@@@ -3,8 -3,8 +3,8 @@@
  import distutils.core as DC
  import mdwsetup as MS
  
 -MS.pkg_config('catacomb', '2.3.0.1+4')
 -MS.pkg_config('mLib', '2.0.4')
 +MS.pkg_config('catacomb', '2.5.0')
 +MS.pkg_config('mLib', '2.2.2.1')
  
  cat = DC.Extension('catacomb._base',
                     ['catacomb.c', 'bytestring.c', 'buffer.c',
@@@ -23,7 -23,11 +23,12 @@@ MS.setup(name = 'catacomb-python'
           author_email = 'mdw@distorted.org.uk',
           license = 'GNU General Public License',
           packages = ['catacomb'],
 -         scripts = ['pwsafe'],
 +         scripts = ['pock', 'pwsafe'],
 +         data_files = [('share/man/man1', ['pock.1', 'pwsafe.1'])],
           genfiles = [MS.Generate('algorithms.h')],
+          unittest_dir = "t",
+          unittests = ["t-misc", "t-algorithms", "t-bytes", "t-buffer",
+                       "t-convert", "t-ec", "t-field", "t-group", "t-key",
+                       "t-mp", "t-passphrase", "t-pgen", "t-pubkey",
+                       "t-rand", "t-rat", "t-share"],
           ext_modules = [cat])
diff --combined util.c
index 2e6ee258db18d622c549bd209bf95d9fc2143ebc,29f7d123269e934db11843d5db75498a4275ac1b..723c819cc82dd3e8daaa39b58085a7c965d449a4
--- 1/util.c
--- 2/util.c
+++ b/util.c
  
  #include "catacomb-python.h"
  
+ /* #undef HAVE_LONG_LONG */
  /*----- External values ---------------------------------------------------*/
  
  static PyObject *modname = 0;
+ PyObject *home_module = 0;
  
  /*----- Conversions -------------------------------------------------------*/
  
@@@ -42,12 -45,17 +45,17 @@@ PyObject *getulong(unsigned long w
      return (PyLong_FromUnsignedLong(w));
  }
  
+ #ifndef HAVE_LONG_LONG
  static PyObject *i32 = 0;
  static int init_i32(void)
    { if (!i32 && (i32 = PyInt_FromLong(32)) == 0) return (-1); return (0); }
+ #endif
  
  PyObject *getk64(kludge64 u)
  {
+ #ifdef HAVE_LONG_LONG
+   return (PyLong_FromUnsignedLongLong(GET64(unsigned PY_LONG_LONG, u)));
+ #else
    PyObject *i = 0, *j = 0, *t;
    PyObject *rc = 0;
  
@@@ -63,6 -71,7 +71,7 @@@ end
    if (i) Py_DECREF(i);
    if (j) Py_DECREF(j);
    return (rc);
+ #endif
  }
  
  PyObject *getbool(int b)
@@@ -99,15 -108,32 +108,32 @@@ end
    return (0);
  }
  
+ #ifdef HAVE_UINT64
+ #  define CONVu64(n) do {                                             \
+      kludge64 k;                                                      \
+      uint64 t;                                                                \
+      if (!convk64(o, &k)) goto end;                                   \
+      t = GET64(uint64, k);                                            \
+      if (t > MASK##n) VALERR("out of range");                         \
+      *p = t;                                                          \
+    } while (0)
+ #else
+ #  define CONVu64(n) assert(!"shouldn't be possible")
+ #endif
  #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) VALERR("out of range");                          \
-     *p = u;                                                           \
+     if (MASK##n > ULONG_MAX)                                          \
+       CONVu64(n);                                                     \
+     else {                                                            \
+       if (!convulong(o, &u)) goto end;                                        \
+       if (u > MASK##n) VALERR("out of range");                                \
+       *p = u;                                                         \
+     }                                                                 \
      return (1);                                                               \
    end:                                                                        \
      return (0);                                                               \
@@@ -129,11 -155,22 +155,22 @@@ end
  
  int convk64(PyObject *o, void *pp)
  {
-   PyObject *i = 0, *t;
+   PyObject *i = 0;
    int rc = 0;
+ #if HAVE_LONG_LONG
+   unsigned PY_LONG_LONG t;
+ #else
+   PyObject *t;
    uint32 lo, hi;
+ #endif
  
    if (!o) VALERR("can't delete");
+ #if HAVE_LONG_LONG
+   if ((i = PyNumber_Long(o)) == 0) goto end;
+   t = PyLong_AsUnsignedLongLong(i);
+   if (t == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) goto end;
+   ASSIGN64(*(kludge64 *)pp, t);
+ #else
    if (init_i32()) goto end;
    if ((i = PyNumber_Int(o)) == 0) goto end;
    lo = PyInt_AsUnsignedLongMask(i);
    Py_DECREF(i); i = t;
    if (PyObject_IsTrue(i)) VALERR("out of range");
    SET64(*(kludge64 *)pp, hi, lo);
+ #endif
    rc = 1;
  end:
    if (i) Py_DECREF(i);
    return (rc);
@@@ -241,14 -280,14 +280,14 @@@ PyTypeObject *inittype(PyTypeObject *ty
  void setconstants(PyObject *mod, const struct nameval *c)
  {
    PyObject *x;
+   unsigned long u;
  
    while (c->name) {
-     if (c->value > LONG_MAX)
-       x = PyLong_FromUnsignedLong(c->value);
-     else
-       x = PyInt_FromLong(c->value);
-     PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x);
-     c++;
+     u = c->value;
+     if (u <= LONG_MAX) x = PyInt_FromLong(u);
+     else if (c->f&CF_SIGNED) x = PyInt_FromLong(-1 - (long)(ULONG_MAX - u));
+     else x = PyLong_FromUnsignedLong(u);
+     PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x); c++;
    }
  }
  
@@@ -285,13 -324,7 +324,7 @@@ PyObject *mkexc(PyObject *mod, PyObjec
    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 ((dict = PyDict_New()) == 0) goto fail;
  
    if (mm) {
      while (mm->ml_name) {
      }
    }
  
+   if ((nameobj = PyString_FromFormat("%s.%s",
+                                    PyModule_GetName(mod),
+                                    name)) == 0 ||
+       (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
+                               base, dict)) == 0)
+     goto fail;
  done:
    Py_XDECREF(nameobj);
    Py_XDECREF(dict);
@@@ -318,6 -358,101 +358,101 @@@ fail
    goto done;
  }
  
+ void report_lost_exception_v(struct excinfo *exc,
+                            const char *why, va_list ap)
+ {
+   PyObject *hookfn = 0;
+   PyObject *whyobj = 0;
+   PyObject *obj = 0;
+   /* Make sure we start out without a pending exception, or this will get
+    * really confusing.
+    */
+   assert(!PyErr_Occurred());
+   /* Format the explanation. */
+   if (why) whyobj = PyString_FromFormatV(why, ap);
+   else { whyobj = Py_None; Py_INCREF(whyobj); }
+   /* Find our home module's `lostexchook' function.  This won't work if
+    * there's no module, or the function isn't defined, or it's `None'.
+    */
+   if (!home_module) goto sys;
+   hookfn = PyObject_GetAttrString(home_module, "lostexchook");
+   if (hookfn == Py_None) goto sys;
+   else if (hookfn) ;
+   else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto ouch;
+   else { PyErr_Clear(); goto sys; }
+   /* Call the hook function. */
+   obj = PyObject_CallFunction(hookfn, "(OOOO)",
+                             whyobj, exc->ty, exc->val, exc->tb);
+   if (!obj) goto ouch;
+   goto end;
+   /* Something went wrong reporting the problem. */
+ ouch:
+   PySys_WriteStderr("\n!!! FAILURE REPORTING LOST EXCEPTION\n");
+   PyErr_Print();
+   /* drop through... */
+   /* There was no hook, so try to do something sensible using
+    * `sys.excepthook'.
+    */
+ sys:
+   PySys_WriteStderr("\n!!! LOST EXCEPTION: %s\n",
+                   PyString_AS_STRING(whyobj));
+   RESTORE_EXCINFO(exc);
+   PyErr_Print();
+   /* drop through... */
+   /* Clean up afterwards. */
+ end:
+   Py_XDECREF(hookfn);
+   Py_XDECREF(whyobj);
+   Py_XDECREF(obj);
+ }
+ void report_lost_exception(struct excinfo *exc, const char *why, ...)
+ {
+   va_list ap;
+   va_start(ap, why);
+   report_lost_exception_v(exc, why, ap);
+   va_end(ap);
+ }
+ void stash_exception(struct excinfo *exc, const char *why, ...)
+ {
+   va_list ap;
+   struct excinfo stash;
+   if (!exc->ty)
+     STASH_EXCINFO(exc);
+   else {
+     va_start(ap, why);
+     STASH_EXCINFO(&stash);
+     report_lost_exception_v(&stash, why, ap);
+     va_end(ap);
+   }
+ }
+ void restore_exception(struct excinfo *exc, const char *why, ...)
+ {
+   va_list ap;
+   struct excinfo stash;
+   if (!PyErr_Occurred())
+     RESTORE_EXCINFO(exc);
+   else {
+     va_start(ap, why);
+     STASH_EXCINFO(&stash);
+     report_lost_exception_v(exc, why, ap);
+     RESTORE_EXCINFO(&stash);
+     va_end(ap);
+   }
+ }
  /*----- Generic dictionary methods ----------------------------------------*/
  
  static PyTypeObject *itemiter_pytype, *valiter_pytype;
@@@ -369,7 -504,7 +504,7 @@@ static PyTypeObject itemiter_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
- "Iterates over the items of a mapping.",
+ "Iterates over the keys of a mapping.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -427,7 -562,7 +562,7 @@@ static PyTypeObject valiter_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
- "Iterates over the items of a mapping.",
+ "Iterates over the values of a mapping.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -466,8 -601,7 +601,7 @@@ PySequenceMethods gmap_pysequence = 
  Py_ssize_t gmap_pysize(PyObject *me)
  {
    PyObject *i = 0, *x = 0;
-   int rc = -1;
-   int n = 0;
+   Py_ssize_t rc = -1, n = 0;
  
    if ((i = PyObject_GetIter(me)) == 0) goto done;
    while ((x = PyIter_Next(i)) != 0) { n++; Py_DECREF(x); x = 0; }
@@@ -603,15 -737,13 +737,15 @@@ end
    return (rc);
  }
  
 -static char *def_kwlist[] = { "key", "default", 0 };
 +static const char *const def_kwlist[] = { "key", "default", 0 };
  
  PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
  {
    PyObject *k, *def = Py_None, *v;
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:get",
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", def_kwlist, &k, &def))
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get",
 +                                 (/*unconst*/ char **)def_kwlist,
 +                                 &k, &def))
      return (0);
    if ((v = PyObject_GetItem(me, k)) != 0) return (v);
    PyErr_Clear();
@@@ -622,9 -754,8 +756,9 @@@ PyObject *gmapmeth_setdefault(PyObject 
  {
    PyObject *k, *def = Py_None, *v;
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:setdefault",
+   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
 -                                 def_kwlist, &k, &def))
 +                                 (/*unconst*/ char **)def_kwlist,
 +                                 &k, &def))
      return (0);
    if ((v = PyObject_GetItem(me, k)) != 0) return (v);
    PyErr_Clear();
@@@ -636,16 -767,16 +770,18 @@@ PyObject *gmapmeth_pop(PyObject *me, Py
  {
    PyObject *k, *def = 0, *v;
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:pop",
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", def_kwlist, &k, &def))
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop",
 +                                 (/*unconst*/ char **)def_kwlist,
 +                                 &k, &def))
      return (0);
    if ((v = PyObject_GetItem(me, k)) != 0) {
      PyObject_DelItem(me, k);
      return (v);
-   }
-   PyErr_Clear();
-   RETURN_OBJ(def);
+   } else if (def) {
+     PyErr_Clear();
+     RETURN_OBJ(def);
+   } else
+     return (0);
  }
  
  PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
@@@ -675,7 -806,7 +811,7 @@@ PyObject *gmapmeth_popitem(PyObject *me
    PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
  
    if (!PyArg_ParseTuple(arg, ":popitem") ||
-       (i = PyObject_GetIter(me)))
+       (i = PyObject_GetIter(me)) == 0)
      goto end;
    if ((k = PyIter_Next(i)) == 0) {
      if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
@@@ -697,11 -828,29 +833,29 @@@ PyMethodDef gmap_pymethods[] = 
  
  /*----- Initialization ----------------------------------------------------*/
  
+ static PyObject *meth__set_home_module(PyObject *me, PyObject *arg)
+ {
+   PyObject *mod;
+   if (!PyArg_ParseTuple(arg, "O!:_set_home_module", &PyModule_Type, &mod))
+     return (0);
+   Py_XDECREF(home_module); home_module = mod; Py_INCREF(home_module);
+   RETURN_NONE;
+ }
+ static const PyMethodDef methods[] = {
+ #define METHNAME(func) meth_##func
+   METH        (_set_home_module,      "_set_home_module(MOD)")
+ #undef METHNAME
+   { 0 }
+ };
  void util_pyinit(void)
  {
    modname = PyString_FromString("catacomb");
    INITTYPE(itemiter, root);
    INITTYPE(valiter, root);
+   addmethods(methods);
  }
  
  void util_pyinsert(PyObject *mod)