chiark / gitweb /
Merge branch '1.1.x'
authorMark Wooding <mdw@distorted.org.uk>
Sun, 14 May 2017 03:28:02 +0000 (04:28 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 14 May 2017 03:28:02 +0000 (04:28 +0100)
* 1.1.x:
  Release 1.1.2.
  catacomb/__init__.py: Fix up cipher etc. names better.
  algorithms.c: Support the new 16-bit key-size descriptors.
  group.c: Track Catacomb group internals change.
  utils.c: Raise exceptions from `convTHING' with null arguments.
  Return `long' objects when `int' is requested but the value won't fit.
  bytestring.c: Check for cached hash more carefully.
  rand.c: Careful range checking on `block' and `mp'.
  *.c: Fix docstrings for methods.
  Further fixing to use `Py_ssize_t' in place of int.

Conflicts:
debian/control (already wanted later catacomb-dev)
group.c (no need for compatibility with older Catacombs)

1  2 
algorithms.c
catacomb/__init__.py
util.c

diff --combined algorithms.c
index 5b4f14b0e7d982922a8f0145efd1246c0269ada6,3a618d02a49308eea449b95e6708f83cd6006962..5703901fae129b68ea01e366884c6cbe94ff8e63
@@@ -35,21 -35,31 +35,31 @@@ PyTypeObject *keysz_pytype
  PyTypeObject *keyszany_pytype, *keyszrange_pytype, *keyszset_pytype;
  PyObject *sha_pyobj, *has160_pyobj;
  
+ #ifndef KSZ_OPMASK
+ #  define KSZ_OPMASK 0x1f
+ #endif
+ #ifndef KSZ_16BIT
+ #  define KSZ_16BIT 0x20
+ #endif
  PyObject *keysz_pywrap(const octet *k)
  {
-   switch (k[0]) {
+   unsigned op = *k++;
+ #define ARG(i) (op&KSZ_16BIT ? LOAD16(k + 2*(i)) : k[i])
+   switch (op&KSZ_OPMASK) {
      case KSZ_ANY: {
        keysz_pyobj *o = PyObject_New(keysz_pyobj, keyszany_pytype);
-       o->dfl = k[1];
+       o->dfl = ARG(0);
        return ((PyObject *)o);
      } break;
      case KSZ_RANGE: {
        keyszrange_pyobj *o =
        PyObject_New(keyszrange_pyobj, keyszrange_pytype);
-       o->dfl = k[1];
-       o->min = k[2];
-       o->max = k[3];
-       o->mod = k[4];
+       o->dfl = ARG(0);
+       o->min = ARG(1);
+       o->max = ARG(2);
+       o->mod = ARG(3);
        if (!o->mod) o->mod = 1;
        return ((PyObject *)o);
      } break;
        keyszset_pyobj *o =
        PyObject_New(keyszset_pyobj, keyszset_pytype);
        int i, n;
-       o->dfl = k[1];
-       for (i = 0; k[i + 1]; i++) ;
+       o->dfl = ARG(0);
+       for (i = 0; ARG(i); i++) ;
        n = i; o->set = PyTuple_New(n);
        for (i = 0; i < n; i++)
-       PyTuple_SET_ITEM(o->set, i, PyInt_FromLong(k[i + 1]));
+       PyTuple_SET_ITEM(o->set, i, PyInt_FromLong(ARG(i)));
        return ((PyObject *)o);
      } break;
      default:
        abort();
    }
+ #undef ARG
  }
  
  static PyObject *keyszany_pynew(PyTypeObject *ty,
@@@ -462,7 -473,7 +473,7 @@@ static PyObject *gcipher_pynew(PyTypeOb
  {
    char *kwlist[] = { "k", 0 };
    char *k;
 -  int sz;
 +  Py_ssize_t sz;
  
    if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
      goto end;
@@@ -511,7 -522,7 +522,7 @@@ static PyObject *gccget_blksz(PyObject 
  static PyObject *gcmeth_encrypt(PyObject *me, PyObject *arg)
  {
    char *p;
 -  int sz;
 +  Py_ssize_t sz;
    PyObject *rc = 0;
  
    if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &sz)) return (0);
@@@ -537,7 -548,7 +548,7 @@@ static PyObject *gcmeth_enczero(PyObjec
  static PyObject *gcmeth_decrypt(PyObject *me, PyObject *arg)
  {
    char *p;
 -  int sz;
 +  Py_ssize_t sz;
    PyObject *rc = 0;
  
    if (!PyArg_ParseTuple(arg, "s#:decrypt", &p, &sz)) return (0);
@@@ -563,10 -574,9 +574,10 @@@ static PyObject *gcmeth_deczero(PyObjec
  static PyObject *gcmeth_setiv(PyObject *me, PyObject *arg)
  {
    char *p;
 -  int sz;
 +  Py_ssize_t sz;
  
    if (!PyArg_ParseTuple(arg, "s#:setiv", &p, &sz)) goto end;
 +  if (!GCIPHER_C(me)->ops->setiv) VALERR("`setiv' not supported");
    if (!GC_CLASS(GCIPHER_C(me))->blksz) VALERR("not a block cipher mode");
    if (sz != GC_CLASS(GCIPHER_C(me))->blksz) VALERR("bad IV length");
    GC_SETIV(GCIPHER_C(me), p);
@@@ -578,7 -588,6 +589,7 @@@ end
  static PyObject *gcmeth_bdry(PyObject *me, PyObject *arg)
  {
    if (!PyArg_ParseTuple(arg, ":bdry")) goto end;
 +  if (!GCIPHER_C(me)->ops->bdry) VALERR("`bdry' not supported");
    if (!GC_CLASS(GCIPHER_C(me))->blksz) VALERR("not a block cipher mode");
    GC_BDRY(GCIPHER_C(me));
    RETURN_ME;
@@@ -768,12 -777,33 +779,12 @@@ static PyObject *gchget_bufsz(PyObject 
  static PyObject *ghmeth_hash(PyObject *me, PyObject *arg)
  {
    char *p;
 -  int sz;
 +  Py_ssize_t sz;
    if (!PyArg_ParseTuple(arg, "s#:hash", &p, &sz)) return (0);
    GH_HASH(GHASH_H(me), p, sz);
    RETURN_ME;
  }
  
 -static PyObject *ghmeth_done(PyObject *me, PyObject *arg)
 -{
 -  ghash *g;
 -  PyObject *rc;
 -  if (!PyArg_ParseTuple(arg, ":done")) return (0);
 -  g = GH_COPY(GHASH_H(me));
 -  rc = bytestring_pywrap(0, g->ops->c->hashsz);
 -  GH_DONE(g, PyString_AS_STRING(rc));
 -  GH_DESTROY(g);
 -  return (rc);
 -}
 -
 -static PyGetSetDef gchash_pygetset[] = {
 -#define GETSETNAME(op, name) gch##op##_##name
 -  GET (bufsz,                 "CH.bufsz -> hash buffer size, or zero")
 -  GET (hashsz,                "CH.blksz -> hash output size")
 -  GET (name,                  "CH.name -> name of this kind of hash")
 -#undef GETSETNAME
 -  { 0 }
 -};
 -
  #define GHMETH_HASHU_(n, W, w)                                                \
    static PyObject *ghmeth_hashu##w(PyObject *me, PyObject *arg)               \
    {                                                                   \
@@@ -790,7 -820,7 +801,7 @@@ DOUINTCONV(GHMETH_HASHU_
    static PyObject *ghmeth_hashbuf##w(PyObject *me, PyObject *arg)     \
    {                                                                   \
      char *p;                                                          \
 -    int sz;                                                           \
 +    Py_ssize_t sz;                                                    \
      if (!PyArg_ParseTuple(arg, "s#:hashbuf" #w, &p, &sz)) goto end;   \
      if (sz > MASK##n) TYERR("string too long");                               \
      GH_HASHBUF##W(GHASH_H(me), p, sz);                                        \
@@@ -808,36 -838,13 +819,36 @@@ static PyObject *ghmeth_hashstrz(PyObje
    RETURN_ME;
  }
  
 +static PyObject *ghmeth_done(PyObject *me, PyObject *arg)
 +{
 +  ghash *g;
 +  PyObject *rc;
 +  if (!PyArg_ParseTuple(arg, ":done")) return (0);
 +  g = GH_COPY(GHASH_H(me));
 +  rc = bytestring_pywrap(0, g->ops->c->hashsz);
 +  GH_DONE(g, PyString_AS_STRING(rc));
 +  GH_DESTROY(g);
 +  return (rc);
 +}
 +
 +static PyGetSetDef gchash_pygetset[] = {
 +#define GETSETNAME(op, name) gch##op##_##name
 +  GET (bufsz,                 "CH.bufsz -> hash buffer size, or zero")
 +  GET (hashsz,                "CH.hashsz -> hash output size")
 +  GET (name,                  "CH.name -> name of this kind of hash")
 +#undef GETSETNAME
 +  { 0 }
 +};
 +
  static PyMethodDef ghash_pymethods[] = {
  #define METHNAME(name) ghmeth_##name
    METH        (hash,                  "H.hash(M)")
  #define METHU_(n, W, w) METH(hashu##w, "H.hashu" #w "(WORD)")
    DOUINTCONV(METHU_)
 +#undef METHU_
  #define METHBUF_(n, W, w) METH(hashbuf##w, "H.hashbuf" #w "(BYTES)")
    DOUINTCONV(METHBUF_)
 +#undef METHBUF_
    METH        (hashstrz,              "H.hashstrz(STRING)")
    METH        (done,                  "H.done() -> HASH")
  #undef METHNAME
@@@ -952,7 -959,7 +963,7 @@@ static PyObject *gmac_pynew(PyTypeObjec
  {
    char *kwlist[] = { "k", 0 };
    char *k;
 -  int sz;
 +  Py_ssize_t sz;
  
    if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
      goto end;
@@@ -1000,7 -1007,6 +1011,7 @@@ PyObject *gmac_pywrap(PyObject *cobj, g
    if (!cobj) cobj = gcmac_pywrap((/*unconst*/ gcmac *)GM_CLASS(m));
    else Py_INCREF(cobj);
    g = newtype((PyTypeObject *)cobj, 0, 0);
 +  g->ty.ht_type.tp_basicsize = sizeof(ghash_pyobj);
    g->ty.ht_name = PyString_FromFormat("%s(keyed)", m->ops->c->name);
    g->ty.ht_type.tp_name = PyString_AS_STRING(g->ty.ht_name);
    g->ty.ht_type.tp_base = gmhash_pytype;
@@@ -1187,394 -1193,6 +1198,394 @@@ static PyTypeObject gmhash_pytype_skel 
    0                                   /* @tp_is_gc@ */
  };
  
 +/*----- Special snowflake for Poly1305 ------------------------------------*/
 +
 +PyTypeObject *poly1305cls_pytype, *poly1305key_pytype, *poly1305hash_pytype;
 +
 +typedef struct poly1305key_pyobj {
 +  PyHeapTypeObject ty;
 +  poly1305_key k;
 +} poly1305key_pyobj;
 +
 +typedef struct poly1305hash_pyobj {
 +  PyObject_HEAD
 +  unsigned f;
 +#define f_mask 1u
 +  poly1305_ctx ctx;
 +} poly1305hash_pyobj;
 +
 +#define P1305_F(o) (((poly1305hash_pyobj *)(o))->f)
 +#define P1305_CTX(o) (&((poly1305hash_pyobj *)(o))->ctx)
 +CONVFUNC(poly1305hash, poly1305_ctx *, P1305_CTX)
 +
 +static PyObject *poly1305hash_pynew(PyTypeObject *ty,
 +                                  PyObject *arg, PyObject *kw)
 +{
 +  char *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))
 +    return (0);
 +  if (m && sz != POLY1305_MASKSZ) VALERR("bad mask length");
 +  ph = PyObject_NEW(poly1305hash_pyobj, ty);
 +  ph->f = 0;
 +  if (m) ph->f |= f_mask;
 +  poly1305_macinit(&ph->ctx, &pk->k, m);
 +  Py_INCREF(ty);
 +  return ((PyObject *)ph);
 +end:
 +  return (0);
 +}
 +
 +static PyObject *poly1305key_pynew(PyTypeObject *ty,
 +                                 PyObject *arg, PyObject *kw)
 +{
 +  char *kwlist[] = { "k", 0 };
 +  poly1305key_pyobj *pk;
 +  char *k;
 +  Py_ssize_t sz;
 +
 +  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
 +    goto end;
 +  if (keysz(sz, poly1305_keysz) != sz) VALERR("bad key length");
 +
 +  pk = newtype(ty, 0, 0);
 +  pk->ty.ht_name = PyString_FromString("poly1305(keyed)");
 +  pk->ty.ht_type.tp_basicsize = sizeof(poly1305hash_pyobj);
 +  pk->ty.ht_type.tp_name = PyString_AS_STRING(pk->ty.ht_name);
 +  pk->ty.ht_type.tp_base = poly1305hash_pytype;
 +  Py_INCREF(poly1305key_pytype);
 +  pk->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
 +                           Py_TPFLAGS_BASETYPE |
 +                           Py_TPFLAGS_HEAPTYPE);
 +  pk->ty.ht_type.tp_alloc = PyType_GenericAlloc;
 +  pk->ty.ht_type.tp_free = 0;
 +  pk->ty.ht_type.tp_new = poly1305hash_pynew;
 +  typeready(&pk->ty.ht_type);
 +
 +  poly1305_keyinit(&pk->k, k, sz);
 +  return ((PyObject *)pk);
 +
 +end:
 +  return (0);
 +}
 +
 +static PyObject *poly1305clsget_name(PyObject *me, void *hunoz)
 +  { return (PyString_FromString("poly1305")); }
 +
 +static PyObject *poly1305clsget_keysz(PyObject *me, void *hunoz)
 +  { return (keysz_pywrap(poly1305_keysz)); }
 +
 +static PyObject *poly1305clsget_masksz(PyObject *me, void *hunoz)
 +  { return (PyInt_FromLong(POLY1305_MASKSZ)); }
 +
 +static PyObject *poly1305clsget_tagsz(PyObject *me, void *hunoz)
 +  { return (PyInt_FromLong(POLY1305_TAGSZ)); }
 +
 +static PyObject *polymeth_copy(PyObject *me, PyObject *arg)
 +{
 +  poly1305hash_pyobj *ph;
 +  if (!PyArg_ParseTuple(arg, ":copy")) return (0);
 +  ph = PyObject_NEW(poly1305hash_pyobj, me->ob_type);
 +  poly1305_copy(&ph->ctx, P1305_CTX(me));
 +  Py_INCREF(me->ob_type);
 +  return ((PyObject *)ph);
 +}
 +
 +static PyObject *polymeth_hash(PyObject *me, PyObject *arg)
 +{
 +  char *p;
 +  Py_ssize_t sz;
 +  if (!PyArg_ParseTuple(arg, "s#:hash", &p, &sz)) return (0);
 +  poly1305_hash(P1305_CTX(me), p, sz);
 +  RETURN_ME;
 +}
 +
 +#define POLYMETH_HASHU_(n, W, w)                                      \
 +  static PyObject *polymeth_hashu##w(PyObject *me, PyObject *arg)     \
 +  {                                                                   \
 +    uint##n x;                                                                \
 +    octet b[SZ_##W];                                                  \
 +    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end;        \
 +    STORE##W(b, x); poly1305_hash(P1305_CTX(me), b, sizeof(b));               \
 +    RETURN_ME;                                                                \
 +  end:                                                                        \
 +    return (0);                                                               \
 +  }
 +DOUINTCONV(POLYMETH_HASHU_)
 +
 +#define POLYMETH_HASHBUF_(n, W, w)                                    \
 +  static PyObject *polymeth_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); poly1305_hash(P1305_CTX(me), b, sizeof(b));      \
 +    poly1305_hash(P1305_CTX(me), p, sz);                              \
 +    RETURN_ME;                                                                \
 +  end:                                                                        \
 +    return (0);                                                               \
 +  }
 +DOUINTCONV(POLYMETH_HASHBUF_)
 +
 +static PyObject *polymeth_hashstrz(PyObject *me, PyObject *arg)
 +{
 +  char *p;
 +  if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
 +  poly1305_hash(P1305_CTX(me), p, strlen(p) + 1);
 +  RETURN_ME;
 +}
 +
 +static PyObject *polymeth_flush(PyObject *me, PyObject *arg)
 +{
 +  if (!PyArg_ParseTuple(arg, ":flush")) return (0);
 +  poly1305_flush(P1305_CTX(me));
 +  RETURN_ME;
 +}
 +
 +static PyObject *polymeth_flushzero(PyObject *me, PyObject *arg)
 +{
 +  if (!PyArg_ParseTuple(arg, ":flushzero")) return (0);
 +  poly1305_flushzero(P1305_CTX(me));
 +  RETURN_ME;
 +}
 +
 +static PyObject *polymeth_concat(PyObject *me, PyObject *arg)
 +{
 +  PyObject *pre, *suff;
 +  if (!PyArg_ParseTuple(arg, "OO:concat", &pre, &suff)) return (0);
 +  if (!PyObject_TypeCheck(pre, poly1305hash_pytype) ||
 +      !PyObject_TypeCheck(suff, poly1305hash_pytype))
 +    TYERR("wanted a poly1305hash");
 +  if (me->ob_type != pre->ob_type || me->ob_type != suff->ob_type)
 +    TYERR("key mismatch");
 +  if (P1305_CTX(pre)->nbuf) VALERR("prefix is not block-aligned");
 +  poly1305_concat(P1305_CTX(me), P1305_CTX(pre), P1305_CTX(suff));
 +  RETURN_ME;
 +end:
 +  return (0);
 +}
 +
 +static PyObject *polymeth_done(PyObject *me, PyObject *arg)
 +{
 +  PyObject *rc;
 +  if (!PyArg_ParseTuple(arg, ":done")) return (0);
 +  if (!(P1305_F(me) & f_mask)) VALERR("no mask");
 +  rc = bytestring_pywrap(0, POLY1305_TAGSZ);
 +  poly1305_done(P1305_CTX(me), PyString_AS_STRING(rc));
 +  return (rc);
 +end:
 +  return (0);
 +}
 +
 +static PyGetSetDef poly1305cls_pygetset[] = {
 +#define GETSETNAME(op, name) poly1305cls##op##_##name
 +  GET (keysz,                 "PC.keysz -> acceptable key sizes")
 +  GET (masksz,                "PC.masksz -> mask size")
 +  GET (tagsz,                 "PC.tagsz -> MAC output size")
 +  GET (name,                  "PC.name -> name of this kind of MAC")
 +#undef GETSETNAME
 +  { 0 }
 +};
 +
 +static PyMethodDef poly1305hash_pymethods[] = {
 +#define METHNAME(name) polymeth_##name
 +  METH  (copy,                        "P.copy() -> PP")
 +  METH        (hash,                  "P.hash(M)")
 +#define METHU_(n, W, w) METH(hashu##w, "P.hashu" #w "(WORD)")
 +  DOUINTCONV(METHU_)
 +#undef METHU_
 +#define METHBUF_(n, W, w) METH(hashbuf##w, "P.hashbuf" #w "(BYTES)")
 +  DOUINTCONV(METHBUF_)
 +#undef METHBUF_
 +  METH        (hashstrz,              "P.hashstrz(STRING)")
 +  METH  (flush,                       "P.flush()")
 +  METH  (flushzero,           "P.flushzero()")
 +  METH  (concat,              "P.concat(PREFIX, SUFFIX)")
 +  METH        (done,                  "P.done() -> TAG")
 +#undef METHNAME
 +  { 0 }
 +};
 +
 +static PyTypeObject poly1305cls_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "Poly1305Class",                    /* @tp_name@ */
 +  sizeof(PyHeapTypeObject),           /* @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@ */
 +"Poly1305 metametaclass.  Best not to ask.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  0,                                  /* @tp_methods@ */
 +  0,                                  /* @tp_members@ */
 +  poly1305cls_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 poly1305key_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "poly1305",                         /* @tp_name@ */
 +  sizeof(poly1305key_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@ */
 +"Poly1305 key.",
 +
 +  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@ */
 +  poly1305key_pynew,                  /* @tp_new@ */
 +  0,                                  /* @tp_free@ */
 +  0                                   /* @tp_is_gc@ */
 +};
 +
 +static PyTypeObject poly1305hash_pytype_skel = {
 +  PyObject_HEAD_INIT(0) 0,            /* Header */
 +  "Poly1305Hash",                     /* @tp_name@ */
 +  sizeof(poly1305hash_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@ */
 +"Poly1305 MAC context base class.",
 +
 +  0,                                  /* @tp_traverse@ */
 +  0,                                  /* @tp_clear@ */
 +  0,                                  /* @tp_richcompare@ */
 +  0,                                  /* @tp_weaklistoffset@ */
 +  0,                                  /* @tp_iter@ */
 +  0,                                  /* @tp_iternext@ */
 +  poly1305hash_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@ */
 +};
 +
 +/*----- Special snowflake for HSalsa and HChaCha --------------------------*/
 +
 +#define DEF_HDANCE(DANCE, HDANCE, dance, hdance)                      \
 +  static PyObject *meth_##hdance##_prf(PyObject *me, PyObject *arg)   \
 +  {                                                                   \
 +    dance##_ctx dance;                                                        \
 +    char *k, *n;                                                      \
 +    Py_ssize_t ksz, nsz;                                              \
 +    PyObject *rc;                                                     \
 +    if (!PyArg_ParseTuple(arg, "s#s#:" #hdance "_prf",                        \
 +                        &k, &ksz, &n, &nsz))                          \
 +      goto end;                                                               \
 +    if (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);                                  \
 +    hdance##_prf(&dance, n, PyString_AS_STRING(rc));                  \
 +    return (rc);                                                      \
 +  end:                                                                        \
 +    return (0);                                                               \
 +  }
 +
 +DEF_HDANCE(SALSA20, HSALSA20, salsa20, hsalsa20)
 +DEF_HDANCE(SALSA20, HSALSA20, salsa20, hsalsa2012)
 +DEF_HDANCE(SALSA20, HSALSA20, salsa20, hsalsa208)
 +
 +DEF_HDANCE(CHACHA, HCHACHA, chacha, hchacha20)
 +DEF_HDANCE(CHACHA, HCHACHA, chacha, hchacha12)
 +DEF_HDANCE(CHACHA, HCHACHA, chacha, hchacha8)
 +
  /*----- Pseudorandom permutations -----------------------------------------*/
  
  static PyTypeObject *gcprp_pytype, *gprp_pytype;
@@@ -1636,7 -1254,7 +1647,7 @@@ static PyObject *gprp_pynew(PyTypeObjec
  {
    char *kwlist[] = { "key", 0 };
    char *k;
 -  int sz;
 +  Py_ssize_t sz;
    const prpinfo *prp = GCPRP_PRP(ty);
    PyObject *me;
  
@@@ -1682,7 -1300,7 +1693,7 @@@ static PyObject *gcpget_blksz(PyObject 
  static PyObject *gpmeth_encrypt(PyObject *me, PyObject *arg)
  {
    char *p;
 -  int n;
 +  Py_ssize_t n;
    PyObject *rc = 0;
  
    if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &n)) goto end;
@@@ -1696,7 -1314,7 +1707,7 @@@ end
  static PyObject *gpmeth_decrypt(PyObject *me, PyObject *arg)
  {
    char *p;
 -  int n;
 +  Py_ssize_t n;
    PyObject *rc = 0;
  
    if (!PyArg_ParseTuple(arg, "s#:decrypt", &p, &n)) goto end;
@@@ -1840,17 -1458,6 +1851,17 @@@ toschnorr(N) -> M: convert work factor 
  toif(N) -> M: convert work factor to integer factorization problem size")
    METH        (_KeySZ_toec,           "\
  toec(N) -> M: convert work factor to elliptic curve group order")
 +  METH        (_KeySZ_toec,           "\
 +toec(N) -> M: convert work factor to elliptic curve group order")
 +#define METH_HDANCE(hdance, HDance) METH(hdance##_prf, "\
 +" #hdance "_prf(K, N) -> H: calculate " HDance " hash of N with K")
 +  METH_HDANCE(hsalsa20, "HSalsa20")
 +  METH_HDANCE(hsalsa2012, "HSalsa20/12")
 +  METH_HDANCE(hsalsa208, "HSalsa20/8")
 +  METH_HDANCE(hchacha20, "HChaCha20")
 +  METH_HDANCE(hchacha12, "HChaCha12")
 +  METH_HDANCE(hchacha8, "HChaCha8")
 +#undef METH_DANCE
  #undef METHNAME
    { 0 }
  };
@@@ -1868,9 -1475,6 +1879,9 @@@ void algorithms_pyinit(void
    INITTYPE(gcmac, type);
    INITTYPE(gmac, type);
    INITTYPE(gmhash, ghash);
 +  INITTYPE(poly1305cls, type);
 +  INITTYPE_META(poly1305key, type, poly1305cls);
 +  INITTYPE(poly1305hash, root);
    INITTYPE(gcprp, type);
    INITTYPE(gprp, root);
    addmethods(methods);
@@@ -1901,9 -1505,6 +1912,9 @@@ void algorithms_pyinsert(PyObject *mod
    INSERT("GMAC", gmac_pytype);
    INSERT("GMACHash", gmhash_pytype);
    INSERT("gcmacs", gcmacs());
 +  INSERT("Poly1305Class", poly1305cls_pytype);
 +  INSERT("poly1305", poly1305key_pytype);
 +  INSERT("Poly1305Hash", poly1305hash_pytype);
    INSERT("GCPRP", gcprp_pytype);
    INSERT("GPRP", gprp_pytype);
    INSERT("gcprps", gcprps());
diff --combined catacomb/__init__.py
index 6fc3725abf51aed1bbce16ab42fabe662e8df029,f79b4d29b210c56129138a0c08607dff637d5bca..785838e67eb94830a2358670bcd86049ac0e889c
@@@ -34,6 -34,20 +34,20 @@@ from sys import argv as _arg
  ## For the benefit of the default keyreporter, we need the program na,e.
  _base._ego(_argv[0])
  
+ ## How to fix a name back into the right identifier.  Alas, the rules are not
+ ## consistent.
+ def _fixname(name):
+   ## Hyphens consistently become underscores.
+   name = name.replace('-', '_')
+   ## But slashes might become underscores or just vanish.
+   if name.startswith('salsa20'): name = name.translate(None, '/')
+   else: name = name.replace('/', '_')
+   ## Done.
+   return name
  ## Initialize the module.  Drag in the static methods of the various
  ## classes; create names for the various known crypto algorithms.
  def _init():
@@@ -56,9 -70,9 +70,9 @@@
          setattr(c, j[plen:], classmethod(b[j]))
    for i in [gcciphers, gchashes, gcmacs, gcprps]:
      for c in i.itervalues():
-       d[c.name.replace('-', '_').translate(None, '/')] = c
+       d[_fixname(c.name)] = c
    for c in gccrands.itervalues():
-     d[c.name.replace('-', '_').translate(None, '/') + 'rand'] = c
+     d[_fixname(c.name + 'rand')] = c
  _init()
  
  ## A handy function for our work: add the methods of a named class to an
@@@ -82,44 -96,6 +96,44 @@@ def _checkend(r)
      raise SyntaxError, 'junk at end of string'
    return x
  
 +## Some pretty-printing utilities.
 +PRINT_SECRETS = False
 +def _clsname(me): return type(me).__name__
 +def _repr_secret(thing, secretp = True):
 +  if not secretp or PRINT_SECRETS: return repr(thing)
 +  else: return '#<SECRET>'
 +def _pp_str(me, pp, cyclep): pp.text(cyclep and '...' or str(me))
 +def _pp_secret(pp, thing, secretp = True):
 +  if not secretp or PRINT_SECRETS: pp.pretty(thing)
 +  else: pp.text('#<SECRET>')
 +def _pp_bgroup(pp, text):
 +  ind = len(text)
 +  pp.begin_group(ind, text)
 +  return ind
 +def _pp_bgroup_tyname(pp, obj, open = '('):
 +  return _pp_bgroup(pp, _clsname(obj) + open)
 +def _pp_kv(pp, k, v, secretp = False):
 +  ind = _pp_bgroup(pp, k + ' = ')
 +  _pp_secret(pp, v, secretp)
 +  pp.end_group(ind, '')
 +def _pp_commas(pp, printfn, items):
 +  firstp = True
 +  for i in items:
 +    if firstp: firstp = False
 +    else: pp.text(','); pp.breakable()
 +    printfn(i)
 +def _pp_dict(pp, items):
 +  def p((k, v)):
 +    pp.begin_group(0)
 +    pp.pretty(k)
 +    pp.text(':')
 +    pp.begin_group(2)
 +    pp.breakable()
 +    pp.pretty(v)
 +    pp.end_group(2)
 +    pp.end_group(0)
 +  _pp_commas(pp, p, items)
 +
  ###--------------------------------------------------------------------------
  ### Bytestrings.
  
@@@ -132,39 -108,8 +146,39 @@@ class _tmp
    def __repr__(me):
      return 'bytes(%r)' % hex(me)
  _augment(ByteString, _tmp)
 +ByteString.__hash__ = str.__hash__
  bytes = ByteString.fromhex
  
 +###--------------------------------------------------------------------------
 +### Hashing.
 +
 +class _tmp:
 +  def check(me, h):
 +    hh = me.done()
 +    return ctstreq(h, hh)
 +_augment(GHash, _tmp)
 +_augment(Poly1305Hash, _tmp)
 +
 +###--------------------------------------------------------------------------
 +### 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)
 +
 +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:])
 +
  ###--------------------------------------------------------------------------
  ### Multiprecision integers and binary polynomials.
  
@@@ -187,8 -132,7 +201,8 @@@ class BaseRat (object)
    @property
    def denom(me): return me._d
    def __str__(me): return '%s/%s' % (me._n, me._d)
 -  def __repr__(me): return '%s(%s, %s)' % (type(me).__name__, me._n, me._d)
 +  def __repr__(me): return '%s(%s, %s)' % (_clsname(me), me._n, me._d)
 +  _repr_pretty_ = _pp_str
  
    def __add__(me, you):
      n, d = _split_rat(you)
@@@ -233,7 -177,6 +247,7 @@@ class _tmp
    def reduce(x): return MPReduce(x)
    def __div__(me, you): return IntRat(me, you)
    def __rdiv__(me, you): return IntRat(you, me)
 +  _repr_pretty_ = _pp_str
  _augment(MP, _tmp)
  
  class _tmp:
    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)
 +  _repr_pretty_ = _pp_str
  _augment(GF, _tmp)
  
  class _tmp:
@@@ -264,24 -206,14 +278,24 @@@ class _tmp
  _augment(Field, _tmp)
  
  class _tmp:
 -  def __repr__(me): return '%s(%sL)' % (type(me).__name__, me.p)
 +  def __repr__(me): return '%s(%sL)' % (_clsname(me), me.p)
    def __hash__(me): return 0x114401de ^ hash(me.p)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep: pp.text('...')
 +    else: pp.pretty(me.p)
 +    pp.end_group(ind, ')')
    def ec(me, a, b): return ECPrimeProjCurve(me, a, b)
  _augment(PrimeField, _tmp)
  
  class _tmp:
 -  def __repr__(me): return '%s(%sL)' % (type(me).__name__, hex(me.p))
 +  def __repr__(me): return '%s(%#xL)' % (_clsname(me), me.p)
    def ec(me, a, b): return ECBinProjCurve(me, a, b)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep: pp.text('...')
 +    else: pp.text('%#x' % me.p)
 +    pp.end_group(ind, ')')
  _augment(BinField, _tmp)
  
  class _tmp:
@@@ -299,7 -231,6 +313,7 @@@ _augment(BinNormField, _tmp
  class _tmp:
    def __str__(me): return str(me.value)
    def __repr__(me): return '%s(%s)' % (repr(me.field), repr(me.value))
 +  _repr_pretty_ = _pp_str
  _augment(FE, _tmp)
  
  ###--------------------------------------------------------------------------
  
  class _tmp:
    def __repr__(me):
 -    return '%s(%r, %s, %s)' % (type(me).__name__, me.field, me.a, me.b)
 +    return '%s(%r, %s, %s)' % (_clsname(me), me.field, me.a, me.b)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      pp.pretty(me.field); pp.text(','); pp.breakable()
 +      pp.pretty(me.a); pp.text(','); pp.breakable()
 +      pp.pretty(me.b)
 +    pp.end_group(ind, ')')
    def frombuf(me, s):
      return ecpt.frombuf(me, s)
    def fromraw(me, s):
@@@ -345,37 -267,17 +359,37 @@@ _augment(ECBinCurve, _tmp
  
  class _tmp:
    def __repr__(me):
 -    if not me: return 'ECPt()'
 -    return 'ECPt(%s, %s)' % (me.ix, me.iy)
 +    if not me: return '%s()' % _clsname(me)
 +    return '%s(%s, %s)' % (_clsname(me), me.ix, me.iy)
    def __str__(me):
      if not me: return 'inf'
      return '(%s, %s)' % (me.ix, me.iy)
 +  def _repr_pretty_(me, pp, cyclep):
 +    if cyclep:
 +      pp.text('...')
 +    elif not me:
 +      pp.text('inf')
 +    else:
 +      ind = _pp_bgroup(pp, '(')
 +      pp.pretty(me.ix); pp.text(','); pp.breakable()
 +      pp.pretty(me.iy)
 +      pp.end_group(ind, ')')
  _augment(ECPt, _tmp)
  
  class _tmp:
    def __repr__(me):
 -    return 'ECInfo(curve = %r, G = %r, r = %s, h = %s)' % \
 -           (me.curve, me.G, me.r, me.h)
 +    return '%s(curve = %r, G = %r, r = %s, h = %s)' % \
 +           (_clsname(me), me.curve, me.G, me.r, me.h)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_kv(pp, 'curve', me.curve); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'r', me.r); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'h', me.h)
 +    pp.end_group(ind, ')')
    def __hash__(me):
      h = 0x9bedb8de
      h ^=   hash(me.curve)
@@@ -392,41 -294,21 +406,41 @@@ class _tmp
    def __str__(me):
      if not me: return 'inf'
      return '(%s, %s)' % (me.x, me.y)
 +  def _repr_pretty_(me, pp, cyclep):
 +    if cyclep:
 +      pp.text('...')
 +    elif not me:
 +      pp.text('inf')
 +    else:
 +      ind = _pp_bgroup(pp, '(')
 +      pp.pretty(me.x); pp.text(','); pp.breakable()
 +      pp.pretty(me.y)
 +      pp.end_group(ind, ')')
  _augment(ECPtCurve, _tmp)
  
  ###--------------------------------------------------------------------------
  ### Key sizes.
  
  class _tmp:
 -  def __repr__(me): return 'KeySZAny(%d)' % me.default
 +  def __repr__(me): return '%s(%d)' % (_clsname(me), me.default)
    def check(me, sz): return True
    def best(me, sz): return sz
  _augment(KeySZAny, _tmp)
  
  class _tmp:
    def __repr__(me):
 -    return 'KeySZRange(%d, %d, %d, %d)' % \
 -           (me.default, me.min, me.max, me.mod)
 +    return '%s(%d, %d, %d, %d)' % \
 +           (_clsname(me), me.default, me.min, me.max, me.mod)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      pp.pretty(me.default); pp.text(','); pp.breakable()
 +      pp.pretty(me.min); pp.text(','); pp.breakable()
 +      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 best(me, sz):
      if sz < me.min: raise ValueError, 'key too small'
  _augment(KeySZRange, _tmp)
  
  class _tmp:
 -  def __repr__(me): return 'KeySZSet(%d, %s)' % (me.default, me.set)
 +  def __repr__(me): return '%s(%d, %s)' % (_clsname(me), me.default, me.set)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      pp.pretty(me.default); pp.text(','); pp.breakable()
 +      ind1 = _pp_bgroup(pp, '{')
 +      _pp_commas(pp, pp.pretty, me.set)
 +      pp.end_group(ind1, '}')
 +    pp.end_group(ind, ')')
    def check(me, sz): return sz in me.set
    def best(me, sz):
      found = -1
      return found
  _augment(KeySZSet, _tmp)
  
 +###--------------------------------------------------------------------------
 +### Key data objects.
 +
 +class _tmp:
 +  def __repr__(me): return '%s(%r)' % (_clsname(me), me.name)
 +_augment(KeyFile, _tmp)
 +
 +class _tmp:
 +  def __repr__(me): return '%s(%r)' % (_clsname(me), me.fulltag)
 +_augment(Key, _tmp)
 +
 +class _tmp:
 +  def __repr__(me):
 +    return '%s({%s})' % (_clsname(me),
 +                         ', '.join(['%r: %r' % kv for kv in me.iteritems()]))
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep: pp.text('...')
 +    else: _pp_dict(pp, me.iteritems())
 +    pp.end_group(ind, ')')
 +_augment(KeyAttributes, _tmp)
 +
 +class _tmp:
 +  def __repr__(me):
 +    return '%s(%s, %r)' % (_clsname(me),
 +                           _repr_secret(me._guts(),
 +                                        not (me.flags & KF_NONSECRET)),
 +                           me.writeflags(me.flags))
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_secret(pp, me._guts(), not (me.flags & KF_NONSECRET))
 +      pp.text(','); pp.breakable()
 +      pp.pretty(me.writeflags(me.flags))
 +    pp.end_group(ind, ')')
 +_augment(KeyData, _tmp)
 +
 +class _tmp:
 +  def _guts(me): return me.bin
 +_augment(KeyDataBinary, _tmp)
 +
 +class _tmp:
 +  def _guts(me): return me.ct
 +_augment(KeyDataEncrypted, _tmp)
 +
 +class _tmp:
 +  def _guts(me): return me.mp
 +_augment(KeyDataMP, _tmp)
 +
 +class _tmp:
 +  def _guts(me): return me.str
 +_augment(KeyDataString, _tmp)
 +
 +class _tmp:
 +  def _guts(me): return me.ecpt
 +_augment(KeyDataECPt, _tmp)
 +
 +class _tmp:
 +  def __repr__(me):
 +    return '%s({%s})' % (_clsname(me),
 +                         ', '.join(['%r: %r' % kv for kv in me.iteritems()]))
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me, '({ ')
 +    if cyclep: pp.text('...')
 +    else: _pp_dict(pp, me.iteritems())
 +    pp.end_group(ind, ' })')
 +_augment(KeyDataStructured, _tmp)
 +
  ###--------------------------------------------------------------------------
  ### Abstract groups.
  
  class _tmp:
    def __repr__(me):
 -    return '%s(p = %s, r = %s, g = %s)' % \
 -           (type(me).__name__, me.p, me.r, me.g)
 +    return '%s(p = %s, r = %s, g = %s)' % (_clsname(me), me.p, me.r, me.g)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'r', me.r); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'g', me.g)
 +    pp.end_group(ind, ')')
  _augment(FGInfo, _tmp)
  
  class _tmp:
@@@ -552,12 -346,7 +566,12 @@@ _augment(BinDHInfo, _tmp
  
  class _tmp:
    def __repr__(me):
 -    return '%s(%r)' % (type(me).__name__, me.info)
 +    return '%s(%r)' % (_clsname(me), me.info)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep: pp.text('...')
 +    else: pp.pretty(me.info)
 +    pp.end_group(ind, ')')
  _augment(Group, _tmp)
  
  class _tmp:
      h ^= 2*hash(info.r) & 0xffffffff
      h ^= 5*hash(info.g) & 0xffffffff
      return h
 +  def _get_geval(me, x): return MP(x)
  _augment(PrimeGroup, _tmp)
  
  class _tmp:
      h ^= 2*hash(info.r) & 0xffffffff
      h ^= 5*hash(info.g) & 0xffffffff
      return h
 +  def _get_geval(me, x): return GF(x)
  _augment(BinGroup, _tmp)
  
  class _tmp:
    def __hash__(me): return 0x0ec23dab ^ hash(me.info)
 +  def _get_geval(me, x): return x.toec()
  _augment(ECGroup, _tmp)
  
  class _tmp:
    def __repr__(me):
      return '%r(%r)' % (me.group, str(me))
 +  def _repr_pretty_(me, pp, cyclep):
 +    pp.pretty(type(me)._get_geval(me))
  _augment(GE, _tmp)
  
  ###--------------------------------------------------------------------------
@@@ -650,158 -434,13 +664,158 @@@ class _tmp
        return x is None or x == msg
      except ValueError:
        return False
 +  def __repr__(me):
 +    return '%s(n = %r, e = %r)' % (_clsname(me), me.n, me.e)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_kv(pp, 'n', me.n); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'e', me.e)
 +    pp.end_group(ind, ')')
  _augment(RSAPub, _tmp)
  
  class _tmp:
    def decrypt(me, ct, enc): return enc.decode(me.privop(ct), me.n.nbits)
    def sign(me, msg, enc): return me.privop(enc.encode(msg, me.n.nbits))
 +  def __repr__(me):
 +    return '%s(n = %r, e = %r, d = %s, ' \
 +      'p = %s, q = %s, dp = %s, dq = %s, q_inv = %s)' % \
 +      (_clsname(me), me.n, me.e,
 +       _repr_secret(me.d), _repr_secret(me.p), _repr_secret(me.q),
 +       _repr_secret(me.dp), _repr_secret(me.dq), _repr_secret(me.q_inv))
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_kv(pp, 'n', me.n); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'e', me.e); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'd', me.d, secretp = True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'p', me.p, secretp = True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'q', me.q, secretp = True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'dp', me.dp, secretp = True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'dq', me.dq, secretp = True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'q_inv', me.q_inv, secretp = True)
 +    pp.end_group(ind, ')')
  _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_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.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_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, 'u', me.u, True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'p', me.p)
 +    pp.end_group(ind, ')')
 +_augment(DSAPriv, _tmp)
 +_augment(KCDSAPriv, _tmp)
 +
 +###--------------------------------------------------------------------------
 +### Bernstein's elliptic curve crypto and related schemes.
 +
 +X25519_BASE = \
 +  bytes('0900000000000000000000000000000000000000000000000000000000000000')
 +
 +X448_BASE = \
 +  bytes('05000000000000000000000000000000000000000000000000000000'
 +        '00000000000000000000000000000000000000000000000000000000')
 +
 +Z128 = bytes('00000000000000000000000000000000')
 +
 +class _BoxyPub (object):
 +  def __init__(me, pub, *kw, **kwargs):
 +    if len(pub) != me._PUBSZ: raise ValueError, 'bad public key'
 +    super(_BoxyPub, me).__init__(*kw, **kwargs)
 +    me.pub = pub
 +  def __repr__(me): return '%s(pub = %r)' % (_clsname(me), me.pub)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_kv(pp, 'pub', me.pub)
 +    pp.end_group(ind, ')')
 +
 +class _BoxyPriv (_BoxyPub):
 +  def __init__(me, priv, pub = None, *kw, **kwargs):
 +    if len(priv) != me._KEYSZ: raise ValueError, 'bad private key'
 +    if pub is None: pub = me._op(priv, me._BASE)
 +    super(_BoxyPriv, me).__init__(pub = pub, *kw, **kwargs)
 +    me.priv = priv
 +  def agree(me, you): return me._op(me.priv, you.pub)
 +  def boxkey(me, recip):
 +    return me._hashkey(me.agree(recip))
 +  def box(me, recip, n, m):
 +    return secret_box(me.boxkey(recip), n, m)
 +  def unbox(me, recip, n, c):
 +    return secret_unbox(me.boxkey(recip, n, c))
 +  def __repr__(me): return '%s(priv = %s, pub = %r)' % \
 +      (_clsname(me), _repr_secret(me.priv), me.pub)
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup_tyname(pp, me)
 +    if cyclep:
 +      pp.text('...')
 +    else:
 +      _pp_kv(pp, 'priv', me.priv, True); pp.text(','); pp.breakable()
 +      _pp_kv(pp, 'pub', me.pub)
 +    pp.end_group(ind, ')')
 +
 +class X25519Pub (_BoxyPub):
 +  _PUBSZ = X25519_PUBSZ
 +  _BASE = X25519_BASE
 +
 +class X25519Priv (_BoxyPriv, X25519Pub):
 +  _KEYSZ = X25519_KEYSZ
 +  def _op(me, k, X): return x25519(k, X)
 +  def _hashkey(me, z): return hsalsa20_prf(z, Z128)
 +
 +class X448Pub (_BoxyPub):
 +  _PUBSZ = X448_PUBSZ
 +  _BASE = X448_BASE
 +
 +class X448Priv (_BoxyPriv, X448Pub):
 +  _KEYSZ = X448_KEYSZ
 +  def _op(me, k, X): return x448(k, X)
 +  ##def _hashkey(me, z): return ???
 +
 +class Ed25519Pub (object):
 +  def __init__(me, pub):
 +    me.pub = pub
 +  def verify(me, msg, sig):
 +    return ed25519_verify(me.pub, msg, sig)
 +
 +class Ed25519Priv (Ed25519Pub):
 +  def __init__(me, priv):
 +    me.priv = priv
 +    Ed25519Pub.__init__(me, ed25519_pubkey(priv))
 +  def sign(me, msg):
 +    return ed25519_sign(me.priv, msg, pub = me.pub)
 +  @classmethod
 +  def generate(cls, rng = rand):
 +    return cls(rng.block(ED25519_KEYSZ))
 +
  ###--------------------------------------------------------------------------
  ### Built-in named curves and prime groups.
  
@@@ -809,17 -448,9 +823,17 @@@ class _groupmap (object)
    def __init__(me, map, nth):
      me.map = map
      me.nth = nth
 -    me.i = [None] * (max(map.values()) + 1)
 +    me._n = max(map.values()) + 1
 +    me.i = me._n*[None]
    def __repr__(me):
 -    return '{%s}' % ', '.join(['%r: %r' % (k, me[k]) for k in me])
 +    return '{%s}' % ', '.join(['%r: %r' % kv for kv in me.iteritems()])
 +  def _repr_pretty_(me, pp, cyclep):
 +    ind = _pp_bgroup(pp, '{ ')
 +    if cyclep: pp.text('...')
 +    else: _pp_dict(pp, me.iteritems())
 +    pp.end_group(ind, ' }')
 +  def __len__(me):
 +    return me._n
    def __contains__(me, k):
      return k in me.map
    def __getitem__(me, k):
diff --combined util.c
index 58d2fe7ebd7525998e4211983541b3ed5125fa6d,8fab0bf59979e43e26fecd868a6a20d2ac3553f1..d4b7fb060d189ffbea18e68cc1f4d572cb7ef391
--- 1/util.c
--- 2/util.c
+++ b/util.c
@@@ -42,29 -42,6 +42,29 @@@ PyObject *getulong(unsigned long w
      return (PyLong_FromUnsignedLong(w));
  }
  
 +static PyObject *i32 = 0;
 +static int init_i32(void)
 +  { if (!i32 && (i32 = PyInt_FromLong(32)) == 0) return (-1); return (0); }
 +
 +PyObject *getk64(kludge64 u)
 +{
 +  PyObject *i = 0, *j = 0, *t;
 +  PyObject *rc = 0;
 +
 +  if (init_i32()) goto end;
 +  if ((i = PyLong_FromUnsignedLong(HI64(u))) == 0) goto end;
 +  if ((t = PyNumber_InPlaceLshift(i, i32)) == 0) goto end;
 +  Py_DECREF(i); i = t;
 +  if ((j = PyLong_FromUnsignedLong(LO64(u))) == 0) goto end;
 +  if ((t = PyNumber_InPlaceOr(i, j)) == 0) goto end;
 +  Py_DECREF(i); i = t;
 +  if ((rc = PyNumber_Int(i)) == 0) goto end;
 +end:
 +  if (i) Py_DECREF(i);
 +  if (j) Py_DECREF(j);
 +  return (rc);
 +}
 +
  PyObject *getbool(int b)
  {
    if (b) RETURN_TRUE;
@@@ -83,6 -60,7 +83,7 @@@ int convulong(PyObject *o, void *pp
    unsigned long *p = pp;
    PyObject *t;
  
+   if (!o) VALERR("can't delete");
    if (PyInt_Check(o)) {
      i = PyInt_AS_LONG(o);
      if (i < 0) VALERR("must be nonnegative");
@@@ -126,28 -104,6 +127,28 @@@ end
    return (0);
  }
  
 +int convk64(PyObject *o, void *pp)
 +{
 +  PyObject *i = 0, *t;
 +  int rc = 0;
 +  uint32 lo, hi;
 +
 +  if (init_i32()) goto end;
 +  if ((i = PyNumber_Int(o)) == 0) goto end;
 +  lo = PyInt_AsUnsignedLongMask(i);
 +  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
 +  Py_DECREF(i); i = t;
 +  hi = PyInt_AsUnsignedLongMask(i);
 +  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
 +  Py_DECREF(i); i = t;
 +  if (PyObject_IsTrue(i)) VALERR("out of range");
 +  SET64(*(kludge64 *)pp, hi, lo);
 +  rc = 1;
 +end:
 +  if (i) Py_DECREF(i);
 +  return (rc);
 +}
 +
  int convmpw(PyObject *o, void *pp)
  {
    unsigned long u;
@@@ -176,8 -132,11 +177,11 @@@ end
  
  int convbool(PyObject *o, void *pp)
  {
+   if (!o) VALERR("can't delete");
    *(int *)pp = PyObject_IsTrue(o);
    return (1);
+ end:
+   return (0);
  }
  
  /*----- Type messing ------------------------------------------------------*/
@@@ -223,9 -182,9 +227,9 @@@ void typeready(PyTypeObject *ty
    PyDict_SetItemString(ty->tp_dict, "__module__", modname);
  }
  
 -PyTypeObject *inittype(PyTypeObject *tyskel)
 +PyTypeObject *inittype(PyTypeObject *tyskel, PyTypeObject *meta)
  {
 -  PyTypeObject *ty = newtype(&PyType_Type, tyskel, 0);
 +  PyTypeObject *ty = newtype(meta, tyskel, 0);
    ty->tp_flags |= Py_TPFLAGS_HEAPTYPE;
    typeready(ty);
    return (ty);
@@@ -271,8 -230,8 +275,8 @@@ PyMethodDef *donemethods(void
  
  /*----- Exceptions --------------------------------------------------------*/
  
 -PyObject * mkexc(PyObject *mod, PyObject *base,
 -               const char *name, PyMethodDef *mm)
 +PyObject *mkexc(PyObject *mod, PyObject *base,
 +              const char *name, PyMethodDef *mm)
  {
    PyObject *nameobj = 0;
    PyObject *dict = 0;