@@@ py_buffer/freebin wip
[catacomb-python] / rand.c
diff --git a/rand.c b/rand.c
index d4f1fd0..e72f450 100644 (file)
--- a/rand.c
+++ b/rand.c
@@ -1,13 +1,11 @@
 /* -*-c-*-
  *
- * $Id$
- *
  * Random-number generators
  *
  * (c) 2004 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of the Python interface to Catacomb.
  *
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * Catacomb/Python is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with Catacomb/Python; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 /*----- Header files ------------------------------------------------------*/
 
 #include "catacomb-python.h"
+PUBLIC_SYMBOLS;
+#include "algorithms.h"
+PRIVATE_SYMBOLS;
 
 /*----- Main code ---------------------------------------------------------*/
 
-PyTypeObject *grand_pytype, *truerand_pytype;
-PyTypeObject *lcrand_pytype, *fibrand_pytype;
-PyTypeObject *dsarand_pytype, *bbs_pytype, *bbspriv_pytype;
-PyTypeObject *sslprf_pytype, *tlsdx_pytype, *tlsprf_pytype;
+PyTypeObject *grand_pytype;
+static PyTypeObject *truerand_pytype;
+static PyTypeObject *lcrand_pytype, *fibrand_pytype;
+static PyTypeObject *dsarand_pytype, *bbs_pytype, *bbspriv_pytype;
+static PyTypeObject *sslprf_pytype, *tlsdx_pytype, *tlsprf_pytype;
 PyObject *rand_pyobj;
 
+static PyObject *gccrands_dict;
+
 static PyObject *grand_dopywrap(PyTypeObject *ty, grand *r, unsigned f)
 {
   grand_pyobj *g;
@@ -51,27 +55,42 @@ static PyObject *grand_dopywrap(PyTypeObject *ty, grand *r, unsigned f)
 PyObject *grand_pywrap(grand *r, unsigned f)
 {
   PyTypeObject *ty = grand_pytype;
-
-  if (strcmp(r->ops->name, "rand") == 0) ty = truerand_pytype;
-  else if (strcmp(r->ops->name, "lcrand") == 0) ty = lcrand_pytype;
-  else if (strcmp(r->ops->name, "fibrand") == 0) ty = fibrand_pytype;
-  else if (strcmp(r->ops->name, "dsarand") == 0) ty = dsarand_pytype;
-  else if (strcmp(r->ops->name, "bbs") == 0) ty = bbs_pytype;
+  PyObject *ob;
+
+  if (STRCMP(r->ops->name, ==, "rand")) ty = truerand_pytype;
+  else if (STRCMP(r->ops->name, ==, "lcrand")) ty = lcrand_pytype;
+  else if (STRCMP(r->ops->name, ==, "fibrand")) ty = fibrand_pytype;
+  else if (STRCMP(r->ops->name, ==, "dsarand")) ty = dsarand_pytype;
+  else if (STRCMP(r->ops->name, ==, "bbs")) ty = bbs_pytype;
+  else if (STRCMP(r->ops->name, ==, "sslprf")) ty = sslprf_pytype;
+  else if (STRCMP(r->ops->name, ==, "tlsdx")) ty = tlsdx_pytype;
+  else if (STRCMP(r->ops->name, ==, "tlsprf")) ty = tlsprf_pytype;
+  else if ((ob = PyMapping_GetItemString
+                  (gccrands_dict, (/*unconst*/ char *)r->ops->name)) != 0)
+    ty = (PyTypeObject *)ob;
   return (grand_dopywrap(ty, r, f));
 }
 
 CONVFUNC(grand, grand *, GRAND_R)
 
-static PyObject *grmeth_byte(PyObject *me, PyObject *arg)
+static int grand_check(PyObject *me)
 {
-  if (!PyArg_ParseTuple(arg, ":byte")) return (0);
+  if (!GRAND_R(me)) VALERR("random generator object is no longer valid");
+  return (0);
+end:
+  return (-1);
+}
+
+static PyObject *grmeth_byte(PyObject *me)
+{
+  if (grand_check(me)) return (0);
   return (PyInt_FromLong(grand_byte(GRAND_R(me))));
 }
 
-static PyObject *grmeth_word(PyObject *me, PyObject *arg)
+static PyObject *grmeth_word(PyObject *me)
 {
-  if (!PyArg_ParseTuple(arg, ":word")) return (0);
-  return (getu32(grand_word(GRAND_R(me))));
+  if (grand_check(me)) return (0);
+  return (getulong(grand_word(GRAND_R(me))));
 }
 
 static PyObject *grmeth_range(PyObject *me, PyObject *arg)
@@ -81,22 +100,21 @@ static PyObject *grmeth_range(PyObject *me, PyObject *arg)
   mp *y = 0;
 
   if (!PyArg_ParseTuple(arg, "O:range", &m)) return (0);
+  if (grand_check(me)) return (0);
   if (PyInt_Check(m)) {
-    long mm = PyInt_AS_LONG(m);
-    if (mm < 0)
-      goto negative;
-    if (mm <= 0xffffffff)
+    long mm = PyInt_AsLong(m);
+    if (mm == -1 && PyErr_Occurred()) PyErr_Clear();
+    else if (mm <= 0) goto notpos;
+    else if (mm <= 0xffffffff)
       return (PyInt_FromLong(grand_range(GRAND_R(me), mm)));
   }
-  if ((x = getmp(m)) == 0)
-    goto end;
-  if (MP_NEGP(x))
-    goto negative;
+  if ((x = getmp(m)) == 0) goto end;
+  if (!MP_POSP(x)) goto notpos;
   y = mprand_range(MP_NEW, x, GRAND_R(me), 0);
   MP_DROP(x);
   return (mp_pywrap(y));
-negative:
-  TYERR("range must be nonnegative");
+notpos:
+  VALERR("range must be strictly positive");
 end:
   if (x) MP_DROP(x);
   return (0);
@@ -105,12 +123,14 @@ end:
 static PyObject *grmeth_mp(PyObject *me, PyObject *arg, PyObject *kw)
 {
   size_t l;
-  mpw o;
-  char *kwlist[] = { "bits", "or", 0 };
+  mpw o = 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);
+  if (l < MPW_BITS && (o >> l)) VALERR("or mask too large");
   return (mp_pywrap(mprand(MP_NEW, l, GRAND_R(me), o)));
 end:
   return (0);
@@ -122,16 +142,16 @@ static PyObject *grmeth_block(PyObject *me, PyObject *arg)
   PyObject *rc = 0;
 
   if (!PyArg_ParseTuple(arg, "O&:block", convulong, &n)) goto end;
+  if (grand_check(me)) return (0);
   rc = bytestring_pywrap(0, n);
-  grand_fill(GRAND_R(me), PyString_AS_STRING(rc), n);
+  grand_fill(GRAND_R(me), BIN_PTR(rc), n);
 end:
   return (rc);
 }
 
 static int checkop(grand *r, unsigned op, const char *what)
 {
-  if (r->ops->misc(r, GRAND_CHECK, op))
-    return (0);
+  if (r->ops->misc(r, GRAND_CHECK, op)) return (0);
   PyErr_Format(PyExc_TypeError, "operation %s not supported", what);
   return (-1);
 }
@@ -141,7 +161,7 @@ static PyObject *grmeth_seedint(PyObject *me, PyObject *arg)
   int i;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "i:seedint", &i) ||
-      checkop(r, GRAND_SEEDINT, "seedint"))
+      grand_check(me) || checkop(r, GRAND_SEEDINT, "seedint"))
     goto end;
   r->ops->misc(r, GRAND_SEEDINT, i);
   RETURN_ME;
@@ -154,7 +174,7 @@ static PyObject *grmeth_seedword(PyObject *me, PyObject *arg)
   uint32 u;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "O&:seedword", convu32, &u) ||
-      checkop(r, GRAND_SEEDUINT32, "seedword"))
+      grand_check(me) || checkop(r, GRAND_SEEDUINT32, "seedword"))
     goto end;
   r->ops->misc(r, GRAND_SEEDUINT32, u);
   RETURN_ME;
@@ -164,13 +184,12 @@ end:
 
 static PyObject *grmeth_seedblock(PyObject *me, PyObject *arg)
 {
-  char *p;
-  int n;
+  struct bin in;
   grand *r = GRAND_R(me);
-  if (!PyArg_ParseTuple(arg, "s#:seedblock", &p, &n) ||
-      checkop(r, GRAND_SEEDBLOCK, "seedblock"))
+  if (!PyArg_ParseTuple(arg, "O&:seedblock", convbin, &in) ||
+      grand_check(me) || checkop(r, GRAND_SEEDBLOCK, "seedblock"))
     goto end;
-  r->ops->misc(r, GRAND_SEEDBLOCK, p, (size_t)n);
+  r->ops->misc(r, GRAND_SEEDBLOCK, in.p, (size_t)in.sz);
   RETURN_ME;
 end:
   return (0);
@@ -182,7 +201,7 @@ static PyObject *grmeth_seedmp(PyObject *me, PyObject *arg)
   mp *xx;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "O:seedmp", &x) ||
-      checkop(r, GRAND_SEEDMP, "seedmp") ||
+      grand_check(me) || checkop(r, GRAND_SEEDMP, "seedmp") ||
       (xx = getmp(x)) == 0)
     goto end;
   r->ops->misc(r, GRAND_SEEDMP, xx);
@@ -194,12 +213,12 @@ 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) ||
-      checkop(r, GRAND_SEEDRAND, "seedrand"))
+      grand_check(me) || checkop(r, GRAND_SEEDRAND, "seedrand"))
     goto end;
   r->ops->misc(r, GRAND_SEEDRAND, rr);
   RETURN_ME;
@@ -210,33 +229,34 @@ end:
 static PyObject *grmeth_mask(PyObject *me, PyObject *arg)
 {
   grand *r = GRAND_R(me);
-  char *p, *q;
-  int sz;
+  struct bin in;
+  const octet *p; size_t n;
+  octet *q;
   PyObject *rc;
 
-  if (!PyArg_ParseTuple(arg, "s#:mask", &p, &sz)) return (0);
-  rc = bytestring_pywrap(0, sz);
-  q = PyString_AS_STRING(rc);
-  GR_FILL(r, q, sz);
-  while (sz--) *q++ ^= *p++;
+  if (!PyArg_ParseTuple(arg, "O&:mask", convbin, &in)) return (0);
+  if (grand_check(me)) return (0);
+  rc = bytestring_pywrap(0, in.sz);
+  q = (octet *)BIN_PTR(rc);
+  GR_FILL(r, q, in.sz);
+  p = in.p; n = in.sz; while (n--) *q++ ^= *p++;
   return (rc);
 }
 
 static void grand_pydealloc(PyObject *me)
 {
   grand_pyobj *g = (grand_pyobj *)me;
-  if (g->f & f_freeme)
-    GR_DESTROY(g->r);
+  if ((g->f & f_freeme) && g->r) GR_DESTROY(g->r);
   FREEOBJ(me);
 }
 
 static PyObject *grget_name(PyObject *me, void *hunoz)
-  { return (PyString_FromString(GRAND_R(me)->ops->name)); }
+  { return (grand_check(me) ? 0 : TEXT_FROMSTR(GRAND_R(me)->ops->name)); }
 
 static PyObject *grget_cryptop(PyObject *me, void *hunoz)
-  { return (getbool(GRAND_R(me)->ops->f & GRAND_CRYPTO)); }
+  { return (grand_check(me) ? 0 : getbool(GRAND_R(me)->ops->f & GRAND_CRYPTO)); }
 
-static PyGetSetDef grand_pygetset[] = {
+static const PyGetSetDef grand_pygetset[] = {
 #define GETSETNAME(op, name) gr##op##_##name
   GET  (name,          "R.name -> name of this kind of generator")
   GET  (cryptop,       "R.cryptop -> flag: cryptographically strong?")
@@ -244,12 +264,12 @@ static PyGetSetDef grand_pygetset[] = {
   { 0 }
 };
 
-static PyMethodDef grand_pymethods[] = {
+static const PyMethodDef grand_pymethods[] = {
 #define METHNAME(name) grmeth_##name
-  METH (byte,          "R.byte() -> BYTE")
-  METH (word,          "R.word() -> WORD")
+  NAMETH(byte,         "R.byte() -> BYTE")
+  NAMETH(word,         "R.word() -> WORD")
   METH (block,         "R.block(N) -> STRING")
-  KWMETH(mp,           "R.mp(bits, or = 0) -> MP")
+  KWMETH(mp,           "R.mp(bits, [or = 0]) -> MP")
   METH (range,         "R.range(MAX) -> INT")
   METH (mask,          "R.mask(STR) -> STR")
   METH (seedint,       "R.seedint(I)")
@@ -261,9 +281,9 @@ static PyMethodDef grand_pymethods[] = {
   { 0 }
 };
 
-static PyTypeObject grand_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.GRand",                    /* @tp_name@ */
+static const PyTypeObject grand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "GRand",                             /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -286,17 +306,17 @@ static PyTypeObject grand_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Generic random number source.",
+  "Generic random number source.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
-  grand_pymethods,                     /* @tp_methods@ */
+  0,                                   /* @tp_iternext@ */
+  PYMETHODS(grand),                    /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  grand_pygetset,                      /* @tp_getset@ */
+  PYGETSET(grand),                     /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -312,15 +332,15 @@ 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));
 }
 
-static PyTypeObject lcrand_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.LCRand",                   /* @tp_name@ */
+static const PyTypeObject lcrand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "LCRand",                            /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -343,14 +363,14 @@ 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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   0,                                   /* @tp_getset@ */
@@ -369,15 +389,15 @@ static PyTypeObject lcrand_pytype_skel = {
 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));
 }
 
-static PyTypeObject fibrand_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.FibRand",                  /* @tp_name@ */
+static const PyTypeObject fibrand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "FibRand",                           /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -400,14 +420,14 @@ static PyTypeObject fibrand_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Fibonacci generator.",
+  "FibRand([seed = 0]): Fibonacci generator.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   0,                                   /* @tp_getset@ */
@@ -425,28 +445,28 @@ static PyTypeObject fibrand_pytype_skel = {
 
 /*----- True random generator ---------------------------------------------*/
 
-static PyObject *trmeth_gate(PyObject *me, PyObject *arg)
-{
-  grand *r = GRAND_R(me);
-  if (!PyArg_ParseTuple(arg, ":gate")) return (0);
-  r->ops->misc(r, RAND_GATE);
-  RETURN_ME;
-}
+static PyObject *trmeth_gate(PyObject *me)
+  { grand *r = GRAND_R(me); r->ops->misc(GRAND_R(me), RAND_GATE); RETURN_ME; }
 
-static PyObject *trmeth_stretch(PyObject *me, PyObject *arg)
+static PyObject *trmeth_stretch(PyObject *me)
+  { grand *r = GRAND_R(me); r->ops->misc(r, RAND_STRETCH); RETURN_ME; }
+
+static PyObject *trmeth_add(PyObject *me, PyObject *arg)
 {
   grand *r = GRAND_R(me);
-  if (!PyArg_ParseTuple(arg, ":stretch")) return (0);
-  r->ops->misc(r, RAND_STRETCH);
+  struct bin in; unsigned goodbits;
+  if (!PyArg_ParseTuple(arg, "O&O&:add", convbin, &in, convuint, &goodbits))
+    return (0);
+  r->ops->misc(r, RAND_ADD, in.p, (size_t)in.sz, goodbits);
   RETURN_ME;
 }
 
 static PyObject *trmeth_key(PyObject *me, PyObject *arg)
 {
   grand *r = GRAND_R(me);
-  char *p; int n;
-  if (!PyArg_ParseTuple(arg, "s#:key", &p, &n)) return (0);
-  r->ops->misc(r, RAND_KEY, p, n);
+  struct bin k;
+  if (!PyArg_ParseTuple(arg, "O&:key", convbin, &k)) return (0);
+  r->ops->misc(r, RAND_KEY, k.p, (size_t)k.sz);
   RETURN_ME;
 }
 
@@ -462,21 +482,16 @@ end:
   return (0);
 }
 
-static PyObject *trmeth_timer(PyObject *me, PyObject *arg)
-{
-  grand *r = GRAND_R(me);
-  if (!PyArg_ParseTuple(arg, ":timer")) return (0);
-  r->ops->misc(r, RAND_TIMER);
-  RETURN_ME;
-}
+static PyObject *trmeth_timer(PyObject *me)
+  { grand *r = GRAND_R(me); r->ops->misc(r, RAND_TIMER); RETURN_ME; }
 
 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;
   r = rand_create();
   r->ops->misc(r, RAND_NOISESRC, &noise_source);
   r->ops->misc(r, RAND_SEED, 160);
@@ -485,13 +500,14 @@ end:
   return (rc);
 }
 
-static PyMethodDef truerand_pymethods[] = {
+static const PyMethodDef truerand_pymethods[] = {
 #define METHNAME(name) trmeth_##name
-  METH (gate,          "R.gate()")
-  METH (stretch,       "R.stretch()")
+  NAMETH(gate,         "R.gate()")
+  NAMETH(stretch,      "R.stretch()")
   METH (key,           "R.key(BYTES)")
   METH (seed,          "R.seed(NBITS)")
-  METH (timer,         "R.timer()")
+  METH (add,           "R.add(BYTES, GOODBITS")
+  NAMETH(timer,                "R.timer()")
 #undef METHNAME
   { 0 }
 };
@@ -502,16 +518,16 @@ static PyObject *trget_goodbits(PyObject *me, void *hunoz)
   return (PyInt_FromLong(r->ops->misc(r, RAND_GOODBITS)));
 }
 
-static PyGetSetDef truerand_pygetset[] = {
+static const PyGetSetDef truerand_pygetset[] = {
 #define GETSETNAME(op, name) tr##op##_##name
-  GET  (goodbits,      "R.goodbits -> good bits of entropy remaining")
+  GET  (goodbits,      "R.goodbits -> good bits of entropy remaining")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyTypeObject truerand_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.TrueRand",                 /* @tp_name@ */
+static const PyTypeObject truerand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "TrueRand",                          /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -534,17 +550,17 @@ static PyTypeObject truerand_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"True random number source.",
+  "TrueRand(): true random number source.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
-  truerand_pymethods,                  /* @tp_methods@ */
+  0,                                   /* @tp_iternext@ */
+  PYMETHODS(truerand),                 /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  truerand_pygetset,                   /* @tp_getset@ */
+  PYGETSET(truerand),                  /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -557,62 +573,404 @@ static PyTypeObject truerand_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
+/*----- Generators from symmetric encryption algorithms -------------------*/
+
+static PyTypeObject *gccrand_pytype, *gcrand_pytype, *gclatinrand_pytype;
+
+typedef grand *gcrand_func(const void *, size_t sz);
+typedef grand *gcirand_func(const void *, size_t sz, uint32);
+typedef grand *gcnrand_func(const void *, size_t sz, const void *);
+typedef grand *gcshakerand_func(const void *, size_t,
+                               const void *, size_t,
+                               const void *, size_t);
+typedef grand *gcshafuncrand_func(const void *, size_t,
+                                 const void *, size_t);
+typedef grand *gckmacrand_func(const void *, size_t, const void *, size_t);
+typedef struct gccrand_info {
+  const char *name;
+  const octet *keysz;
+  unsigned f;
+  size_t noncesz;
+  gcrand_func *func;
+} gccrand_info;
+
+#define RNGF_MASK 255u
+
+enum {
+  RNG_PLAIN = 0,
+  RNG_SEAL,
+  RNG_LATIN,
+  RNG_SHAKE,
+  RNG_KMAC
+};
+
+typedef struct gccrand_pyobj {
+  PyHeapTypeObject ty;
+  const gccrand_info *info;
+} gccrand_pyobj;
+#define GCCRAND_INFO(o) (((gccrand_pyobj *)(o))->info)
+
+#define GCCRAND_DEF(name, ksz, func, f, nsz)                           \
+  static const gccrand_info func##_info =                              \
+    { name, ksz, f, nsz, (gcrand_func *)func };
+RNGS(GCCRAND_DEF)
+
+static const gccrand_info *const gcrandtab[] = {
+#define GCCRAND_ENTRY(name, ksz, func, f, nsz) &func##_info,
+  RNGS(GCCRAND_ENTRY)
+  0
+};
+
+static PyObject *gcrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  const gccrand_info *info = GCCRAND_INFO(ty);
+  static const char *const kwlist[] = { "key", 0 };
+  struct bin k;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST,  convbin, &k))
+    goto end;
+  if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+  return (grand_dopywrap(ty, info->func(k.p, k.sz), f_freeme));
+end:
+  return (0);
+}
+
+static PyObject *gcirand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  const gccrand_info *info = GCCRAND_INFO(ty);
+  uint32 i = 0;
+  static const char *const kwlist[] = { "key", "i", 0 };
+  struct bin k;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+                                  convbin, &k, convu32, &i))
+    goto end;
+  if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+  return (grand_dopywrap(ty,
+                        ((gcirand_func *)info->func)(k.p, k.sz, i),
+                        f_freeme));
+end:
+  return (0);
+}
+
+static PyObject *gcnrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  const gccrand_info *info = GCCRAND_INFO(ty);
+  static const char *const kwlist[] = { "key", "nonce", 0 };
+  static const octet zn[24] = { 0 };
+  struct bin k, n;
+
+  n.p = zn; n.sz = info->noncesz; assert(info->noncesz <= sizeof(zn));
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+                                  convbin, &k, convbin, &n))
+    goto end;
+  if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+  if (n.sz != info->noncesz) VALERR("bad nonce length");
+  return (grand_dopywrap(ty,
+                        ((gcnrand_func *)info->func)(k.p, k.sz, n.p),
+                        f_freeme));
+end:
+  return (0);
+}
+
+static PyObject *gcshakyrand_pynew(PyTypeObject *ty,
+                                  PyObject *arg, PyObject *kw)
+{
+  const gccrand_info *info = GCCRAND_INFO(ty);
+  static const char
+    *const kwlist_shake[] = { "key", "func", "perso", 0 },
+    *const kwlist_func[] = { "key", "perso", 0 };
+  struct bin k, f = { 0, 0 }, p = { 0, 0 };
+
+  if ((info->f&RNGF_MASK) == RNG_SHAKE
+       ? !PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&O&:new",
+                                      (/*unconst*/ char **)kwlist_shake,
+                                      convbin, &k,
+                                      convbin, &f, convbin, &p)
+       : !PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new",
+                                      (/*unconst*/ char **)kwlist_func,
+                                      convbin, &k, convbin, &p))
+    goto end;
+  if (keysz(k.sz, info->keysz) != k.sz) VALERR("bad key length");
+  return (grand_dopywrap(ty,
+                        (info->f&RNGF_MASK) == RNG_SHAKE
+                          ? ((gcshakerand_func *)info->func)(f.p, f.sz,
+                                                             p.p, p.sz,
+                                                             k.p, k.sz)
+                          : ((gcshafuncrand_func *)info->func)(p.p, p.sz,
+                                                               k.p, k.sz),
+                        f_freeme));
+end:
+  return (0);
+}
+
+static PyObject *gccrand_pywrap(const gccrand_info *info)
+{
+  gccrand_pyobj *g = newtype(gccrand_pytype, 0, info->name);
+  g->info = info;
+  g->ty.ht_type.tp_basicsize = sizeof(grand_pyobj);
+  switch (info->f&RNGF_MASK) {
+    case RNG_LATIN: g->ty.ht_type.tp_base = gclatinrand_pytype; break;
+    default: g->ty.ht_type.tp_base = gcrand_pytype; break;
+  }
+  Py_INCREF(g->ty.ht_type.tp_base);
+  g->ty.ht_type.tp_flags = (Py_TPFLAGS_DEFAULT |
+                           Py_TPFLAGS_BASETYPE |
+                           Py_TPFLAGS_HEAPTYPE);
+  g->ty.ht_type.tp_alloc = PyType_GenericAlloc;
+  g->ty.ht_type.tp_free = 0;
+  switch (info->f&RNGF_MASK) {
+    case RNG_LATIN: g->ty.ht_type.tp_new = gcnrand_pynew; break;
+    case RNG_SEAL: g->ty.ht_type.tp_new = gcirand_pynew; break;
+    case RNG_SHAKE: case RNG_KMAC:
+      g->ty.ht_type.tp_new = gcshakyrand_pynew; break;
+    default: g->ty.ht_type.tp_new = gcrand_pynew; break;
+  }
+  typeready(&g->ty.ht_type);
+  return ((PyObject *)g);
+}
+
+static PyObject *gccrget_name(PyObject *me, void *hunoz)
+  { return (TEXT_FROMSTR(GCCRAND_INFO(me)->name)); }
+static PyObject *gccrget_keysz(PyObject *me, void *hunoz)
+  { return (keysz_pywrap(GCCRAND_INFO(me)->keysz)); }
+
+static PyObject *gclrmeth_tell(PyObject *me)
+{
+  grand *r = GRAND_R(me);
+  PyObject *rc = 0;
+  kludge64 off;
+
+  r->ops->misc(r, SALSA20_TELLU64, &off);
+  rc = getk64(off);
+  return (rc);
+}
+
+static PyObject *gclrmeth_seek(PyObject *me, PyObject *arg)
+{
+  grand *r = GRAND_R(me);
+  kludge64 off;
+
+  if (!PyArg_ParseTuple(arg, "O&:seek", convk64, &off)) return (0);
+  r->ops->misc(r, SALSA20_SEEKU64, off);
+  RETURN_ME;
+}
+
+static const PyGetSetDef gccrand_pygetset[] = {
+#define GETSETNAME(op, name) gccr##op##_##name
+  GET  (keysz,         "CR.keysz -> acceptable key sizes")
+  GET  (name,          "CR.name -> name of this kind of generator")
+#undef GETSETNAME
+  { 0 }
+};
+
+static const PyMethodDef gclatinrand_pymethods[] = {
+#define METHNAME(name) gclrmeth_##name
+  NAMETH(tell,         "R.tell() -> OFF")
+  METH (seek,          "R.seek(OFF)")
+#undef METHNAME
+  { 0 }
+};
+
+static const PyTypeObject gccrand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "GCCRand",                           /* @tp_name@ */
+  sizeof(gccrand_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@ */
+  "Metaclass for symmetric crypto-based generators.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  PYGETSET(gccrand),                   /* @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 const PyTypeObject gcrand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "GCRand",                            /* @tp_name@ */
+  sizeof(grand_pyobj),                 /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  grand_pydealloc,                     /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "Abstract base class for symmetric crypto-based generators.",
+
+  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 const PyTypeObject gclatinrand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "GCLatinRand",                       /* @tp_name@ */
+  sizeof(grand_pyobj),                 /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  grand_pydealloc,                     /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "Abstract base class for symmetric crypto-based generators.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  PYMETHODS(gclatinrand),              /* @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@ */
+};
+
 /*----- SSL and TLS generators --------------------------------------------*/
 
 static PyObject *sslprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *k, *s;
-  int ksz, ssz;
+  struct bin k, s;
   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,
-                                  &k, &ksz, &s, &ssz,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", KWLIST,
+                                  convbin, &k, convbin, &s,
                                   convgchash, &hco, convgchash, &hci))
     goto end;
-  rc = grand_dopywrap(ty, sslprf_rand(hco, hci, k, ksz, s, ssz), f_freeme);
+  rc = grand_dopywrap(ty, sslprf_rand(hco, hci, k.p, k.sz, s.p, s.sz),
+                     f_freeme);
 end:
   return (rc);
 }
 
 static PyObject *tlsdx_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *k, *s;
-  int ksz, ssz;
+  struct bin k, s;
   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,
-                                  &k, &ksz, &s, &ssz,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&:new", KWLIST,
+                                  convbin, &k, convbin, &s,
                                   convgcmac, &mc))
     goto end;
-  rc = grand_dopywrap(ty, tlsdx_rand(mc, k, ksz, s, ssz), f_freeme);
+  rc = grand_dopywrap(ty, tlsdx_rand(mc, k.p, k.sz, s.p, s.sz), f_freeme);
 end:
   return (rc);
 }
 
 static PyObject *tlsprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *k, *s;
-  int ksz, ssz;
+  struct bin k, s;
   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,
-                                  &k, &ksz, &s, &ssz,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", KWLIST,
+                                  convbin, &k, convbin, &s,
                                   convgcmac, &mcl, convgcmac, &mcr))
     goto end;
-  rc = grand_dopywrap(ty, tlsprf_rand(mcl, mcr, k, ksz, s, ssz), f_freeme);
+  rc = grand_dopywrap(ty, tlsprf_rand(mcl, mcr, k.p, k.sz, s.p, s.sz),
+                     f_freeme);
 end:
   return (rc);
 }
 
-static PyTypeObject sslprf_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.SSLRand",                  /* @tp_name@ */
+static const PyTypeObject sslprf_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "SSLRand",                           /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -635,14 +993,15 @@ 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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   0,                                   /* @tp_getset@ */
@@ -658,9 +1017,9 @@ static PyTypeObject sslprf_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
-static PyTypeObject tlsdx_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.TLSDataExpansion",         /* @tp_name@ */
+static const PyTypeObject tlsdx_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "TLSDataExpansion",                  /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -683,14 +1042,15 @@ 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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   0,                                   /* @tp_getset@ */
@@ -706,9 +1066,9 @@ static PyTypeObject tlsdx_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
-static PyTypeObject tlsprf_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.TLSPRF",                   /* @tp_name@ */
+static const PyTypeObject tlsprf_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "TLSPRF",                            /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -731,14 +1091,15 @@ 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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   0,                                   /* @tp_getset@ */
@@ -758,16 +1119,21 @@ static PyTypeObject tlsprf_pytype_skel = {
 
 static PyObject *dsarand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *p;
-  int sz;
+  struct bin in;
+  unsigned passes = 1;
+  grand *r;
   PyObject *rc = 0;
-  char *kwlist[] = { "seed", 0 };
+  static const char *const kwlist[] = { "seed", "passes", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+                                  convbin, &in, convuint, &passes))
     goto end;
-  rc = grand_dopywrap(ty, dsarand_create(p, sz), f_freeme);
+  if (!passes) VALERR("must be positive");
+  r = dsarand_create(in.p, in.sz);
+  if (passes != 1) r->ops->misc(r, DSARAND_PASSES, passes);
+  rc = grand_dopywrap(ty, r, f_freeme);
 end:
-  return (0);
+  return (rc);
 }
 
 static PyObject *drget_seed(PyObject *me, void *hunoz)
@@ -775,20 +1141,43 @@ static PyObject *drget_seed(PyObject *me, void *hunoz)
   grand *r = GRAND_R(me);
   int n = r->ops->misc(r, DSARAND_SEEDSZ);
   PyObject *rc = bytestring_pywrap(0, n);
-  r->ops->misc(r, DSARAND_GETSEED, PyString_AS_STRING(rc));
+  r->ops->misc(r, DSARAND_GETSEED, BIN_PTR(rc));
+  return (rc);
+}
+
+static PyObject *drget_passes(PyObject *me, void *hunoz)
+{
+  grand *r = GRAND_R(me);
+  return (PyInt_FromLong(r->ops->misc(r, DSARAND_PASSES, 0)));
+}
+
+static int drset_passes(PyObject *me, PyObject *val, void *hunoz)
+{
+  grand *r = GRAND_R(me);
+  long n;
+  int rc = -1;
+
+  if (!val) NIERR("__del__");
+  n = PyInt_AsLong(val); if (n == -1 && PyErr_Occurred()) goto end;
+  if (n <= 0) VALERR("must be positive");
+  if (n > ULONG_MAX) VALERR("out of range");
+  r->ops->misc(r, DSARAND_PASSES, (unsigned)n);
+  rc = 0;
+end:
   return (rc);
 }
 
-static PyGetSetDef dsarand_pygetset[] = {
+static const PyGetSetDef dsarand_pygetset[] = {
 #define GETSETNAME(op, name) dr##op##_##name
   GET  (seed,          "R.seed -> current generator seed")
+  GETSET(passes,       "R.passes -> number of passes to create output")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyTypeObject dsarand_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.DSARand",                  /* @tp_name@ */
+static const PyTypeObject dsarand_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "DSARand",                           /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -811,17 +1200,17 @@ static PyTypeObject dsarand_pytype_skel = {
     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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  dsarand_pygetset,                    /* @tp_getset@ */
+  PYGETSET(dsarand),                   /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -840,9 +1229,9 @@ static PyObject *bbs_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   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);
@@ -852,27 +1241,21 @@ end:
   return (rc);
 }
 
-static PyObject *bbsmeth_step(PyObject *me, PyObject *arg)
-{
-  grand *r = GRAND_R(me); if (!PyArg_ParseTuple(arg, ":step")) return (0);
-  r->ops->misc(r, BBS_STEP); RETURN_ME;
-}
+static PyObject *bbsmeth_step(PyObject *me)
+  { grand *r = GRAND_R(me); r->ops->misc(r, BBS_STEP); RETURN_ME; }
 
 static PyObject *bbsmeth_bits(PyObject *me, PyObject *arg)
 {
   grand *r = GRAND_R(me); unsigned n; uint32 w;
   if (!PyArg_ParseTuple(arg, "O&:bits", convuint, &n)) goto end;
   if (n > 32) VALERR("can't get more than 32 bits");
-  r->ops->misc(r, BBS_BITS, n, &w); return (getu32(w));
+  r->ops->misc(r, BBS_BITS, n, &w); return (getulong(w));
 end:
   return (0);
 }
 
-static PyObject *bbsmeth_wrap(PyObject *me, PyObject *arg)
-{
-  grand *r = GRAND_R(me); if (!PyArg_ParseTuple(arg, ":wrap")) return (0);
-  r->ops->misc(r, BBS_WRAP); RETURN_ME;
-}
+static PyObject *bbsmeth_wrap(PyObject *me)
+  { grand *r = GRAND_R(me); r->ops->misc(r, BBS_WRAP); RETURN_ME; }
 
 static PyObject *bbsget_n(PyObject *me, void *hunoz)
 {
@@ -888,8 +1271,9 @@ static PyObject *bbsget_x(PyObject *me, void *hunoz)
 
 static int bbsset_x(PyObject *me, PyObject *val, void *hunoz)
 {
-  mp *x = 0; grand *r = GRAND_R(me); int rc = -1;
-  if ((x = getmp(val)) == 0) goto end; r->ops->misc(r, BBS_SET, x); rc = 0;
+  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;
   end: mp_drop(x); return (rc);
 }
 
@@ -899,16 +1283,16 @@ static PyObject *bbsget_stepsz(PyObject *me, void *hunoz)
   return (PyInt_FromLong(r->ops->misc(r, BBS_STEPSZ)));
 }
 
-static PyMethodDef bbs_pymethods[] = {
+static const PyMethodDef bbs_pymethods[] = {
 #define METHNAME(name) bbsmeth_##name
-  METH (step,  "R.step(): steps the generator (not useful)")
-  METH (bits,  "R.bits(N) -> W: returns N bits (<= 32) from the generator")
-  METH (wrap,  "R.wrap(): flushes unused bits in internal buffer")
+  NAMETH(step,         "R.step(): steps the generator (not useful)")
+  METH (bits,   "R.bits(N) -> W: returns N bits (<= 32) from the generator")
+  NAMETH(wrap,         "R.wrap(): flushes unused bits in internal buffer")
 #undef METHNAME
   { 0 }
 };
 
-static PyGetSetDef bbs_pygetset[] = {
+static const PyGetSetDef bbs_pygetset[] = {
 #define GETSETNAME(op, name) bbs##op##_##name
   GET  (n,             "R.n -> Blum modulus")
   GETSET(x,            "R.x -> current seed value")
@@ -917,9 +1301,9 @@ static PyGetSetDef bbs_pygetset[] = {
   { 0 }
 };
 
-static PyTypeObject bbs_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.BlumBlumShub",             /* @tp_name@ */
+static const PyTypeObject bbs_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "BlumBlumShub",                      /* @tp_name@ */
   sizeof(grand_pyobj),                 /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -942,17 +1326,17 @@ 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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
-  bbs_pymethods,                       /* @tp_methods@ */
+  0,                                   /* @tp_iternext@ */
+  PYMETHODS(bbs),                      /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  bbs_pygetset,                                /* @tp_getset@ */
+  PYGETSET(bbs),                       /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -977,9 +1361,9 @@ static PyObject *bbspriv_pynew(PyTypeObject *ty,
 {
   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;
@@ -998,23 +1382,25 @@ end:
   return ((PyObject *)rc);
 }
 
-static PyObject *meth__BBSPriv_generate(PyObject *me,
-                                       PyObject *arg, PyObject *kw)
+static PyObject *bpmeth_generate(PyObject *me, PyObject *arg, PyObject *kw)
 {
   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[] =
+    { "nbits", "event", "rng", "nsteps", "seed", 0 };
   bbspriv_pyobj *rc = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
-                                  &me, convuint, &nbits, convpgev, &evt,
+  evt.exc = &exc;
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&O&O&O&:generate", KWLIST,
+                                  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;
@@ -1023,7 +1409,7 @@ static PyObject *meth__BBSPriv_generate(PyObject *me,
   rc->bp.n = MP_COPY(bp.n);
 end:
   mp_drop(bp.p); mp_drop(bp.q); mp_drop(bp.n); mp_drop(x);
-  return ((PyObject *)rc); 
+  return ((PyObject *)rc);
 }
 
 static void bbspriv_pydealloc(PyObject *me)
@@ -1058,26 +1444,28 @@ static PyObject *bpget_p(PyObject *me, void *hunoz)
 static PyObject *bpget_q(PyObject *me, void *hunoz)
   { return (mp_pywrap(MP_COPY(BBSPRIV_BP(me)->q))); }
 
-static PyMethodDef bbspriv_pymethods[] = {
+static const PyMethodDef bbspriv_pymethods[] = {
 #define METHNAME(name) bpmeth_##name
-  METH (ff,                    "R.ff(N): fast-forward N places")
-  METH (rew,                   "R.rew(N): rewind N places")
+  METH (ff,            "R.ff(N): fast-forward N places")
+  METH (rew,           "R.rew(N): rewind N places")
+  KWSMTH(generate,     "generate(NBITS, [event = pgen_nullev], "
+                             "[rng = rand], [nsteps = 0], [seed = 2]) -> R")
 #undef METHNAME
   { 0 }
 };
 
-static PyGetSetDef bbspriv_pygetset[] = {
+static const PyGetSetDef bbspriv_pygetset[] = {
 #define GETSETNAME(op, name) bp##op##_##name
-  GET  (n,             "R.n -> Blum modulus")
-  GET  (p,             "R.p -> one of the factors of the modulus")
-  GET  (q,             "R.q -> one of the factors of the modulus")
+  GET  (n,             "R.n -> Blum modulus")
+  GET  (p,             "R.p -> one of the factors of the modulus")
+  GET  (q,             "R.q -> one of the factors of the modulus")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyTypeObject bbspriv_pytype_skel = {
-  PyObject_HEAD_INIT(&PyType_Type) 0,  /* Header */
-  "catacomb.BBSPriv",                  /* @tp_name@ */
+static const PyTypeObject bbspriv_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "BBSPriv",                           /* @tp_name@ */
   sizeof(bbspriv_pyobj),               /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -1100,17 +1488,18 @@ static PyTypeObject bbspriv_pytype_skel = {
     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@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
-  0,                                   /* @tp_iternexr@ */
-  bbspriv_pymethods,                   /* @tp_methods@ */
+  0,                                   /* @tp_iternext@ */
+  PYMETHODS(bbspriv),                  /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  bbspriv_pygetset,                    /* @tp_getset@ */
+  PYGETSET(bbspriv),                   /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -1125,11 +1514,8 @@ static PyTypeObject bbspriv_pytype_skel = {
 
 /*----- Global stuff ------------------------------------------------------*/
 
-static PyMethodDef methods[] = {
-#define METHNAME(name) meth_##name
-  KWMETH(_BBSPriv_generate,            "\
-generate(NBITS, [event = pgen_nullev, rng = rand, nsteps = 0, seed = 2])")
-#undef METHNAME
+static const struct nameval consts[] = {
+  CONST(RAND_IBITS),
   { 0 }
 };
 
@@ -1145,11 +1531,18 @@ void rand_pyinit(void)
   INITTYPE(sslprf, grand);
   INITTYPE(tlsdx, grand);
   INITTYPE(tlsprf, grand);
+  INITTYPE(gccrand, type);
+  INITTYPE(gcrand, grand);
+  INITTYPE(gclatinrand, gcrand);
   rand_noisesrc(RAND_GLOBAL, &noise_source);
   rand_seed(RAND_GLOBAL, 160);
-  addmethods(methods);
 }
 
+static const char *crand_namefn(const void *p)
+  { const gccrand_info *const *cls = p; return (*cls ? (*cls)->name : 0); }
+static PyObject *crand_valfn(const void *p)
+  { const gccrand_info *const *cls = p; return (gccrand_pywrap(*cls)); }
+
 void rand_pyinsert(PyObject *mod)
 {
   INSERT("GRand", grand_pytype);
@@ -1162,8 +1555,15 @@ void rand_pyinsert(PyObject *mod)
   INSERT("DSARand", dsarand_pytype);
   INSERT("BlumBlumShub", bbs_pytype);
   INSERT("BBSPriv", bbspriv_pytype);
+  INSERT("GCCRand", gccrand_pytype);
+  INSERT("GCRand", gcrand_pytype);
+  INSERT("GCLatinRand", gclatinrand_pytype);
   rand_pyobj = grand_pywrap(&rand_global, 0); Py_INCREF(rand_pyobj);
+  gccrands_dict = make_algtab(gcrandtab, sizeof(gccrand_info *),
+                             crand_namefn, crand_valfn);
+  INSERT("gccrands", gccrands_dict); Py_INCREF(gccrands_dict);
   INSERT("rand", rand_pyobj);
+  setconstants(mod, consts);
 }
 
 /*----- That's all, folks -------------------------------------------------*/