chiark / gitweb /
Merge branch '1.2.x' into 1.3.x
authorMark Wooding <mdw@distorted.org.uk>
Tue, 7 Apr 2020 23:56:01 +0000 (00:56 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 7 Apr 2020 23:56:01 +0000 (00:56 +0100)
* 1.2.x:
  rand.c: More `Py_ssize_t' fixes.

33 files changed:
.mailmap [new file with mode: 0644]
MANIFEST.in
algorithms.c
algorithms.py
buffer.c
bytestring.c
catacomb-python.h
catacomb.c
catacomb/__init__.py
debian/changelog
debian/control
debian/copyright
ec.c
field.c
group.c
key.c
mp.c
passphrase.c
pgen.c
pock [new file with mode: 0644]
pock.1 [new file with mode: 0644]
pubkey.c
pwsafe
pwsafe.1 [new file with mode: 0644]
rand.c
setup.py
share.c
t/t-algorithms.py
t/t-buffer.py
t/t-bytes.py
t/t-ec.py
t/t-group.py
util.c

diff --git a/.mailmap b/.mailmap
new file mode 100644 (file)
index 0000000..96fe7ad
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1 @@
+Mark Wooding <mdw@distorted.org.uk> <mdw>
index c09417be9d064d3246c7c66838956da8e2740bed..33ce54af5430f17382557d18e9c563f915eb5c05 100644 (file)
@@ -1,4 +1,4 @@
-### Manifest template for Catacomb/Python.
+### Manifest template for Catacomb/Python. -*-conf-*-
 
 ## Generated build machinery.
 include COPYING auto-version mdwsetup.py pysetup.mk
@@ -11,8 +11,14 @@ include *.c *.h
 include t/*.py t/keyring
 include algorithms.py
 exclude algorithms.h
+
+## Scripts.
+include pock
 include pwsafe
 
+## Manual pages.
+include *.[13]
+
 ## Python wrapping.
 recursive-include catacomb *.py
 
index edd73fd8119224577430b7aa7062bc990a58453c..064fb03dabb2c0faf3f6edb4dead5e55e8d77958 100644 (file)
@@ -83,11 +83,11 @@ PyObject *keysz_pywrap(const octet *k)
 static PyObject *keyszany_pynew(PyTypeObject *ty,
                                PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "default", 0 };
+  static const char *const kwlist[] = { "default", 0 };
   int dfl;
   keysz_pyobj *o;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i:new", kwlist, &dfl))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i:new", KWLIST, &dfl))
     goto end;
   if (dfl < 0) VALERR("key size cannot be negative");
   o = (keysz_pyobj *)ty->tp_alloc(ty, 0);
@@ -100,18 +100,16 @@ end:
 static PyObject *keyszrange_pynew(PyTypeObject *ty,
                                  PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "default", "min", "max", "mod", 0 };
+  static const char *const kwlist[] = { "default", "min", "max", "mod", 0 };
   int dfl, min = 0, max = 0, mod = 1;
   keyszrange_pyobj *o;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|iii:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|iii:new", KWLIST,
                                   &dfl, &min, &max, &mod))
     goto end;
-  if (dfl < 0 || min < 0 || max < 0)
-    VALERR("key size cannot be negative");
-  if (min > dfl || (max && dfl > max))
-    VALERR("bad key size bounds");
-  if (mod <= 0 || dfl % mod || min % mod || max % mod)
+  if (dfl < 0 || min < 0) VALERR("key size cannot be negative");
+  if (min > dfl || (max && dfl > max)) VALERR("bad key size bounds");
+  if (mod <= 0 || dfl%mod || min%mod || max%mod)
     VALERR("bad key size modulus");
   o = (keyszrange_pyobj *)ty->tp_alloc(ty, 0);
   o->dfl = dfl;
@@ -126,14 +124,13 @@ end:
 static PyObject *keyszset_pynew(PyTypeObject *ty,
                                PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "default", "set", 0 };
+  static const char *const kwlist[] = { "default", "set", 0 };
   int dfl, i, n, xx;
   PyObject *set = 0;
   PyObject *x = 0, *l = 0;
   keyszset_pyobj *o = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|O:new", kwlist,
-                                         &dfl, &set))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "i|O:new", KWLIST, &dfl, &set))
     goto end;
   if (!set) set = PyTuple_New(0);
   else Py_INCREF(set);
@@ -263,7 +260,7 @@ static PyTypeObject keysz_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key size constraints.",
+"Key size constraints.  Abstract.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -311,7 +308,8 @@ static PyTypeObject keyszany_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key size constraints.  This object imposes no constraints on size.",
+"KeySZAny(DEFAULT)\n\
+  Key size constraints.  This object imposes no constraints on size.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -359,8 +357,9 @@ static PyTypeObject keyszrange_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key size constraints.  This object asserts minimum and maximum (if\n\
-sizes, and requires the key length to be a multiple of some value.",
+"KeySZRange(DEFAULT, [min = 0], [max = 0], [mod = 1])\n\
+  Key size constraints.  Key size must be between MIN and MAX inclusive,\n\
+  and be a multiple of MOD.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -408,8 +407,8 @@ static PyTypeObject keyszset_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key size constraints.  This object requires the key to be one of a\n\
-few listed sizes.",
+"KeySZSet(DEFAULT, SEQ)\n\
+  Key size constraints.  Key size must be DEFAULT or one in SEQ.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -457,29 +456,27 @@ PyTypeObject *gccipher_pytype, *gcipher_pytype;
 CONVFUNC(gccipher, gccipher *, GCCIPHER_CC)
 CONVFUNC(gcipher, gcipher *, GCIPHER_C)
 
-PyObject *gcipher_pywrap(PyObject *cobj, gcipher *c, unsigned f)
+PyObject *gcipher_pywrap(PyObject *cobj, gcipher *c)
 {
   gcipher_pyobj *g;
   if (!cobj) cobj = gccipher_pywrap((/*unconst*/ gccipher *)GC_CLASS(c));
   else Py_INCREF(cobj);
   g = PyObject_NEW(gcipher_pyobj, (PyTypeObject *)cobj);
   g->c = c;
-  g->f = f;
   return ((PyObject *)g);
 }
 
 static PyObject *gcipher_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "k", 0 };
+  static const char *const kwlist[] = { "k", 0 };
   char *k;
   Py_ssize_t sz;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
     goto end;
   if (keysz(sz, GCCIPHER_CC(ty)->keysz) != sz) VALERR("bad key length");
   return (gcipher_pywrap((PyObject *)ty,
-                        GC_INIT(GCCIPHER_CC(ty), k, sz),
-                        f_freeme));
+                        GC_INIT(GCCIPHER_CC(ty), k, sz)));
 end:
   return (0);
 }
@@ -503,8 +500,7 @@ PyObject *gccipher_pywrap(gccipher *cc)
 
 static void gcipher_pydealloc(PyObject *me)
 {
-  if (GCIPHER_F(me) & f_freeme)
-    GC_DESTROY(GCIPHER_C(me));
+  GC_DESTROY(GCIPHER_C(me));
   Py_DECREF(me->ob_type);
   FREEOBJ(me);
 }
@@ -713,6 +709,1085 @@ static PyTypeObject gcipher_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
+/*----- Authenticated encryption ------------------------------------------*/
+
+PyTypeObject *gcaead_pytype, *gaeadkey_pytype;
+PyTypeObject *gcaeadaad_pytype, *gaeadaad_pytype;
+PyTypeObject *gcaeadenc_pytype, *gaeadenc_pytype;
+PyTypeObject *gcaeaddec_pytype, *gaeaddec_pytype;
+
+CONVFUNC(gcaead, gcaead *, GCAEAD_AEC)
+CONVFUNC(gaeadkey, gaead_key *, GAEADKEY_K)
+
+PyObject *gaeadkey_pywrap(PyObject *cobj, gaead_key *k)
+{
+  gaeadkey_pyobj *gk;
+
+  if (!cobj) cobj = gcaead_pywrap((/*unconst*/ gcaead *)GAEAD_CLASS(k));
+  else Py_INCREF(cobj);
+  gk = PyObject_NEW(gaeadkey_pyobj, (PyTypeObject *)cobj);
+  gk->k = k;
+  return ((PyObject *)gk);
+}
+
+static PyObject *gaeadkey_pynew(PyTypeObject *ty,
+                               PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "k", 0 };
+  char *k;
+  Py_ssize_t sz;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
+    goto end;
+  if (keysz(sz, GCAEAD_AEC(ty)->keysz) != sz) VALERR("bad key length");
+  return (gaeadkey_pywrap((PyObject *)ty,
+                         GAEAD_KEY(GCAEAD_AEC(ty), k, sz)));
+end:
+  return (0);
+}
+
+PyObject *gcaead_pywrap(gcaead *aec)
+{
+  gcaead_pyobj *gck;
+  gcaeadaad_pyobj *gca;
+  gcaeadenc_pyobj *gce;
+  gcaeaddec_pyobj *gcd;
+
+#define MKTYPE(obj, thing, newfn, namefmt) do {                                \
+  (obj) = newtype(gcaead_pytype, 0, 0);                                        \
+  (obj)->ty.ht_name = PyString_FromFormat(namefmt, aec->name);         \
+  (obj)->ty.ht_type.tp_name = PyString_AS_STRING((obj)->ty.ht_name);   \
+  (obj)->ty.ht_type.tp_basicsize = sizeof(gaead##thing##_pyobj);       \
+  (obj)->ty.ht_type.tp_base = gaead##thing##_pytype;                   \
+  Py_INCREF(gaead##thing##_pytype);                                    \
+  (obj)->ty.ht_type.tp_flags =                                         \
+    (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE);  \
+  (obj)->ty.ht_type.tp_alloc = PyType_GenericAlloc;                    \
+  (obj)->ty.ht_type.tp_free = 0;                                       \
+  (obj)->ty.ht_type.tp_new = newfn;                                    \
+  typeready(&(obj)->ty.ht_type);                                       \
+} while (0)
+
+  MKTYPE(gck, key, gaeadkey_pynew, "%s(key)");
+  MKTYPE(gca, aad, abstract_pynew, "%s(aad-hash)");
+  MKTYPE(gce, enc, abstract_pynew, "%s(encrypt)");
+  MKTYPE(gcd, dec, abstract_pynew, "%s(decrypt)");
+
+#undef MKTYPE
+
+  gck->aec = aec; gck->aad = gca; gck->enc = gce; gck->dec = gcd;
+  gca->key = gce->key = gcd->key = gck;
+  return ((PyObject *)gck);
+}
+
+static void gaeadkey_pydealloc(PyObject *me)
+  { GAEAD_DESTROY(GAEADKEY_K(me)); Py_DECREF(me->ob_type); FREEOBJ(me); }
+
+static PyObject *gcaeget_name(PyObject *me, void *hunoz)
+  { return (PyString_FromString(GCAEAD_AEC(me)->name)); }
+
+static PyObject *gcaeget_keysz(PyObject *me, void *hunoz)
+  { return (keysz_pywrap(GCAEAD_AEC(me)->keysz)); }
+
+static PyObject *gcaeget_noncesz(PyObject *me, void *hunoz)
+  { return (keysz_pywrap(GCAEAD_AEC(me)->noncesz)); }
+
+static PyObject *gcaeget_tagsz(PyObject *me, void *hunoz)
+  { return (keysz_pywrap(GCAEAD_AEC(me)->tagsz)); }
+
+static PyObject *gcaeget_blksz(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GCAEAD_AEC(me)->blksz)); }
+
+static PyObject *gcaeget_bufsz(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GCAEAD_AEC(me)->bufsz)); }
+
+static PyObject *gcaeget_ohd(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GCAEAD_AEC(me)->ohd)); }
+
+static PyObject *gcaeget_flags(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(GCAEAD_AEC(me)->f)); }
+
+static PyGetSetDef gcaead_pygetset[] = {
+#define GETSETNAME(op, name) gcae##op##_##name
+  GET  (keysz,                 "AEC.keysz -> acceptable key sizes")
+  GET  (noncesz,               "AEC.noncesz -> acceptable nonce sizes")
+  GET  (tagsz,                 "AEC.tagsz -> acceptable tag sizes")
+  GET  (blksz,                 "AEC.blksz -> block size, or zero")
+  GET  (bufsz,                 "AEC.bufsz -> amount of data buffered internally")
+  GET  (ohd,                   "AEC.ohd -> maximum encryption overhead")
+  GET  (name,                  "AEC.name -> name of this kind of AEAD scheme")
+  GET  (flags,                 "AEC.flags -> mask of `AEADF_...' flags")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyObject *gaekmeth_aad(PyObject *me, PyObject *arg)
+{
+  const gaead_key *k = GAEADKEY_K(me);
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, ":aad")) return (0);
+  if (k->ops->c->f&AEADF_AADNDEP)
+    VALERR("aad must be associated with enc/dec op");
+  rc = gaeadaad_pywrap((PyObject *)GCAEAD_AAD(me->ob_type),
+                      GAEAD_AAD(k), 0, 0);
+end:
+  return (rc);
+}
+
+static int check_aead_encdec(const gcaead *aec, unsigned *f_out, size_t nsz,
+                            PyObject *hszobj, size_t *hsz_out,
+                            PyObject *mszobj, size_t *msz_out,
+                            PyObject *tszobj, size_t *tsz_out)
+{
+  unsigned f = 0, miss;
+  int rc = -1;
+
+  if (hszobj != Py_None)
+    { f |= AEADF_PCHSZ; if (!convszt(hszobj, hsz_out)) goto end; }
+  if (mszobj != Py_None)
+    { f |= AEADF_PCMSZ; if (!convszt(mszobj, msz_out)) goto end; }
+  if (tszobj != Py_None)
+    { f |= AEADF_PCTSZ; if (!convszt(tszobj, tsz_out)) goto end; }
+  miss = aec->f&~f;
+  if (miss&AEADF_PCHSZ) VALERR("header length precommitment required");
+  if (miss&AEADF_PCMSZ) VALERR("message length precommitment required");
+  if (miss&AEADF_PCTSZ) VALERR("tag length precommitment required");
+  if (keysz(nsz, aec->noncesz) != nsz) VALERR("bad nonce length");
+  if (tszobj != Py_None && keysz(*tsz_out, aec->tagsz) != *tsz_out)
+    VALERR("bad tag length");
+  *f_out = f | aec->f; rc = 0;
+end:
+  return (rc);
+}
+
+static PyObject *gaekmeth_enc(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "nonce", "hsz", "msz", "tsz", 0 };
+  const gaead_key *k = GAEADKEY_K(me);
+  gaead_enc *e;
+  PyObject *rc = 0;
+  char *n; Py_ssize_t nsz;
+  PyObject *hszobj = Py_None, *mszobj = Py_None, *tszobj = Py_None;
+  size_t hsz = 0, msz = 0, tsz = 0;
+  unsigned f;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:enc", KWLIST,
+                                  &n, &nsz, &hszobj, &mszobj, &tszobj))
+    goto end;
+  if (check_aead_encdec(k->ops->c, &f, nsz,
+                       hszobj, &hsz, mszobj, &msz, tszobj, &tsz))
+    goto end;
+  e = GAEAD_ENC(GAEADKEY_K(me), n, nsz, hsz, msz, tsz);
+  if (!e) VALERR("bad aead parameter combination");
+  rc = gaeadenc_pywrap((PyObject *)GCAEAD_ENC(me->ob_type),
+                      e, f, hsz, msz, tsz);
+end:
+  return (rc);
+}
+
+static PyObject *gaekmeth_dec(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "nonce", "hsz", "csz", "tsz", 0 };
+  const gaead_key *k = GAEADKEY_K(me);
+  gaead_dec *d;
+  PyObject *rc = 0;
+  char *n; Py_ssize_t nsz;
+  PyObject *hszobj = Py_None, *cszobj = Py_None, *tszobj = Py_None;
+  size_t hsz = 0, csz = 0, tsz = 0;
+  unsigned f;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:dec", KWLIST,
+                                  &n, &nsz, &hszobj, &cszobj, &tszobj))
+    goto end;
+  if (check_aead_encdec(k->ops->c, &f, nsz,
+                       hszobj, &hsz, cszobj, &csz, tszobj, &tsz))
+    goto end;
+  d = GAEAD_DEC(GAEADKEY_K(me), n, nsz, hsz, csz, tsz);
+  if (!d) VALERR("bad aead parameter combination");
+  rc = gaeaddec_pywrap((PyObject *)GCAEAD_DEC(me->ob_type),
+                      d, f, hsz, csz, tsz);
+end:
+  return (rc);
+}
+
+static PyMethodDef gaeadkey_pymethods[] = {
+#define METHNAME(name) gaekmeth_##name
+  METH  (aad,                  "KEY.aad() -> AAD")
+  KWMETH(enc,                  "KEY.enc(NONCE, [hsz], [msz], [tsz]) -> ENC")
+  KWMETH(dec,                  "KEY.dec(NONCE, [hsz], [csz], [tsz]) -> DEC")
+#undef METHNAME
+  { 0 }
+};
+
+PyObject *gaeadaad_pywrap(PyObject *cobj, gaead_aad *a,
+                         unsigned f, size_t hsz)
+{
+  gaeadaad_pyobj *ga;
+
+  assert(cobj); Py_INCREF(cobj);
+  ga = PyObject_NEW(gaeadaad_pyobj, (PyTypeObject *)cobj);
+  ga->a = a; ga->f = f; ga->hsz = hsz; ga->hlen = 0;
+  return ((PyObject *)ga);
+}
+
+static void gaeadaad_pydealloc(PyObject *me)
+{
+  gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
+
+  if (ga->a) GAEAD_DESTROY(ga->a);
+  Py_DECREF(me->ob_type); FREEOBJ(me);
+}
+
+static int gaea_check(PyObject *me)
+{
+  gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
+  int rc = -1;
+
+  if ((ga->f&AEADF_DEAD) || !ga->a) VALERR("aad object no longer active");
+  rc = 0;
+end:
+  return (rc);
+}
+
+static void gaea_invalidate(gaeadaad_pyobj *ga)
+  { if (ga) ga->f |= AEADF_DEAD; }
+
+static void gaea_sever(gaeadaad_pyobj **ga_inout)
+{
+  gaeadaad_pyobj *ga = *ga_inout;
+  if (ga) { ga->f |= AEADF_DEAD; ga->a = 0; Py_DECREF(ga); *ga_inout = 0; }
+}
+
+static PyObject *gaeaget_hsz(PyObject *me, void *hunoz)
+{
+  if (gaea_check(me)) return (0);
+  else if (GAEADAAD_F(me)&AEADF_PCHSZ) return getulong(GAEADAAD_HSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaeaget_hlen(PyObject *me, void *hunoz)
+  { return (gaea_check(me) ? 0 : getulong(GAEADAAD_HLEN(me))); }
+
+static PyGetSetDef gaeadaad_pygetset[] = {
+#define GETSETNAME(op, name) gaea##op##_##name
+  GET  (hsz,                   "AAD.hsz -> precommitted header length or `None'")
+  GET  (hlen,                  "AAD.hlen -> header length so far")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyObject *gaeameth_copy(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, ":copy")) goto end;
+  if (gaea_check(me)) goto end;
+  if (GAEADAAD_F(me)&AEADF_AADNDEP)
+    VALERR("can't duplicate nonce-dependent aad");
+  rc = gaeadaad_pywrap((PyObject *)me->ob_type,
+                      GAEAD_DUP(GAEADAAD_A(me)), 0, 0);
+  GAEADAAD_HLEN(rc) = GAEADAAD_HLEN(me);
+end:
+  return (rc);
+}
+
+static int gaeadaad_hash(PyObject *me, const void *h, size_t hsz)
+{
+  gaeadaad_pyobj *ga = (gaeadaad_pyobj *)me;
+  int rc = -1;
+
+  if (gaea_check(me)) goto end;
+  if ((ga->f&AEADF_NOAAD) && hsz)
+    VALERR("header data not permitted");
+  if ((ga->f&AEADF_PCHSZ) && hsz > ga->hsz - ga->hlen)
+    VALERR("too large for precommitted header length");
+  GAEAD_HASH(ga->a, h, hsz); ga->hlen += hsz;
+  rc = 0;
+end:
+  return (rc);
+}
+
+
+static PyObject *gaeameth_hash(PyObject *me, PyObject *arg)
+{
+  char *h; Py_ssize_t hsz;
+
+  if (!PyArg_ParseTuple(arg, "s#:hash", &h, &hsz)) return (0);
+  if (gaeadaad_hash(me, h, hsz)) return (0);
+  RETURN_ME;
+}
+
+#define GAEAMETH_HASHU_(n, W, w)                                       \
+  static PyObject *gaeameth_hashu##w(PyObject *me, PyObject *arg)      \
+  {                                                                    \
+    uint##n x; octet b[SZ_##W];                                                \
+    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
+    STORE##W(b, x); if (gaeadaad_hash(me, b, sizeof(b))) return (0);   \
+    RETURN_ME;                                                         \
+  }
+DOUINTCONV(GAEAMETH_HASHU_)
+
+#define GAEAMETH_HASHBUF_(n, W, w)                                     \
+  static PyObject *gaeameth_hashbuf##w(PyObject *me, PyObject *arg)    \
+  {                                                                    \
+    char *p; Py_ssize_t sz; octet b[SZ_##W];                           \
+    if (!PyArg_ParseTuple(arg, "s#:hashbuf" #w, &p, &sz)) goto end;    \
+    if (sz > MASK##n) TYERR("string too long");                                \
+    STORE##W(b, sz); if (gaeadaad_hash(me, b, sizeof(b))) goto end;    \
+    if (gaeadaad_hash(me, p, sz)) goto end;                            \
+    RETURN_ME;                                                         \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTCONV(GAEAMETH_HASHBUF_)
+
+static PyObject *gaeameth_hashstrz(PyObject *me, PyObject *arg)
+{
+  char *p;
+  if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+  if (gaeadaad_hash(me, p, strlen(p) + 1)) return (0);
+  RETURN_ME;
+}
+
+static PyMethodDef gaeadaad_pymethods[] = {
+#define METHNAME(name) gaeameth_##name
+  METH  (copy,                 "AAD.copy() -> AAD'")
+  METH  (hash,                 "AAD.hash(H)")
+#define METHU_(n, W, w) METH(hashu##w, "AAD.hashu" #w "(WORD)")
+  DOUINTCONV(METHU_)
+#undef METHU_
+#define METHBUF_(n, W, w) METH(hashbuf##w, "AAD.hashbuf" #w "(BYTES)")
+  DOUINTCONV(METHBUF_)
+#undef METHBUF_
+  METH  (hashstrz,             "AAD.hashstrz(STRING)")
+#undef METHNAME
+  { 0 }
+};
+
+PyObject *gaeadenc_pywrap(PyObject *cobj, gaead_enc *e, unsigned f,
+                         size_t hsz, size_t msz, size_t tsz)
+{
+  gaeadenc_pyobj *ge;
+
+  assert(cobj); Py_INCREF(cobj);
+  ge = PyObject_NEW(gaeadenc_pyobj, (PyTypeObject *)cobj);
+  ge->e = e; ge->f = f; ge->hsz = hsz; ge->msz = msz; ge->tsz = tsz;
+  ge->aad = 0; ge->mlen = 0;
+  return ((PyObject *)ge);
+}
+
+static void gaeadenc_pydealloc(PyObject *me)
+{
+  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+
+  gaea_sever(&ge->aad); GAEAD_DESTROY(ge->e);
+  Py_DECREF(me->ob_type); FREEOBJ(me);
+}
+
+static PyObject *gaeeget_hsz(PyObject *me, void *hunoz)
+{
+  if (GAEADENC_F(me)&AEADF_PCHSZ) return getulong(GAEADENC_HSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaeeget_msz(PyObject *me, void *hunoz)
+{
+  if (GAEADENC_F(me)&AEADF_PCMSZ) return getulong(GAEADENC_MSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaeeget_tsz(PyObject *me, void *hunoz)
+{
+  if (GAEADENC_F(me)&AEADF_PCTSZ) return getulong(GAEADENC_TSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaeeget_mlen(PyObject *me, void *hunoz)
+  { return getulong(GAEADENC_MLEN(me)); }
+
+static PyGetSetDef gaeadenc_pygetset[] = {
+#define GETSETNAME(op, name) gaee##op##_##name
+  GET  (hsz,                   "ENC.hsz -> precommitted header length or `None'")
+  GET  (msz,                   "ENC.msz -> precommitted message length or `None'")
+  GET  (tsz,                   "ENC.tsz -> precommitted tag length or `None'")
+  GET  (mlen,                  "ENC.mlen -> message length so far")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyObject *gaeemeth_aad(PyObject *me, PyObject *arg)
+{
+  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, ":aad")) return (0);
+  if (!(ge->f&AEADF_AADNDEP))
+    rc = gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(ge->ob_type)->aad,
+                        GAEAD_AAD(ge->e), 0, 0);
+  else {
+    if ((ge->f&AEADF_AADFIRST) && ge->mlen)
+      VALERR("too late for aad");
+    if (!ge->aad)
+      ge->aad = (gaeadaad_pyobj *)
+       gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(ge->ob_type)->aad,
+                       GAEAD_AAD(ge->e), ge->f&(AEADF_PCHSZ | AEADF_NOAAD),
+                       ge->hsz);
+    Py_INCREF(ge->aad);
+    rc = (PyObject *)ge->aad;
+  }
+end:
+  return (rc);
+}
+
+static PyObject *gaeemeth_reinit(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "nonce", "hsz", "msz", "tsz", 0 };
+  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+  char *n; Py_ssize_t nsz;
+  PyObject *hszobj = Py_None, *mszobj = Py_None, *tszobj = Py_None;
+  size_t hsz = 0, msz = 0, tsz = 0;
+  unsigned f;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:enc", KWLIST,
+                                  &n, &nsz, &hszobj, &mszobj, &tszobj))
+    goto end;
+  if (check_aead_encdec(ge->e->ops->c, &f, nsz,
+                       hszobj, &hsz, mszobj, &msz, tszobj, &tsz))
+    goto end;
+  if (GAEAD_REINIT(ge->e, n, nsz, hsz, msz, tsz))
+    VALERR("bad aead parameter combination");
+  gaea_sever(&ge->aad);
+  ge->f = f; ge->hsz = hsz; ge->msz = msz; ge->tsz = tsz;
+end:
+  return (0);
+}
+
+static PyObject *gaeemeth_encrypt(PyObject *me, PyObject *arg)
+{
+  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+  char *m; Py_ssize_t msz;
+  char *c = 0; size_t csz; buf b;
+  int err;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "s#:encrypt", &m, &msz)) goto end;
+  if (ge->f&AEADF_AADFIRST) {
+    if ((ge->f&AEADF_PCHSZ) && (ge->aad ? ge->aad->hlen : 0) != ge->hsz)
+      VALERR("header doesn't match precommitted length");
+    gaea_invalidate(ge->aad);
+  }
+  if ((ge->f&AEADF_PCMSZ) && msz > ge->msz - ge->mlen)
+    VALERR("too large for precommitted message length");
+  csz = msz + ge->e->ops->c->bufsz; c = xmalloc(csz); buf_init(&b, c, csz);
+  err = GAEAD_ENCRYPT(ge->e, m, msz, &b); assert(!err); (void)err;
+  buf_flip(&b); rc = bytestring_pywrapbuf(&b); ge->mlen += msz;
+end:
+  xfree(c);
+  return (rc);
+}
+
+static PyObject *gaeemeth_done(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "tsz", "aad", 0 };
+  gaeadenc_pyobj *ge = (gaeadenc_pyobj *)me;
+  PyObject *aad = Py_None;
+  char *c = 0; size_t csz; buf b;
+  PyObject *tszobj = Py_None; PyObject *tag; size_t tsz;
+  int err;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:done", KWLIST,
+                                  &tszobj, &aad))
+    goto end;
+  if (tszobj != Py_None && !convszt(tszobj, &tsz)) goto end;
+  if (aad != Py_None &&
+      !PyObject_TypeCheck(aad,
+                         (PyTypeObject *)GCAEADENC_KEY(me->ob_type)->aad))
+    TYERR("wanted aad");
+  if ((ge->f&AEADF_AADNDEP) && aad != Py_None && aad != (PyObject *)ge->aad)
+    VALERR("mismatched aad");
+  if ((ge->f&AEADF_PCHSZ) &&
+      (aad == Py_None ? 0 : GAEADAAD_HLEN(aad)) != ge->hsz)
+    VALERR("header doesn't match precommitted length");
+  if ((ge->f&AEADF_PCMSZ) && ge->mlen != ge->msz)
+    VALERR("message doesn't match precommitted length");
+  if (tszobj == Py_None) {
+    if (ge->f&AEADF_PCTSZ) tsz = ge->tsz;
+    else tsz = keysz(0, ge->e->ops->c->tagsz);
+  } else {
+    if ((ge->f&AEADF_PCTSZ) && tsz != ge->tsz)
+      VALERR("tag length doesn't match precommitted value");
+    if (keysz(tsz, ge->e->ops->c->tagsz) != tsz) VALERR("bad tag length");
+  }
+  csz = ge->e->ops->c->bufsz; c = xmalloc(csz); buf_init(&b, c, csz);
+  tag = bytestring_pywrap(0, tsz);
+  err = GAEAD_DONE(ge->e, aad == Py_None ? 0 : GAEADAAD_A(aad), &b,
+                  PyString_AS_STRING(tag), tsz);
+  assert(!err); (void)err;
+  buf_flip(&b); rc = Py_BuildValue("NN", bytestring_pywrapbuf(&b), tag);
+end:
+  xfree(c);
+  return (rc);
+}
+
+static PyMethodDef gaeadenc_pymethods[] = {
+#define METHNAME(name) gaeemeth_##name
+  METH  (aad,                  "ENC.aad() -> AAD")
+  KWMETH(reinit,               "ENC.reinit(NONCE, [hsz], [msz], [tsz])")
+  METH  (encrypt,              "ENC.encrypt(MSG) -> CT")
+  KWMETH(done,                 "ENC.done([tsz], [aad]) -> CT, TAG")
+#undef METHNAME
+  { 0 }
+};
+
+PyObject *gaeaddec_pywrap(PyObject *cobj, gaead_dec *d, unsigned f,
+                         size_t hsz, size_t csz, size_t tsz)
+{
+  gaeaddec_pyobj *gd;
+  assert(cobj); Py_INCREF(cobj);
+  gd = PyObject_NEW(gaeaddec_pyobj, (PyTypeObject *)cobj);
+  gd->d = d; gd->f = f; gd->hsz = hsz; gd->csz = csz; gd->tsz = tsz;
+  gd->aad = 0; gd->clen = 0;
+  return ((PyObject *)gd);
+}
+
+static void gaeaddec_pydealloc(PyObject *me)
+{
+  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+
+  gaea_sever(&gd->aad); GAEAD_DESTROY(GAEADDEC_D(me));
+  Py_DECREF(me->ob_type); FREEOBJ(me);
+}
+
+static PyObject *gaedget_hsz(PyObject *me, void *hunoz)
+{
+  if (GAEADDEC_F(me)&AEADF_PCHSZ) return getulong(GAEADDEC_HSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaedget_csz(PyObject *me, void *hunoz)
+{
+  if (GAEADDEC_F(me)&AEADF_PCMSZ) return getulong(GAEADDEC_CSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaedget_tsz(PyObject *me, void *hunoz)
+{
+  if (GAEADDEC_F(me)&AEADF_PCTSZ) return getulong(GAEADDEC_TSZ(me));
+  else RETURN_NONE;
+}
+
+static PyObject *gaedget_clen(PyObject *me, void *hunoz)
+  { return getulong(GAEADDEC_CLEN(me)); }
+
+static PyGetSetDef gaeaddec_pygetset[] = {
+#define GETSETNAME(op, name) gaed##op##_##name
+  GET  (hsz,                   "DEC.hsz -> precommitted header length or `None'")
+  GET  (csz,                   "DEC.csz -> precommitted ciphertext length or `None'")
+  GET  (tsz,                   "DEC.tsz -> precommitted tag length or `None'")
+  GET  (clen,                  "DEC.clen -> ciphertext length so far")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyObject *gaedmeth_aad(PyObject *me, PyObject *arg)
+{
+  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+
+  if (!PyArg_ParseTuple(arg, ":aad")) return (0);
+  if (!(gd->f&AEADF_AADNDEP))
+    return (gaeadaad_pywrap((PyObject *)GCAEADDEC_KEY(gd->ob_type)->aad,
+                           GAEAD_AAD(gd->d), 0, 0));
+  else {
+    if (!gd->aad)
+      gd->aad = (gaeadaad_pyobj *)
+       gaeadaad_pywrap((PyObject *)GCAEADENC_KEY(gd->ob_type)->aad,
+                       GAEAD_AAD(gd->d), gd->f&(AEADF_PCHSZ | AEADF_NOAAD),
+                       gd->hsz);
+    Py_INCREF(gd->aad);
+    return ((PyObject *)gd->aad);
+  }
+}
+
+static PyObject *gaedmeth_reinit(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "nonce", "hsz", "csz", "tsz", 0 };
+  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+  char *n; Py_ssize_t nsz;
+  PyObject *hszobj = Py_None, *cszobj = Py_None, *tszobj = Py_None;
+  size_t hsz = 0, csz = 0, tsz = 0;
+  unsigned f;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|OOO:enc", KWLIST,
+                                  &n, &nsz, &hszobj, &cszobj, &tszobj))
+    goto end;
+  if (check_aead_encdec(gd->d->ops->c, &f, nsz,
+                       hszobj, &hsz, cszobj, &csz, tszobj, &tsz))
+    goto end;
+  if (GAEAD_REINIT(gd->d, n, nsz, hsz, csz, tsz))
+    VALERR("bad aead parameter combination");
+  gaea_sever(&gd->aad);
+  gd->f = f; gd->hsz = hsz; gd->csz = csz; gd->tsz = tsz;
+end:
+  return (0);
+}
+
+static PyObject *gaedmeth_decrypt(PyObject *me, PyObject *arg)
+{
+  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+  char *c; Py_ssize_t csz;
+  char *m = 0; size_t msz; buf b;
+  int err;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, "s#:decrypt", &c, &csz)) goto end;
+  if (gd->f&AEADF_AADFIRST) {
+    if ((gd->f&AEADF_PCHSZ) && (gd->aad ? gd->aad->hlen : 0) != gd->hsz)
+      VALERR("header doesn't match precommitted length");
+    gaea_invalidate(gd->aad);
+  }
+  if ((gd->f&AEADF_PCMSZ) && csz > gd->csz - gd->clen)
+    VALERR("too large for precommitted message length");
+  msz = csz + gd->d->ops->c->bufsz; m = xmalloc(msz); buf_init(&b, m, msz);
+  err = GAEAD_DECRYPT(gd->d, c, csz, &b); assert(!err); (void)err;
+  buf_flip(&b); rc = bytestring_pywrapbuf(&b); gd->clen += csz;
+end:
+  xfree(m);
+  return (rc);
+}
+
+static PyObject *gaedmeth_done(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  static const char *const kwlist[] = { "tag", "aad", 0 };
+  gaeaddec_pyobj *gd = (gaeaddec_pyobj *)me;
+  PyObject *aad = Py_None;
+  char *t; Py_ssize_t tsz;
+  char *m = 0; size_t msz; buf b;
+  int err;
+  PyObject *rc = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O:done", KWLIST,
+                                  &t, &tsz, &aad))
+    goto end;
+  if (aad != Py_None &&
+      !PyObject_TypeCheck(aad,
+                         (PyTypeObject *)GCAEADENC_KEY(me->ob_type)->aad))
+    TYERR("wanted aad");
+  if ((gd->f&AEADF_AADNDEP) && aad != Py_None && aad != (PyObject *)gd->aad)
+    VALERR("mismatched aad");
+  if ((gd->f&AEADF_PCHSZ) &&
+      (aad == Py_None ? 0 : GAEADAAD_HLEN(aad)) != gd->hsz)
+    VALERR("header doesn't match precommitted length");
+  if ((gd->f&AEADF_PCMSZ) && gd->clen != gd->csz)
+    VALERR("message doesn't match precommitted length");
+  if ((gd->f&AEADF_PCTSZ) && tsz != gd->tsz)
+    VALERR("tag length doesn't match precommitted value");
+  if (keysz(tsz, gd->d->ops->c->tagsz) != tsz) VALERR("bad tag length");
+  msz = gd->d->ops->c->bufsz; m = xmalloc(msz); buf_init(&b, m, msz);
+  err = GAEAD_DONE(gd->d, aad == Py_None ? 0 : GAEADAAD_A(aad), &b, t, tsz);
+  assert(err >= 0);
+  if (!err) VALERR("decryption failed");
+  buf_flip(&b); rc = bytestring_pywrapbuf(&b);
+end:
+  xfree(m);
+  return (rc);
+}
+
+static PyMethodDef gaeaddec_pymethods[] = {
+#define METHNAME(name) gaedmeth_##name
+  METH  (aad,                  "DEC.aad() -> AAD")
+  KWMETH(reinit,               "DEC.reinit(NONCE, [hsz], [csz], [tsz])")
+  METH  (decrypt,              "DEC.decrypt(CT) -> MSG")
+  KWMETH(done,                 "DEC.done(TAG, [aad]) -> MSG | None")
+#undef METHNAME
+  { 0 }
+};
+
+static PyTypeObject gcaead_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GCAEAD",                            /* @tp_name@ */
+  sizeof(gcaead_pyobj),                        /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated encryption (key) metaclass.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  gcaead_pygetset,                     /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gaeadkey_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEKey",                            /* @tp_name@ */
+  sizeof(gaeadkey_pyobj),              /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gaeadkey_pydealloc,                  /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated encryption key.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  gaeadkey_pymethods,                  /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gcaeadaad_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEAADClass",                       /* @tp_name@ */
+  sizeof(gcaeadaad_pyobj),             /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated encryption additional-data hash metaclass.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gaeadaad_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEAAD",                            /* @tp_name@ */
+  sizeof(gaeadaad_pyobj),              /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gaeadaad_pydealloc,                  /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated encryption AAD hash.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  gaeadaad_pymethods,                  /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  gaeadaad_pygetset,                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gcaeadenc_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEEncClass",                       /* @tp_name@ */
+  sizeof(gcaeadenc_pyobj),             /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated encryption operation metaclass.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gaeadenc_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEEnc",                            /* @tp_name@ */
+  sizeof(gaeadenc_pyobj),              /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gaeadenc_pydealloc,                  /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated encryption operation.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  gaeadenc_pymethods,                  /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  gaeadenc_pygetset,                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gcaeaddec_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEDecClass",                       /* @tp_name@ */
+  sizeof(gcaeaddec_pyobj),             /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated decryption operation metaclass.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject gaeaddec_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "GAEDec",                            /* @tp_name@ */
+  sizeof(gaeaddec_pyobj),              /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  gaeaddec_pydealloc,                  /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+"Authenticated decryption operation.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  gaeaddec_pymethods,                  /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  gaeaddec_pygetset,                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
 /*----- Hash functions ----------------------------------------------------*/
 
 PyTypeObject *gchash_pytype, *ghash_pytype;
@@ -722,10 +1797,10 @@ CONVFUNC(ghash, ghash *, GHASH_H)
 
 static PyObject *ghash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist))
+  static const char *const kwlist[] = { 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST))
     goto end;
-  return (ghash_pywrap((PyObject *)ty, GH_INIT(GCHASH_CH(ty)), f_freeme));
+  return (ghash_pywrap((PyObject *)ty, GH_INIT(GCHASH_CH(ty))));
 end:
   return (0);
 }
@@ -747,21 +1822,19 @@ PyObject *gchash_pywrap(gchash *ch)
   return ((PyObject *)g);
 }
 
-PyObject *ghash_pywrap(PyObject *cobj, ghash *h, unsigned f)
+PyObject *ghash_pywrap(PyObject *cobj, ghash *h)
 {
   ghash_pyobj *g;
   if (!cobj) cobj = gchash_pywrap((/*unconst*/ gchash *)GH_CLASS(h));
   else Py_INCREF(cobj);
   g = PyObject_NEW(ghash_pyobj, (PyTypeObject *)cobj);
   g->h = h;
-  g->f = f;
   return ((PyObject *)g);
 }
 
 static void ghash_pydealloc(PyObject *me)
 {
-  if (GHASH_F(me) & f_freeme)
-    GH_DESTROY(GHASH_H(me));
+  GH_DESTROY(GHASH_H(me));
   Py_DECREF(me->ob_type);
   FREEOBJ(me);
 }
@@ -775,6 +1848,12 @@ static PyObject *gchget_hashsz(PyObject *me, void *hunoz)
 static PyObject *gchget_bufsz(PyObject *me, void *hunoz)
   { return (PyInt_FromLong(GCHASH_CH(me)->bufsz)); }
 
+static PyObject *ghmeth_copy(PyObject *me, PyObject *arg)
+{
+  if (!PyArg_ParseTuple(arg, ":copy")) return (0);
+  return (ghash_pywrap((PyObject *)me->ob_type, GH_COPY(GHASH_H(me))));
+}
+
 static PyObject *ghmeth_hash(PyObject *me, PyObject *arg)
 {
   char *p;
@@ -788,11 +1867,9 @@ static PyObject *ghmeth_hash(PyObject *me, PyObject *arg)
   static PyObject *ghmeth_hashu##w(PyObject *me, PyObject *arg)                \
   {                                                                    \
     uint##n x;                                                         \
-    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end; \
+    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
     GH_HASHU##W(GHASH_H(me), x);                                       \
     RETURN_ME;                                                         \
-  end:                                                                 \
-    return (0);                                                                \
   }
 DOUINTCONV(GHMETH_HASHU_)
 
@@ -841,6 +1918,7 @@ static PyGetSetDef gchash_pygetset[] = {
 
 static PyMethodDef ghash_pymethods[] = {
 #define METHNAME(name) ghmeth_##name
+  METH (copy,                  "H.copy() -> HH")
   METH (hash,                  "H.hash(M)")
 #define METHU_(n, W, w) METH(hashu##w, "H.hashu" #w "(WORD)")
   DOUINTCONV(METHU_)
@@ -960,29 +2038,27 @@ CONVFUNC(gmhash, ghash *, GHASH_H)
 
 static PyObject *gmac_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "k", 0 };
+  static const char *const kwlist[] = { "k", 0 };
   char *k;
   Py_ssize_t sz;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
     goto end;
   if (keysz(sz, GCMAC_CM(ty)->keysz) != sz) VALERR("bad key length");
   return (gmac_pywrap((PyObject *)ty,
-                     GM_KEY(GCMAC_CM(ty), k, sz),
-                     f_freeme));
+                     GM_KEY(GCMAC_CM(ty), k, sz)));
 end:
   return (0);
 }
 
 static PyObject *gmhash_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { 0 };
+  static const char *const kwlist[] = { 0 };
   ghash_pyobj *g;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist)) return (0);
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) return (0);
   g = PyObject_NEW(ghash_pyobj, ty);
   g->h = GM_INIT(GMAC_M(ty));
-  g->f = f_freeme;
   Py_INCREF(ty);
   return ((PyObject *)g);
 }
@@ -1004,7 +2080,7 @@ PyObject *gcmac_pywrap(gcmac *cm)
   return ((PyObject *)g);
 }
 
-PyObject *gmac_pywrap(PyObject *cobj, gmac *m, unsigned f)
+PyObject *gmac_pywrap(PyObject *cobj, gmac *m)
 {
   gmac_pyobj *g;
   if (!cobj) cobj = gcmac_pywrap((/*unconst*/ gcmac *)GM_CLASS(m));
@@ -1023,14 +2099,12 @@ PyObject *gmac_pywrap(PyObject *cobj, gmac *m, unsigned f)
   g->ty.ht_type.tp_new = gmhash_pynew;
   typeready(&g->ty.ht_type);
   g->m = m;
-  g->f = f;
   return ((PyObject *)g);
 }
 
 static void gmac_pydealloc(PyObject *me)
 {
-  if (GMAC_F(me) & f_freeme)
-    GM_DESTROY(GMAC_M(me));
+  GM_DESTROY(GMAC_M(me));
   Py_DECREF(me->ob_type);
   PyType_Type.tp_dealloc(me);
 }
@@ -1220,13 +2294,13 @@ CONVFUNC(poly1305hash, poly1305_ctx *, P1305_CTX)
 static PyObject *poly1305hash_pynew(PyTypeObject *ty,
                                    PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "mask", 0 };
+  static const char *const kwlist[] = { "mask", 0 };
   poly1305key_pyobj *pk = (poly1305key_pyobj *)ty;
   poly1305hash_pyobj *ph;
   char *m = 0;
   Py_ssize_t sz;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#:new", kwlist, &m, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#:new", KWLIST, &m, &sz))
     return (0);
   if (m && sz != POLY1305_MASKSZ) VALERR("bad mask length");
   ph = PyObject_NEW(poly1305hash_pyobj, ty);
@@ -1242,12 +2316,12 @@ end:
 static PyObject *poly1305key_pynew(PyTypeObject *ty,
                                   PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "k", 0 };
+  static const char *const kwlist[] = { "k", 0 };
   poly1305key_pyobj *pk;
   char *k;
   Py_ssize_t sz;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
     goto end;
   if (keysz(sz, poly1305_keysz) != sz) VALERR("bad key length");
 
@@ -1308,11 +2382,9 @@ static PyObject *polymeth_hash(PyObject *me, PyObject *arg)
   {                                                                    \
     uint##n x;                                                         \
     octet b[SZ_##W];                                                   \
-    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end; \
+    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
     STORE##W(b, x); poly1305_hash(P1305_CTX(me), b, sizeof(b));                \
     RETURN_ME;                                                         \
-  end:                                                                 \
-    return (0);                                                                \
   }
 DOUINTCONV(POLYMETH_HASHU_)
 
@@ -1484,7 +2556,7 @@ static PyTypeObject poly1305key_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Poly1305 key.",
+"poly1305(K): Poly1305 key.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1600,8 +2672,8 @@ static PyObject *kxvik_pynew(PyTypeObject *ty,
 {
   unsigned n = 24;
   kxvik_pyobj *rc = 0;
-  char *kwlist[] = { "nround", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist,
+  static const char *const kwlist[] = { "nround", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
                                   convuint, &n))
     goto end;
   rc = (kxvik_pyobj *)ty->tp_alloc(ty, 0);
@@ -1611,6 +2683,16 @@ end:
   return ((PyObject *)rc);
 }
 
+static PyObject *kxvikmeth_copy(PyObject *me, PyObject *arg)
+{
+  kxvik_pyobj *k = (kxvik_pyobj *)me, *rc = 0;
+  if (!PyArg_ParseTuple(arg, ":copy")) goto end;
+  rc = (kxvik_pyobj *)k->ob_type->tp_alloc(k->ob_type, 0);
+  rc->s = k->s; rc->n = k->n;
+end:
+  return ((PyObject *)rc);
+}
+
 static PyObject *kxvikmeth_mix(PyObject *me, PyObject *arg)
 {
   kxvik_pyobj *k = (kxvik_pyobj *)me;
@@ -1693,6 +2775,7 @@ static PyGetSetDef kxvik_pygetset[] = {
 
 static PyMethodDef kxvik_pymethods[] = {
 #define METHNAME(func) kxvikmeth_##func
+  METH (copy,                  "KECCAK.copy() -> KECCAK'")
   METH (mix,                   "KECCAK.mix(DATA)")
   METH (extract,               "KECCAK.extract(NOCTETS)")
   METH (step,                  "KECCAK.step()")
@@ -1725,7 +2808,7 @@ static PyTypeObject kxvik_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Keccak-p[1600, n] state.",
+"Keccak1600([nround = 24]): Keccak-p[1600, n] state.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1768,9 +2851,9 @@ static PyObject *shake_dopynew(void (*initfn)(shake_ctx *,
   shake_pyobj *rc = 0;
   char *p = 0, *f = 0;
   Py_ssize_t psz = 0, fsz = 0;
-  char *kwlist[] = { "perso", "func", 0 };
+  static const char *const kwlist[] = { "perso", "func", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#s#:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#s#:new", KWLIST,
                                   &p, &psz, &f, &fsz))
     goto end;
   rc = (shake_pyobj *)ty->tp_alloc(ty, 0);
@@ -1811,12 +2894,10 @@ static PyObject *shakemeth_hash(PyObject *me, PyObject *arg)
   {                                                                    \
     uint##n x;                                                         \
     octet b[SZ_##W];                                                   \
-    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end; \
-    if (shake_check(me, 0)) goto end;                                  \
+    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) return (0); \
+    if (shake_check(me, 0)) return (0);                                        \
     STORE##W(b, x); shake_hash(SHAKE_H(me), b, sizeof(b));             \
     RETURN_ME;                                                         \
-  end:                                                                 \
-    return (0);                                                                \
   }
 DOUINTCONV(SHAKEMETH_HASHU_)
 
@@ -2022,7 +3103,7 @@ static PyTypeObject shake128_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"SHAKE128/cSHAKE128 XOF.",
+"Shake128([perso = STR], [func = STR]): SHAKE128/cSHAKE128 XOF.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2070,7 +3151,7 @@ static PyTypeObject shake256_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"SHAKE256/cSHAKE256 XOF.",
+"Shake256([perso = STR], [func = STR]): SHAKE256/cSHAKE256 XOF.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2152,13 +3233,13 @@ typedef struct prp {
 
 static PyObject *gprp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "key", 0 };
+  static const char *const kwlist[] = { "key", 0 };
   char *k;
   Py_ssize_t sz;
   const prpinfo *prp = GCPRP_PRP(ty);
   PyObject *me;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &sz))
     goto end;
   if (keysz(sz, prp->keysz) != sz) VALERR("bad key length");
   me = (PyObject *)ty->tp_alloc(ty, 0);
@@ -2381,6 +3462,14 @@ void algorithms_pyinit(void)
   INITTYPE(keyszset, keysz);
   INITTYPE(gccipher, type);
   INITTYPE(gcipher, root);
+  INITTYPE(gcaead, type);
+  INITTYPE(gaeadkey, root);
+  INITTYPE(gcaeadaad, type);
+  INITTYPE(gaeadaad, root);
+  INITTYPE(gcaeadenc, type);
+  INITTYPE(gaeadenc, root);
+  INITTYPE(gcaeaddec, type);
+  INITTYPE(gaeaddec, root);
   INITTYPE(gchash, type);
   INITTYPE(ghash, root);
   INITTYPE(gcmac, type);
@@ -2399,6 +3488,7 @@ void algorithms_pyinit(void)
 }
 
 GEN(gcciphers, cipher)
+GEN(gcaeads, aead)
 GEN(gchashes, hash)
 GEN(gcmacs, mac)
 #define gcprp prpinfo
@@ -2414,6 +3504,15 @@ void algorithms_pyinsert(PyObject *mod)
   INSERT("GCCipher", gccipher_pytype);
   INSERT("GCipher", gcipher_pytype);
   INSERT("gcciphers", gcciphers());
+  INSERT("GCAEAD", gcaead_pytype);
+  INSERT("GAEKey", gaeadkey_pytype);
+  INSERT("GAEAADClass", gcaeadaad_pytype);
+  INSERT("GAEAAD", gaeadaad_pytype);
+  INSERT("GAEEncClass", gcaeadenc_pytype);
+  INSERT("GAEEnc", gaeadenc_pytype);
+  INSERT("GAEDecClass", gcaeaddec_pytype);
+  INSERT("GAEDec", gaeaddec_pytype);
+  INSERT("gcaeads", gcaeads());
   INSERT("GCHash", gchash_pytype);
   INSERT("GHash", ghash_pytype);
   INSERT("gchashes", d = gchashes());
index 46abccbc7b8942217e61c8671f7bcc577cefb466..5730c4567804a200a7f93b11aa31cee17f43c7be 100644 (file)
@@ -25,6 +25,8 @@ serpent noekeon
 '''.split()
 pmodes = '''
 ecb cbc cfb ofb counter
+cmac pmac1
+ccm eax gcm ocb1 ocb3
 '''.split()
 streamciphers = '''
 rc4 seal
index ac3e56be66e130bee408eddd7b7267b8a9eca88c..5b7bb64692a4530d9ef05671185351001096942f 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -55,9 +55,9 @@ static PyObject *rbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   char *p, *q;
   Py_ssize_t n;
   buf_pyobj *me = 0;
-  static char *kwlist[] = { "data", 0 };
+  static const char *const kwlist[] = { "data", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &n))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &p, &n))
     goto end;
   q = xmalloc(n);
   memcpy(q, p, n);
@@ -172,9 +172,9 @@ end:
 static PyObject *rbmeth_getecpt(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *cobj = Py_None;
-  static char *kwlist[] = { "curve", 0 };
+  static const char *const kwlist[] = { "curve", 0 };
   ec pt = EC_INIT;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", kwlist, &cobj))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", KWLIST, &cobj))
     goto end;
   if (cobj == Py_None) cobj = (PyObject *)ecpt_pytype;
   if (!PyType_Check(cobj) ||
@@ -309,7 +309,7 @@ static PyTypeObject rbuf_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "A read buffer.",
+"ReadBuffer(STR): a read buffer.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -355,9 +355,9 @@ static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   char *p;
   size_t n = 64;
   buf_pyobj *me = 0;
-  static char *kwlist[] = { "size", 0 };
+  static const char *const kwlist[] = { "size", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
                                   convszt, &n))
     goto end;
   me = (buf_pyobj *)ty->tp_alloc(ty, 0);
@@ -486,9 +486,13 @@ static PyObject *wbmeth_putgeraw(PyObject *me, PyObject *arg)
 static PyObject *wbget_size(PyObject *me, void *hunoz)
   { return (PyInt_FromLong(BLEN(BUF_B(me)))); }
 
+static PyObject *wbget_contents(PyObject *me, void *hunoz)
+  { return (bytestring_pywrap(BBASE(BUF_B(me)), BLEN(BUF_B(me)))); }
+
 static PyGetSetDef wbuf_pygetset[] = {
 #define GETSETNAME(op, name) wb##op##_##name
   GET  (size,                  "WBUF.size -> SIZE")
+  GET  (contents,              "WBUF.contents -> STR")
 #undef GETSETNAME
   { 0 }
 };
@@ -545,7 +549,7 @@ static PyTypeObject wbuf_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "A write buffer.",
+"WriteBuffer([size = ?]): a write buffer.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
index 2b74648d91d9ca2793f206ba4091e941123465f1..82d117f44fcf25bfbaf6fa853b3736005aace6da 100644 (file)
 
 PyTypeObject *bytestring_pytype;
 
-static PyObject *dowrap(PyTypeObject *ty, const void *p, size_t n)
+static PyObject *empty, *bytev[256];
+
+static PyObject *allocate(PyTypeObject *ty, size_t n)
 {
-  PyStringObject *x = (PyStringObject *)ty->tp_alloc(ty, n);
-  if (p) memcpy(x->ob_sval, p, n);
+  PyStringObject *x;
+  x = (PyStringObject *)ty->tp_alloc(ty, n);
   x->ob_sval[n] = 0;
 #if defined(CACHE_HASH) || PY_VERSION_HEX >= 0x02030000
   x->ob_shash = -1;
@@ -44,6 +46,27 @@ static PyObject *dowrap(PyTypeObject *ty, const void *p, size_t n)
   return ((PyObject *)x);
 }
 
+static PyObject *dowrap(PyTypeObject *ty, const void *p, size_t n)
+{
+  PyObject *x;
+  int ch;
+
+  if (p && ty == bytestring_pytype) {
+    if (!n) {
+      if (!empty) empty = allocate(ty, 0);
+      Py_INCREF(empty); return (empty);
+    } else if (n == 1 && (ch = *(unsigned char *)p) < sizeof(bytev)) {
+      if (!bytev[ch])
+       { bytev[ch] = allocate(ty, 1); *PyString_AS_STRING(bytev[ch]) = ch; }
+      Py_INCREF(bytev[ch]); return (bytev[ch]);
+    }
+  }
+
+  x = allocate(ty, n);
+  if (p) memcpy(PyString_AS_STRING(x), p, n);
+  return (x);
+}
+
 PyObject *bytestring_pywrap(const void *p, size_t n)
   { return (dowrap(bytestring_pytype, p, n)); }
 
@@ -55,8 +78,8 @@ static PyObject *bytestring_pynew(PyTypeObject *ty,
 {
   const char *p;
   Py_ssize_t n;
-  static char *kwlist[] = { "data", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &n))
+  static const char *const kwlist[] = { "data", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &p, &n))
     return (0);
   return (dowrap(ty, p, n));
 }
@@ -118,6 +141,91 @@ static PyObject *bytestring_pyrichcompare(PyObject *me,
   else RETURN_FALSE;
 }
 
+static PyObject *bytestring_pyconcat(PyObject *x, PyObject *y)
+{
+  const void *xv; Py_ssize_t xsz;
+  const void *yv; Py_ssize_t ysz;
+  PyObject *z = 0; char *zp; size_t zsz;
+
+  if (PyObject_AsReadBuffer(x, &xv, &xsz) ||
+      PyObject_AsReadBuffer(y, &yv, &ysz))
+    goto end;
+  zsz = (size_t)xsz + (size_t)ysz;
+  if (xsz < 0 || ysz < 0 || zsz < xsz) VALERR("too long");
+  z = bytestring_pywrap(0, zsz); zp = PyString_AS_STRING(z);
+  memcpy(zp, xv, xsz); memcpy(zp + xsz, yv, ysz);
+end:
+  return (z);
+}
+
+static PyObject *bytestring_pyrepeat(PyObject *me, Py_ssize_t n)
+{
+  const unsigned char *xp; size_t xsz;
+  PyObject *z = 0; char *zp; size_t zsz;
+
+  xp = (const unsigned char *)PyString_AS_STRING(me);
+  xsz = PyString_GET_SIZE(me);
+  if (n < 0 || (n && xsz >= (size_t)-1/n)) VALERR("too long");
+  zsz = n*xsz; z = bytestring_pywrap(0, zsz); zp = PyString_AS_STRING(z);
+  if (xsz == 1) memset(zp, *xp, zsz);
+  else while (zsz) { memcpy(zp, xp, xsz); zp += xsz; zsz -= xsz; }
+end:
+  return (z);
+}
+
+static PyObject *bytestring_pyitem(PyObject *me, Py_ssize_t i)
+{
+  PyObject *rc = 0;
+
+  if (i < 0 || i >= PyString_GET_SIZE(me)) IXERR("out of range");
+  rc = bytestring_pywrap(PyString_AS_STRING(me) + i, 1);
+end:
+  return (rc);
+}
+
+static PyObject *bytestring_pyslice(PyObject *me, Py_ssize_t i, Py_ssize_t j)
+{
+  PyObject *rc = 0;
+  size_t n = PyString_GET_SIZE(me);
+
+  if (i < 0) i = 0;
+  if (j < 0) j = 0;
+  else if (j > n) j = n;
+  if (j < i) i = j = 0;
+  if (i == 0 && j == n && me->ob_type == bytestring_pytype)
+    { Py_INCREF(me); rc = me; goto end; }
+  rc = bytestring_pywrap(PyString_AS_STRING(me) + i, j - i);
+end:
+  return (rc);
+}
+
+static PyObject *bytestring_pysubscript(PyObject *me, PyObject *ix)
+{
+  Py_ssize_t i, j, k, n;
+  const unsigned char *p;
+  unsigned char *q;
+  PyObject *rc = 0;
+
+  if (PyIndex_Check(ix)) {
+    i = PyNumber_AsSsize_t(ix, PyExc_IndexError);
+    if (i == -1 && PyErr_Occurred()) return (0);
+    if (i < 0) i += PyString_GET_SIZE(me);
+    rc = bytestring_pyitem(me, i);
+  } else if (PySlice_Check(ix)) {
+    if (PySlice_GetIndicesEx((PySliceObject *)ix, PyString_GET_SIZE(me),
+                            &i, &j, &k, &n))
+      return (0);
+    if (k == 1) return bytestring_pyslice(me, i, j);
+    rc = bytestring_pywrap(0, n);
+    p = (unsigned char *)PyString_AS_STRING(me) + i;
+    q = (unsigned char *)PyString_AS_STRING(rc);
+    while (n--) { *q++ = *p; p += k; }
+  } else
+    TYERR("wanted integer or slice");
+end:
+  return (rc);
+}
+
 #define BINOP(name, op)                                                        \
   static PyObject *bytestring_py##name(PyObject *x, PyObject *y) {     \
     const void *xv, *yv;                                               \
@@ -183,6 +291,25 @@ static PyNumberMethods bytestring_pynumber = {
   0,                                   /* @nb_hex@ */
 };
 
+static PySequenceMethods bytestring_pysequence = {
+  0,                                   /* @sq_length@ */
+  bytestring_pyconcat,                 /* @sq_concat@ */
+  bytestring_pyrepeat,                 /* @sq_repeat@ */
+  bytestring_pyitem,                   /* @sq_item@ */
+  bytestring_pyslice,                  /* @sq_slice@ */
+  0,                                   /* @sq_ass_item@ */
+  0,                                   /* @sq_ass_slice@ */
+  0,                                   /* @sq_contains@ */
+  0,                                   /* @sq_inplace_concat@ */
+  0,                                   /* @sq_inplace_repeat@ */
+};
+
+static PyMappingMethods bytestring_pymapping = {
+  0,                                   /* @mp_length@ */
+  bytestring_pysubscript,              /* @mp_subscript@ */
+  0,                                   /* @mp_ass_subscript@ */
+};
+
 static PyBufferProcs bytestring_pybuffer;
 
 static PyTypeObject bytestring_pytype_skel = {
@@ -198,8 +325,8 @@ static PyTypeObject bytestring_pytype_skel = {
   0,                                   /* @tp_compare@ */
   0,                                   /* @tp_repr@ */
   &bytestring_pynumber,                        /* @tp_as_number@ */
-  0,                                   /* @tp_as_sequence@ */
-  0,                                   /* @tp_as_mapping@ */
+  &bytestring_pysequence,              /* @tp_as_sequence@ */
+  &bytestring_pymapping,               /* @tp_as_mapping@ */
   0,                                   /* @tp_hash@ */
   0,                                   /* @tp_call@ */
   0,                                   /* @tp_str@ */
@@ -211,7 +338,7 @@ static PyTypeObject bytestring_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Byte string class.",
+"ByteString(STR): byte string class.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
index ad27461008a58b811aa48270aaf3b02c57451bdd..6c1ae2139030fae290f9c1b72efd290cded32da9 100644 (file)
@@ -61,6 +61,7 @@
 #include <catacomb/blkc.h>
 
 #include <catacomb/gcipher.h>
+#include <catacomb/gaead.h>
 #include <catacomb/ghash.h>
 #include <catacomb/gmac.h>
 #include <catacomb/md5.h>
 #define VALERR(str) EXCERR(PyExc_ValueError, str)
 #define OVFERR(str) EXCERR(PyExc_OverflowError, str)
 #define TYERR(str) EXCERR(PyExc_TypeError, str)
+#define IXERR(str) EXCERR(PyExc_IndexError, str)
 #define ZDIVERR(str) EXCERR(PyExc_ZeroDivisionError, str)
 #define SYSERR(str) EXCERR(PyExc_SystemError, str)
 #define NIERR(str) EXCERR(PyExc_NotImplementedError, str)
@@ -234,6 +236,8 @@ MODULES(DO)
     return (d);                                                                \
   }
 
+#define KWLIST (/*unconst*/ char **)kwlist
+
 struct nameval { const char *name; unsigned f; unsigned long value; };
 #define CF_SIGNED 1u
 extern void setconstants(PyObject *, const struct nameval *);
@@ -531,24 +535,130 @@ typedef struct gccipher_pyobj {
 extern PyTypeObject *gccipher_pytype;
 #define GCCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gccipher_pytype)
 #define GCCIPHER_CC(o) (((gccipher_pyobj *)(o))->cc)
-#define GCCIPHER_F(o) (((gccipher_pyobj *)(o))->f)
 extern PyObject *gccipher_pywrap(gccipher *);
 extern int convgccipher(PyObject *, void *);
-extern int convgcipher(PyObject *, void *);
 
 typedef struct gcipher_pyobj {
   PyObject_HEAD
-  unsigned f;
   gcipher *c;
 } gcipher_pyobj;
 
 extern PyTypeObject *gcipher_pytype;
 #define GCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gcipher_pytype)
 #define GCIPHER_C(o) (((gcipher_pyobj *)(o))->c)
-#define GCIPHER_F(o) (((gcipher_pyobj *)(o))->f)
-extern PyObject *gcipher_pywrap(PyObject *, gcipher *, unsigned);
+extern PyObject *gcipher_pywrap(PyObject *, gcipher *);
 extern int convgcipher(PyObject *, void *);
 
+typedef struct gcaead_pyobj {
+  PyHeapTypeObject ty;
+  gcaead *aec;
+  struct gcaeadaad_pyobj *aad;
+  struct gcaeadenc_pyobj *enc;
+  struct gcaeaddec_pyobj *dec;
+} gcaead_pyobj;
+
+extern PyTypeObject *gcaead_pytype;
+#define GCAEAD_PYCHECK(o) PyObject_TypeCheck((o), gcaead_pytype)
+#define GCAEAD_AEC(o) (((gcaead_pyobj *)(o))->aec)
+#define GCAEAD_AAD(o) (((gcaead_pyobj *)(o))->aad)
+#define GCAEAD_ENC(o) (((gcaead_pyobj *)(o))->enc)
+#define GCAEAD_DEC(o) (((gcaead_pyobj *)(o))->dec)
+extern PyObject *gcaead_pywrap(gcaead *);
+extern int convgcaead(PyObject *, void *);
+
+typedef struct gaeadkey_pyobj {
+  PyObject_HEAD
+  gaead_key *k;
+} gaeadkey_pyobj;
+
+extern PyTypeObject *gaeadkey_pytype;
+#define GAEADKEY_PYCHECK(o) PyObject_TypeCheck((o), gaeadkey_pytype)
+#define GAEADKEY_K(o) (((gaeadkey_pyobj *)(o))->k)
+extern PyObject *gaeadkey_pywrap(PyObject *, gaead_key *);
+extern int convgaeadkey(PyObject *, void *);
+
+typedef struct gcaeadaad_pyobj {
+  PyHeapTypeObject ty;
+  gcaead_pyobj *key;
+} gcaeadaad_pyobj;
+#define GCAEADAAD_KEY(o) (((gcaeadaad_pyobj *)(o))->key)
+extern PyTypeObject *gcaeadaad_pytype;
+
+typedef struct gaeadaad_pyobj {
+  PyObject_HEAD
+  gaead_aad *a;
+  unsigned f;
+#define AEADF_DEAD 32768u
+  size_t hsz, hlen;
+} gaeadaad_pyobj;
+
+extern PyTypeObject *gaeadaad_pytype;
+#define GAEADAAD_PYCHECK(o) PyObject_TypeCheck((o), gaeadaad_pytype)
+#define GAEADAAD_A(o) (((gaeadaad_pyobj *)(o))->a)
+#define GAEADAAD_F(o) (((gaeadaad_pyobj *)(o))->f)
+#define GAEADAAD_HSZ(o) (((gaeadaad_pyobj *)(o))->hsz)
+#define GAEADAAD_HLEN(o) (((gaeadaad_pyobj *)(o))->hlen)
+extern PyObject *gaeadaad_pywrap(PyObject *, gaead_aad *, unsigned, size_t);
+extern int convgaeadaad(PyObject *, void *);
+
+typedef struct gcaeadenc_pyobj {
+  PyHeapTypeObject ty;
+  gcaead_pyobj *key;
+} gcaeadenc_pyobj;
+#define GCAEADENC_KEY(o) (((gcaeadenc_pyobj *)(o))->key)
+extern PyTypeObject *gcaeadenc_pytype;
+
+typedef struct gaeadenc_pyobj {
+  PyObject_HEAD
+  gaead_enc *e;
+  gaeadaad_pyobj *aad;
+  unsigned f;
+  size_t hsz, msz, tsz;
+  size_t mlen;
+} gaeadenc_pyobj;
+
+extern PyTypeObject *gaeadenc_pytype;
+#define GAEADENC_PYCHECK(o) PyObject_TypeCheck((o), gaeadenc_pytype)
+#define GAEADENC_AAD(o) (((gaeadenc_pyobj *)(o))->aad)
+#define GAEADENC_E(o) (((gaeadenc_pyobj *)(o))->e)
+#define GAEADENC_F(o) (((gaeadenc_pyobj *)(o))->f)
+#define GAEADENC_HSZ(o) (((gaeadenc_pyobj *)(o))->hsz)
+#define GAEADENC_MSZ(o) (((gaeadenc_pyobj *)(o))->msz)
+#define GAEADENC_TSZ(o) (((gaeadenc_pyobj *)(o))->tsz)
+#define GAEADENC_MLEN(o) (((gaeadenc_pyobj *)(o))->mlen)
+extern PyObject *gaeadenc_pywrap(PyObject *, gaead_enc *, unsigned,
+                                size_t, size_t, size_t);
+extern int convgaeadenc(PyObject *, void *);
+
+typedef struct gcaeaddec_pyobj {
+  PyHeapTypeObject ty;
+  gcaead_pyobj *key;
+} gcaeaddec_pyobj;
+#define GCAEADDEC_KEY(o) (((gcaeaddec_pyobj *)(o))->key)
+extern PyTypeObject *gcaeaddec_pytype;
+
+typedef struct gaeaddec_pyobj {
+  PyObject_HEAD
+  gaead_dec *d;
+  gaeadaad_pyobj *aad;
+  unsigned f;
+  size_t hsz, csz, tsz;
+  size_t clen;
+} gaeaddec_pyobj;
+
+extern PyTypeObject *gaeaddec_pytype;
+#define GAEADDEC_PYCHECK(o) PyObject_TypeCheck((o), gaeaddec_pytype)
+#define GAEADDEC_AAD(o) (((gaeaddec_pyobj *)(o))->aad)
+#define GAEADDEC_D(o) (((gaeaddec_pyobj *)(o))->d)
+#define GAEADDEC_F(o) (((gaeaddec_pyobj *)(o))->f)
+#define GAEADDEC_HSZ(o) (((gaeaddec_pyobj *)(o))->hsz)
+#define GAEADDEC_CSZ(o) (((gaeaddec_pyobj *)(o))->csz)
+#define GAEADDEC_TSZ(o) (((gaeaddec_pyobj *)(o))->tsz)
+#define GAEADDEC_CLEN(o) (((gaeaddec_pyobj *)(o))->clen)
+extern PyObject *gaeaddec_pywrap(PyObject *, gaead_dec *, unsigned,
+                                size_t, size_t, size_t);
+extern int convgaeaddec(PyObject *, void *);
+
 typedef struct gchash_pyobj {
   PyHeapTypeObject ty;
   gchash *ch;
@@ -557,13 +667,11 @@ typedef struct gchash_pyobj {
 extern PyTypeObject *gchash_pytype;
 #define GCHASH_PYCHECK(o) PyObject_TypeCheck((o), gchash_pytype)
 #define GCHASH_CH(o) (((gchash_pyobj *)(o))->ch)
-#define GCHASH_F(o) (((gchash_pyobj *)(o))->f)
 extern PyObject *gchash_pywrap(gchash *);
 extern int convgchash(PyObject *, void *);
 
 typedef struct ghash_pyobj {
   PyObject_HEAD
-  unsigned f;
   ghash *h;
 } ghash_pyobj;
 
@@ -571,8 +679,7 @@ extern PyTypeObject *ghash_pytype, *gmhash_pytype;
 extern PyObject *sha_pyobj, *has160_pyobj;
 #define GHASH_PYCHECK(o) PyObject_TypeCheck((o), ghash_pytype)
 #define GHASH_H(o) (((ghash_pyobj *)(o))->h)
-#define GHASH_F(o) (((ghash_pyobj *)(o))->f)
-extern PyObject *ghash_pywrap(PyObject *, ghash *, unsigned);
+extern PyObject *ghash_pywrap(PyObject *, ghash *);
 extern int convghash(PyObject *, void *);
 extern int convgmhash(PyObject *, void *);
 
@@ -590,7 +697,6 @@ extern int convgcmac(PyObject *, void *);
 
 typedef struct gmac_pyobj {
   PyHeapTypeObject ty;
-  unsigned f;
   gmac *m;
 } gmac_pyobj;
 
@@ -598,7 +704,7 @@ extern PyTypeObject *gmac_pytype;
 #define GMAC_PYCHECK(o) PyObject_TypeCheck((o), gmac_pytype)
 #define GMAC_M(o) (((gmac_pyobj *)(o))->m)
 #define GMAC_F(o) (((gmac_pyobj *)(o))->f)
-extern PyObject *gmac_pywrap(PyObject *, gmac *, unsigned);
+extern PyObject *gmac_pywrap(PyObject *, gmac *);
 extern int convgmac(PyObject *, void *);
 
 /*----- Key generation ----------------------------------------------------*/
index e24e5ec9f47e033f27129b1b002da90a2b68c273..e9c951385ae4f23f1b3ec9afcc75aa9dfde5a904 100644 (file)
@@ -52,6 +52,8 @@ static const struct nameval consts[] = {
   C(ED25519_KEYSZ), C(ED25519_PUBSZ), C(ED25519_SIGSZ),
     C(ED25519_MAXPERSOSZ),
   C(ED448_KEYSZ), C(ED448_PUBSZ), C(ED448_SIGSZ), C(ED448_MAXPERSOSZ),
+  C(AEADF_PCHSZ), C(AEADF_PCMSZ), C(AEADF_PCTSZ),
+  C(AEADF_AADNDEP), C(AEADF_AADFIRST), C(AEADF_NOAAD),
 #define ENTRY(tag, val, str) C(KERR_##tag),
   KEY_ERRORS(ENTRY)
 #undef ENTRY
index 24265acb3398ebf4c2209e0224431a1aacfe4168..65695bb89bc2dfa4af84f30f0b647254a5c0d2c9 100644 (file)
@@ -112,7 +112,7 @@ def _init():
     for j in b:
       if j[:plen] == pre:
         setattr(c, j[plen:], classmethod(b[j]))
-  for i in [gcciphers, gchashes, gcmacs, gcprps]:
+  for i in [gcciphers, gcaeads, gchashes, gcmacs, gcprps]:
     for c in i.itervalues():
       d[_fixname(c.name)] = c
   for c in gccrands.itervalues():
@@ -193,6 +193,27 @@ _augment(ByteString, _tmp)
 ByteString.__hash__ = str.__hash__
 bytes = ByteString.fromhex
 
+###--------------------------------------------------------------------------
+### Symmetric encryption.
+
+class _tmp:
+  def encrypt(me, n, m, tsz = None, h = ByteString('')):
+    if tsz is None: tsz = me.__class__.tagsz.default
+    e = me.enc(n, len(h), len(m), tsz)
+    if not len(h): a = None
+    else: a = e.aad().hash(h)
+    c0 = e.encrypt(m)
+    c1, t = e.done(aad = a)
+    return c0 + c1, t
+  def decrypt(me, n, c, t, h = ByteString('')):
+    d = me.dec(n, len(h), len(c), len(t))
+    if not len(h): a = None
+    else: a = d.aad().hash(h)
+    m = d.decrypt(c)
+    m += d.done(t, aad = a)
+    return m
+_augment(GAEKey, _tmp)
+
 ###--------------------------------------------------------------------------
 ### Hashing.
 
@@ -293,7 +314,7 @@ class _tmp:
     me.bytepad_after()
 _augment(Shake, _tmp)
 _augment(_ShakeBase, _tmp)
-Shake._Z = _ShakeBase._Z = ByteString(200*'\0')
+Shake._Z = _ShakeBase._Z = ByteString.zero(200)
 
 class KMAC (_ShakeBase):
   _FUNC = 'KMAC'
@@ -317,21 +338,12 @@ class KMAC256 (KMAC): _SHAKE = Shake256; _TAGSZ = 32
 ### NaCl `secretbox'.
 
 def secret_box(k, n, m):
-  E = xsalsa20(k).setiv(n)
-  r = E.enczero(poly1305.keysz.default)
-  s = E.enczero(poly1305.masksz)
-  y = E.encrypt(m)
-  t = poly1305(r)(s).hash(y).done()
-  return ByteString(t + y)
+  y, t = salsa20_naclbox(k).encrypt(n, m)
+  return t + y
 
 def secret_unbox(k, n, c):
-  E = xsalsa20(k).setiv(n)
-  r = E.enczero(poly1305.keysz.default)
-  s = E.enczero(poly1305.masksz)
-  y = c[poly1305.tagsz:]
-  if not poly1305(r)(s).hash(y).check(c[0:poly1305.tagsz]):
-    raise ValueError, 'decryption failed'
-  return E.decrypt(c[poly1305.tagsz:])
+  tsz = poly1305.tagsz
+  return salsa20_naclbox(k).decrypt(n, c[tsz:], c[0:tsz])
 
 ###--------------------------------------------------------------------------
 ### Multiprecision integers and binary polynomials.
@@ -586,6 +598,7 @@ class _tmp:
   def __repr__(me): return '%s(%d)' % (_clsname(me), me.default)
   def check(me, sz): return True
   def best(me, sz): return sz
+  def pad(me, sz): return sz
 _augment(KeySZAny, _tmp)
 
 class _tmp:
@@ -602,11 +615,15 @@ class _tmp:
       pp.pretty(me.max); pp.text(','); pp.breakable()
       pp.pretty(me.mod)
     pp.end_group(ind, ')')
-  def check(me, sz): return me.min <= sz <= me.max and sz % me.mod == 0
+  def check(me, sz): return me.min <= sz <= me.max and sz%me.mod == 0
   def best(me, sz):
     if sz < me.min: raise ValueError, 'key too small'
     elif sz > me.max: return me.max
-    else: return sz - (sz % me.mod)
+    else: return sz - sz%me.mod
+  def pad(me, sz):
+    if sz > me.max: raise ValueError, 'key too large'
+    elif sz < me.min: return me.min
+    else: sz += me.mod - 1; return sz - sz%me.mod
 _augment(KeySZRange, _tmp)
 
 class _tmp:
@@ -628,6 +645,12 @@ class _tmp:
       if found < i <= sz: found = i
     if found < 0: raise ValueError, 'key too small'
     return found
+  def pad(me, sz):
+    found = -1
+    for i in me.set:
+      if sz <= i and (found == -1 or i < found): found = i
+    if found < 0: raise ValueError, 'key too large'
+    return found
 _augment(KeySZSet, _tmp)
 
 ###--------------------------------------------------------------------------
@@ -866,21 +889,23 @@ _augment(RSAPriv, _tmp)
 ### DSA and related schemes.
 
 class _tmp:
-  def __repr__(me): return '%s(G = %r, p = %r)' % (_clsname(me), me.G, me.p)
+  def __repr__(me): return '%s(G = %r, p = %r, hash = %r)' % \
+        (_clsname(me), me.G, me.p, me.hash)
   def _repr_pretty_(me, pp, cyclep):
     ind = _pp_bgroup_tyname(pp, me)
     if cyclep:
       pp.text('...')
     else:
       _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
-      _pp_kv(pp, 'p', me.p)
+      _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
+      _pp_kv(pp, 'hash', me.hash)
     pp.end_group(ind, ')')
 _augment(DSAPub, _tmp)
 _augment(KCDSAPub, _tmp)
 
 class _tmp:
-  def __repr__(me): return '%s(G = %r, u = %s, p = %r)' % \
-      (_clsname(me), me.G, _repr_secret(me.u), me.p)
+  def __repr__(me): return '%s(G = %r, u = %s, p = %r, hash = %r)' % \
+      (_clsname(me), me.G, _repr_secret(me.u), me.p, me.hash)
   def _repr_pretty_(me, pp, cyclep):
     ind = _pp_bgroup_tyname(pp, me)
     if cyclep:
@@ -888,7 +913,8 @@ class _tmp:
     else:
       _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable()
       _pp_kv(pp, 'u', me.u, True); pp.text(','); pp.breakable()
-      _pp_kv(pp, 'p', me.p)
+      _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable()
+      _pp_kv(pp, 'hash', me.hash)
     pp.end_group(ind, ')')
 _augment(DSAPriv, _tmp)
 _augment(KCDSAPriv, _tmp)
index ca06b5c968208f1a6b0528e00ac762c73209a6de..039886401cd8b6d44e3352a4957a9d21d11ab3c3 100644 (file)
@@ -1,3 +1,26 @@
+catacomb-python (1.3.0.1) experimental; urgency=medium
+
+  * Fix required Catacomb version in `setup.py' script.  Only affects the
+    source package.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sun, 22 Sep 2019 01:21:28 +0100
+
+catacomb-python (1.3.0) experimental; urgency=medium
+
+  * catacomb: Bindings for new blockcipher-based MACs, and AEAD schemes.
+  * catacomb: Invalidate `grand' objects passed into Python through prime-
+    generation events.
+  * catacomb: Improve class docstrings.  (They're still extremely terse.)
+  * catacomb: Add missing `copy' methods on hash and Keccak objects.
+  * catacomb: Add `WriteBuffer.contents' as a more convenient way to
+    extract the contents than coercing to `str' or `ByteString'.
+  * catacomb: Set `RTLD_DEEPBIND' while loading the native module to work
+    around #868366.
+  * pock: New program for generating efficiently verifiable prime numbers,
+    and for verifying their certificates.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sat, 21 Sep 2019 23:00:25 +0100
+
 catacomb-python (1.2.1.1) experimental; urgency=medium
 
   * Fixing to build against Debian `stretch'.
index 61f54a845c44fa38f1d9b1ac478c04a958463480..1bde721196a09361b89f26a041899fb94205e1c4 100644 (file)
@@ -5,7 +5,7 @@ XS-Python-Version: >= 2.6, << 2.8
 Maintainer: Mark Wooding <mdw@distorted.org.uk>
 Build-Depends: debhelper (>= 9), dh-python, pkg-config,
        python (>= 2.6.6-3~), python-all-dev,
-       mlib-dev (>= 2.2.2.1), catacomb-dev (>= 2.4.0)
+       mlib-dev (>= 2.2.2.1), catacomb-dev (>= 2.5.0)
 Standards-Version: 3.8.0
 
 Package: python-catacomb
index 050e1e1bf0a4a39fd12eb652f1247e04fb5257a5..65c943274118d818acfe1561d1c3677080c0b595 100644 (file)
@@ -1,16 +1,30 @@
-Catacomb/Python is copyright (c) 2005 Straylight/Edgeware
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Copyright: 2006--2010, 2012--2017 Straylight/Edgeware
+Upstream-Name: catacomb-python
+Upstream-Contact: Mark Wooding <mdw@distorted.org.uk>
+Source: https://ftp.distorted.org.uk/pub/mdw/
+License: LGPL-2.0+
 
-Catacomb/Python is free software; you can redistribute it and/or modify
-it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
+Files: *
+Copyright: 2006--2010, 2012--2017 Straylight/Edgeware
+License: LGPL-2.0+
 
-Catacomb/Python is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Library General Public License for more details.
-
-You should have a copy of the GNU Library General Public License in
-/usr/share/common-licenses/LGPL-2; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-USA.
+License: LGPL-2.0+
+ Catacomb/Python is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+ .
+ Catacomb/Python is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Library General Public License for more details.
+ .
+ You should have a copy of the GNU Library General Public License in
+ /usr/share/common-licenses/LGPL-2; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+ .
+ On Debian systems, the full text of the GNU Library General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/LGPL-2'.
diff --git a/ec.c b/ec.c
index fe5f6fbc4a890e39daa1c4cf704d1b6e9cd6600c..b555431dea1911ad2fb060ffe576ba8e31432c9b 100644 (file)
--- a/ec.c
+++ b/ec.c
@@ -292,9 +292,9 @@ static PyObject *epmeth_ec2osp(PyObject *me, PyObject *arg, PyObject *kw)
   ec pp = EC_INIT;
   unsigned f = EC_EXPLY;
   int len;
-  char *kwlist[] = { "flags", 0 };
+  static const char *const kwlist[] = { "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:ec2osp", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:ec2osp", KWLIST,
                                   convuint, &f))
     return (0);
   len = c->f->noctets * 2 + 1;
@@ -502,9 +502,9 @@ static PyObject *ecptnc_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   PyObject *x = 0, *y = 0, *z = 0;
   ec p = EC_INIT;
-  char *kwlist[] = { "x", "y", 0 };
+  static const char *const kwlist[] = { "x", "y", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:new", kwlist, &x, &y) ||
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OO:new", KWLIST, &x, &y) ||
       ecptxl(0, &p, x, y, z))
     goto end;
   return (ecpt_pywrapout(ty, &p));
@@ -543,9 +543,9 @@ static PyObject *ecpt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   PyObject *x = 0, *y = 0, *z = 0;
   ec p = EC_INIT;
-  char *kwlist[] = { "x", "y", "z", 0 };
+  static const char *const kwlist[] = { "x", "y", "z", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OOO:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OOO:new", KWLIST,
                                   &x, &y, &z) ||
       ecptxl(ECCURVE_C(ty), &p, x, y, z))
     goto end;
@@ -640,7 +640,9 @@ static PyTypeObject ecpt_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Elliptic curve points, not associated with any curve.",
+"ECPt([X, [Y]]): elliptic curve points, not associated with any curve.\n\
+  X alone may be None, an existing point, a string 'X, Y', an\n\
+  x-coordinate, or a pair (X, Y); X and Y should be a coordinate pair.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -862,9 +864,9 @@ static PyObject *meth__ECPtCurve_os2ecp(PyObject *me,
   ec_curve *cc;
   unsigned f = EC_XONLY | EC_LSB | EC_SORT | EC_EXPLY;
   ec pp = EC_INIT;
-  char *kwlist[] = { "class", "buf", "flags", 0 };
+  static const char *const kwlist[] = { "class", "buf", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|O&:os2ecp", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|O&:os2ecp", KWLIST,
                                   &me, &p, &len, convuint, &f))
     return (0);
   buf_init(&b, p, len);
@@ -940,11 +942,11 @@ end:
 
 static PyObject *ecmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   grand *r = &rand_global;
   ec p = EC_INIT;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
                                   convgrand, &r))
     return (0);
   ec_rand(ECCURVE_C(me), &p, r);
@@ -1000,10 +1002,10 @@ static PyObject *eccurve_pynew(PyTypeObject *ty,
 {
   PyObject *fobj;
   PyObject *cobj = 0;
-  char *kwlist[] = { "field", "a", "b", 0 };
+  static const char *const kwlist[] = { "field", "a", "b", 0 };
   mp *aa = 0, *bb = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&O&", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&O&", KWLIST,
                                   field_pytype, &fobj,
                                   convmp, &aa, convmp, &bb))
     goto end;
@@ -1094,7 +1096,7 @@ static PyTypeObject eccurve_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "An elliptic curve.  Abstract class.",
+"An elliptic curve.  Abstract class.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1148,7 +1150,8 @@ static PyTypeObject ecprimecurve_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "An elliptic curve over a prime field.  Use ecprimeprojcurve.",
+"ECPrimeCurve(FIELD, A, B): an elliptic curve over a prime field.\n\
+  Use ECPrimeProjCurve instead.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1202,7 +1205,8 @@ static PyTypeObject ecprimeprojcurve_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "An elliptic curve over a prime field, using projective coordinates.",
+"ECPrimeProjCurve(FIELD, A, B): an elliptic curve over a prime field\n\
+  using projective coordinates.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1256,7 +1260,8 @@ static PyTypeObject ecbincurve_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "An elliptic curve over a binary field.  Use ecbinprojcurve.",
+"ECBinCurve(FIELD, A, B): an elliptic curve over a binary field.\n\
+  Use ECBinProjCurve instead.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1310,7 +1315,8 @@ static PyTypeObject ecbinprojcurve_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "An elliptic curve over a binary field, using projective coordinates.",
+"ECBinProjCurve(FIELD, A, B): an elliptic curve over a binary field,\n\
+  using projective coordinates.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1371,10 +1377,10 @@ static PyObject *ecinfo_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   ec_info ei = { 0 };
   PyObject *e, *g;
-  char *kwlist[] = { "curve", "G", "r", "h", 0 };
+  static const char *const kwlist[] = { "curve", "G", "r", "h", 0 };
   ecinfo_pyobj *rc = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!O&O&:new", KWLIST,
                                   eccurve_pytype, &e, ecpt_pytype, &g,
                                   convmp, &ei.r, convmp, &ei.h))
     goto end;
@@ -1442,11 +1448,11 @@ end:
 
 static PyObject *eimeth_check(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   grand *r = &rand_global;
   const char *p;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:check", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:check", KWLIST,
                                   convgrand, &r))
     goto end;
   if ((p = ec_checkinfo(ECINFO_EI(me), r)) != 0)
@@ -1515,7 +1521,7 @@ static PyTypeObject ecinfo_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Elliptic curve domain parameters.",
+"ECInfo(CURVE, G, R, H): elliptic curve domain parameters.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
diff --git a/field.c b/field.c
index 224363dfce270e4d63c95eb842a4f89c6bec5632..c23f3119ce6af8199e1a67377b91b9130fa22895 100644 (file)
--- a/field.c
+++ b/field.c
@@ -42,9 +42,9 @@ static PyObject *fe_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   PyObject *x;
   mp *z;
-  char *kwlist[] = { "x", 0 };
+  static const char *const kwlist[] = { "x", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", kwlist, &x))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:fe", KWLIST, &x))
     return (0);
   if (FE_PYCHECK(x) && FE_F(x) == FIELD_F(ty)) RETURN_OBJ(x);
   if ((z = getmp(x)) == 0) return (0);
@@ -456,10 +456,10 @@ end:
 
 static PyObject *fmeth_rand(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   grand *r = &rand_global;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:rand", KWLIST,
                                   convgrand, &r))
     return (0);
   return (fe_pywrap(me, F_RAND(FIELD_F(me), MP_NEW, r)));
@@ -575,9 +575,9 @@ static PyObject *primefield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0;
   field *f;
-  char *kwlist[] = { "p", 0 };
+  static const char *const kwlist[] = { "p", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:primefield", KWLIST,
                                   convmp, &xx))
     goto end;
   if ((f = field_prime(xx)) == 0)
@@ -623,7 +623,7 @@ static PyTypeObject primefield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Prime fields.",
+"PrimeField(P): prime fields.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -651,10 +651,10 @@ static PyObject *niceprimefield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0;
   field *f;
-  char *kwlist[] = { "p", 0 };
+  static const char *const kwlist[] = { "p", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:niceprimefield",
-                                  kwlist, convmp, &xx))
+                                  KWLIST, convmp, &xx))
     goto end;
   if ((f = field_niceprime(xx)) == 0)
     VALERR("bad prime for niceprimefield");
@@ -690,7 +690,7 @@ static PyTypeObject niceprimefield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Nice prime fields.",
+"NicePrimeField(P): prime field using Solinas reduction.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -750,7 +750,7 @@ static PyTypeObject binfield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Binary fields.  Abstract class.",
+"Binary fields.  Abstract class.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -778,9 +778,9 @@ static PyObject *binpolyfield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0;
   field *f;
-  char *kwlist[] = { "p", 0 };
+  static const char *const kwlist[] = { "p", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:binpolyfield", KWLIST,
                                   convgf, &xx))
     goto end;
   if ((f = field_binpoly(xx)) == 0) VALERR("bad poly for binpolyfield");
@@ -826,7 +826,7 @@ static PyTypeObject binpolyfield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Binary fields with polynomial basis representation.",
+"BinPolyField(P): binary fields with polynomial basis representation.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -854,10 +854,10 @@ static PyObject *binnormfield_pynew(PyTypeObject *ty,
 {
   mp *xx = 0, *yy = 0;
   field *f;
-  char *kwlist[] = { "p", "beta", 0 };
+  static const char *const kwlist[] = { "p", "beta", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:binnormfield",
-                                  kwlist, convgf, &xx, convgf, &yy))
+                                  KWLIST, convgf, &xx, convgf, &yy))
     goto end;
   if ((f = field_binnorm(xx, yy)) == 0) VALERR("bad args for binnormfield");
   MP_DROP(xx); MP_DROP(yy);
@@ -908,7 +908,7 @@ static PyTypeObject binnormfield_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Binary fields with normal basis representation.",
+"BinNormField(P, BETA): binary fields with normal basis representation.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
diff --git a/group.c b/group.c
index e00e3fb63748562c80c1792adc91626ad6aca07c..3d290705dac8766e1a4052f03d252fcea543dd24 100644 (file)
--- a/group.c
+++ b/group.c
@@ -40,11 +40,11 @@ PyObject *fginfo_pywrap(gprime_param *dp, PyTypeObject *ty)
 static PyObject *fginfo_pynew(PyTypeObject *ty,
                              PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "p", "r", "g", 0 };
+  static const char *const kwlist[] = { "p", "r", "g", 0 };
   gprime_param dp = { 0 };
   fginfo_pyobj *z = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&O&:new", KWLIST,
                                   convmp, &dp.p,
                                   convmp, &dp.q,
                                   convmp, &dp.g))
@@ -94,12 +94,12 @@ static PyObject *meth__DHInfo_generate(PyObject *me,
   grand *r = &rand_global;
   struct excinfo exc = EXCINFO_INIT;
   pypgev evt = { { 0 } };
-  char *kwlist[] =
+  static const char *const kwlist[] =
     { "class", "pbits", "qbits", "event", "rng", "nsteps", 0 };
   PyObject *rc = 0;
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &steps))
@@ -123,15 +123,17 @@ static PyObject *meth__DHInfo_genlimlee(PyObject *me,
   pypgev oe = { { 0 } }, ie = { { 0 } };
   int subgroupp = 1;
   unsigned f = 0;
-  char *kwlist[] = { "class", "pbits", "qbits", "event", "ievent",
-                    "rng", "nsteps", "subgroupp", 0 };
+  static const char *const kwlist[] = {
+    "class", "pbits", "qbits", "event", "ievent",
+    "rng", "nsteps", "subgroupp", 0
+  };
   size_t i, nf;
   mp **v = 0;
   PyObject *rc = 0, *vec = 0;
 
   oe.exc = ie.exc = &exc;
   if (!PyArg_ParseTupleAndKeywords(arg, kw,
-                                  "OO&O&|O&O&O&O&O&:genlimlee", kwlist,
+                                  "OO&O&|O&O&O&O&O&:genlimlee", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   convpgev, &oe, convpgev, &ie,
                                   convgrand, &r, convuint, &steps,
@@ -160,13 +162,13 @@ static PyObject *meth__DHInfo_genkcdsa(PyObject *me,
   grand *r = &rand_global;
   struct excinfo exc = EXCINFO_INIT;
   pypgev evt = { { 0 } };
-  char *kwlist[] = { "class", "pbits", "qbits",
-                    "event", "rng", "nsteps", 0 };
+  static const char *const kwlist[] =
+    { "class", "pbits", "qbits", "event", "rng", "nsteps", 0 };
   mp *v = MP_NEW;
   PyObject *rc = 0;
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|O&O&O&:genkcdsa", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|O&O&O&:genkcdsa", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &steps))
@@ -193,12 +195,12 @@ static PyObject *meth__DHInfo_gendsa(PyObject *me,
   Py_ssize_t ksz;
   struct excinfo exc = EXCINFO_INIT;
   pypgev evt = { { 0 } };
-  char *kwlist[] =
+  static const char *const kwlist[] =
     { "class", "pbits", "qbits", "seed", "event", "nsteps", 0 };
   PyObject *rc = 0;
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&s#|O&O&:gendsa", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&s#|O&O&:gendsa", KWLIST,
                                   &me, convuint, &pl, convuint, &ql,
                                   &k, &ksz, convpgev, &evt,
                                   convuint, &steps))
@@ -333,7 +335,7 @@ static PyTypeObject fginfo_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Abstract base class for field-group information objects.",
+"Abstract base class for field-group information objects.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -381,7 +383,7 @@ static PyTypeObject dhinfo_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Standard (integer) Diffie-Hellman group information.",
+"DHInfo(P, R, G): standard (integer) Diffie-Hellman group information.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -429,7 +431,7 @@ static PyTypeObject bindhinfo_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Binary-field Diffie-Hellman group information.",
+"BinDHInfo(P, R, G): binary-field Diffie-Hellman group information.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -501,7 +503,7 @@ PyObject *ge_pywrap(PyObject *gobj, ge *x)
 
 static PyObject *ge_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "x", 0 };
+  static const char *const kwlist[] = { "x", 0 };
   PyObject *x;
   group *g;
   ec p = EC_INIT;
@@ -510,7 +512,7 @@ static PyObject *ge_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   mptext_stringctx sc;
 
   g = GROUP_G(ty);
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &x)) goto end;
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &x)) goto end;
   xx = G_CREATE(g);
   if (ECPT_PYCHECK(x)) {
     getecptout(&p, x);
@@ -724,14 +726,14 @@ end:
 
 static PyObject *gemeth_toec(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "curve", 0 };
+  static const char *const kwlist[] = { "curve", 0 };
   PyTypeObject *cty = 0;
   PyObject *rc = 0;
   group *g;
   ec_curve *c;
   ec p = EC_INIT;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:toec", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:toec", KWLIST,
                                   &cty)) goto end;
   g = GROUP_G(GE_GOBJ(me));
   if (cty) {
@@ -808,11 +810,11 @@ static PyObject *gmeth_mexp(PyObject *me, PyObject *arg)
 
 static PyObject *gmeth_checkgroup(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   grand *r = &rand_global;
   const char *p;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:checkgroup", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:checkgroup", KWLIST,
                                   convgrand, &r))
     goto end;
   if ((p = G_CHECK(GROUP_G(me), r)) != 0)
@@ -1161,9 +1163,9 @@ static PyObject *primegroup_pynew(PyTypeObject *ty,
                                  PyObject *arg, PyObject *kw)
 {
   PyObject *i;
-  char *kwlist[] = { "info", 0 };
+  static const char *const kwlist[] = { "info", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
                                   dhinfo_pytype, &i))
     return (0);
   return (group_dopywrap(ty, group_prime(FGINFO_DP(i))));
@@ -1194,7 +1196,7 @@ static PyTypeObject primegroup_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Subgroups of prime fields.",
+"PrimeGroup(INFO): subgroups of prime fields.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1238,9 +1240,9 @@ static PyObject *bingroup_pynew(PyTypeObject *ty,
                                PyObject *arg, PyObject *kw)
 {
   PyObject *i;
-  char *kwlist[] = { "info", 0 };
+  static const char *const kwlist[] = { "info", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
                                   bindhinfo_pytype, &i))
     return (0);
   return (group_dopywrap(ty, group_binary(FGINFO_DP(i))));
@@ -1271,7 +1273,7 @@ static PyTypeObject bingroup_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Subgroups of binary fields.",
+"BinGroup(INFO): subgroups of binary fields.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1315,9 +1317,9 @@ static PyObject *ecgroup_pynew(PyTypeObject *ty,
 {
   PyObject *i;
   ec_info ei;
-  char *kwlist[] = { "info", 0 };
+  static const char *const kwlist[] = { "info", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!:new", KWLIST,
                                   ecinfo_pytype, &i))
     return (0);
   ecinfo_copy(&ei, ECINFO_EI(i));
@@ -1349,7 +1351,7 @@ static PyTypeObject ecgroup_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Elliptic curve groups.",
+"ECGroup(INFO): elliptic curve groups.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
diff --git a/key.c b/key.c
index 99c25aba5d1f688365c12c2065001be0372abf9a..87462ab59b76e77deac3f2d98ff0ac0328b67cf0 100644 (file)
--- a/key.c
+++ b/key.c
@@ -353,10 +353,10 @@ static PyObject *kdmeth_split(PyObject *me, PyObject *arg)
 static PyObject *kdmeth_copy(PyObject *me, PyObject *arg, PyObject *kw)
 {
   key_filter f = { 0, 0 };
-  static char *kwlist[] = { "filter", 0 };
+  static const char *const kwlist[] = { "filter", 0 };
   key_data *kd;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:copy", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:copy", KWLIST,
                                   convfilter, &f))
     return (0);
   if ((kd = key_copydata(KEYDATA_KD(me), &f)) == 0)
@@ -370,9 +370,9 @@ static PyObject *kdmeth_write(PyObject *me, PyObject *arg, PyObject *kw)
   key_filter f = { 0, 0 };
   dstr d = DSTR_INIT;
   PyObject *rc = 0;
-  static char *kwlist[] = { "filter", 0 };
+  static const char *const kwlist[] = { "filter", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:write", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:write", KWLIST,
                                   convfilter, &f))
     return (0);
   key_write(KEYDATA_KD(me), &d, &f);
@@ -386,9 +386,9 @@ static PyObject *kdmeth_encode(PyObject *me, PyObject *arg, PyObject *kw)
   key_filter f = { 0, 0 };
   dstr d = DSTR_INIT;
   PyObject *rc = 0;
-  static char *kwlist[] = { "filter", 0 };
+  static const char *const kwlist[] = { "filter", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:encode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:encode", KWLIST,
                                   convfilter, &f))
     return (0);
   key_encode(KEYDATA_KD(me), &d, &f);
@@ -538,9 +538,9 @@ static PyObject *keydatabin_pynew(PyTypeObject *ty,
   Py_ssize_t n;
   unsigned f = 0;
   keydata_pyobj *me = 0;
-  static char *kwlist[] = { "key", "flags", 0 };
+  static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", KWLIST,
                                   &p, &n, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@ -585,7 +585,7 @@ static PyTypeObject keydatabin_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key data for binary keys.",
+"KeyDataBinary(KEY, [flags = 0]): key data for binary keys.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -615,9 +615,9 @@ static PyObject *keydataenc_pynew(PyTypeObject *ty,
   Py_ssize_t n;
   unsigned f = 0;
   keydata_pyobj *me = 0;
-  static char *kwlist[] = { "key", "flags", 0 };
+  static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", KWLIST,
                                   &p, &n, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@ -722,7 +722,7 @@ static PyTypeObject keydataenc_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key data for encrypted keys.",
+"KeyDataEncrypted(KEY, [flags = 0]): key data for encrypted keys.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -751,9 +751,9 @@ static PyObject *keydatamp_pynew(PyTypeObject *ty,
   mp *x = 0;
   unsigned f = 0;
   keydata_pyobj *me = 0;
-  static char *kwlist[] = { "key", "flags", 0 };
+  static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
                                   convmp, &x, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@ -798,7 +798,7 @@ static PyTypeObject keydatamp_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key data for large-integer keys.",
+"KeyDataMP(KEY, [flags = 0]): key data for large-integer keys.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -827,9 +827,9 @@ static PyObject *keydatastr_pynew(PyTypeObject *ty,
   char *p;
   unsigned f = 0;
   keydata_pyobj *me = 0;
-  static char *kwlist[] = { "key", "flags", 0 };
+  static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:new", KWLIST,
                                   &p, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@ -873,7 +873,7 @@ static PyTypeObject keydatastr_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key data for string keys.",
+"KeyDataString(KEY, [flags = 0]): key data for string keys.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -902,9 +902,9 @@ static PyObject *keydataec_pynew(PyTypeObject *ty,
   ec x = EC_INIT;
   unsigned f = 0;
   keydata_pyobj *me = 0;
-  static char *kwlist[] = { "key", "flags", 0 };
+  static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
                                   convecpt, &x, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
@@ -953,7 +953,7 @@ static PyTypeObject keydataec_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key data for elliptic-curve keys.",
+"KeyDataECPt(KEY, [flags = 0]): key data for elliptic-curve keys.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1055,10 +1055,10 @@ static PyObject *keydatastruct_pynew(PyTypeObject *ty,
   char *p;
   keydata_pyobj *me = 0;
   key_data *kd = 0;
-  static char *kwlist[] = { "subkeys", 0 };
+  static const char *const kwlist[] = { "subkeys", 0 };
 
   Py_XINCREF(arg); Py_XINCREF(kw);
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", kwlist, &sub))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", KWLIST, &sub))
     goto end;
   kd = key_newstruct();
   if (sub) {
@@ -1159,7 +1159,7 @@ static PyTypeObject keydatastruct_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key data for structured keys.",
+"KeyDataStructured([subkeys = []]): key data for structured keys.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1388,11 +1388,12 @@ static PyObject *key_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   uint32 id;
   char *type;
   unsigned long exptime = KEXP_FOREVER;
-  static char *kwlist[] = { "keyfile", "id", "type", "exptime", 0 };
+  static const char *const kwlist[] =
+    { "keyfile", "id", "type", "exptime", 0 };
   key *k;
   int err;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&s|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O&s|O&:new", KWLIST,
                                   keyfile_pytype, &kfobj, convu32, &id,
                                   &type, convulong, &exptime))
     goto end;
@@ -1450,9 +1451,9 @@ static PyObject *kmeth_extract(PyObject *me, PyObject *arg, PyObject *kw)
   PyObject *nameobj;
   char *name;
   FILE *fp;
-  static char *kwlist[] = { "file", "filter", 0 };
+  static const char *const kwlist[] = { "file", "filter", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:extract", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:extract", KWLIST,
                                   &PyFile_Type, &file,
                                   convfilter, &f) ||
       (fp = PyFile_AsFile(file)) == 0 ||
@@ -1471,9 +1472,9 @@ static PyObject *kmeth_fingerprint(PyObject *me,
 {
   ghash *h;
   key_filter f = { KF_NONSECRET, KF_NONSECRET };
-  static char *kwlist[] = { "hash", "filter", 0 };
+  static const char *const kwlist[] = { "hash", "filter", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:fingerprint", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:fingerprint", KWLIST,
                                   convghash, &h, convfilter, &f))
     return (0);
   return (getbool(key_fingerprint(KEY_K(me), h, &f)));
@@ -1649,7 +1650,7 @@ static PyTypeObject key_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Key object.",
+"Key(KF, ID, TYPE, [exptime = KEXP_FOREVER]): key object.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1777,10 +1778,10 @@ static PyObject *keyfile_pynew(PyTypeObject *ty,
   char *file = 0;
   unsigned how = KOPEN_READ;
   keyfile_pyobj *rc = 0;
-  static char *kwlist[] = { "file", "how", "report", 0 };
+  static const char *const kwlist[] = { "file", "how", "report", 0 };
 
   Py_XINCREF(arg); Py_XINCREF(kw);
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", KWLIST,
                                   &file, &how, &ri.func))
     goto end;
   if (ri.func && !PyCallable_Check(ri.func))
@@ -1836,10 +1837,10 @@ static PyObject *kfmeth_merge(PyObject *me, PyObject *arg, PyObject *kw)
   PyObject *x = 0;
   FILE *fp = 0;
   int rc;
-  static char *kwlist[] = { "file", "report", 0 };
+  static const char *const kwlist[] = { "file", "report", 0 };
 
   Py_XINCREF(arg); Py_XINCREF(kw);
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O:merge", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O:merge", KWLIST,
                                   &PyFile_Type, &x, &ri.func))
     goto end;
   if (ri.func && !PyCallable_Check(ri.func))
@@ -1935,11 +1936,11 @@ static PyObject *kfmeth_newkey(PyObject *me, PyObject *arg, PyObject *kw)
   uint32 id;
   char *type;
   long exptime = KEXP_FOREVER;
-  static char *kwlist[] = { "id", "type", "exptime", 0 };
+  static const char *const kwlist[] = { "id", "type", "exptime", 0 };
   key *k;
   int err;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s|l:newkey", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s|l:newkey", KWLIST,
                                   convu32, &id, &type, &exptime))
     goto end;
   if ((err = key_new(KEYFILE_KF(me), id, type, exptime, &k)) != 0)
@@ -1957,9 +1958,9 @@ static PyObject *kfmeth_qtag(PyObject *me, PyObject *arg, PyObject *kw)
   char *tag;
   dstr d = DSTR_INIT;
   PyObject *rc = 0;
-  static char *kwlist[] = { "tag", "new", 0 };
+  static const char *const kwlist[] = { "tag", "new", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O!:qtag", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O!:qtag", KWLIST,
                                   &tag, keydata_pytype, &newkdobj))
     goto end;
   if (key_qtag(KEYFILE_KF(me), tag, &d, &k, &kd))
@@ -2045,7 +2046,8 @@ static PyTypeObject keyfile_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Keyring file.",
+"KeyFile(FILE, [how = KOPEN_READ], [report = ?]): Keyring file.\n\
+   calls REPORT(FILE, LINE, MSG) on problems",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
diff --git a/mp.c b/mp.c
index ab15d3fd10e115c476f1623e1eb162c37bb1d68f..31eec247760b63318f6082704ed551205007dd68 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -528,9 +528,9 @@ static PyObject *mp_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   mp *z;
   mp_pyobj *zz = 0;
   int radix = 0;
-  char *kwlist[] = { "x", "radix", 0 };
+  static const char *const kwlist[] = { "x", "radix", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", kwlist, &x, &radix))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:new", KWLIST, &x, &radix))
     goto end;
   if (MP_PYCHECK(x)) RETURN_OBJ(x);
   if (!good_radix_p(radix, 1)) VALERR("bad radix");
@@ -662,8 +662,8 @@ end:
 static PyObject *mpmeth_tostring(PyObject *me, PyObject *arg, PyObject *kw)
 {
   int radix = 10;
-  char *kwlist[] = { "radix", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", kwlist, &radix))
+  static const char *const kwlist[] = { "radix", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|i:tostring", KWLIST, &radix))
     goto end;
   if (!good_radix_p(radix, 0)) VALERR("bad radix");
   return (mp_topystring(MP_X(me), radix, 0, 0, 0));
@@ -703,11 +703,11 @@ end:
                                 PyObject *arg, PyObject *kw)           \
   {                                                                    \
     long len = -1;                                                     \
-    char *kwlist[] = { "len", 0 };                                     \
+    static const char *const kwlist[] = { "len", 0 };                                  \
     PyObject *rc = 0;                                                  \
                                                                        \
     if (!PyArg_ParseTupleAndKeywords(arg, kw, "|l:" #name,             \
-                                   kwlist, &len))                      \
+                                   KWLIST, &len))                      \
       goto end;                                                                \
     if (len < 0) {                                                     \
       len = mp_octets##c(MP_X(me));                                    \
@@ -766,10 +766,10 @@ static PyObject *mpmeth_tobuf(PyObject *me, PyObject *arg)
 static PyObject *mpmeth_primep(PyObject *me, PyObject *arg, PyObject *kw)
 {
   grand *r = &rand_global;
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   PyObject *rc = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", kwlist, convgrand, &r))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&", KWLIST, convgrand, &r))
     goto end;
   rc = getbool(pgen_primep(MP_X(me), r));
 end:
@@ -905,7 +905,7 @@ with an integer conversion.\n\
 \n\
 Notes:\n\
 \n\
-  * Use `//' for division.  MPs don't have `/' division.",
+  * Use `//' for integer division: `/' gives exact rational division.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -937,10 +937,10 @@ static PyObject *meth__MP_fromstring(PyObject *me,
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
-  char *kwlist[] = { "class", "x", "radix", 0 };
+  static const char *const kwlist[] = { "class", "x", "radix", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
-                                  kwlist, &me, &p, &len, &r))
+                                  KWLIST, &me, &p, &len, &r))
     goto end;
   if (!good_radix_p(r, 1)) VALERR("bad radix");
   sc.buf = p; sc.lim = p + len;
@@ -1108,7 +1108,7 @@ static PyTypeObject *mpmul_pytype, mpmul_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"An object for multiplying many small integers.",
+"MPMul(N_0, N_1, ....): an object for multiplying many small integers.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1301,10 +1301,10 @@ static void mpmont_pydealloc(PyObject *me)
 static PyObject *mpmont_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mpmont_pyobj *mm = 0;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
     goto end;
   if (!MP_POSP(xx) || !MP_ODDP(xx)) VALERR("m must be positive and odd");
   mm = (mpmont_pyobj *)ty->tp_alloc(ty, 0);
@@ -1375,7 +1375,7 @@ static PyTypeObject *mpmont_pytype, mpmont_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A Montgomery reduction context.",
+"MPMont(N): a Montgomery reduction context.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1455,10 +1455,10 @@ static PyObject *mpbarrett_pynew(PyTypeObject *ty,
                                 PyObject *arg, PyObject *kw)
 {
   mpbarrett_pyobj *mb = 0;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
     goto end;
   if (!MP_POSP(xx)) VALERR("m must be positive");
   mb = (mpbarrett_pyobj *)ty->tp_alloc(ty, 0);
@@ -1514,7 +1514,7 @@ static PyTypeObject *mpbarrett_pytype, mpbarrett_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A Barrett reduction context.",
+"MPBarrett(N): a Barrett reduction context.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1585,10 +1585,10 @@ static PyObject *mpreduce_pynew(PyTypeObject *ty,
 {
   mpreduce_pyobj *mr = 0;
   mpreduce r;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &xx))
     goto end;
   if (!MP_POSP(xx)) VALERR("m must be positive");
   if (mpreduce_create(&r, xx)) VALERR("bad modulus (must be 2^k - ...)");
@@ -1642,7 +1642,7 @@ static PyTypeObject *mpreduce_pytype, mpreduce_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A reduction context for reduction modulo primes of special form.",
+"MPReduce(N): a reduction context for reduction modulo Solinas primes.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1721,7 +1721,7 @@ static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mpcrt_mod *v = 0;
   Py_ssize_t n, i = 0, j;
-  char *kwlist[] = { "mv", 0 };
+  static const char *const kwlist[] = { "mv", 0 };
   PyObject *q = 0, *x;
   mp *xx = MP_NEW, *y = MP_NEW, *g = MP_NEW;
   mpmul mm;
@@ -1729,7 +1729,7 @@ static PyObject *mpcrt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 
   if (PyTuple_Size(arg) > 1)
     q = arg;
-  else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &q))
+  else if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &q))
     goto end;
   Py_INCREF(q);
   if (!PySequence_Check(q)) TYERR("want a sequence of moduli");
@@ -1824,7 +1824,7 @@ static PyTypeObject *mpcrt_pytype, mpcrt_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A context for the solution of Chinese Remainder Theorem problems.",
+"MPCRT(SEQ): a context for solving Chinese Remainder Theorem problems.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1884,9 +1884,9 @@ static PyObject *gf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   mp *z;
   mp_pyobj *zz = 0;
   int radix = 0;
-  char *kwlist[] = { "x", "radix", 0 };
+  static const char *const kwlist[] = { "x", "radix", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", kwlist, &x, &radix))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|i:gf", KWLIST, &x, &radix))
     goto end;
   if (GF_PYCHECK(x)) RETURN_OBJ(x);
   if (!good_radix_p(radix, 1)) VALERR("radix out of range");
@@ -2112,7 +2112,7 @@ but it's much easier to type than `p2' or `c2' or whatever.\n\
 \n\
 Notes:\n\
 \n\
-  * Use `//' for division.  GFs don't have `/' division.",
+  * Use `//' for Euclidean division: `/' gives exact rational division.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2144,10 +2144,10 @@ static PyObject *meth__GF_fromstring(PyObject *me,
   PyObject *z = 0;
   mp *zz;
   mptext_stringctx sc;
-  char *kwlist[] = { "class", "x", "radix", 0 };
+  static const char *const kwlist[] = { "class", "x", "radix", 0 };
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "Os#|i:fromstring",
-                                  kwlist, &me, &p, &len, &r))
+                                  KWLIST, &me, &p, &len, &r))
     goto end;
   if (!good_radix_p(r, 1)) VALERR("bad radix");
   sc.buf = p; sc.lim = p + len;
@@ -2262,10 +2262,10 @@ static PyObject *gfreduce_pynew(PyTypeObject *ty,
 {
   gfreduce_pyobj *mr = 0;
   gfreduce r;
-  char *kwlist[] = { "m", 0 };
+  static const char *const kwlist[] = { "m", 0 };
   mp *xx = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convgf, &xx))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convgf, &xx))
     goto end;
   if (MP_ZEROP(xx)) ZDIVERR("modulus is zero!");
   gfreduce_create(&r, xx);
@@ -2323,7 +2323,7 @@ static PyTypeObject *gfreduce_pytype, gfreduce_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"A reduction context for reduction modulo sparse irreducible polynomials.",
+"GFReduce(N): a context for reduction modulo sparse polynomials.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -2364,9 +2364,9 @@ static PyObject *gfn_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mp *p = 0, *beta = 0;
   gfn_pyobj *gg = 0;
-  char *kwlist[] = { "p", "beta", 0 };
+  static const char *const kwlist[] = { "p", "beta", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   convgf, &p, convgf, &beta))
     goto end;
   gg = PyObject_New(gfn_pyobj, ty);
@@ -2461,8 +2461,8 @@ static PyTypeObject gfn_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"An object for transforming elements of binary fields between polynomial\n\
-and normal basis representations.",
+"GFN(P, BETA): an object for transforming elements of binary fields\n\
+  between polynomial and normal basis representations.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
index 7e28f93fdd926f00bf28d7bb0c0e40f29b45fd5f..1796629ce8b85eb6fce2d60c5b3a1f193e34d5d7 100644 (file)
@@ -54,11 +54,11 @@ end:
 static PyObject *pixie_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   pixie_pyobj *rc = 0;
-  char *kwlist[] = { "socket", 0 };
+  static const char *const kwlist[] = { "socket", 0 };
   char *sock = 0;
   int fd;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s:new", kwlist, &sock))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s:new", KWLIST, &sock))
     goto end;
   if ((fd = pixie_open(sock)) < 0)
     OSERR(sock);
@@ -78,12 +78,12 @@ static PyObject *pixmeth_read(PyObject *me, PyObject *arg, PyObject *kw)
 {
   unsigned mode = PMODE_READ;
   char *tag;
-  char *kwlist[] = { "tag", "mode", 0 };
+  static const char *const kwlist[] = { "tag", "mode", 0 };
   PyObject *rc = 0;
   int r;
   char buf[1024];
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:read", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:read", KWLIST,
                                   &tag, convuint, &mode))
     goto end;
   r = pixie_read(PIXIE_FD(me), tag, mode, buf, sizeof(buf));
@@ -152,7 +152,7 @@ static PyTypeObject pixie_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Passphrase pixie connection.",
+"Pixie([socket = ?]): passphrase pixie connection.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -182,10 +182,10 @@ static PyObject *meth_ppread(PyObject *me, PyObject *arg, PyObject *kw)
   char *tag;
   unsigned f = PMODE_READ;
   PyObject *rc = 0;
-  char *kwlist[] = { "tag", "mode", 0 };
+  static const char *const kwlist[] = { "tag", "mode", 0 };
   char buf[1024];
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:ppread", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O&:ppread", KWLIST,
                                   &tag, convuint, &f))
     goto end;
   if (passphrase_read(tag, f, buf, sizeof(buf)))
diff --git a/pgen.c b/pgen.c
index 94603100b23c03e7f78a8e06cceab018676922ef..9f9e1a2199b17b36ff85175784def45fce77c0dd 100644 (file)
--- a/pgen.c
+++ b/pgen.c
@@ -57,10 +57,10 @@ end:
 
 static PyObject *pfilt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "x", 0 };
+  static const char *const kwlist[] = { "x", 0 };
   PyObject *xobj;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &xobj))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &xobj))
     return (0);
   return (pfilt_pymake(ty, xobj));
 }
@@ -219,7 +219,7 @@ static PyTypeObject pfilt_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Small-primes filter.",
+"PrimeFilter(X): small-primes filter.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -256,9 +256,9 @@ static PyObject *rabin_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mp *x = 0;
   rabin_pyobj *o = 0;
-  char *kwlist[] = { "x", 0 };
+  static const char *const kwlist[] = { "x", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmp, &x))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &x))
     goto end;
   if (!MP_POSP(x) || MP_EVENP(x)) VALERR("must be positive and odd");
   o = (rabin_pyobj *)ty->tp_alloc(ty, 0);
@@ -352,7 +352,7 @@ static PyTypeObject rabin_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Rabin-Miller strong primality test.",
+"RabinMiller(X): Rabin-Miller strong primality test.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -549,14 +549,14 @@ static int pgev_python(int rq, pgen_event *ev, void *p)
   PyObject *rc = 0;
   int st = PGEN_ABORT;
   long l;
-  char *meth[] = {
-    "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass"
-  };
+  static const char *const meth[] =
+    { "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass" };
 
   rq++;
   if (rq > N(meth)) SYSERR("event code out of range");
   pyev = pgevent_pywrap(ev);
-  if ((rc = PyObject_CallMethod(pg->obj, meth[rq], "(O)", pyev)) == 0)
+  if ((rc = PyObject_CallMethod(pg->obj, (/*unconst*/ char *)meth[rq],
+                               "(O)", pyev)) == 0)
     goto end;
   if (rc == Py_None)
     st = PGEN_TRY;
@@ -696,9 +696,9 @@ static PyObject *pgstep_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   mpw s;
   pgstep_pyobj *rc = 0;
-  char *kwlist[] = { "step", 0 };
+  static const char *const kwlist[] = { "step", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", kwlist, convmpw, &s))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmpw, &s))
     goto end;
   rc = (pgstep_pyobj *)ty->tp_alloc(ty, 0);
   rc->f.step = s;
@@ -743,7 +743,7 @@ static PyTypeObject pgstep_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Simple prime-number stepper with small-factors filter.",
+"PrimeGenStepper(STEP): simple stepper with small-factors filter.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -770,9 +770,9 @@ static PyObject *pgjump_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   PyObject *o, *fobj;
   pgjump_pyobj *rc = 0;
-  char *kwlist[] = { "jump", 0 };
+  static const char *const kwlist[] = { "jump", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &o) ||
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &o) ||
       (fobj = pfilt_pymake(pfilt_pytype, o)) == 0)
     goto end;
   rc = (pgjump_pyobj *)ty->tp_alloc(ty, 0);
@@ -825,7 +825,7 @@ static PyTypeObject pgjump_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Stepper for larger steps, with small-factors filter.",
+"PrimeGenJumper(JUMP): stepper for larger steps with small-factors filter.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -851,9 +851,9 @@ static PyTypeObject pgjump_pytype_skel = {
 static PyObject *pgtest_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   pgtest_pyobj *rc = 0;
-  char *kwlist[] = { 0 };
+  static const char *const kwlist[] = { 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", kwlist)) goto end;
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
   rc = (pgtest_pyobj *)ty->tp_alloc(ty, 0);
   rc->pg.proc = pgen_test;
   rc->pg.ctx = &rc->r;
@@ -886,7 +886,7 @@ static PyTypeObject pgtest_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Rabin-Miller tester.",
+"PrimeGenTester(): Rabin-Miller tester.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -928,13 +928,13 @@ static PyObject *meth_pgen(PyObject *me, PyObject *arg, PyObject *kw)
   struct excinfo exc = EXCINFO_INIT;
   pypgev step = { { 0 } }, test = { { 0 } }, evt = { { 0 } };
   unsigned nsteps = 0, ntests = 0;
-  char *kwlist[] = { "start", "name", "stepper", "tester", "event",
-                    "nsteps", "ntests", 0 };
+  static const char *const kwlist[] =
+    { "start", "name", "stepper", "tester", "event", "nsteps", "ntests", 0 };
 
   step.exc = &exc; step.ev.proc = pgen_filter; step.ev.ctx = &fc;
   test.exc = &exc; test.ev.proc = pgen_test; test.ev.ctx = &tc;
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&O&O&:pgen", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&O&O&:pgen", KWLIST,
                                   convmp, &x, &p, convpgev, &step,
                                   convpgev, &test, convpgev, &evt,
                                   convuint, &nsteps, convuint, &ntests))
@@ -963,10 +963,11 @@ static PyObject *meth_strongprime_setup(PyObject *me,
   struct excinfo exc = EXCINFO_INIT;
   pypgev evt = { { 0 } };
   PyObject *rc = 0;
-  char *kwlist[] = { "nbits", "name", "event", "rng", "nsteps", 0 };
+  static const char *const kwlist[] =
+    { "nbits", "name", "event", "rng", "nsteps", 0 };
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
                                   convuint, &nbits, &name,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &n))
@@ -992,10 +993,11 @@ static PyObject *meth_strongprime(PyObject *me, PyObject *arg, PyObject *kw)
   struct excinfo exc = EXCINFO_INIT;
   pypgev evt = { { 0 } };
   PyObject *rc = 0;
-  char *kwlist[] = { "nbits", "name", "event", "rng", "nsteps", 0 };
+  static const char *const kwlist[] =
+    { "nbits", "name", "event", "rng", "nsteps", 0 };
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
                                   convuint, &nbits, &name,
                                   convpgev, &evt, convgrand, &r,
                                   convuint, &n))
@@ -1021,12 +1023,12 @@ static PyObject *meth_limlee(PyObject *me, PyObject *arg, PyObject *kw)
   unsigned on = 0;
   size_t i, nf = 0;
   PyObject *rc = 0, *vec;
-  char *kwlist[] = { "pbits", "qbits", "name", "event", "ievent",
-                    "rng", "nsteps", 0 };
+  static const char *const kwlist[] =
+    { "pbits", "qbits", "name", "event", "ievent", "rng", "nsteps", 0 };
   mp *x = 0, **v = 0;
 
   ie.exc = oe.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|sO&O&O&O&:limlee", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|sO&O&O&O&:limlee", KWLIST,
                                   convuint, &pl, convuint, &ql,
                                   &p, convpgev, &oe, convpgev, &ie,
                                   convgrand, &r, convuint, &on))
diff --git a/pock b/pock
new file mode 100644 (file)
index 0000000..ff62d8b
--- /dev/null
+++ b/pock
@@ -0,0 +1,1065 @@
+#! /usr/bin/python
+### -*- mode: python, coding: utf-8 -*-
+###
+### Tool for generating and verifying primality certificates
+###
+### (c) 2017 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to Catacomb.
+###
+### Catacomb/Python is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### Catacomb/Python is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with Catacomb/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### Imported modules.
+
+from sys import argv, stdin, stdout, stderr
+import os as OS
+import itertools as I
+import math as M
+import optparse as OP
+
+import catacomb as C
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+class ExpectedError (Exception):
+  """
+  I represent an expected error, which should be reported in a friendly way.
+  """
+  pass
+
+def prod(ff, one = 1):
+  """
+  Return ONE times the product of the elements of FF.
+
+  This is not done very efficiently.
+  """
+  return reduce(lambda prod, f: prod*f, ff, one)
+
+def parse_label(line):
+  """
+  Split LINE at an `=' character and return the left and right halves.
+
+  The returned pieces have leading and trailing whitespace trimmed.
+  """
+  eq = line.find('=')
+  if eq < 0: raise ExpectedError('expected `LABEL = ...\'')
+  return line[:eq].strip(), line[eq + 1:].strip()
+
+def parse_list(s, n):
+  l = s.split(',', n - 1)
+  if n is not None and len(l) != n:
+    raise ExpectedError('expected `,\'-separated list of %d items' % n)
+  return l
+
+def conv_int(s):
+  """Convert S to a integer."""
+  try: return C.MP(s, 0)
+  except TypeError: raise ExpectedError('invalid integer `%s\'' % s)
+
+VERBOSITY = 1
+
+class ProgressReporter (object):
+  """
+  I keep users amused while the program looks for large prime numbers.
+
+  My main strategy is the printing of incomprehensible runes.  I can be
+  muffled by lowering by verbosity level.
+
+  Prime searches are recursive in nature.  When a new recursive level is
+  started, call `push'; and call `pop' when the level is finished.  This must
+  be done around the top level too.
+  """
+  def __init__(me):
+    """Initialize the ProgressReporter."""
+    me._level = -1
+    me._update()
+  def _update(me):
+    """
+    Update our idea of whether we're active.
+
+    We don't write inscrutable runes when inactive.  The current policy is to
+    write nothing if verbosity is zero, to write runes for the top level only
+    if verbosity is 1, and to write runes always if verbosity is higher than
+    that.
+    """
+    me._active = VERBOSITY >= 2 or (VERBOSITY == 1 and me._level == 0)
+  def push(me):
+    """Push a new search level."""
+    me._level += 1
+    me._update()
+    if me._level > 0: me.p('[')
+    else: me.p(';; ')
+  def pop(me):
+    """Pop a search level."""
+    if me._level > 0: me.p(']')
+    else: me.p('\n')
+    me._level -= 1
+    me._update()
+  def p(me, ch):
+    """Print CH as a progress rune."""
+    if me._active: stderr.write(ch); stderr.flush()
+
+def combinations(r, v):
+  """
+  Return an iterator which yields all combinations of R elements from V.
+
+  V must be an indexable sequence.  The each combination is returned as a
+  list, containing elements from V in their original order.
+  """
+
+  ## Set up the selection vector.  C will contain the indices of the items of
+  ## V we've selected for the current combination.  At all times, C contains
+  ## a strictly increasing sequence of integers in the interval [0, N).
+  n = len(v)
+  c = range(r)
+
+  while True:
+
+    ## Yield up the current combination.
+    vv = [v[i] for i in c]
+    yield vv
+
+    ## Now advance to the next one.  Find the last index in C which we can
+    ## increment subject to the rules.  As we iterate downwards, i will
+    ## contain the index into C, and j will be the maximum acceptable value
+    ## for the corresponding item.  We'll step the last index until it
+    ## reaches the limit, and then step the next one down, resetting the last
+    ## index, and so on.
+    i, j = r, n
+    while True:
+
+      ## If i is zero here, then we've advanced everything as far as it will
+      ## go.  We're done.
+      if i == 0: return
+
+      ## Move down to the next index.
+      i -= 1; j -= 1
+
+      ## If this index isn't at its maximum value, then we've found the place
+      ## to step.
+      if c[i] != j: break
+
+    ## Step this index on by one, and set the following indices to the
+    ## immediately following values.
+    j = c[i] + 1
+    while i < r: c[i] = j; i += 1; j += 1
+
+class ArgFetcher (object):
+  """
+  I return arguments from a list, reporting problems when they occur.
+  """
+  def __init__(me, argv, errfn):
+    """
+    Initialize, returning successive arguments from ARGV.
+
+    Errors are reported to ERRFN.
+    """
+    me._argv = argv
+    me._argc = len(argv)
+    me._errfn = errfn
+    me._i = 0
+  def arg(me, default = None, must = True):
+    """
+    Return the next argument.
+
+    If MUST is true, then report an error (to the ERRFN) if there are no more
+    arguments; otherwise, return the DEFAULT.
+    """
+    if me._i >= me._argc:
+      if must: me._errfn('missing argument')
+      return default
+    arg = me._argv[me._i]; me._i += 1
+    return arg
+  def int(me, default = None, must = True, min = None, max = None):
+    """
+    Return the next argument converted to an integer.
+
+    If MUST is true, then report an error (to the ERRFN) if there are no more
+    arguments; otherwise return the DEFAULT.  Report an error if the next
+    argument is not a valid integer, or if the integer is beyond the MIN and
+    MAX bounds.
+    """
+    arg = me.arg(default = None, must = must)
+    if arg is None: return default
+    try: arg = int(arg)
+    except ValueError: me._errfn('bad integer')
+    if (min is not None and arg < min) or (max is not None and arg > max):
+      me._errfn('out of range')
+    return arg
+
+###--------------------------------------------------------------------------
+### Sieving for small primes.
+
+class Sieve (object):
+  """
+  I represent a collection of small primes, up to some chosen limit.
+
+  The limit is available as the `limit' attribute.  Let L be this limit;
+  then, if N < L^2 is some composite, then N has at least one prime factor
+  less than L.
+  """
+
+  ## Figure out the number of bits in a (nonnegative) primitive `int'.  We'll
+  ## use a list of these as our sieve.
+  _NBIT = 15
+  while type(1 << (_NBIT + 1)) == int: _NBIT += 1
+
+  def __init__(me, limit):
+    """
+    Initialize a sieve holding all primes below LIMIT.
+    """
+
+    ## The sieve is maintained in the `_bits' attribute.  This is a list of
+    ## integers, used as a bitmask: let 2 < n < L be an odd integer; then bit
+    ## (n - 3)/2 will be clear iff n is prime.  Let W be the value of
+    ## `_NBIT', above; then bit W i + j in the sieve is stored in bit j of
+    ## `_bits[i]'.
+
+    ## Store the limit for later inspection.
+    me.limit = limit
+
+    ## Calculate the size of sieve we'll need and initialize the bit list.
+    n = (limit - 2)/2
+    sievesz = (n + me._NBIT - 1)/me._NBIT
+    me._sievemax = sievesz*me._NBIT
+    me._bits = sievesz*[0]
+
+    ## This is standard Sieve of Eratosthenes.  For each index i: if
+    ## bit i is clear, then p = 2 i + 3 is prime, so set the bits
+    ## corresponding to each multiple of p, i.e., bits (k p - 3)/2 =
+    ## (2 k i + 3 - 3)/2 = k i for k > 1.
+    for i in xrange(me._sievemax):
+      if me._bitp(i): i += 1; continue
+      p = 2*i + 3
+      if p >= limit: break
+      for j in xrange(i + p, me._sievemax, p): me._setbit(j)
+      i += 1
+
+  def _bitp(me, i): i, j = divmod(i, me._NBIT); return (me._bits[i] >> j)&1
+  def _setbit(me, i): i, j = divmod(i, me._NBIT); me._bits[i] |= 1 << j
+
+  def smallprimes(me):
+    """
+    Return an iterator over the known small primes.
+    """
+    yield 2
+    n = 3
+    for b in me._bits:
+      for j in xrange(me._NBIT):
+        if not (b&1): yield n
+        b >>= 1; n += 2
+
+## We generate the sieve on demand.
+SIEVE = None
+
+def initsieve(sievebits):
+  """
+  Generate the sieve.
+
+  Ensure that it can be used to check the primality of numbers up to (but not
+  including) 2^SIEVEBITS.
+  """
+  global SIEVE
+  if SIEVE is not None: raise ValueError('sieve already defined')
+  if sievebits < 6: sievebits = 6
+  SIEVE = Sieve(1 << (sievebits + 1)/2)
+
+###--------------------------------------------------------------------------
+### Primality checking.
+
+def small_test(p):
+  """
+  Check that P is a small prime.
+
+  If not, raise an `ExpectedError'.  The `SIEVE' variable must have been
+  initialized.
+  """
+  if p < 2: raise ExpectedError('%d too small' % p)
+  if SIEVE.limit*SIEVE.limit < p:
+    raise ExpectedError('%d too large for small prime' % p)
+  for q in SIEVE.smallprimes():
+    if q*q > p: return
+    if p%q == 0: raise ExpectedError('%d divides %d' % (q, p))
+
+def pock_test(p, a, qq):
+  """
+  Check that P is prime using Pocklington's criterion.
+
+  If not, raise an `ExpectedError'.
+
+  Let Q be the product of the elements of the sequence QQ.  The test works as
+  follows.  Suppose p is the smallest prime factor of P.  If A^{P-1} /== 1
+  (mod P) then P is certainly composite (Fermat's test); otherwise, we have
+  established that the order of A in (Z/pZ)^* divides P - 1.  Next, let t =
+  A^{(P-1)/q} for some prime factor q of Q, and let g = gcd(t - 1, P).  If g
+  = P then the proof is inconclusive; if 1 < g < P then g is a nontrivial
+  factor of P, so P is composite; otherwise, t has order q in (Z/pZ)^*, so
+  (Z/pZ)^* contains a subgroup of size q, and therefore q divides p - 1.  If
+  QQ is a sequence of distinct primes, and the preceding criterion holds for
+  all q in QQ, then Q divides p - 1.  If Q^2 < P then the proof is
+  inconclusive; otherwise, let p' be any prime dividing P/p.  Then p' >= p >
+  Q, so p p' > Q^2 > P; but p p' divides P, so this is a contradiction.
+  Therefore P/p has no prime factors, and P is prime.
+  """
+
+  ## We don't actually need the distinctness criterion.  Suppose that q^e
+  ## divides Q.  Then gcd(t - 1, P) = 1 implies that A^{(P-1)/q^{e-1}} has
+  ## order q^e in (Z/pZ)^*, which accounts for the multiplicity.
+
+  Q = prod(qq)
+  if p < 2: raise ExpectedError('%d too small' % p)
+  if Q*Q <= p:
+    raise ExpectedError('too few Pocklington factors for %d' % p)
+  if pow(a, p - 1, p) != 1:
+    raise ExpectedError('%d is Fermat witness for %d' % (a, p))
+  for q in qq:
+    if Q%(q*q) == 0:
+      raise ExpectedError('duplicate Pocklington factor %d for %d' % (q, p))
+    g = p.gcd(pow(a, (p - 1)/q, p) - 1)
+    if g == p:
+      raise ExpectedError('%d order not multiple of %d mod %d' % (a, q, p))
+    elif g != 1:
+      raise ExpectedError('%d divides %d' % (g, p))
+
+def ecpp_test(p, a, b, x, y, qq):
+  """
+  Check that P is prime using Goldwasser and Kilian's ECPP method.
+
+  If not, raise an `ExpectedError'.
+
+  Let Q be the product of the elements of the sequence QQ.  Suppose p is the
+  smallest prime factor of P.  Let g = gcd(4 A^3 + 27 B^2, P).  If g = P then
+  the test is inconclusive; otherwise, if g /= 1 then g is a nontrivial
+  factor of P.  Define E(GF(p)) = { (x, y) | y^2 = x^3 + A x + B } U { inf }
+  to be the elliptic curve over p with short-Weierstraß coefficients A and B;
+  we have just checked that this curve is not singular.  If R = (X, Y) is not
+  a point on this curve, then the test is inconclusive.  If Q R is not the
+  point at infinity, then the test fails; otherwise we deduce that P has
+  Q-torsion in E.  Let S = (Q/q) R for some prime factor q of Q.  If S is the
+  point at infinity then the test is inconclusive; otherwise, q divides the
+  order of S in E.  If QQ is a sequence of distinct primes, and the preceding
+  criterion holds for all q in QQ, then Q divides the order of S.  Therefore
+  #E(p) >= Q.  If Q <= (qrrt(P) + 1)^2 then the test is inconclusive.
+  Otherwise, Hasse's theorem tells us that |p + 1 - #E(p)| <= 2 sqrt(p);
+  hence we must have p + 1 + 2 sqrt(p) = (sqrt(p) + 1)^2 >= #E(p) >= Q >
+  (qrrt(P) + 1)^2; so sqrt(p) + 1 > qrrt(P) + 1, i.e., p^2 > P.  As for
+  Pocklington above, if p' is any prime factor of P/p, then p p' >= p^2 > P,
+  which is a contradiction, and we conclude that P is prime.
+  """
+
+  ## This isn't going to work if gcd(P, 6) /= 1: we're going to use the
+  ## large-characteristic addition formulae.
+  g = p.gcd(6)
+  if g != 1: raise ExpectedError('%d divides %d' % (g, p))
+
+  ## We want to check that Q > (qrrt(P) + 1)^2 iff sqrt(Q) > qrrt(P) + 1; but
+  ## calculating square roots is not enjoyable (partly because we have to
+  ## deal with the imprecision).  Fortunately, some algebra will help: the
+  ## condition holds iff qrrt(P) < sqrt(Q) - 1 iff P < Q^2 - 4 Q sqrt(Q) +
+  ## 6 Q - 4 sqrt(Q) + 1 = Q (Q + 6) + 1 - 4 sqrt(Q) (Q + 1) iff Q (Q + 6) -
+  ## P + 1 > 4 sqrt(Q) (Q + 1) iff (Q (Q + 6) - P + 1)^2 > 16 Q (Q + 1)^2
+  Q = prod(qq)
+  t, u = Q*(Q + 6) - p + 1, 4*(Q + 1)
+  if t*t <= Q*u*u: raise ExpectedError('too few subgroups for ECPP')
+
+  ## Construct the curve.
+  E = C.PrimeField(p).ec(a, b) # careful: may not be a prime!
+
+  ## Find the base point.
+  R = E(x, y)
+  if not R.oncurvep():
+    raise ExpectedError('(%d, %d) is not on the curve' % (x, y))
+
+  ## Check that it has Q-torsion.
+  if Q*R: raise ExpectedError('(%d, %d) not a %d-torsion point' % (x, y, Q))
+
+  ## Now check the individual factors.
+  for q in qq:
+    if Q%(q*q) == 0:
+      raise ExpectedError('duplicate ECPP factor %d for %d' % (q, p))
+    S = (Q/q)*R
+    if not S:
+      raise ExpectedError('(%d, %d) order not a multiple of %d' % (x, y, q))
+    g = p.gcd(S._z)
+    if g != 1:
+      raise ExpectedError('%d divides %d' % (g, p))
+
+###--------------------------------------------------------------------------
+### Proof steps and proofs.
+
+class BaseStep (object):
+  """
+  I'm a step in a primality proof.
+
+  I assert that a particular number is prime, and can check this.
+
+  This class provides basic protocol for proof steps, mostly to do with
+  handling labels.
+
+  The step's label is kept in its `label' attribute.  It can be set by the
+  constructor, and is `None' by default.  Users can modify this attribute if
+  they like.  Labels beginning `$' are assumed to be internal and
+  uninteresting; other labels cause `check' lines to be written to the output
+  listing the actual number of interest.
+
+  Protocol that proof steps should provide:
+
+  label         A string labelling the proof step and the associated prime
+                number.
+
+  p             The prime number which this step proves to be prime.
+
+  check()       Check that the proof step is actually correct, assuming that
+                any previous steps have already been verified.
+
+  out(FILE)     Write an appropriate encoding of the proof step to the output
+                FILE.
+  """
+  def __init__(me, label = None, *arg, **kw):
+    """Initialize a proof step, setting a default label if necessary."""
+    super(BaseStep, me).__init__(*arg, **kw)
+    me.label = label
+  def out(me, file):
+    """
+    Write the proof step to an output FILE.
+
+    Subclasses must implement a method `_out' which actually does the work.
+    Here, we write a `check' line to verify that the proof actually applies
+    to the number we wanted, if the label says that this is an interesting
+    step.
+    """
+    me._out(file)
+    if me.label is not None and not me.label.startswith('$'):
+      file.write('check %s, %d, %d\n' % (me.label, me.p.nbits, me.p))
+
+class SmallStep (BaseStep):
+  """
+  I represent a claim that a number is a small prime.
+
+  Such claims act as the base cases in a complicated primality proof.  When
+  verifying, the claim is checked by trial division using a collection of
+  known small primes.
+  """
+  def __init__(me, pp, p, *arg, **kw):
+    """
+    Initialize a small-prime step.
+
+    PP is the overall PrimeProof object of which this is a step; P is the
+    small number whose primality is asserted.
+    """
+    super(SmallStep, me).__init__(*arg, **kw)
+    me.p = p
+  def check(me):
+    """Check that the number is indeed a small prime."""
+    return small_test(me.p)
+  def _out(me, file):
+    """Write a small-prime step to the FILE."""
+    file.write('small %s = %d\n' % (me.label, me.p))
+  def __repr__(me): return 'SmallStep(%d)' % (me.p)
+  @classmethod
+  def parse(cls, pp, line):
+    """
+    Parse a small-prime step from a LINE in a proof file.
+
+    SMALL-STEP ::= `small' LABEL `=' P
+
+    PP is a PrimeProof object holding the results from the previous steps.
+    """
+    if SIEVE is None: raise ExpectedError('missing `sievebits\' line')
+    label, p = parse_label(line)
+    return cls(pp, conv_int(p), label = label)
+
+class PockStep (BaseStep):
+  """
+  I represent a Pocklington certificate for a number.
+
+  The number is not explicitly represented in a proof file.  See `pock_test'
+  for the underlying mathematics.
+  """
+  def __init__(me, pp, a, R, qqi, *arg, **kw):
+    """
+    Inititialize a Pocklington step.
+
+    PP is the overall PrimeProof object of which this is a step; A is the
+    generator of a substantial subgroup of units; R is a cofactor; and QQI is
+    a sequence of labels for previous proof steps.  If Q is the product of
+    the primes listed in QQI, then the number whose primality is asserted is
+    2 Q R + 1.
+    """
+    super(PockStep, me).__init__(*arg, **kw)
+    me._a = a
+    me._R = R
+    me._qqi = qqi
+    me._qq = [pp.get_step(qi).p for qi in qqi]
+    me.p = prod(me._qq, 2*R) + 1
+  def check(me):
+    """Verify a proof step based on Pocklington's theorem."""
+    return pock_test(me.p, me._a, me._qq)
+  def _out(me, file):
+    """Write a Pocklington step to the FILE."""
+    file.write('pock %s = %d, %d, [%s]\n' % \
+                 (me.label, me._a,
+                  me._R, ', '.join('%s' % qi for qi in me._qqi)))
+  def __repr__(me): return 'PockStep(%d, %d, %s)' % (me._a, me._R, me._qqi)
+  @classmethod
+  def parse(cls, pp, line):
+    """
+    Parse a Pocklington step from a LINE in a proof file.
+
+    POCK-STEP ::= `pock' LABEL `=' A `,' R `,' `[' Q-LIST `]'
+    Q-LIST ::= Q [`,' Q-LIST]
+
+    PP is a PrimeProof object holding the results from the previous steps.
+    """
+    label, rest = parse_label(line)
+    a, R, qq = parse_list(rest, 3)
+    qq = qq.strip()
+    if not qq.startswith('[') or not qq.endswith(']'):
+      raise ExpectedError('missing `[...]\' around Pocklington factors')
+    return cls(pp, conv_int(a), conv_int(R),
+               [q.strip() for q in qq[1:-1].split(',')], label = label)
+
+class ECPPStep (BaseStep):
+  """
+  I represent a Goldwasser--Kilian ECPP certificate for a number.
+  """
+  def __init__(me, pp, p, a, b, x, y, qqi, *arg, **kw):
+    """
+    Inititialize an ECPP step.
+
+    PP is the overall PrimeProof object of which this is a step; P is the
+    number whose primality is asserted; A and B are the short Weierstraß
+    curve coefficients; X and Y are the base point coordinates; and QQI is a
+    sequence of labels for previous proof steps.
+    """
+    super(ECPPStep, me).__init__(*arg, **kw)
+    me._a, me._b = a, b
+    me._x, me._y = x, y
+    me._qqi = qqi
+    me._qq = [pp.get_step(qi).p for qi in qqi]
+    me.p = p
+  def check(me):
+    """Verify a proof step based on Goldwasser and Kilian's theorem."""
+    return ecpp_test(me.p, me._a, me._b, me._x, me._y, me._qq)
+  def _out(me, file):
+    """Write an ECPP step to the FILE."""
+    file.write('ecpp %s = %d, %d, %d, %d, %d, [%s]\n' % \
+                 (me.label, me.p, me._a, me._b, me._x, me._y,
+                  ', '.join('%s' % qi for qi in me._qqi)))
+  def __repr__(me):
+    return 'ECPPstep(%d, %d, %d, %d, %d, %s)' % \
+        (me.p, me._a, me._b, me._x, me._y, me._qqi)
+  @classmethod
+  def parse(cls, pp, line):
+    """
+    Parse an ECPP step from a LINE in a proof file.
+
+    ECPP-STEP ::= `ecpp' LABEL `=' P `,' A `,' B `,' X `,' Y `,'
+        `[' Q-LIST `]'
+    Q-LIST ::= Q [`,' Q-LIST]
+
+    PP is a PrimeProof object holding the results from the previous steps.
+    """
+    label, rest = parse_label(line)
+    p, a, b, x, y, qq = parse_list(rest, 6)
+    qq = qq.strip()
+    if not qq.startswith('[') or not qq.endswith(']'):
+      raise ExpectedError('missing `[...]\' around ECPP factors')
+    return cls(pp, conv_int(p), conv_int(a), conv_int(b),
+               conv_int(x), conv_int(y),
+               [q.strip() for q in qq[1:-1].split(',')], label = label)
+
+def check(pp, line):
+  """
+  Handle a `check' line in a proof file.
+
+  CHECK ::= `check' LABEL, B, N
+
+  Verify that the proof step with the given LABEL asserts the primality of
+  the integer N, and that 2^{B-1} <= N < 2^B.
+  """
+  label, nb, p = parse_list(line, 3)
+  label, nb, p = label.strip(), conv_int(nb), conv_int(p)
+  pi = pp.get_step(label).p
+  if pi != p:
+    raise ExpectedError('check failed: %s = %d /= %d' % (label, pi, p))
+  if p.nbits != nb:
+    raise ExpectedError('check failed: nbits(%s) = %d /= %d' % \
+                        (label, p.nbits, nb))
+  if VERBOSITY: print ';; %s = %d [%d]' % (label, p, nb)
+
+def setsievebits(pp, line):
+  """
+  Handle a `sievebits' line in a proof file.
+
+  SIEVEBITS ::= `sievebits' N
+
+  Ensure that the verifier is willing to accept small primes up to 2^N.
+  """
+  initsieve(int(line))
+
+class PrimeProof (object):
+  """
+  I represent a proof of primality for one or more numbers.
+
+  I can encode my proof as a line-oriented text file, in a simple format, and
+  read such a proof back to check it.
+  """
+
+  ## A table to dispatch on keywords read from a file.
+  STEPMAP = { 'small': SmallStep.parse,
+              'pock': PockStep.parse,
+              'ecpp': ECPPStep.parse,
+              'sievebits': setsievebits,
+              'check': check }
+
+  def __init__(me):
+    """
+    Initialize a proof object.
+    """
+    me._steps = {}                      # Maps labels to steps.
+    me._stepseq = []                    # Sequence of labels, in order.
+    me._pmap = {}                       # Maps primes to steps.
+    me._i = 0
+
+  def addstep(me, step):
+    """
+    Add a new STEP to the proof.
+
+    The STEP may have a label already.  If not, a new internal label is
+    chosen.  The proof step is checked before being added to the proof.  The
+    label is returned.
+    """
+
+    ## If there's already a step for this prime, and the new step doesn't
+    ## have a label, then return the old one instead.
+    if step.label is None:
+      try: return me._pmap[step.p]
+      except KeyError: pass
+
+    ## Make sure the step is actually correct.
+    step.check()
+
+    ## Generate a label if the step doesn't have one already.
+    if step.label is None: step.label = '$t%d' % me._i; me._i += 1
+
+    ## If the label is already taken then we have a problem.
+    if step.label in me._steps:
+      raise ExpectedError('duplicate label `%s\'' % step.label)
+
+    ## Store the proof step.
+    me._pmap[step.p] = step.label
+    me._steps[step.label] = step
+    me._stepseq.append(step.label)
+    return step.label
+
+  def get_step(me, label):
+    """
+    Check that LABEL labels a known step, and return that step.
+    """
+    try: return me._steps[label]
+    except KeyError: raise ExpectedError('unknown label `%s\'' % label)
+
+  def write(me, file):
+    """
+    Write the proof to the given FILE.
+    """
+
+    ## Prefix the main steps with a `sievebits' line.
+    file.write('sievebits %d\n' % (2*(SIEVE.limit.bit_length() - 1)))
+
+    ## Write the steps out one by one.
+    for label in me._stepseq: me._steps[label].out(file)
+
+  def read(me, file):
+    """
+    Read a proof from a given FILE.
+
+    FILE ::= {STEP | CHECK | SIEVEBITS} [FILE]
+    STEP ::= SMALL-STEP | POCK-STEP
+
+    Comments (beginning `;') and blank lines are ignored.  Other lines begin
+    with a keyword.
+    """
+    lastp = None
+    for lno, line in enumerate(file, 1):
+      line = line.strip()
+      if line.startswith(';'): continue
+      ww = line.split(None, 1)
+      if not ww: continue
+      w = ww[0]
+      if len(ww) > 1: tail = ww[1]
+      else: tail = ''
+      try:
+        try: op = me.STEPMAP[w]
+        except KeyError:
+          raise ExpectedError('unrecognized keyword `%s\'' % w)
+        step = op(me, tail)
+        if step is not None:
+          me.addstep(step)
+          lastp = step.p
+      except ExpectedError, e:
+        raise ExpectedError('%s:%d: %s' % (file.name, lno, e.message))
+    return lastp
+
+###--------------------------------------------------------------------------
+### Finding provable primes.
+
+class BasePrime (object):
+  """
+  I represent a prime number which has been found and can be proven.
+
+  This object can eventually be turned into a sequence of proof steps and
+  added to a PrimeProof.  This isn't done immediately, because some
+  prime-search strategies want to build a pool of provable primes and will
+  then select some subset of them to actually construct the number of final
+  interest.  This way, we avoid cluttering the output proof with proofs of
+  uninteresting numbers.
+
+  Protocol required.
+
+  p             The prime number in question.
+
+  label(LABEL)  Associate LABEL with this prime, and the corresponding proof
+                step.  A label can be set in the constructor, or later using
+                this method.
+
+  register(PP)  Register the prime with a PrimeProof, adding any necessary
+                proof steps.  Returns the label of the proof step for this
+                number.
+
+  _mkstep(PP, **KW)
+                Return a proof step for this prime.
+  """
+  def __init__(me, label = None, *args, **kw):
+    """Initialize a provable prime number object."""
+    super(BasePrime, me).__init__(*args, **kw)
+    me._index = me._pp = None
+    me._label = label
+  def label(me, label):
+    """Set this number's LABEL."""
+    me._label = label
+  def register(me, pp):
+    """
+    Register the prime's proof steps with PrimeProof PP.
+
+    Return the final step's label.
+    """
+    if me._pp is not None:
+      assert me._pp == pp
+    else:
+      me._pp = pp
+      me._index = pp.addstep(me._mkstep(pp, label = me._label))
+      ##try: me._index = pp.addstep(me._mkstep(pp, label = me._label))
+      ##except: raise RuntimeError('generated proof failed sanity check')
+    return me._index
+
+class SmallPrime (BasePrime):
+  """I represent a prime small enough to be checked in isolation."""
+  def __init__(me, p, *args, **kw):
+    super(SmallPrime, me).__init__(*args, **kw)
+    me.p = p
+  def _mkstep(me, pp, **kw):
+    return SmallStep(pp, me.p, **kw)
+
+class PockPrime (BasePrime):
+  """I represent a prime proven using Pocklington's theorem."""
+  def __init__(me, p, a, qq, *args, **kw):
+    super(PockPrime, me).__init__(*args, **kw)
+    me.p = p
+    me._a = a
+    me._qq = qq
+  def _mkstep(me, pp, **kw):
+    return PockStep(pp, me._a, (me.p - 1)/prod((q.p for q in me._qq), 2),
+                    [q.register(pp) for q in me._qq], **kw)
+
+def gen_small(nbits, label = None, p = None):
+  """
+  Return a new small prime.
+
+  The prime will be exactly NBITS bits long.  The proof step will have the
+  given LABEL attached.  Report progress to the ProgressReporter P.
+  """
+  while True:
+
+    ## Pick a random NBITS-bit number.
+    n = C.rand.mp(nbits, 1)
+    assert n.nbits == nbits
+
+    ## If it's probably prime, then check it against the small primes we
+    ## know.  If it passes then we're done.  Otherwise, try again.
+    if n.primep():
+      for q in SIEVE.smallprimes():
+        if q*q > n: return SmallPrime(n, label = label)
+        if n%q == 0: break
+
+def gen_pock(nbits, nsubbits = 0, label = None, p = ProgressReporter()):
+  """
+  Return a new prime provable using Pocklington's theorem.
+
+  The prime N will be exactly NBITS long, of the form N = 2 Q R + 1.  If
+  NSUBBITS is nonzero, then each prime factor of Q will be NSUBBITS bits
+  long; otherwise a suitable default will be chosen.  The proof step will
+  have the given LABEL attached.  Report progress to the ProgressReporter P.
+
+  The prime numbers this function returns are a long way from being uniformly
+  distributed.
+  """
+
+  ## Pick a suitable value for NSUBBITS if we don't have one.
+  if not nsubbits:
+
+    ## This is remarkably tricky.  Picking about 1/3 sqrt(NBITS) factors
+    ## seems about right for large numbers, but there's serious trouble
+    ## lurking for small sizes.
+    nsubbits = int(3*M.sqrt(nbits))
+    if nbits < nsubbits + 3: nsubbits = nbits//2 + 1
+    if nbits == 2*nsubbits: nsubbits += 1
+
+  ## Figure out how many subgroups we'll need.
+  npiece = ((nbits + 1)//2 + nsubbits - 1)//nsubbits
+  p.push()
+
+  ## Keep searching...
+  while True:
+
+    ## Come up with a collection of known prime factors.
+    p.p('!'); qq = [gen(nsubbits, p = p) for i in xrange(npiece)]
+    Q = prod(q.p for q in qq)
+
+    ## Come up with bounds on the cofactor.  If we're to have N = 2 Q R + 1,
+    ## and 2^{B-1} <= N < 2^B, then we must have 2^{B-2}/Q <= R < 2^{B-1}/Q.
+    Rbase = (C.MP(0).setbit(nbits - 2) + Q - 1)//Q
+    Rwd = C.MP(0).setbit(nbits - 2)//Q
+
+    ## Probe the available space of cofactors.  If the space is kind of
+    ## narrow, then we want to give up quickly if we're not finding anything
+    ## suitable.
+    step = 0
+    while step < Rwd:
+      step += 1
+
+      ## Pick a random cofactor and examine the number we ended up with.
+      ## Make sure it really does have the length we expect.
+      R = C.rand.range(Rwd) + Rbase
+      n = 2*Q*R + 1
+      assert n.nbits == nbits
+
+      ## As a complication, if NPIECE is 1, it's just about possible that Q^2
+      ## <= n, in which case this isn't going to work.
+      if Q*Q < n: continue
+
+      ## If n has small factors, then pick another cofactor.
+      if C.PrimeFilter.smallfactor(n) == C.PGEN_FAIL: continue
+
+      ## Work through the small primes to find a suitable generator.  The
+      ## value 2 is almost always acceptable, so don't try too hard here.
+      for a in I.islice(SIEVE.smallprimes(), 16):
+
+        ## First, try the Fermat test.  If that fails, then n is definitely
+        ## composite.
+        if pow(a, n - 1, n) != 1: p.p('.'); break
+        p.p('*')
+
+        ## Work through the subgroup orders, checking that suitable powers of
+        ## a generate the necessary subgroups.
+        for q in qq:
+          if n.gcd(pow(a, (n - 1)/q.p, n) - 1) != 1:
+            p.p('@'); ok = False; break
+        else:
+          ok = True
+
+        ## we're all good.
+        if ok: p.pop(); return PockPrime(n, a, qq, label = label)
+
+def gen(nbits, label = None, p = ProgressReporter()):
+  """
+  Generate a prime number with NBITS bits.
+
+  Give it the LABEL, and report progress to P.
+  """
+  if SIEVE.limit >> (nbits + 1)/2: g = gen_small
+  else: g = gen_pock
+  return g(nbits, label = label, p = p)
+
+def gen_limlee(nbits, nsubbits,
+               label = None, qlfmt = None, p = ProgressReporter()):
+  """
+  Generate a Lim--Lee prime with NBITS bits.
+
+  Let p be the prime.  Then we'll have p = 2 q_0 q_1 ... q_k, with all q_i at
+  least NSUBBITS bits long, and all but q_k exactly that long.
+
+  The prime will be given the LABEL; progress is reported to P.  The factors
+  q_i will be labelled by filling in the `printf'-style format string QLFMT
+  with the argument i.
+  """
+
+  ## Figure out how many factors (p - 1)/2 will have.
+  npiece = nbits//nsubbits
+  if npiece < 2: raise ExpectedError('too few pieces')
+
+  ## Decide how big to make the pool of factors.
+  poolsz = max(3*npiece + 5, 25) # Heuristic from GnuPG
+
+  ## Prepare for the main loop.
+  disp = nstep = 0
+  qbig = None
+  p.push()
+
+  ## Try to make a prime.
+  while True:
+    p.p('!')
+
+    ## Construct a pool of NSUBBITS-size primes.  There's a problem with very
+    ## small sizes: we might not be able to build a pool of distinct primes.
+    pool = []; qmap = {}
+    for i in xrange(poolsz):
+      for j in xrange(64):
+        q = gen(nsubbits, p = p)
+        if q.p not in qmap: break
+      else:
+        raise ExpectedError('insufficient diversity')
+      qmap[q.p] = q
+      pool.append(q)
+
+    ## Work through combinations of factors from the pool.
+    for qq in combinations(npiece - 1, pool):
+
+      ## Construct the product of the selected factors.
+      qsmall = prod(q.p for q in qq)
+
+      ## Maybe we'll need to replace the large factor.  Try not to do this
+      ## too often.  DISP measures the large factor's performance at
+      ## producing candidates with the right length.  If it looks bad then
+      ## we'll have to replace it.
+      if 3*disp*disp > nstep*nstep:
+        qbig = None
+        if disp < 0: p.p('<')
+        else: p.p('>')
+
+      ## If we don't have a large factor, then make one.
+      if qbig is None:
+        qbig = gen(nbits - qsmall.nbits, p = p)
+        disp = 0; nstep = 0
+
+      ## We have a candidate.  Calculate it and make sure it has the right
+      ## length.
+      n = 2*qsmall*qbig.p + 1
+      nstep += 1
+      if n.nbits < nbits: disp -= 1
+      elif n.nbits > nbits: disp += 1
+      elif C.PrimeFilter.smallfactor(n) == C.PGEN_FAIL: pass
+      else:
+
+        ## The candidate has passed the small-primes test.  Now check it
+        ## against Pocklington.
+        for a in I.islice(SIEVE.smallprimes(), 16):
+
+          ## Fermat test.
+          if pow(a, n - 1, n) != 1: p.p('.'); break
+          p.p('*')
+
+          ## Find a generator of a sufficiently large subgroup.
+          if n.gcd(pow(a, (n - 1)/qbig.p, n) - 1) != 1: p.p('@'); continue
+          ok = True
+          for q in qq:
+            if n.gcd(pow(a, (n - 1)/q.p, n) - 1) != 1:
+              p.p('@'); ok = False; break
+
+          ## We're done.
+          if ok:
+
+            ## Label the factors.
+            qq.append(qbig)
+            if qlfmt:
+              for i, q in enumerate(qq): q.label(qlfmt % i)
+
+            ## Return the number we found.
+            p.pop(); return PockPrime(n, a, qq, label = label)
+
+###--------------------------------------------------------------------------
+### Main program.
+
+def __main__():
+  global VERBOSITY
+
+  ## Prepare an option parser.
+  op = OP.OptionParser(
+    usage = '''\
+pock [-qv] [-s SIEVEBITS] CMD ARGS...
+        gen NBITS
+        ll NBITS NSUBBITS
+        check [FILE]''',
+    description = 'Generate or verify certified prime numbers.')
+  op.add_option('-v', '--verbose', dest = 'verbosity',
+                action = 'count', default = 1,
+                help = 'print mysterious runes while looking for prime numbers')
+  op.add_option('-q', '--quiet', dest = 'quietude',
+                action = 'count', default = 0,
+                help = 'be quiet while looking for prime numbers')
+  op.add_option('-s', '--sievebits', dest = 'sievebits',
+                type = 'int', default = 32,
+                help = 'size (in bits) of largest small prime')
+  opts, argv = op.parse_args()
+  VERBOSITY = opts.verbosity - opts.quietude
+  p = ProgressReporter()
+  a = ArgFetcher(argv, op.error)
+
+  ## Process arguments and do what the user asked.
+  w = a.arg()
+
+  if w == 'gen':
+    ## Generate a prime with no special structure.
+    initsieve(opts.sievebits)
+    nbits = a.int(min = 4)
+    pp = PrimeProof()
+    p = gen(nbits, 'p', p = p)
+    p.register(pp)
+    pp.write(stdout)
+
+  elif w == 'll':
+    ## Generate a Lim--Lee prime.
+    initsieve(opts.sievebits)
+    nbits = a.int(min = 4)
+    nsubbits = a.int(min = 4, max = nbits)
+    pp = PrimeProof()
+    p = gen_limlee(nbits, nsubbits, 'p', 'q_%d', p = p)
+    p.register(pp)
+    pp.write(stdout)
+
+  elif w == 'check':
+    ## Check an existing certificate.
+    fn = a.arg(default = '-', must = False)
+    if fn == '-': f = stdin
+    else: f = open(fn, 'r')
+    pp = PrimeProof()
+    p = pp.read(f)
+
+  else:
+    raise ExpectedError("unknown command `%s'" % w)
+
+if __name__ == '__main__':
+  prog = OS.path.basename(argv[0])
+  try: __main__()
+  except ExpectedError, e: exit('%s: %s' % (prog, e.message))
+  except IOError, e: exit('%s: %s' % (prog, e))
+
+###----- That's all, folks --------------------------------------------------
diff --git a/pock.1 b/pock.1
new file mode 100644 (file)
index 0000000..6879bc5
--- /dev/null
+++ b/pock.1
@@ -0,0 +1,853 @@
+.\" -*-nroff-*-
+.\"
+.\" Describe the primality certificate generator and checker
+.\"
+.\" (c) 2016 Straylight/Edgeware
+.\"
+.
+.\"----- Licensing notice ---------------------------------------------------
+.\"
+.\" This file is part of the Python interface to Catacomb.
+.\"
+.\" Catacomb/Python is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" Catacomb/Python is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with Catacomb/Python; if not, write to the Free Software Foundation,
+.\" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.
+.ie t \{\
+.  if \n(.g \{\
+.    fam P
+.  \}
+.  ds o \(bu
+.  ds ss \s7\v'-4p'
+.  ds se \v'4p'\s0
+.  ds us \s7\v'2p'
+.  ds ue \v'-2p'\s0
+.  ds *e \(*e
+.  ds mo \(mo
+.  ds sr \(sr
+.  ds cu \(cu
+.  ds ca \(ca
+.  ds iy \(if
+.  ds == \(==
+.  ds .. \&.\h'2p'.\h'2p'.\&
+.  ds /= \h'(\w'\*(=='-\w'/')/2'/\h'-(\w'\*(=='+\w'/')/2'\*(==
+.\}
+.el \{\
+.  ds o o
+.  ds ss ^
+.  ds se
+.  ds us _
+.  ds ue
+.  ds *e \fIepsilon\fP
+.  ds mo in
+.  ds sr sqrt
+.  ds cu union
+.  ds ca isect
+.  ds iy infty
+.  ds == ==
+.  ds .. \&...\&
+.  ds /= /==
+.\}
+.de hP
+.IP
+\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c
+..
+.
+.TH pock 1 "28 May 2017" "Straylight/Edgeware" "Catacomb cryptographic library"
+.SH NAME
+pock \- generate and verify primality certificates
+.
+.\"--------------------------------------------------------------------------
+.SH SYNOPSIS
+.
+.B pock
+.RB [ \-qv ]
+.I command
+.IR [ arguments ...]
+.PP
+Subcommands:
+.RS
+.br
+.B gen
+.I nbits
+.br
+.B ll
+.I nbits
+.I nsubbits
+.br
+.B check
+.RI [ file ]
+.RE
+.
+.\"--------------------------------------------------------------------------
+.SH DESCRIPTION
+.
+Many cryptographic protocols make use of large prime numbers.
+The usual way of determining primality in such circumstances
+is due to Rabin and Miller.
+Given a candidate
+.I n
+and a
+.I witness
+2 \(<=
+.I a
+<
+.IR n ,
+the test answers either `composite' or `unknown'.
+If
+.I n
+is prime then the test answers `unknown' for every witness
+.IR a ;
+if
+.I n
+is in fact composite
+then the test answers `composite'
+for at least three quarters of the possible witnesses.
+If
+.I n
+is a composite,
+then the witnesses
+.I a
+for which the test reports `unknown'
+are called
+.IR liars .
+.PP
+Naively, then,
+to reduce the probability of falsely accepting a composite
+below some bound \*(*e,
+one must perform
+\-(log\*(us2\*(ue \*(*e)/2
+iterations of the test,
+with independent, uniformly distributed witnesses.
+This is especially slow when high security levels are wanted,
+both because tests take longer on larger candidate numbers,
+and because one must do more tests
+to reach the necessary higher confidence level.
+.PP
+The above is a worst-case bound:
+very few composite numbers
+.I n
+have anywhere near
+.IR n /4
+liars.
+In situations such as RSA key generation,
+the user generating the prime number is the only one
+who must be convinced of the number's primality,
+and they have valuable additional knowledge:
+specifically that the candidate has been chosen at random
+according to some suitable probability distribution.
+In this case, one needs many fewer iterations,
+and the number of iterations needed
+.I decreases
+with the size of the candidate being tested.
+.PP
+But in cases where many users must share some large prime parameter,
+each of them must test the proposed prime separately,
+and often they must pessimistically assume
+that the number was chosen maliciously,
+and the worst-case
+.IR n /4
+bound is the best one can do using the Rabin\(enMiller test.
+For large candidates,
+this is inconveniently slow.
+(Also, many implementations incorrectly use
+a number of iterations suitable for randomly chosen primes
+for testing candidates of unknown provenance.)
+.PP
+There
+.I are
+stronger probabilistic tests.
+A combination of Rabin\(enMiller and
+Grantham's Frobenius test
+is known as the
+Baillie\(enPSW test
+(after Baillie, Pomerance, Selfridge, and Wagstaff);
+there are
+.I no
+known composites which pass this test,
+nor is it known how to construct any.
+On the other hand, it's been conjectured that
+infinitely many Baillie\(enPSW pseudoprimes exist.
+While it may be reasonable to assume
+the strength of the Baillie\(enPSW test,
+it must be borne in mind that this
+.I does
+constitute a security assumption.
+.PP
+By contrast,the
+.B pock
+program will generate prime numbers
+of sizes suitable for use in cryptography,
+along with a
+.I certificate of primality
+which can be independently verified fairly efficiently.
+Specifically, verifying a proof takes a little longer
+than a single iteration of the Rabin\(enMiller probabilistic test.
+It can also verify such certificates.
+.PP
+Note that the primes selected by
+.B pock
+are a long way from uniformly distributed.
+Indeed, they have somewhat unusual structure,
+but it seems unlikely that this structure
+renders them unsuitable for e.g., discrete-logarithm cryptography.
+.
+.SS "Command line"
+The following options are recognized.
+.TP
+.B "\-h, \-\-help"
+Write help text to standard output and exit with status 0.
+.TP
+.B "\-q, \-\-quiet"
+Be less verbose during prime generation or certificate verification.
+.TP
+.B "\-v, \-\-verbose"
+Be more verbose during prime generation or certificate verification.
+.TP
+.BI "\-s, \-\-sievebits " b
+When generating certificates,
+require that the verifier can check numbers smaller than
+.RI 2\*(ss b \*(se
+without assistance.
+Larger values lead to sightly shorter proofs,
+but spend more time at startup constructing a sieve;
+smaller values lead to somewhat longer proofs,
+but spend less time constructing the sieve.
+The default is 32,
+which seems to work well in practice.
+.
+.SS "gen"
+The
+.B gen
+command generates a prime
+.I p
+which is
+.I nbits
+long (i.e.,
+.RI 2\*(ss nbits \-1\*(se
+<
+.I p
+<
+.RI 2\*(ss nbits \*(se,
+and writes a certificate to standard output.
+By default, mysterious runes are written to standard error
+to keep the user amused and informed about the operation's progress;
+the
+.B \-q
+option suppresses the runes;
+the
+.B \-v
+option produces more detailed runes.
+.
+.SS "ll"
+The
+.B ll
+command generates a Lim\(enLee prime
+.I p
+=
+2
+.IR q \*(us0\*(ue
+.IR q \*(us1\*(ue
+\*(..
+.IR q \*(us k \*(ue
++
+1
+which is
+.I nbits
+long (i.e.,
+.RI 2\*(ss nbits \-1\*(se
+<
+.I p
+<
+.RI 2\*(ss nbits \*(se,
+such that each
+.IR q \*(us i \*(ue
+is an
+.IR nsubbits -bit
+prime, for
+0 \(<=
+.I i
+<
+.IR k ,
+and
+.IR q \*(us k \*(ue
+is an
+.IR nsubbits -bit
+prime,
+and writes a certificate to standard output.
+By default, mysterious runes are written to standard error
+to keep the user amused and informed about the operation's progress;
+the
+.B \-q
+option suppresses the runes;
+the
+.B \-v
+option produces more detailed runes.
+.
+.SS "check"
+The
+.B check
+command reads a primality certificate from the named
+.I file
+or from standard input,
+and checks it.
+By default,
+each
+.B check
+line in the certificate causes a line
+.IP
+.B ;;
+.I label
+.B =
+.I n
+.BI [ b ]
+.PP
+to be printed to standard output,
+listing the prime's
+.IR label ,
+value
+.IR n ,
+and length
+.I b
+in bits;
+this behaviour is inhibited by the
+.B \-q
+option.
+.
+.SS Runes
+The following mysterious runes are printed during prime searches.
+This information is provided for diagnostic purposes
+and to satisfy idle curiosity:
+later versions may print different runes,
+or use the same rune characters to mean different things.
+.TP
+.B !
+Started to generate a large prime.
+The next step is to generate a number of smaller primes.
+Usually, this will only need to be done once.
+.TP
+.B .
+Candidate failed a Fermat test.
+.TP
+.B *
+Candidate passed a Fermat test.
+This is usually the last rune for a prime search.
+.TP
+.B @
+A candidate generator failed to generate the necessary subgroup.
+This is unusual.
+.TP
+.B <
+For Lim\(enLee primes,
+discarding the large prime
+because it produces results which are too small.
+.TP
+.B >
+For Lim\(enLee primes,
+discarding the large prime
+because it produces results which are too large.
+.TP
+.B [
+Starting a subsidiary prime search.
+.TP
+.B ]
+Finished a subsidiary prime search.
+.
+.\"--------------------------------------------------------------------------
+.SH CERTIFICATE FORMAT
+.
+A certificate consists of a number of lines.
+Blank lines,
+and lines beginning with a
+.RB ` ; ',
+are ignored.
+Other lines are as follows.
+.TP
+.BI "sievebits " b
+Declares that the verifier is expected to be able to check
+primes smaller than
+.RI 2\*(ss b \*(se
+for primality for itself.
+A
+.B sievebits
+line must appear before the first
+.B small
+line.
+.TP
+.BI "small " label " = " p
+Asserts that
+.I p
+is a small prime,
+and associates it with the
+.IR label .
+It is an error if the label has been assigned by a previous line.
+It is required that
+1 <
+.I p
+<
+.RI 2\*(ss b \*(se
+and that
+.I p
+is prime.
+Such small primes constitute the leaves of a proof tree.
+.TP
+.BI "pock " label " = " a ", " R ", [" i ", " j ", \fR\*(..\fB]"
+Asserts that a number
+.I n
+(defined below) is prime by Pocklington's criterion,
+and associates it with the
+.IR label .
+It is an error if the label has been assigned by a previous line.
+.RS
+.PP
+The strings
+.IR i ,
+.IR j ,
+\*(..
+must be labels assigned by earlier lines.
+For each such label
+.IR i ,
+let
+.IR q \*(us i \*(ue
+be the associated prime.
+Let
+.I Q
+=
+.IR q \*(us i \*(ue
+.IR q \*(us j \*(ue
+\*(..
+be the product of these primes.
+Let
+.I n
+=
+.RI 2 QR
++
+1.
+It is required that:
+.hP 1.
+The
+.IR q \*(us i \*(ue
+are distinct.
+.hP 2.
+.IR Q \*(ss2\*(se
+\(>=
+.IR n .
+.hP 3.
+.IR a \*(ss n \-1\*(se
+\*(==
+1
+(mod
+.IR n ).
+.hP 4.
+.RI gcd( a \*(ss( n \-1)/ q \*(se
+\-
+1,
+.IR n )
+=
+1
+for each prime
+.IR q
+dividing
+.IR Q .
+.PP
+To see why this works, let
+.I p
+be the smallest prime factor of
+.IR n .
+From
+.B 3
+we see that
+the order of
+.I a
+in
+.RB ( Z /\fIp Z )\*(ss\(**\*(se
+divides
+.I n
+\-
+1.
+Consider some prime
+.I q
+dividing
+.I Q
+and let
+.I t
+=
+.IR a \*(ss( n \-1)/ q \*(se;
+then
+.I t
+has order dividing
+.IR q .
+From
+.BR 4 ,
+we have
+.I t
+\*(/=
+1
+(mod
+.IR p ),
+and hence
+.I t
+has order precisely
+.I q
+in
+.RB ( Z /\fIp Z )\*(ss\(**\*(se.
+This implies that
+.I q
+divides
+.I p
+\-
+1.
+Since this holds for each prime
+.I q
+dividing
+.IR Q ,
+and,
+from
+.BR 1 ,
+these primes are distinct,
+we deduce that
+.I Q
+divides
+.I p
+\-
+1
+and hence that
+.I p
+>
+.IR Q .
+Let
+.IR p \(fm
+be any prime factor of
+.IR n / p .
+Then
+.IR p \(fm
+\(>=
+.I p
+>
+.I Q
+so,
+from
+.BR 2 ,
+.IR pp \(fm
+>
+.IR Q \*(ss2\*(se
+\(>=
+.IR n ;
+but
+.IR pp \(fm
+divides
+.I n
+so this is a contradiction.
+We must conclude that
+.IR p \(fm
+does not exist,
+and
+.I n
+must be prime.
+.RE
+.TP
+.BI "ecpp " label " = " n ", " A ", " B ", " x ", " y ", [" i ", " j ", \fR\*(..\fB]"
+Asserts that the number
+.I n
+is prime by Goldwasser and Kilian's ECPP method,
+and associates it with the
+.IR label .
+It is an error if the label has been assigned by a previous line.
+.RS
+.PP
+The strings
+.IR i ,
+.IR j ,
+\*..
+must be labels assigned by earlier lines.
+For each such label
+.IR i ,
+let
+.IR q \*(us i \*(ue
+be the associated prime.
+Let
+.I Q
+=
+.IR q \*(us i \*(ue
+.IR q \*(us j \*(ue
+\*(..
+be the product of these primes.
+Define
+.I E\*(usn\*(ue
+= {
+.RI ( x ", " y )
+\*(mo
+.RB ( Z /\fIn Z )\*(ss2\*(se
+|
+.IR y \*(ss2\*(se
+=
+.IR x \*(ss3\*(se
++
+.I Ax
++
+.I B
+}
+\*(cu
+{ \*(iy };
+if
+.I n
+is prime and the curve is not singular
+then this is the elliptic curve over
+.RI GF( n )
+with short-Weierstrass coefficients
+.I A
+and
+.IR B .
+Let
+.I R
+=
+.RI ( x ,
+.IR y ).
+It is required that:
+.hP 1.
+.I g
+=
+.RI gcd(4 a \*(ss3\*(se
++
+.RI 27 b \*(ss2\*(se,
+.IR n )
+= 1.
+.hP 2.
+.I R
+\*(mo
+.IR E\*(usn\*(ue ;
+i.e.,
+.IR y \*(ss2\*(se
+\*(==
+.IR x \*(ss3\*(se
++
+.I Ax
++
+.I B
+(mod
+.IR n ).
+.hP 3. The
+.I q\*(usi\*(ue
+are distinct.
+.hP 4.
+.IR QR ,
+the elliptic-curve scalar product of the point
+.I R
+by the integer
+.IR Q ,
+calculated as if
+.I E
+is a true elliptic curve,
+is the point at infinity.
+.hP 5.
+.RI ( Q / q ) R
+is finite for each prime
+.I q
+dividing
+.IR Q .
+.hP 6.
+.I Q
+>
+.RI ( n \*(ss1/4\*(se
++ 1)\*(ss2\*(se.
+.PP
+To see why this test works, let
+.I p
+be the smallest prime factor of
+.IR n ,
+and let
+.I E\*(usp\*(ue
+= {
+.RI ( x ", " y )
+\*(mo
+.RI GF( p )\*(ss2\*(se
+|
+.IR y \*(ss2\*(se
+=
+.IR x \*(ss3\*(se
++
+.I Ax
++
+.I B
+}
+\*(cu
+{ \*(iy }.
+From
+.BR 1 ,
+.I g
+= 1,
+.I E\*(usp\*(ue
+is an elliptic curve.
+(If 1 <
+.I g
+<
+.I n
+then
+.I g
+is a proper factor of
+.I n
+and
+.I n
+is certainly not prime;
+if
+.I g
+=
+.I n
+then the curve will be singular and the test fails.)
+From
+.BR 2 ,
+.I R
+is a point on
+.IR E\*(usp\*(ue .
+From
+.BR 4 ,
+.I R
+has
+.IR Q -torsion
+in
+.IR E\*(usp\*(ue .
+Consider some prime
+.I q
+dividing
+.I Q
+and let
+.I T
+=
+.RI ( Q/q ) R ;
+then
+.I T
+has torsion dividing
+.IR q .
+From
+.BR 5 ,
+.RI ( Q/q ) R
+\(!= 0,
+and hence
+.I T
+has order precisely
+.I q
+in
+.IR E\*(usp\*(ue .
+This implies that
+.I q
+divides
+.RI # E\*(usp\*(ue .
+Since this holds for each prime
+.I q
+dividing
+.IR Q ,
+and, from
+.BR 3 ,
+the
+.I q
+are distinct,
+we deduce that
+.I Q
+divides
+.RI # E\*(usp\*(ue
+and hence that
+.RI # E\*(usp\*(ue
+\(>=
+.IR Q .
+Hasse's theorem tells us that
+.RI | p
++ 1 \-
+.RI # E\*(usp\*(ue |
+\(<=
+.RI 2\*(sr p ,
+so, in particular,
+.RI # E\*(usp\*(ue
+\-
+.I p
+\- 1
+\(<=
+.RI 2\*(sr p ,
+whence
+.I p
++ 1 +
+.RI 2\*(sr p
+=
+.RI (\*(sr p
++ 1)\*(ss2\*(se
+\(>=
+.RI # E\*(usp\*(ue
+\(>=
+.IR Q
+>
+.RI ( n \*(ss1/4\*(se
++ 1)\*(ss2\*(se
+(from
+.BR 6 );
+so
+.IR p\*(ss2\*(se
+>
+.IR n .
+Let
+.IR p \(fm
+be any prime factor of
+.IR n / p .
+Then
+.IR p \(fm
+\(>=
+.I p
+and
+.IR pp \(fm
+\(>=
+.IR p \*(ss2\*(se
+>
+.IR n ;
+but
+.IR pp \(fm
+divides
+.I n
+so this is a contradiction.
+We must conclude that
+.IR p \(fm
+does not exist,
+and
+.I n
+must be prime.
+.PP
+Note that
+.B pock
+currently cannot generate proofs which use
+.BR ecpp ,
+though it will verify them.
+.RE
+.TP
+.BI "check " label ", " b ", " p
+Verify that the prime associated with the
+.I label
+is equal to
+.I p
+and that it is
+.I b
+bits long;
+i.e., that
+.RI 2\*(ss b \-1\*(se
+\(<=
+.I p
+<
+.RI 2\*(ss b \*(se.
+Unless
+inhibited by
+.BR \-q ,
+the label and value are printed to stdout during verification.
+.
+.\"--------------------------------------------------------------------------
+.SH "SEE ALSO"
+.BR key (1).
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
index 0abb011b82d705329767b108874ee05aa5bbdcd1..5680429c1a410cd81ee5724078a9b8abcf5b99be 100644 (file)
--- a/pubkey.c
+++ b/pubkey.c
@@ -96,9 +96,9 @@ static PyObject *dsapub_pynew(PyTypeObject *ty,
 {
   PyObject *G, *p, *rng = rand_pyobj, *hash = sha_pyobj;
   PyObject *rc = 0;
-  char *kwlist[] = { "G", "p", "hash", "rng", 0 };
+  static const char *const kwlist[] = { "G", "p", "hash", "rng", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   ge_pytype, &p,
                                   gchash_pytype, &hash,
@@ -112,7 +112,7 @@ end:
 static PyObject *dsameth_beginhash(PyObject *me, PyObject *arg)
 {
   if (!PyArg_ParseTuple(arg, ":beginhash")) return (0);
-  return (ghash_pywrap(DSA_HASH(me), gdsa_beginhash(DSA_D(me)), f_freeme));
+  return (ghash_pywrap(DSA_HASH(me), gdsa_beginhash(DSA_D(me))));
 }
 
 static PyObject *dsameth_endhash(PyObject *me, PyObject *arg)
@@ -135,9 +135,9 @@ static PyObject *dsameth_sign(PyObject *me, PyObject *arg, PyObject *kw)
   Py_ssize_t n;
   mp *k = 0;
   PyObject *rc = 0;
-  char *kwlist[] = { "msg", "k", 0 };
+  static const char *const kwlist[] = { "msg", "k", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", KWLIST,
                                   &p, &n, convmp, &k))
     goto end;
   if (n != DSA_D(me)->h->hashsz)
@@ -175,9 +175,9 @@ static PyObject *dsapriv_pynew(PyTypeObject *ty,
 {
   PyObject *G, *p = 0, *u, *rng = rand_pyobj, *hash = sha_pyobj;
   PyObject *rc = 0;
-  char *kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
+  static const char *const kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   &u,
                                   ge_pytype, &p,
@@ -247,7 +247,7 @@ static PyTypeObject dsapub_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"DSA public key information.",
+"DSAPub(GROUP, P, [hash = sha], [rng = rand]): DSA public key.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -295,7 +295,7 @@ static PyTypeObject dsapriv_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"DSA private key information.",
+"DSAPriv(GROUP, U, [p = u G], [hash = sha], [rng = rand]): DSA private key.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -323,9 +323,9 @@ static PyObject *kcdsapub_pynew(PyTypeObject *ty,
 {
   PyObject *G, *p, *rng = rand_pyobj, *hash = has160_pyobj;
   PyObject *rc = 0;
-  char *kwlist[] = { "G", "p", "hash", "rng", 0 };
+  static const char *const kwlist[] = { "G", "p", "hash", "rng", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O!|O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   ge_pytype, &p,
                                   gchash_pytype, &hash,
@@ -348,9 +348,9 @@ static PyObject *kcdsapriv_pynew(PyTypeObject *ty,
 {
   PyObject *G, *u, *p = 0, *rng = rand_pyobj, *hash = has160_pyobj;
   PyObject *rc = 0;
-  char *kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
+  static const char *const kwlist[] = { "G", "u", "p", "hash", "rng", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!O|O!O!O!:new", KWLIST,
                                   group_pytype, &G,
                                   &u,
                                   ge_pytype, &p,
@@ -366,7 +366,7 @@ end:
 static PyObject *kcdsameth_beginhash(PyObject *me, PyObject *arg)
 {
   if (!PyArg_ParseTuple(arg, ":beginhash")) return (0);
-  return (ghash_pywrap(DSA_HASH(me), gkcdsa_beginhash(DSA_D(me)), f_freeme));
+  return (ghash_pywrap(DSA_HASH(me), gkcdsa_beginhash(DSA_D(me))));
 }
 
 static PyObject *kcdsameth_endhash(PyObject *me, PyObject *arg)
@@ -389,9 +389,9 @@ static PyObject *kcdsameth_sign(PyObject *me, PyObject *arg, PyObject *kw)
   Py_ssize_t n;
   mp *k = 0;
   PyObject *r = 0, *rc = 0;
-  char *kwlist[] = { "msg", "k", 0 };
+  static const char *const kwlist[] = { "msg", "k", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:sign", KWLIST,
                                   &p, &n, convmp, &k))
     goto end;
   if (n != DSA_D(me)->h->hashsz)
@@ -467,7 +467,7 @@ static PyTypeObject kcdsapub_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"KCDSA public key information.",
+"KCDSAPub(GROUP, P, [hash = sha], [rng = rand]): KCDSA public key.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -515,7 +515,7 @@ static PyTypeObject kcdsapriv_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"KCDSA private key information.",
+"KCDSAPriv(GROUP, U, [p = u G], [hash = sha], [rng = rand]): KCDSA private key.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -569,9 +569,9 @@ static PyObject *rsapub_pynew(PyTypeObject *ty,
 {
   rsa_pub rp = { 0 };
   rsapub_pyobj *o;
-  char *kwlist[] = { "n", "e", 0 };
+  static const char *const kwlist[] = { "n", "e", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   convmp, &rp.n, convmp, &rp.e))
     goto end;
   if (!MP_ODDP(rp.n)) VALERR("RSA modulus must be even");
@@ -636,10 +636,10 @@ static PyObject *rsapriv_pynew(PyTypeObject *ty,
 {
   rsa_priv rp = { 0 };
   PyObject *rng = Py_None;
-  char *kwlist[] =
+  static const char *const kwlist[] =
     { "n", "e", "d", "p", "q", "dp", "dq", "q_inv", "rng", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&O&O&O&O&O:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&O&O&O&O&O&O&O&O:new", KWLIST,
                                   convmp, &rp.n, convmp, &rp.e,
                                   convmp, &rp.d,
                                   convmp, &rp.p, convmp, &rp.q,
@@ -711,9 +711,9 @@ static PyObject *rsameth_privop(PyObject *me, PyObject *arg, PyObject *kw)
   PyObject *rng = RSA_RNG(me);
   mp *x = 0;
   PyObject *rc = 0;
-  char *kwlist[] = { "x", "rng", 0 };
+  static const char *const kwlist[] = { "x", "rng", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:privop", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:privop", KWLIST,
                                   convmp, &x, &rng))
     goto end;
   if (rng != Py_None && !GRAND_PYCHECK(rng))
@@ -735,11 +735,12 @@ static PyObject *meth__RSAPriv_generate(PyObject *me,
   mp *e = 0;
   struct excinfo exc = EXCINFO_INIT;
   pypgev evt = { { 0 } };
-  char *kwlist[] = { "class", "nbits", "event", "rng", "nsteps", "e", 0 };
+  static const char *const kwlist[] =
+    { "class", "nbits", "event", "rng", "nsteps", "e", 0 };
   PyObject *rc = 0;
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", KWLIST,
                                   &me, convuint, &nbits, convpgev, &evt,
                                   convgrand, &r, convuint, &n,
                                   convmp, &e))
@@ -815,7 +816,7 @@ static PyTypeObject rsapub_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"RSA public key information.",
+"RSAPub(N, E): RSA public key.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -863,7 +864,8 @@ static PyTypeObject rsapriv_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"RSA private key information.",
+"RSAPriv(..., [rng = rand]): RSA private key.\n\
+  Keywords: n, e, d, p, q, dp, dq, q_inv; must provide enough",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -899,10 +901,10 @@ static PyObject *meth__p1crypt_encode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x;
-  char *kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
+  static const char *const kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
 
   p1.r = &rand_global; ep = 0; epsz = 0;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
     goto end;
@@ -929,10 +931,10 @@ static PyObject *meth__p1crypt_decode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x = 0;
-  char *kwlist[] = { "ct", "nbits", "ep", "rng", 0 };
+  static const char *const kwlist[] = { "ct", "nbits", "ep", "rng", 0 };
 
   p1.r = &rand_global; ep = 0; epsz = 0;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|s#O&:decode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|s#O&:decode", KWLIST,
                                   convmp, &x, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
     goto end;
@@ -960,10 +962,10 @@ static PyObject *meth__p1sig_encode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x;
-  char *kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
+  static const char *const kwlist[] = { "msg", "nbits", "ep", "rng", 0 };
 
   p1.r = &rand_global; ep = 0; epsz = 0;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|s#O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
     goto end;
@@ -991,10 +993,11 @@ static PyObject *meth__p1sig_decode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x = 0;
-  char *kwlist[] = { "msg", "sig", "nbits", "ep", "rng", 0 };
+  static const char *const kwlist[] =
+    { "msg", "sig", "nbits", "ep", "rng", 0 };
 
   p1.r = &rand_global; ep = 0; epsz = 0;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|s#O&:decode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&O&|s#O&:decode", KWLIST,
                                   &hukairz, convmp, &x, convulong, &nbits,
                                   &ep, &epsz, convgrand, &p1.r))
     goto end;
@@ -1022,10 +1025,11 @@ static PyObject *meth__oaep_encode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x;
-  char *kwlist[] = { "msg", "nbits", "mgf", "hash", "ep", "rng", 0 };
+  static const char *const kwlist[] =
+    { "msg", "nbits", "mgf", "hash", "ep", "rng", 0 };
 
   o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha; ep = 0; epsz = 0;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&s#O&:encode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&s#O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   convgccipher, &o.cc,
                                   convgchash, &o.ch,
@@ -1055,10 +1059,11 @@ static PyObject *meth__oaep_decode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x = 0;
-  char *kwlist[] = { "ct", "nbits", "mgf", "hash", "ep", "rng", 0 };
+  static const char *const kwlist[] =
+    { "ct", "nbits", "mgf", "hash", "ep", "rng", 0 };
 
   o.r = &rand_global; o.cc = &sha_mgf; o.ch = &sha; ep = 0; epsz = 0;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&s#O&:decode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&s#O&:decode", KWLIST,
                                   convmp, &x, convulong, &nbits,
                                   convgccipher, &o.cc,
                                   convgchash, &o.ch,
@@ -1089,10 +1094,11 @@ static PyObject *meth__pss_encode(PyObject *me,
   octet *b = 0;
   size_t sz;
   mp *x = 0;
-  char *kwlist[] = { "msg", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
+  static const char *const kwlist[] =
+    { "msg", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
 
   p.cc = &sha_mgf; p.ch = &sha; p.r = &rand_global; p.ssz = (size_t)-1;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&O&O&:encode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&|O&O&O&O&:encode", KWLIST,
                                   &m, &msz, convulong, &nbits,
                                   convgccipher, &p.cc,
                                   convgchash, &p.ch,
@@ -1122,11 +1128,11 @@ static PyObject *meth__pss_decode(PyObject *me,
   size_t sz;
   int n;
   mp *x = 0;
-  char *kwlist[] =
+  static const char *const kwlist[] =
     { "msg", "sig", "nbits", "mgf", "hash", "saltsz", "rng", 0 };
 
   p.cc = &sha_mgf; p.ch = &sha; p.r = &rand_global; p.ssz = (size_t)-1;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&O&|O&O&O&O&:decode", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&O&|O&O&O&O&:decode", KWLIST,
                                   &m, &msz, convmp, &x, convulong, &nbits,
                                   convgccipher, &p.cc,
                                   convgchash, &p.ch,
@@ -1202,10 +1208,11 @@ XDHS(DEFXDH)
     int ph = phdflt;                                                   \
     PyObject *rc = 0;                                                  \
     octet pp[ED##_PUBSZ];                                              \
-    char *kwlist[] = { "key", "msg", "pub", "perso", "phflag", 0 };    \
+    static const char *const kwlist[] =                                        \
+      { "key", "msg", "pub", "perso", "phflag", 0 };                   \
     if (!PyArg_ParseTupleAndKeywords(arg, kw,                          \
                                     "s#s#|s#s#O&:" #ed "_sign",        \
-                                    kwlist,                            \
+                                    KWLIST,                            \
                                     &k, &ksz, &m, &msz, &p, &psz,      \
                                     &c, &csz, convbool, &ph))          \
       goto end;                                                                \
@@ -1229,10 +1236,11 @@ XDHS(DEFXDH)
     Py_ssize_t psz, csz = 0, msz, ssz;                                 \
     int ph = phdflt;                                                   \
     PyObject *rc = 0;                                                  \
-    char *kwlist[] = { "pub", "msg", "sig", "perso", "phflag", 0 };    \
+    static const char *const kwlist[] =                                        \
+      { "pub", "msg", "sig", "perso", "phflag", 0 };                   \
     if (!PyArg_ParseTupleAndKeywords(arg, kw,                          \
                                     "s#s#s#|s#O&:" #ed "_verify",      \
-                                    kwlist,                            \
+                                    KWLIST,                            \
                                     &p, &psz, &m, &msz, &s, &ssz,      \
                                     &c, &csz, convbool, &ph))          \
       goto end;                                                                \
diff --git a/pwsafe b/pwsafe
index c12f856bd5e7dee42bfec95c67dd254c2b16219c..9de0b3103bdf86a108055942acf26a9bbbba17c8 100644 (file)
--- a/pwsafe
+++ b/pwsafe
@@ -59,8 +59,8 @@ def die(msg):
 def cmd_create(av):
 
   ## Default crypto-primitive selections.
-  cipher = 'blowfish-cbc'
-  hash = 'rmd160'
+  cipher = 'rijndael-cbc'
+  hash = 'sha256'
   mac = None
 
   ## Parse the options.
diff --git a/pwsafe.1 b/pwsafe.1
new file mode 100644 (file)
index 0000000..361ba95
--- /dev/null
+++ b/pwsafe.1
@@ -0,0 +1,878 @@
+.\" -*-nroff-*-
+.\"
+.\" Describe the password safe
+.\"
+.\" (c) 2016 Straylight/Edgeware
+.\"
+.
+.\"----- Licensing notice ---------------------------------------------------
+.\"
+.\" This file is part of the Python interface to Catacomb.
+.\"
+.\" Catacomb/Python is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" Catacomb/Python is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with Catacomb/Python; if not, write to the Free Software Foundation,
+.\" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.
+.ie t \{\
+.  if \n(.g \{\
+.    fam P
+.  \}
+.  ds o \(bu
+.\}
+.el \{\
+.  ds o o
+.\}
+.
+.de hP
+.IP
+\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c
+..
+.
+.TH pwsafe 1 "13 May 2016" "Straylight/Edgeware" "Catacomb cryptographic library"
+.SH NAME
+pwsafe \- store passwords somewhat securely
+.
+.\"--------------------------------------------------------------------------
+.SH SYNOPSIS
+.
+.B pwsafe
+.RB [ \-f
+.IR file ]
+.I command
+.RI [ arguments
+\&...]
+.PP
+Subcommands:
+'RS
+.B changepp
+.br
+.B copy
+.I dest-file
+.RI [ glob-pattern ]
+.br
+.B create
+.RB [ \-c
+.IR cipher ]
+.RB [ \-d
+.IR db-type ]
+.RB [ \-h
+.IR hash ]
+.RB [ \-m
+.IR mac ]
+.RI [ pp-tag ]
+.br
+.B delete
+.I tag
+.br
+.RB [ find ]
+.I label
+.br
+.B list
+.RI [ glob-pattern ]
+.br
+.B store
+.I label
+.RI [ value ]
+.br
+.B to-pixie
+.RI [ label
+.RI [ pixie-tag ]]
+.br
+.B xfer
+.RB [ \-d
+.IR db-type ]
+.I dest-file
+.RE
+.
+.\"--------------------------------------------------------------------------
+.SH DESCRIPTION
+.
+The
+.B pwsafe
+command maintains a database of passwords
+or other short-ish textual secrets.
+Each password is identified by a
+.IR label .
+The database is ultimately protected by a master password.
+It should quite difficult for an adversary
+who has the database file(s)
+but not your master password
+to find out what any of the stored passwords are,
+or even what the labels are.
+.PP
+The
+.B pwsafe
+program will prompt you for the password as necessary.
+If you are running the Catacomb
+.BR pixie (1)
+then it will first ask the pixie for any necessary passwords,
+and use the pixie to remember the master password for a short while.
+.PP
+A number of database backends are available.
+.TP
+.B gdbm
+Uses the GDBM database library to store a database as a single file.
+Provided for compatibility;
+not recommended for new databases.
+.TP
+.B sqlite
+Uses the SQLite3 library to store a database as a single file.
+SQLite3 has good performance and integrity properties.
+.TP
+.B flat
+Stores the password database as a flat text file.
+Not recommended for large databases because performance will be bad,
+but the simple format admits easy hacking.
+.TP
+.B dir
+Stores the password database as a directory structure.
+This uses much more disk space than the alternatives,
+and enumerating passwords is slow,
+but the directory structure can easily be managed by
+a version control system such as Git.
+.PP
+The following command-line options are available.
+.TP
+.B "\-h, \-\-help"
+Print a help summary to standard output,
+and exit with status zero.
+.TP
+.B "\-v, \-\-version"
+Print the program's version number to standard output,
+and exit with status zero.
+.TP
+.B "\-u, \-\-usage"
+Print a very terse usage summary to standard output,
+and exit with status zero.
+.TP
+.BI "\-f, \-\-file=" file
+Use
+.I file
+as the password database file or directory.
+If this is not specified,
+then the value of the
+.B PWSAFE
+environment variable is used;
+if that too is unset, then the default
+.IB home /.pwsafe \fR,
+is used, where
+.I home
+is the value of the
+.B HOME
+environment variable.
+It is a fatal error if
+.B HOME
+is unset.
+.PP
+The provided commands are described in their own sections below.
+.
+.SS "create"
+Create a new, empty password database.
+As a safety check,
+the file/directory named by the top-level
+.B \-f
+option (or default value)
+must not exist.
+.PP
+You will be prompted (twice) for a master password for the database.
+.PP
+The optional positional argument is the tag
+by which the database's master password
+will be known to the passphrase pixie.
+The default tag is
+.BR pwsafe .
+.PP
+The following options are accepted.
+.TP
+.BI "\-c, \-\-cipher=" cipher
+Use the Catacomb
+.I cipher
+to encrypt blobs.
+Run
+.B catcrypt show cipher
+for a list.
+The default is
+.BR rijndael-cbc .
+.TP
+.BI "\-d, \-\-database=" db-type
+Use the
+.I db-type
+database backend.
+See above for a list of the supported backends.
+Note that
+.B gdbm
+and
+.B sqlite
+depend on external modules, and may not be available.
+The default is
+.BR flat .
+.TP
+.BI "\-h, \-\-hash=" hash
+Use the Catacomb
+.I hash
+for key derivation and password-label mangling.
+Run
+.B catcrypt show hash
+for a list.
+The default is
+.BR sha256 .
+.TP
+.BI "\-m, \-\-mac=" mac
+Use the Catacomb
+.I mac
+to protect the integrity of blobs.
+Run
+.B catcrypt show mac
+for a list.
+The default is
+.IR hash -hmac
+where
+.I hash
+is the hash function chosen by the
+.B \-h
+option.
+.
+.SS "changepp"
+Change the master password for a database.
+This will
+.I not
+re-encrypt all of the records,
+so its utility is somewhat limited.
+See also the
+.B copy
+command.
+The program will prompt you for
+the existing master password (if it's not known by the pixie)
+and the new one (twice, always).
+.
+.SS "copy"
+Copy password records from the
+.I file
+to the
+.I dest-file
+which must be an existing password database.
+If a
+.B glob-pattern
+is given,
+then only records whose
+.I label
+matches the pattern are copied;
+otherwise all password records are copied.
+Any existing passwords in the destination database with the same labels
+will be overwritten.
+.PP
+The destination need not use the same database backend
+or cryptographic parameters as the source.
+.PP
+You will be prompted for the necessary master passwords.
+.
+.SS "delete"
+Delete the password with the given
+.I label
+from the database.
+An error is reported if there is no such password.
+.PP
+You will be prompted for master password if necessary.
+.
+.SS "find"
+Write the password with the given label to standard output,
+followed by a newline.
+.PP
+You will be prompted for master password if necessary.
+.PP
+This is the default operation:
+as a convenience,
+you can write
+.IP
+.B pwsafe
+.I label
+.PP
+rather than
+.IP
+.B pwsafe
+.B find
+.I label
+.PP
+if the
+.I label
+isn't the same as any of
+.BR pwsafe 's
+command names.
+.
+.SS "list"
+Write the labels of the passwords in the database,
+one per line,
+to standard output.
+(If labels contain newline characters,
+you will end up with a mess.)
+If a
+.I glob-pattern
+is supplied,
+then only labels which match the pattern are written.
+.PP
+You will be prompted for master password if necessary.
+.
+.SS "store"
+Store a password, associating it with the given
+.IR label .
+.PP
+If a
+.I value
+is supplied on the command line,
+then it is used as the password value.
+(Note that command-line arguments can be seen
+by other users of the system,
+and may be recorded by the shell.
+This is usually a bad idea.)
+.PP
+As a special case, if the
+.I value
+is
+.BR \- ,
+then the password is read from the first line of standard input;
+the trailing newline is removed.
+The author commonly writes
+.IP
+.BI "gorp -fbase64 | pwsafe store " label " \-"
+.PP
+to set random passwords.
+.PP
+Finally, if no
+.I value
+is given,
+then
+.B pwsafe
+will prompt twice for the password.
+.PP
+You will be prompted for the master password if necessary
+.I before
+the new password value is fetched.
+.PP
+If there is an existing password with the same
+.I label
+then it is overwritten.
+.
+.SS "topixie"
+With no arguments,
+store
+.I all
+of the passwords in the database in the pixie,
+with correspondingly named tags.
+This is probably a bad idea.
+.PP
+With a
+.IR label ,
+store only the labelled password in the pixie.
+With a
+.IR pixie-tag ,
+use that as the tag;
+otherwise use the
+.IR label .
+.PP
+You will be prompted for the master password if necessary.
+.
+.SS "xfer"
+Create a new database containing all of the records of an existing one.
+.PP
+This works at the storage-backend level.
+The new database contains exactly the same metadata and passwords
+as the original.
+It is
+.I not
+necessary to enter a password:
+the blobs are simply copied over without being decrypted.
+.PP
+The following options are accepted.
+.TP
+.BI "\-d, \-\-database=" db-type
+Use the
+.I db-type
+database backend.
+See above for a list of the supported backends.
+Note that
+.B gdbm
+and
+.B sqlite
+depend on external modules, and may not be available.
+The default is
+.BR flat .
+.
+.\"--------------------------------------------------------------------------
+.SH TECHNICAL DETAILS
+.
+The password database contains two kinds of records:
+.I metadata records
+hold important information about the database itself,
+and particularly the various cryptographic options
+chosen when the database was created,
+and the various internal keys used to secure the database;
+while
+.I password records
+actually store your encrypted passwords.
+The various backends store these kinds records in different ways;
+see below for the gory details.
+.
+.SS Metadata
+The metadata records are as follows.
+.TP
+.B cipher
+The symmetric cipher used to encrypt data.
+This names a Catacomb
+.B cipher
+class.
+.TP
+.B hash
+The hash function used in various places.
+This names a Catacomb
+.B hash
+class.
+.TP
+.B key
+A blob,
+protected by the
+.I derived
+keys (see below),
+containing the
+.I main
+secrecy and integrity keys.
+The blob payload consists of the main secrecy and integrity keys,
+each prefixed by its 16-bit length (in network byte order)
+and concatenated.
+.TP
+.B mac
+The message authentication code used to protect integrity.
+This names a Catacomb
+.B mac
+class.
+.TP
+.B magic
+A blob containing a string
+used to construct the database keys for password records;
+see below.
+The magic string is chosen at random
+when the database is created,
+and never changes;
+it is the same length as the chosen hash function's output.
+The blob is protected by the
+.I main
+keys.
+.TP
+.B salt
+A random string
+mixed into the key derivation process.
+.TP
+.B tag
+The passphrase tag,
+used to identify the master password
+to the passphrase
+.BR pixie (1).
+.
+.SS Keys
+The following keys are used.
+.TP
+The \fImaster password\fP
+Remembered (hopefully) by the user;
+used to unlock the
+.I main
+keys.
+.TP
+The \fIderived\fP keys
+A secrecy and integrity pair,
+derived from the
+.I master password
+and
+salt using a hash function.
+.TP
+The \fImain\fP keys
+A secrecy and integrity pair,
+kept in a blob in the database
+(the
+.B key
+metadata item)
+protected by the
+.I derived
+keys.
+The main keys are generated at random
+when the database is created
+and they never change;
+the Catacomb default key lengths are used.
+.
+.SS Deriving keys from the master password
+The keys used for protecting the
+.I main
+secrecy and integrity keys
+are derived by hashing strings of the form
+.IB purpose : password \e0 salt \fR,
+where
+.I purpose
+is
+.B cipher
+or
+.B mac
+to derive the secrecy and integrity keys, respectively.
+The
+.I salt
+string is the value of the
+.B salt
+metadata item described below.
+.PP
+No attempt is made to make the key derivation slow;
+.B pwsafe
+takes the view that you are have been specifically targetted for attack
+by a well-resourced adversary,
+and that you
+.I will
+lose if your password is guessable.
+.
+.SS Making a blob
+A
+.I blob
+contains a
+.IR payload ,
+protecting its secrecy and integrity.
+A blob is constructed using a pair of secrecy and integrity keys;
+most blobs are protected with the
+.I main
+keys;
+the main keys themselves are protected with the
+.I derived
+keys.
+.PP
+The steps to construct a blob are as follows.
+.hP 1.
+Choose a random IV of the appropriate length for the chosen
+.BR cipher .
+.hP 2.
+Encrypt the blob payload
+using the chosen
+.B cipher
+with the secrecy key
+and the IV from step 1,
+to form a ciphertext.
+Prefix the ciphertext with the IV
+to form an augmented ciphertext.
+.hP 3.
+Compute a tag over the augmented ciphertext from step 2
+using the chosen
+.B mac
+with the integrity key.
+Prefix the augmented ciphertext with the tag
+to form the blob.
+.PP
+(It seems more usual to put the tag on the end of the ciphertext,
+but that turned out to be pointlessly harder to implement.)
+.
+.SS Password records
+Conceptually,
+password records are indexed with a textual
+.I label
+chosen by the user.
+But users may want to not only keep their passwords secret,
+but also information about
+.I which
+passwords they have.
+The
+.B pwsafe
+program attempts to maintain the privacy of password record labels,
+but it isn't completely successful, as we shall see.
+Most critically,
+the database backends tend to leak information about
+the
+.I order
+in which records were added into the database.
+.PP
+At the database backend,
+the key used for looking up a password record is a hash,
+in binary:
+specifically, it's a hash of
+the record label
+prefixed by the
+.I magic
+string which is the payload of the blob stored in the
+.B magic
+metadata record.
+.PP
+The value of the password record is a blob,
+protected by the
+.I main
+keys,
+whose payload consists of
+.hP \*o
+the 16-bit network-byte-order length of the record label;
+.hP \*o
+the record label itself;
+.hP \*o
+the 16-bit network-byte-order length of the password;
+.hP \*o
+the password itself; and
+.hP \*o
+zero or more zero-valued octets,
+so as to make the payload a multiple of 256 octets long.
+.PP
+The padding serves to conceal the length of the label and password
+from an adversary who has obtained a copy of the database.
+.
+.SS Backend formats
+The various password-database backends
+represent the records described above as follows.
+.TP
+.B gdbm
+A GDBM-backed database is stored in a single file.
+A metadata record with key
+.I r
+and value
+.I v
+is stored in a GDBM record also named
+.IR r ,
+and with value
+.I v ;
+a password record with hash
+.I h
+and blob
+.I b
+is stored in a GDBM record named
+.BI $ h
+with value
+.IR b ,
+both in raw binary.
+.TP
+.B sqlite
+A SQLite-backed database is stored in a single file.
+It contains two tables,
+named
+.B meta
+and
+.BR passwd .
+The
+.B meta
+table has a primary key
+.B name
+and a further column
+.BR value ;
+a metadata record with key
+.I r
+and value
+.I v
+is held in a
+.B meta
+record
+with
+.B name
+.I r
+and
+.B value
+.IR v ;
+additionally, there is a record with
+.B name
+.B $version
+whose
+.B value
+is the schema version;
+this is currently always 0.
+The
+.B passwd
+table has a primary key
+.B label
+and a further column
+.BR payload ;
+a password record with hash
+.I h
+and blob
+.I b
+is stored in a
+.B passwd
+record with
+.B label
+.IR h
+and
+.B payload
+.IR b ,
+both in raw binary.
+.TP
+.B flat
+A flat-file-backed database is stored in a single file,
+with one record per line.
+The first line must be exactly
+.RS
+.IP
+.B "### pwsafe password database"
+.PP
+Blank lines and lines beginning with a
+.RB ` # '
+are ignored.
+.PP
+A metadata record named
+.I r
+with value
+.I v
+is stored as a line of the form
+.IB r\fR\(fm = v\fR\(fm
+where
+.IR r \fR\(fm
+and
+.IR v \fR\(fm
+are encodings of the strings
+.I r
+and
+.I v
+respectively.
+If
+.I r
+consists only of letters, digits,
+and the punctuation characters
+.RB ` \- ',
+.RB ` _ ',
+.RB ` : ',
+.RB ` . ',
+and
+.RB ` / '
+then
+.IR r \fR\(fm
+is simply
+.IR r ;
+otherwise
+.IR r \fR\(fm
+is formed by (simultaneously) replacing
+each space character in
+.I r
+with
+.RB ` + '
+and each other character
+which is not a letter, digit, or
+one of the punctuation characters listed above
+with
+.RB ` % '
+followed by that character's ASCII code in hex,
+and prefixing the whole lot by
+.RB ` ! '.
+Similarly,
+if
+.I v
+consists of letters, digits,
+and the punctuation characters listed above,
+then
+.IR v \fR\(fm
+is simply
+.IR v ;
+otherwise
+.IR v \fR\(fm
+consists of a
+.RB ` ? '
+followed by the base64 encoding of
+.IR v ,
+without any trailing
+.RB ` = '
+characters.
+.PP
+A password record with hash
+.I h
+and blob
+.I b
+is stored as a line of the form
+.BI $ h\fR\(fm = b\fR\(fm
+where
+.IR h \(fm
+and
+.IR b \(fm
+are the base64 encodings of
+.I h
+and
+.I b
+respectively,
+without trailing
+.RB ` = '
+characters.
+.PP
+The records may appear in any order.
+The file is completely rewritten when any change is made;
+if the file is named
+.I f
+then this is done by writing the new contents to
+.IB f .new
+and then renaming
+.IB f .new
+to
+.IR f .
+.RE
+.TP
+.B dir
+A directory-backed database is stored as a directory,
+named
+.I d
+in the sequel.
+The directory must contain a file
+.IB d /meta
+whose first line is
+.RS
+.IP
+.B "### pwsafe password directory metadata"
+.PP
+and directories
+.IB d /pw
+and
+.IB d /tmp \fR.
+.PP
+Metadata records are stored in file
+.IB d /meta
+with one record per line,
+exactly as for the
+.B file
+backend described above.
+.PP
+A password record with hash
+.I h
+and blob
+.I b
+is stored as file named
+.IB d /pw/ h \fR\(fm
+where
+.IR h \(fm
+is the base64 encodings of
+.I h
+without trailing
+.RB ` = '
+characters,
+and with all
+.RB ` / '
+characters
+replaced by
+.RB ` . 's,
+whose content is
+.IR b .
+.PP
+The directory
+.IB d /tmp/
+is used in an unspecified manner
+when creating new password-record files.
+The
+.IB d /meta
+and
+.IB d /pw/ h \fR\(fm
+files are updated by creating a new temporary file and renaming.
+.RE
+.
+.\"--------------------------------------------------------------------------
+.
+.SH BUGS
+This is quite an old program,
+though the manpage is new.
+It provides more footguns than is ideal.
+.
+.SH SEE ALSO
+.BR catcrypt (1),
+.BR pixie (1).
+.
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
diff --git a/rand.c b/rand.c
index 641b9bdbcbbe461c74791c095229b85fab042f70..841a402bf08a2a5b5b5e44f05c89aefd701b59ad 100644 (file)
--- a/rand.c
+++ b/rand.c
@@ -124,9 +124,9 @@ static PyObject *grmeth_mp(PyObject *me, PyObject *arg, PyObject *kw)
 {
   size_t l;
   mpw o = 0;
-  char *kwlist[] = { "bits", "or", 0 };
+  static const char *const kwlist[] = { "bits", "or", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", KWLIST,
                                   convszt, &l, convmpw, &o))
     goto end;
   if (grand_check(me)) return (0);
@@ -214,10 +214,10 @@ end:
 
 static PyObject *grmeth_seedrand(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  char *kwlist[] = { "rng", 0 };
+  static const char *const kwlist[] = { "rng", 0 };
   grand *r = GRAND_R(me);
   grand *rr = &rand_global;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", KWLIST,
                                   convgrand, &rr) ||
       grand_check(me) || checkop(r, GRAND_SEEDRAND, "seedrand"))
     goto end;
@@ -332,8 +332,8 @@ static PyTypeObject grand_pytype_skel = {
 static PyObject *lcrand_pynew(PyTypeObject *me, PyObject *arg, PyObject *kw)
 {
   uint32 n = 0;
-  char *kwlist[] = { "seed", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist, convu32, &n))
+  static const char *const kwlist[] = { "seed", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST, convu32, &n))
     return (0);
   return (grand_dopywrap(lcrand_pytype, lcrand_create(n), f_freeme));
 }
@@ -363,7 +363,7 @@ 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@ */
@@ -389,8 +389,8 @@ 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));
 }
@@ -420,7 +420,7 @@ static PyTypeObject fibrand_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Fibonacci generator.",
+"FibRand([seed = 0]): Fibonacci generator.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -503,10 +503,10 @@ static PyObject *trmeth_timer(PyObject *me, PyObject *arg)
 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);
@@ -565,7 +565,7 @@ 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@ */
@@ -639,11 +639,11 @@ static const gccrand_info *const gcrandtab[] = {
 static PyObject *gcrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   const gccrand_info *info = GCCRAND_INFO(ty);
-  static char *kwlist[] = { "key", 0 };
+  static const char *const kwlist[] = { "key", 0 };
   char *k;
   Py_ssize_t n;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &k, &n))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &k, &n))
     goto end;
   if (keysz(n, info->keysz) != n) VALERR("bad key length");
   return (grand_dopywrap(ty, info->func(k, n), f_freeme));
@@ -655,11 +655,11 @@ static PyObject *gcirand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   const gccrand_info *info = GCCRAND_INFO(ty);
   uint32 i = 0;
-  static char *kwlist[] = { "key", "i", 0 };
+  static const char *const kwlist[] = { "key", "i", 0 };
   char *k;
   Py_ssize_t n;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&:new", KWLIST,
                                   &k, &n, convu32, &i))
     goto end;
   if (keysz(n, info->keysz) != n) VALERR("bad key length");
@@ -673,11 +673,11 @@ end:
 static PyObject *gcnrand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
   const gccrand_info *info = GCCRAND_INFO(ty);
-  static char *kwlist[] = { "key", "nonce", 0 };
+  static const char *const kwlist[] = { "key", "nonce", 0 };
   char *k, *n;
   Py_ssize_t ksz, nsz;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#:new", KWLIST,
                                   &k, &ksz, &n, &nsz))
     goto end;
   if (keysz(ksz, info->keysz) != ksz) VALERR("bad key length");
@@ -693,15 +693,18 @@ static PyObject *gcshakyrand_pynew(PyTypeObject *ty,
                                   PyObject *arg, PyObject *kw)
 {
   const gccrand_info *info = GCCRAND_INFO(ty);
-  static char *kwlist_shake[] = { "key", "func", "perso", 0 };
-  static char *kwlist_func[] = { "key", "perso", 0 };
+  static const char
+    *const kwlist_shake[] = { "key", "func", "perso", 0 },
+    *const kwlist_func[] = { "key", "perso", 0 };
   char *k, *f = 0, *p = 0;
   Py_ssize_t ksz, fsz = 0, psz = 0;
 
   if ((info->f&RNGF_MASK) == RNG_SHAKE
-       ? !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#s#:new", kwlist_shake,
+       ? !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#s#:new",
+                                      (/*unconst*/ char **)kwlist_shake,
                                       &k, &ksz, &f, &fsz, &p, &psz)
-       : !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#:new", kwlist_func,
+       : !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#:new",
+                                      (/*unconst*/ char **)kwlist_func,
                                       &k, &ksz, &p, &psz))
     goto end;
   if (keysz(ksz, info->keysz) != ksz) VALERR("bad key length");
@@ -938,9 +941,9 @@ static PyObject *sslprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   Py_ssize_t ksz, ssz;
   const gchash *hco = &md5, *hci = &sha;
   PyObject *rc = 0;
-  char *kwlist[] = { "key", "seed", "ohash", "ihash", 0 };
+  static const char *const kwlist[] = { "key", "seed", "ohash", "ihash", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", KWLIST,
                                   &k, &ksz, &s, &ssz,
                                   convgchash, &hco, convgchash, &hci))
     goto end;
@@ -955,9 +958,9 @@ static PyObject *tlsdx_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   Py_ssize_t ksz, ssz;
   const gcmac *mc = &sha_hmac;
   PyObject *rc = 0;
-  char *kwlist[] = { "key", "seed", "mac", 0 };
+  static const char *const kwlist[] = { "key", "seed", "mac", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&:new", KWLIST,
                                   &k, &ksz, &s, &ssz,
                                   convgcmac, &mc))
     goto end;
@@ -972,9 +975,9 @@ static PyObject *tlsprf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   Py_ssize_t ksz, ssz;
   const gcmac *mcl = &md5_hmac, *mcr = &sha_hmac;
   PyObject *rc = 0;
-  char *kwlist[] = { "key", "seed", "lmac", "rmac", 0 };
+  static const char *const kwlist[] = { "key", "seed", "lmac", "rmac", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#s#|O&O&:new", KWLIST,
                                   &k, &ksz, &s, &ssz,
                                   convgcmac, &mcl, convgcmac, &mcr))
     goto end;
@@ -1008,7 +1011,8 @@ static PyTypeObject sslprf_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Random number generator for SSL master secret.",
+"SSLRand(KEY, SEED, [ohash = md5], [ihash = sha]):\n\
+  RNG for SSL master secret.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1056,7 +1060,8 @@ static PyTypeObject tlsdx_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"TLS data expansion function.",
+"TLSDataExpansion(KEY, SEED, [mac = sha_hmac]):\n\
+  TLS data expansion function.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1104,7 +1109,8 @@ static PyTypeObject tlsprf_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"TLS pseudorandom function.",
+"TLSPRF(KEY, SEED, [lmac = md5_hmac], [rmac = sha_hmac]):\n\
+  TLS pseudorandom function.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1134,9 +1140,9 @@ static PyObject *dsarand_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   char *p;
   Py_ssize_t sz;
   PyObject *rc = 0;
-  char *kwlist[] = { "seed", 0 };
+  static const char *const kwlist[] = { "seed", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &sz))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", KWLIST, &p, &sz))
     goto end;
   rc = grand_dopywrap(ty, dsarand_create(p, sz), f_freeme);
 end:
@@ -1184,7 +1190,7 @@ 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@ */
@@ -1213,9 +1219,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);
@@ -1262,7 +1268,8 @@ 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 (!val) NIERR("__del__");
-  if ((x = getmp(val)) == 0) goto end; r->ops->misc(r, BBS_SET, x); rc = 0;
+  if ((x = getmp(val)) == 0) goto end;
+  r->ops->misc(r, BBS_SET, x); rc = 0;
   end: mp_drop(x); return (rc);
 }
 
@@ -1315,7 +1322,7 @@ static PyTypeObject bbs_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Blum-Blum-Shub strong pseudorandom number generator.",
+"BlumBlumShub(N, [x = 2]): Blum-Blum-Shub pseudorandom number generator.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -1350,9 +1357,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;
@@ -1380,11 +1387,12 @@ static PyObject *meth__BBSPriv_generate(PyObject *me,
   pypgev evt = { { 0 } };
   unsigned nbits, n = 0;
   grand *r = &rand_global;
-  char *kwlist[] = { "class", "nbits", "event", "rng", "nsteps", "seed", 0 };
+  static const char *const kwlist[] =
+    { "class", "nbits", "event", "rng", "nsteps", "seed", 0 };
   bbspriv_pyobj *rc = 0;
 
   evt.exc = &exc;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO&|O&O&O&O&:generate", KWLIST,
                                   &me, convuint, &nbits, convpgev, &evt,
                                   convgrand, &r, convuint, &n, convmp, &x))
     goto end;
@@ -1475,7 +1483,8 @@ 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@ */
index 80a154772a0504041c2e2793d3588c25d69e03d0..d7781775a5d3e8f63594f13bf38f8a6183040421 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -3,8 +3,8 @@
 import distutils.core as DC
 import mdwsetup as MS
 
-MS.pkg_config('catacomb', '2.3.0.1+4')
-MS.pkg_config('mLib', '2.0.4')
+MS.pkg_config('catacomb', '2.5.0')
+MS.pkg_config('mLib', '2.2.2.1')
 
 cat = DC.Extension('catacomb._base',
                    ['catacomb.c', 'bytestring.c', 'buffer.c',
@@ -23,7 +23,8 @@ MS.setup(name = 'catacomb-python',
          author_email = 'mdw@distorted.org.uk',
          license = 'GNU General Public License',
          packages = ['catacomb'],
-         scripts = ['pwsafe'],
+         scripts = ['pock', 'pwsafe'],
+         data_files = [('share/man/man1', ['pock.1', 'pwsafe.1'])],
          genfiles = [MS.Generate('algorithms.h')],
          unittest_dir = "t",
          unittests = ["t-misc", "t-algorithms", "t-bytes", "t-buffer",
diff --git a/share.c b/share.c
index 2caa718bb23290ec613a0f378ada8052964ffabe..77da423571f8a8b7378a64bf10e3b705cc21326e 100644 (file)
--- a/share.c
+++ b/share.c
@@ -117,8 +117,8 @@ static PyObject *gfsharesplit_pynew(PyTypeObject *ty,
   unsigned t;
   grand *r = &rand_global;
   gfshare_pyobj *s;
-  char *kwlist[] = { "threshold", "secret", "rng", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s#|O&:new", kwlist,
+  static const char *const kwlist[] = { "threshold", "secret", "rng", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&s#|O&:new", KWLIST,
                                   convuint, &t, &p, &n, convgrand, &r))
     goto end;
   if (!t || t > 255) VALERR("threshold must be nonzero and < 256");
@@ -174,7 +174,8 @@ static PyTypeObject gfsharesplit_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Binary field secret sharing: split secret into shares.",
+"GFShareSplit(THRESHOLD, SECRET, [rng = rand]): binary-field sharing:\n\
+   split secret into shares.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -202,8 +203,8 @@ static PyObject *gfsharejoin_pynew(PyTypeObject *ty,
 {
   unsigned t, sz;
   gfshare_pyobj *s;
-  char *kwlist[] = { "threshold", "size", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+  static const char *const kwlist[] = { "threshold", "size", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   convuint, &t, convuint, &sz))
     goto end;
   if (!t || t > 255) VALERR("threshold must be nonzero and < 256");
@@ -295,7 +296,8 @@ static PyTypeObject gfsharejoin_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Binary field secret sharing: join shares to recover secret.",
+"GFShareJoin(THRESHOLD, SIZE): binary field sharing:\n\
+  join shares to recover secret.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -407,8 +409,10 @@ static PyObject *sharesplit_pynew(PyTypeObject *ty,
   grand *r = &rand_global;
   mp *m = 0;
   share_pyobj *s;
-  char *kwlist[] = { "threshold", "secret", "modulus", "rng", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", kwlist,
+  static const char *const kwlist[] =
+    { "threshold", "secret", "modulus", "rng", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|O&O&:new", KWLIST,
                                   convuint, &t, convmp, &sec,
                                   convmp, &m, convgrand, &r))
     goto end;
@@ -467,7 +471,8 @@ static PyTypeObject sharesplit_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Prime field secret sharing: split secret into shares.",
+"ShareSplit(THRESHOLD, SECRET, [modulus = ?], [rng = rand]):\n\
+  prime field secret sharing: split secret into shares.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -496,8 +501,8 @@ static PyObject *sharejoin_pynew(PyTypeObject *ty,
   unsigned t;
   mp *m = 0;
   share_pyobj *s;
-  char *kwlist[] = { "threshold", "modulus", 0 };
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", kwlist,
+  static const char *const kwlist[] = { "threshold", "modulus", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&:new", KWLIST,
                                   convuint, &t, convmp, &m))
     goto end;
   if (!t) VALERR("threshold must be nonzero");
@@ -590,7 +595,8 @@ static PyTypeObject sharejoin_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Prime field secret sharing: join shares to recover secret.",
+"ShareJoin(THRESHOLD, MODULUS): prime field secret sharing:\n\
+  join shares to recover secret.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
index 8e073f2cdd4aeae2cb3830df293f720da7df6ecc..d7b97cbc403a6ee6cc31f5ec3ea4d1ad2431a4e9 100644 (file)
@@ -149,6 +149,7 @@ class TestKeysize (U.TestCase):
     for n in [0, 12, 20, 5000]:
       me.assertTrue(ksz.check(n))
       me.assertEqual(ksz.best(n), n)
+      me.assertEqual(ksz.pad(n), n)
 
     ## A typical two-byte spec.  (No published algorithms actually /need/ a
     ## two-byte key-size spec, but all of the HMAC variants use one anyway.)
@@ -160,6 +161,7 @@ class TestKeysize (U.TestCase):
     for n in [0, 12, 20, 5000]:
       me.assertTrue(ksz.check(n))
       me.assertEqual(ksz.best(n), n)
+      me.assertEqual(ksz.pad(n), n)
 
     ## Check construction.
     ksz = C.KeySZAny(15)
@@ -186,6 +188,8 @@ class TestKeysize (U.TestCase):
       else: me.assertFalse(ksz.check(x))
       if best is None: me.assertRaises(ValueError, ksz.best, x)
       else: me.assertEqual(ksz.best(x), best)
+      if pad is None: me.assertRaises(ValueError, ksz.pad, x)
+      else: me.assertEqual(ksz.pad(x), pad)
 
     ## Check construction.
     ksz = C.KeySZSet(7)
@@ -210,13 +214,15 @@ class TestKeysize (U.TestCase):
     me.assertEqual(ksz.min, 4)
     me.assertEqual(ksz.max, 32)
     me.assertEqual(ksz.mod, 4)
-    for x, best in [(3, None), (4, 4), (5, 4),
-                    (15, 12), (16, 16), (17, 16),
-                    (31, 28), (32, 32), (33, 32)]:
-      if x == best: me.assertTrue(ksz.check(x))
+    for x, best, pad in [(3, None, 4), (4, 4, 4), (5, 4, 8),
+                         (15, 12, 16), (16, 16, 16), (17, 16, 20),
+                         (31, 28, 32), (32, 32, 32), (33, 32, None)]:
+      if x == best == pad: me.assertTrue(ksz.check(x))
       else: me.assertFalse(ksz.check(x))
       if best is None: me.assertRaises(ValueError, ksz.best, x)
       else: me.assertEqual(ksz.best(x), best)
+      if pad is None: me.assertRaises(ValueError, ksz.pad, x)
+      else: me.assertEqual(ksz.pad(x), pad)
 
     ## Check construction.
     ksz = C.KeySZRange(28, 21, 35, 7)
@@ -309,6 +315,280 @@ TestCipher.generate_testcases((name, C.gcciphers[name]) for name in
   ["des-ecb", "rijndael-cbc", "twofish-cfb", "serpent-ofb",
    "blowfish-counter", "rc4", "seal", "salsa20/8", "shake128-xof"])
 
+###--------------------------------------------------------------------------
+class TestAuthenticatedEncryption \
+        (HashBufferTestMixin, T.GenericTestMixin):
+  """Test authenticated encryption schemes."""
+
+  def _test_aead(me, aecls):
+
+    ## Check the class properties.
+    me.assertEqual(type(aecls.name), str)
+    me.assertTrue(isinstance(aecls.keysz, C.KeySZ))
+    me.assertTrue(isinstance(aecls.noncesz, C.KeySZ))
+    me.assertTrue(isinstance(aecls.tagsz, C.KeySZ))
+    me.assertEqual(type(aecls.blksz), int)
+    me.assertEqual(type(aecls.bufsz), int)
+    me.assertEqual(type(aecls.ohd), int)
+    me.assertEqual(type(aecls.flags), int)
+
+    ## Check round-tripping, with full precommitment.  First, select some
+    ## parameters.  (It's conceivable that some AEAD schemes are more
+    ## restrictive than advertised by the various properties, but this works
+    ## out OK in practice.)
+    k = T.span(aecls.keysz.default)
+    n = T.span(aecls.noncesz.default)
+    if aecls.flags&C.AEADF_NOAAD: h = T.span(0)
+    else: h = T.span(131)
+    m = T.span(253)
+    tsz = aecls.tagsz.default
+    key = aecls(k)
+
+    ## Next, encrypt a message, checking that things are proper as we go.
+    enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+    me.assertEqual(enc.hsz, len(h))
+    me.assertEqual(enc.msz, len(m))
+    me.assertEqual(enc.mlen, 0)
+    me.assertEqual(enc.tsz, tsz)
+    aad = enc.aad()
+    if aecls.flags&C.AEADF_AADNDEP: me.assertEqual(aad.hsz, len(h))
+    else: me.assertEqual(aad.hsz, None)
+    me.assertEqual(aad.hlen, 0)
+    if not aecls.flags&C.AEADF_NOAAD:
+      aad.hash(h[0:83])
+      me.assertEqual(aad.hlen, 83)
+      aad.hash(h[83:131])
+      me.assertEqual(aad.hlen, 131)
+    c0 = enc.encrypt(m[0:57])
+    me.assertEqual(enc.mlen, 57)
+    me.assertTrue(57 - aecls.bufsz <= len(c0) <= 57 + aecls.ohd)
+    c1 = enc.encrypt(m[57:189])
+    me.assertEqual(enc.mlen, 189)
+    me.assertTrue(132 - aecls.bufsz <= len(c1) <=
+                  132 + aecls.bufsz + aecls.ohd)
+    c2 = enc.encrypt(m[189:253])
+    me.assertEqual(enc.mlen, 253)
+    me.assertTrue(64 - aecls.bufsz <= len(c2) <=
+                  64 + aecls.bufsz + aecls.ohd)
+    c3, t = enc.done(aad = aad)
+    me.assertTrue(len(c3) <= aecls.bufsz + aecls.ohd)
+    c = c0 + c1 + c2 + c3
+    me.assertTrue(len(m) <= len(c) <= len(m) + aecls.ohd)
+    me.assertEqual(len(t), tsz)
+
+    ## And now decrypt it again, with different record boundaries.
+    dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+    me.assertEqual(dec.hsz, len(h))
+    me.assertEqual(dec.csz, len(c))
+    me.assertEqual(dec.clen, 0)
+    me.assertEqual(dec.tsz, tsz)
+    aad = dec.aad()
+    if aecls.flags&C.AEADF_AADNDEP: me.assertEqual(aad.hsz, len(h))
+    else: me.assertEqual(aad.hsz, None)
+    me.assertEqual(aad.hlen, 0)
+    aad.hash(h)
+    m0 = dec.decrypt(c[0:156])
+    me.assertTrue(156 - aecls.bufsz <= len(m0) <= 156)
+    m1 = dec.decrypt(c[156:])
+    me.assertTrue(len(c) - 156 - aecls.bufsz <= len(m1) <=
+                  len(c) - 156 + aecls.bufsz)
+    m2 = dec.done(tag = t, aad = aad)
+    me.assertEqual(m0 + m1 + m2, m)
+
+    ## And again, with the wrong tag.
+    dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+    aad = dec.aad(); aad.hash(h)
+    _ = dec.decrypt(c)
+    me.assertRaises(ValueError, dec.done, tag = t ^ tsz*C.bytes("55"))
+
+    ## Check that the all-in-one methods work.
+    me.assertEqual((c, t),
+                   key.encrypt(n = n, h = h, m = m, tsz = tsz))
+    me.assertEqual(m,
+                   key.decrypt(n = n, h = h, c = c, t = t))
+
+    ## Check that bad key, nonce, and tag lengths are rejected.
+    badlen = bad_key_size(aecls.keysz)
+    if badlen is not None: me.assertRaises(ValueError, aecls, T.span(badlen))
+    badlen = bad_key_size(aecls.noncesz)
+    if badlen is not None:
+      me.assertRaises(ValueError, key.enc, nonce = T.span(badlen),
+                      hsz = len(h), msz = len(m), tsz = tsz)
+      me.assertRaises(ValueError, key.dec, nonce = T.span(badlen),
+                      hsz = len(h), csz = len(c), tsz = tsz)
+      if not aecls.flags&C.AEADF_PCTSZ:
+        enc = key.enc(nonce = n, hsz = 0, msz = len(m))
+        _ = enc.encrypt(m)
+        me.assertRaises(ValueError, enc.done, tsz = badlen)
+    badlen = bad_key_size(aecls.tagsz)
+    if badlen is not None:
+      me.assertRaises(ValueError, key.enc, nonce = n,
+                      hsz = len(h), msz = len(m), tsz = badlen)
+      me.assertRaises(ValueError, key.dec, nonce = n,
+                      hsz = len(h), csz = len(c), tsz = badlen)
+
+    ## Check that we can't get a loose `aad' object from a scheme which has
+    ## nonce-dependent AAD processing.
+    if aecls.flags&C.AEADF_AADNDEP: me.assertRaises(ValueError, key.aad)
+
+    ## Check the menagerie of AAD hashing methods.
+    if not aecls.flags&C.AEADF_NOAAD:
+      def mkhash(hsz):
+        enc = key.enc(nonce = n, hsz = hsz, msz = 0, tsz = tsz)
+        aad = enc.aad()
+        return aad, lambda: enc.done(aad = aad)[1]
+      me.check_hashbuffer(mkhash)
+
+    ## Check that encryption/decryption works with the given precommitments.
+    def quick_enc_check(**kw):
+      enc = key.enc(**kw)
+      aad = enc.aad().hash(h)
+      c0 = enc.encrypt(m); c1, tt = enc.done(aad = aad, tsz = tsz)
+      me.assertEqual((c, t), (c0 + c1, tt))
+    def quick_dec_check(**kw):
+      dec = key.dec(**kw)
+      aad = dec.aad().hash(h)
+      m0 = dec.decrypt(c); m1 = dec.done(aad = aad, tag = t)
+      me.assertEqual(m, m0 + m1)
+
+    ## Check that we can get away without precommitting to the header length
+    ## if and only if the AEAD scheme says it will let us.
+    if aecls.flags&C.AEADF_PCHSZ:
+      me.assertRaises(ValueError, key.enc, nonce = n,
+                      msz = len(m), tsz = tsz)
+      me.assertRaises(ValueError, key.dec, nonce = n,
+                      csz = len(c), tsz = tsz)
+    else:
+      quick_enc_check(nonce = n, msz = len(m), tsz = tsz)
+      quick_dec_check(nonce = n, csz = len(c), tsz = tsz)
+
+    ## Check that we can get away without precommitting to the message/
+    ## ciphertext length if and only if the AEAD scheme says it will let us.
+    if aecls.flags&C.AEADF_PCMSZ:
+      me.assertRaises(ValueError, key.enc, nonce = n,
+                      hsz = len(h), tsz = tsz)
+      me.assertRaises(ValueError, key.dec, nonce = n,
+                      hsz = len(h), tsz = tsz)
+    else:
+      quick_enc_check(nonce = n, hsz = len(h), tsz = tsz)
+      quick_dec_check(nonce = n, hsz = len(h), tsz = tsz)
+
+    ## Check that we can get away without precommitting to the tag length if
+    ## and only if the AEAD scheme says it will let us.
+    if aecls.flags&C.AEADF_PCTSZ:
+      me.assertRaises(ValueError, key.enc, nonce = n,
+                      hsz = len(h), msz = len(m))
+      me.assertRaises(ValueError, key.dec, nonce = n,
+                      hsz = len(h), csz = len(c))
+    else:
+      quick_enc_check(nonce = n, hsz = len(h), msz = len(m))
+      quick_dec_check(nonce = n, hsz = len(h), csz = len(c))
+
+    ## Check that if we precommit to the header length, we're properly held
+    ## to the commitment.
+    if not aecls.flags&C.AEADF_NOAAD:
+
+      ## First, check encryption with underrun.  If we must supply AAD first,
+      ## then the underrun will be reported when we start trying to encrypt;
+      ## otherwise, checking is delayed until `done'.
+      enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+      aad = enc.aad().hash(h[0:83])
+      if aecls.flags&C.AEADF_AADFIRST:
+        me.assertRaises(ValueError, enc.encrypt, m)
+      else:
+        _ = enc.encrypt(m)
+        me.assertRaises(ValueError, enc.done, aad = aad)
+
+      ## Next, check decryption with underrun.  If we must supply AAD first,
+      ## then the underrun will be reported when we start trying to encrypt;
+      ## otherwise, checking is delayed until `done'.
+      dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+      aad = dec.aad().hash(h[0:83])
+      if aecls.flags&C.AEADF_AADFIRST:
+        me.assertRaises(ValueError, dec.decrypt, c)
+      else:
+        _ = dec.decrypt(c)
+        me.assertRaises(ValueError, dec.done, tag = t, aad = aad)
+
+      ## If AAD processing is nonce-dependent then an overrun will be
+      ## detected imediately.
+      if aecls.flags&C.AEADF_AADNDEP:
+        enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+        aad = enc.aad().hash(h[0:83])
+        me.assertRaises(ValueError, aad.hash, h[82:131])
+        dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
+        aad = dec.aad().hash(h[0:83])
+        me.assertRaises(ValueError, aad.hash, h[82:131])
+
+    ## Some additional tests for nonce-dependent `aad' objects.
+    if aecls.flags&C.AEADF_AADNDEP:
+
+      ## Check that `aad' objects can't be used once their parents are gone.
+      enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+      aad = enc.aad()
+      del enc
+      me.assertRaises(ValueError, aad.hash, h)
+
+      ## Check that they can't be crossed over.
+      enc0 = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+      enc1 = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
+      enc0.aad().hash(h)
+      aad1 = enc1.aad().hash(h)
+      _ = enc0.encrypt(m)
+      me.assertRaises(ValueError, enc0.done, tsz = tsz, aad = aad1)
+
+    ## Test copying AAD.
+    if not aecls.flags&C.AEADF_AADNDEP and not aecls.flags&C.AEADF_NOAAD:
+      aad0 = key.aad()
+      aad0.hash(h[0:83])
+      aad1 = aad0.copy()
+      aad2 = aad1.copy()
+      aad0.hash(h[83:131])
+      aad1.hash(h[83:131])
+      aad2.hash(h[83:131] ^ 48*C.bytes("ff"))
+      me.assertEqual(key.enc(nonce = n, hsz = len(h),
+                             msz = 0, tsz = tsz).done(aad = aad0),
+                     key.enc(nonce = n, hsz = len(h),
+                             msz = 0, tsz = tsz).done(aad = aad1))
+      me.assertNotEqual(key.enc(nonce = n, hsz = len(h),
+                                msz = 0, tsz = tsz).done(aad = aad0),
+                        key.enc(nonce = n, hsz = len(h),
+                                msz = 0, tsz = tsz).done(aad = aad2))
+
+    ## Check that if we precommit to the message length, we're properly held
+    ## to the commitment.  (Fortunately, this is way simpler than the AAD
+    ## case above.)  First, try an underrun.
+    enc = key.enc(nonce = n, hsz = 0, msz = len(m), tsz = tsz)
+    _ = enc.encrypt(m[0:183])
+    me.assertRaises(ValueError, enc.done, tsz = tsz)
+    dec = key.dec(nonce = n, hsz = 0, csz = len(c), tsz = tsz)
+    _ = dec.decrypt(c[0:183])
+    me.assertRaises(ValueError, dec.done, tag = t)
+
+    ## And now an overrun.
+    enc = key.enc(nonce = n, hsz = 0, msz = 183, tsz = tsz)
+    me.assertRaises(ValueError, enc.encrypt, m)
+    dec = key.dec(nonce = n, hsz = 0, csz = 183, tsz = tsz)
+    me.assertRaises(ValueError, dec.decrypt, c)
+
+    ## Finally, check that if we precommit to a tag length, we're properly
+    ## held to the commitment.  This depends on being able to find a tag size
+    ## which isn't the default.
+    tsz1 = different_key_size(aecls.tagsz, tsz)
+    if tsz1 is not None:
+      enc = key.enc(nonce = n, hsz = 0, msz = len(m), tsz = tsz1)
+      _ = enc.encrypt(m)
+      me.assertRaises(ValueError, enc.done, tsz = tsz)
+      dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz1)
+      aad = dec.aad().hash(h)
+      _ = dec.decrypt(c)
+      me.assertRaises(ValueError, enc.done, tsz = tsz, aad = aad)
+
+TestAuthenticatedEncryption.generate_testcases \
+  ((name, C.gcaeads[name]) for name in
+   ["des3-ccm", "blowfish-ocb1", "square-ocb3", "rijndael-gcm",
+    "serpent-eax", "salsa20-naclbox", "chacha20-poly1305"])
+
 ###--------------------------------------------------------------------------
 class BaseTestHash (HashBufferTestMixin):
   """Base class for testing hash functions."""
@@ -432,7 +712,7 @@ class TestHLatin (U.TestCase):
   """Test the `hsalsa20' and `hchacha20' functions."""
 
   def test_hlatin(me):
-    kk = [T.span(sz) for sz in [32]]
+    kk = [T.span(sz) for sz in [10, 16, 32]]
     n = T.span(16)
     bad_k = T.span(18)
     bad_n = T.span(13)
@@ -462,6 +742,13 @@ class TestKeccak (HashBufferTestMixin):
     st1.mix(m0).step()
     me.assertNotEqual(st0.extract(32), st1.extract(32))
 
+    ## Check state copying.
+    st1 = st0.copy()
+    mask = st1.extract(len(m1))
+    st0.mix(m1)
+    st1.mix(m1)
+    me.assertEqual(st0.extract(32), st1.extract(32))
+
     ## Check error conditions.
     _ = st0.extract(200)
     me.assertRaises(ValueError, st0.extract, 201)
@@ -501,7 +788,7 @@ class TestKeccak (HashBufferTestMixin):
 
     ## Check masking.
     x = xcls().hash(m).xof()
-    me.assertEqual(x.mask(m), C.ByteString(m) ^ C.ByteString(h[0:len(m)]))
+    me.assertEqual(x.mask(m), m ^ h[0:len(m)])
 
     ## Check the `check' method.
     me.assertTrue(xcls().hash(m).check(h0))
index 24823c65d2fccb993d1dc024d161b1959caa30a0..096e35b479134b2d9dfd0a9357a078155a36869b 100644 (file)
@@ -180,11 +180,10 @@ class TestWriteBuffer (U.TestCase):
     buf.putu8(0x00)
     putfn(buf, T.bytes_as_int(w, bigendp))
     buf.putu8(w + 1)
-    me.assertEqual(C.ByteString(buf), T.span(w + 2))
-    me.assertEqual(C.ByteString(putfn(C.WriteBuffer(), (1 << 8*w) - 1)),
+    me.assertEqual(buf.contents, T.span(w + 2))
+    me.assertEqual(putfn(C.WriteBuffer(), (1 << 8*w) - 1).contents,
                    w*C.bytes("ff"))
-    me.assertEqual(C.ByteString(putfn(C.WriteBuffer(), C.MP(0))),
-                   w*C.bytes("00"))
+    me.assertEqual(putfn(C.WriteBuffer(), C.MP(0)).contents, w*C.bytes("00"))
 
     ## Check overflow detection.
     me.assertRaises((OverflowError, ValueError),
@@ -196,8 +195,8 @@ class TestWriteBuffer (U.TestCase):
     ## Go through a number of different sizes.
     for n in [0, 1, 7, 8, 19, 255, 12345, 65535, 123456]:
       if n >= 1 << 8*w: continue
-      me.assertEqual(C.ByteString(putfn(C.WriteBuffer().putu8(0x00),
-                                        T.span(n)).putu8(0xff)),
+      me.assertEqual(putfn(C.WriteBuffer().putu8(0x00),
+                           T.span(n)).putu8(0xff).contents,
                      T.prep_lenseq(w, n, bigendp, True))
 
     ## Check blocks which are too large for the length prefix.
@@ -244,7 +243,7 @@ class TestWriteBuffer (U.TestCase):
     buf.zero(17)
     buf.put(T.span(23))
     me.assertEqual(buf.size, 40)
-    me.assertEqual(C.ByteString(buf), C.ByteString.zero(17) + T.span(23))
+    me.assertEqual(buf.contents, C.ByteString.zero(17) + T.span(23))
 
 ###----- That's all, folks --------------------------------------------------
 
index 6755e96b0196c32827fc9cd1ffd7311f645f3845..36a3f9f4af0b4bc7371cf2bcc67f082bf491d84c 100644 (file)
@@ -48,6 +48,7 @@ class TestByteString (U.TestCase):
     x = C.ByteString(T.bin("once upon a time there was a string"))
 
     ## Check that simple indexing works.
+    me.assertEqual(type(x[3]), C.ByteString)
     me.assertEqual(x[3], 'e')
     me.assertEqual(x[-5], 't')
 
@@ -55,7 +56,8 @@ class TestByteString (U.TestCase):
     x[34]; me.assertRaises(IndexError, lambda: x[35])
     x[-35]; me.assertRaises(IndexError, lambda: x[-36])
 
-    ## Check slicing.
+    ## Check slicing.  This should always give us bytes.
+    me.assertEqual(type(x[7:17]), C.ByteString)
     me.assertEqual(x[7:17], T.bin("on a time "))
 
     ## Complex slicing is also supported.
@@ -143,13 +145,18 @@ class TestByteString (U.TestCase):
     me.assertEqual(~x, C.bytes("fc5a03"))
 
     ## Concatenation.
+    me.assertEqual(type(x + y), C.ByteString)
     me.assertEqual(x + y, C.bytes("03a5fc5fac30"))
 
     ## Replication (asymmetric but commutative).
+    me.assertEqual(type(3*x), C.ByteString)
+    me.assertEqual(type(x*3), C.ByteString)
     me.assertEqual(3*x, C.bytes("03a5fc03a5fc03a5fc"))
     me.assertEqual(x*3, C.bytes("03a5fc03a5fc03a5fc"))
 
     ## Replication by zero (regression test).
+    me.assertEqual(type(0*x), C.ByteString)
+    me.assertEqual(type(x*0), C.ByteString)
     me.assertEqual(0*x, C.ByteString(T.bin("")))
     me.assertEqual(x*0, C.ByteString(T.bin("")))
 
index ef80b90b2b4bc11bdc7f8138294d9174614cd2a5..870297eee674ee26d97af2efc7dbfa240c6da5fa 100644 (file)
--- a/t/t-ec.py
+++ b/t/t-ec.py
@@ -194,21 +194,23 @@ class TestCurves (T.GenericTestMixin):
     Z1 = C.ByteString.zero(1)
     me.assertEqual(O.ec2osp(), Z1)
     me.assertEqual(E.os2ecp(Z1), (O, Z0))
-    t = C.ByteString(C.WriteBuffer()
-                       .putu8(0x04)
-                       .put(P.ix.storeb(k.noctets))
-                       .put(P.iy.storeb(k.noctets)))
+    t = C.WriteBuffer() \
+         .putu8(0x04) \
+         .put(P.ix.storeb(k.noctets)) \
+         .put(P.iy.storeb(k.noctets)) \
+         .contents
     me.assertEqual(P.ec2osp(), t)
-    me.assertEqual(C.ByteString(C.WriteBuffer().putecptraw(P)), t)
+    me.assertEqual(C.WriteBuffer().putecptraw(P).contents, t)
     me.assertEqual(E.os2ecp(t), (P, Z0))
     me.assertEqual(C.ReadBuffer(t).getecptraw(E), P)
     if isinstance(k, C.PrimeField): ybit = int(P.iy&1)
     else:
       try: ybit = int((P.y/P.x).value&C.GF(1))
       except ZeroDivisionError: ybit = 0
-    t = C.ByteString(C.WriteBuffer()
-                       .putu8(0x02 | ybit)
-                       .put(P.ix.storeb(k.noctets)))
+    t = C.WriteBuffer() \
+         .putu8(0x02 | ybit) \
+         .put(P.ix.storeb(k.noctets)) \
+         .contents
     me.assertEqual(P.ec2osp(C.EC_LSB), t)
     me.assertEqual(E.os2ecp(t, C.EC_LSB), (P, Z0))
 
index 203ca16a133b118b306c9010fd9cf61433dd0be3..e91de94be2a4cbf6c47cc867c22cf03265f4e9d6 100644 (file)
@@ -111,14 +111,14 @@ class TestGroups (T.GenericTestMixin):
 
       ## Encoding.
       t = y.tobuf()
-      me.assertEqual(t, C.ByteString(C.WriteBuffer().putmp(i)))
+      me.assertEqual(t, C.WriteBuffer().putmp(i).contents)
       me.assertEqual(G.frombuf(t)[0], y)
       me.assertEqual(id.tobuf(), C.bytes("000101"))
       t = y.toraw()
       me.assertEqual(t, i.storeb(G.noctets))
       me.assertEqual(G.fromraw(t)[0], y)
       me.assertEqual(id.toraw(),
-                     C.ByteString(C.WriteBuffer().zero(G.noctets - 1).putu8(1)))
+                     C.WriteBuffer().zero(G.noctets - 1).putu8(1).contents)
 
     ## String conversion.
     ystr = str(y)
diff --git a/util.c b/util.c
index 29f7d123269e934db11843d5db75498a4275ac1b..723c819cc82dd3e8daaa39b58085a7c965d449a4 100644 (file)
--- a/util.c
+++ b/util.c
@@ -737,13 +737,15 @@ end:
   return (rc);
 }
 
-static char *def_kwlist[] = { "key", "default", 0 };
+static const char *const def_kwlist[] = { "key", "default", 0 };
 
 PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = Py_None, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", def_kwlist, &k, &def))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get",
+                                  (/*unconst*/ char **)def_kwlist,
+                                  &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
   PyErr_Clear();
@@ -755,7 +757,8 @@ PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
   PyObject *k, *def = Py_None, *v;
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
-                                  def_kwlist, &k, &def))
+                                  (/*unconst*/ char **)def_kwlist,
+                                  &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
   PyErr_Clear();
@@ -767,7 +770,9 @@ PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = 0, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", def_kwlist, &k, &def))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop",
+                                  (/*unconst*/ char **)def_kwlist,
+                                  &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) {
     PyObject_DelItem(me, k);