Add support for SHA3 and related algorithms.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 11 May 2017 09:42:15 +0000 (10:42 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 14 May 2017 03:29:20 +0000 (04:29 +0100)
This comes in three tranches.

  * There are the basic generic-interface algorithms for SHA3-*, SHAKE*,
    KMAC*, etc., which basically just turn up by themselves, and the
    RNGs based on SHAKE and KMAC which took a little more work.

  * There's a full implementation of the cSHAKE128 and cSHAKE256 XOFs as
    a new kind of object.

  * Based on this, there's a full KMAC implementation, with the fiddly
    bits in Python (but all the heavy lifting is done in C), with
    variable-length tag and everything.  Other constructions, e.g.,
    TupleHash, can easily be made in the same way.

Annoyingly, KMAC can't just be made from SHAKE by multiple inheritance
because Python gets confused about how it's supposed to construct the
objects, and, in particular, which `__new__' methods are OK to use.  It
seems that the relevant code is trying to use the `HEAPTYPE' flag as a
proxy for whether a type is implemented in C, which doesn't work for our
classes.  So there's a bunch of ugly delegation to do.

algorithms.c
algorithms.py
catacomb-python.h
catacomb/__init__.py
rand.c

index e4233fa..0f05f75 100644 (file)
@@ -1745,6 +1745,351 @@ static PyTypeObject kxvik_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
+static PyTypeObject *shake_pytype, *shake128_pytype, *shake256_pytype;
+
+typedef struct shake_pyobj {
+  PyObject_HEAD
+  int st;
+  shake_ctx h;
+} shake_pyobj;
+
+#define SHAKE_H(o) (&((shake_pyobj *)(o))->h)
+#define SHAKE_ST(o) (((shake_pyobj *)(o))->st)
+
+static PyObject *shake_dopynew(void (*initfn)(shake_ctx *,
+                                             const void *, size_t,
+                                             const void *, size_t),
+                              PyTypeObject *ty,
+                              PyObject *arg, PyObject *kw)
+{
+  shake_pyobj *rc = 0;
+  char *p = 0, *f = 0;
+  Py_ssize_t psz = 0, fsz = 0;
+  char *kwlist[] = { "perso", "func", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|s#s#:new", kwlist,
+                                  &p, &psz, &f, &fsz))
+    goto end;
+  rc = (shake_pyobj *)ty->tp_alloc(ty, 0);
+  initfn(&rc->h, f, fsz, p, psz);
+  rc->st = 0;
+end:
+  return ((PyObject *)rc);
+}
+
+static PyObject *shake128_pynew(PyTypeObject *ty,
+                               PyObject *arg, PyObject *kw)
+  { return (shake_dopynew(cshake128_init, ty, arg, kw)); }
+
+static PyObject *shake256_pynew(PyTypeObject *ty,
+                               PyObject *arg, PyObject *kw)
+  { return (shake_dopynew(cshake256_init, ty, arg, kw)); }
+
+static int shake_check(PyObject *me, int st)
+{
+  if (SHAKE_ST(me) != st) VALERR("wrong state");
+  return (0);
+end:
+  return (-1);
+}
+
+static PyObject *shakemeth_hash(PyObject *me, PyObject *arg)
+{
+  char *p;
+  Py_ssize_t sz;
+  if (!PyArg_ParseTuple(arg, "s#:hash", &p, &sz)) return (0);
+  if (shake_check(me, 0)) return (0);
+  shake_hash(SHAKE_H(me), p, sz);
+  RETURN_ME;
+}
+
+#define SHAKEMETH_HASHU_(n, W, w)                                      \
+  static PyObject *shakemeth_hashu##w(PyObject *me, PyObject *arg)     \
+  {                                                                    \
+    uint##n x;                                                         \
+    octet b[SZ_##W];                                                   \
+    if (!PyArg_ParseTuple(arg, "O&:hashu" #w, convu##n, &x)) goto end; \
+    if (shake_check(me, 0)) goto end;                                  \
+    STORE##W(b, x); shake_hash(SHAKE_H(me), b, sizeof(b));             \
+    RETURN_ME;                                                         \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTCONV(SHAKEMETH_HASHU_)
+
+#define SHAKEMETH_HASHBUF_(n, W, w)                                    \
+  static PyObject *shakemeth_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");                                \
+    if (shake_check(me, 0)) goto end;                                  \
+    STORE##W(b, sz); shake_hash(SHAKE_H(me), b, sizeof(b));            \
+    shake_hash(SHAKE_H(me), p, sz);                                    \
+    RETURN_ME;                                                         \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+DOUINTCONV(SHAKEMETH_HASHBUF_)
+
+static PyObject *shakemeth_hashstrz(PyObject *me, PyObject *arg)
+{
+  char *p;
+  if (!PyArg_ParseTuple(arg, "s:hashstrz", &p)) return (0);
+  if (shake_check(me, 0)) return (0);
+  shake_hash(SHAKE_H(me), p, strlen(p) + 1);
+  RETURN_ME;
+}
+
+static PyObject *shakemeth_xof(PyObject *me, PyObject *arg)
+{
+  if (!PyArg_ParseTuple(arg, ":xof")) goto end;
+  if (shake_check(me, 0)) goto end;
+  shake_xof(SHAKE_H(me));
+  SHAKE_ST(me) = 1;
+  RETURN_ME;
+end:
+  return (0);
+}
+
+static PyObject *shakemeth_done(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  size_t n;
+  if (!PyArg_ParseTuple(arg, "O&:done", convszt, &n)) goto end;
+  if (shake_check(me, 0)) goto end;
+  rc = bytestring_pywrap(0, n);
+  shake_done(SHAKE_H(me), PyString_AS_STRING(rc), n);
+  SHAKE_ST(me) = -1;
+end:
+  return (rc);
+}
+
+static PyObject *shakemeth_copy(PyObject *me, PyObject *arg)
+{
+  shake_pyobj *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, ":copy")) goto end;
+  rc = PyObject_NEW(shake_pyobj, me->ob_type);
+  rc->h = *SHAKE_H(me);
+  rc->st = SHAKE_ST(me);
+end:
+  return ((PyObject *)me);
+}
+
+static PyObject *shakemeth_get(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  size_t sz;
+
+  if (!PyArg_ParseTuple(arg, "O&:get", convszt, &sz)) goto end;
+  if (shake_check(me, 1)) goto end;
+  rc = bytestring_pywrap(0, sz);
+  shake_get(SHAKE_H(me), PyString_AS_STRING(rc), sz);
+end:
+  return (rc);
+}
+
+static PyObject *shakemeth_mask(PyObject *me, PyObject *arg)
+{
+  PyObject *rc = 0;
+  char *p; Py_ssize_t sz;
+
+  if (!PyArg_ParseTuple(arg, "s#:mask", &p, &sz)) goto end;
+  if (shake_check(me, 1)) goto end;
+  rc = bytestring_pywrap(0, sz);
+  shake_mask(SHAKE_H(me), p, PyString_AS_STRING(rc), sz);
+end:
+  return (rc);
+}
+
+static PyObject *shakeget_rate(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(SHAKE_H(me)->h.r)); }
+
+static PyObject *shakeget_buffered(PyObject *me, void *hunoz)
+  { return (PyInt_FromLong(SHAKE_H(me)->h.n)); }
+
+static PyObject *shakeget_state(PyObject *me, void *hunoz)
+{
+  int st = SHAKE_ST(me);
+  return (PyString_FromString(st == 0 ? "absorb" :
+                             st == 1 ? "squeeze" : "dead"));
+}
+
+static PyGetSetDef shake_pygetset[] = {
+#define GETSETNAME(op, name) shake##op##_##name
+  GET  (rate,                  "S.rate -> rate, in bytes")
+  GET  (buffered,              "S.buffered -> amount currently buffered")
+  GET  (state,                 "S.state -> `absorb', `squeeze', `dead'")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyMethodDef shake_pymethods[] = {
+#define METHNAME(func) shakemeth_##func
+  METH  (copy,                 "S.copy() -> SS")
+  METH (hash,                  "S.hash(M)")
+#define METHU_(n, W, w) METH(hashu##w, "S.hashu" #w "(WORD)")
+  DOUINTCONV(METHU_)
+#undef METHU_
+#define METHBUF_(n, W, w) METH(hashbuf##w, "S.hashbuf" #w "(BYTES)")
+  DOUINTCONV(METHBUF_)
+#undef METHBUF_
+  METH (hashstrz,              "S.hashstrz(STRING)")
+  METH (xof,                   "S.xof()")
+  METH (done,                  "S.done(LEN) ->H")
+  METH (get,                   "S.get(LEN) -> H")
+  METH (mask,                  "S.mask(M) -> C")
+#undef METHNAME
+  { 0 }
+};
+
+static PyTypeObject shake_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "Shake",                             /* @tp_name@ */
+  sizeof(shake_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@ */
+"SHAKE/cSHAKE base class.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  shake_pymethods,                     /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  shake_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 shake128_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "Shake128",                          /* @tp_name@ */
+  0,                                   /* @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@ */
+"SHAKE128/cSHAKE128 XOF.",
+
+  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@ */
+  shake128_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyTypeObject shake256_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "Shake256",                          /* @tp_name@ */
+  0,                                   /* @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@ */
+"SHAKE256/cSHAKE256 XOF.",
+
+  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@ */
+  shake256_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
 /*----- Pseudorandom permutations -----------------------------------------*/
 
 static PyTypeObject *gcprp_pytype, *gprp_pytype;
@@ -2042,6 +2387,9 @@ void algorithms_pyinit(void)
   INITTYPE_META(poly1305key, type, poly1305cls);
   INITTYPE(poly1305hash, root);
   INITTYPE(kxvik, root);
+  INITTYPE(shake, root);
+  INITTYPE(shake128, shake);
+  INITTYPE(shake256, shake);
   INITTYPE(gcprp, type);
   INITTYPE(gprp, root);
   addmethods(methods);
@@ -2076,6 +2424,9 @@ void algorithms_pyinsert(PyObject *mod)
   INSERT("poly1305", poly1305key_pytype);
   INSERT("Poly1305Hash", poly1305hash_pytype);
   INSERT("Keccak1600", kxvik_pytype);
+  INSERT("Shake", shake_pytype);
+  INSERT("Shake128", shake128_pytype);
+  INSERT("Shake256", shake256_pytype);
   INSERT("GCPRP", gcprp_pytype);
   INSERT("GPRP", gprp_pytype);
   INSERT("gcprps", gcprps());
index 4187c46..0fa434a 100644 (file)
@@ -43,6 +43,7 @@ md2 md4 md5 tiger has160
 sha sha224 sha256 sha512/224 sha512/256 sha384 sha512
 rmd128 rmd160 rmd256 rmd320
 whirlpool whirlpool256
+sha3-224 sha3-256 sha3-384 sha3-512
 '''.split()
 hmodes = '''
 mgf hmac
@@ -95,5 +96,12 @@ for i in latindances:
          'RNG_LATIN, %(ROOT)s_NONCESZ) \\') % \
       {'name': i, 'id': i.translate(None, '/').replace('-', '_'),
        'root': root, 'ROOT': root.upper()}
+for i in [128, 256]:
+  print ('\t_("shake%(w)d", shake%(w)d_keysz, cshake%(w)d_rand, ' +
+         'RNG_SHAKE, 0) \\') % \
+         {'w': i}
+  print ('\t_("kmac%(w)d", kmac%(w)d_keysz, kmac%(w)d_rand, ' +
+         'RNG_KMAC, 0) \\') % \
+         {'w': i}
 print '\t/* end */'
 print
index 3b9d8f7..65f2ed1 100644 (file)
@@ -70,6 +70,7 @@
 #include <catacomb/sha-mgf.h>
 #include <catacomb/sha-hmac.h>
 #include <catacomb/keccak1600.h>
+#include <catacomb/sha3.h>
 
 #include <catacomb/mp.h>
 #include <catacomb/mpint.h>
index d4eac30..7ea8d57 100644 (file)
 ### along with Catacomb/Python; if not, write to the Free Software Foundation,
 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+from __future__ import with_statement
+
 import _base
 import types as _types
 from binascii import hexlify as _hexify, unhexlify as _unhexify
+from contextlib import contextmanager as _ctxmgr
 from sys import argv as _argv
+from struct import pack as _pack
 
 ###--------------------------------------------------------------------------
 ### Basic stuff.
@@ -160,6 +164,96 @@ class _tmp:
 _augment(GHash, _tmp)
 _augment(Poly1305Hash, _tmp)
 
+class _HashBase (object):
+  ## The standard hash methods.  Assume that `hash' is defined and returns
+  ## the receiver.
+  def hashu8(me, n): return me.hash(_pack('B', n))
+  def hashu16l(me, n): return me.hash(_pack('<H', n))
+  def hashu16b(me, n): return me.hash(_pack('>H', n))
+  hashu16 = hashu16b
+  def hashu32l(me, n): return me.hash(_pack('<L', n))
+  def hashu32b(me, n): return me.hash(_pack('>L', n))
+  hashu32 = hashu32b
+  def hashu64l(me, n): return me.hash(_pack('<Q', n))
+  def hashu64b(me, n): return me.hash(_pack('>Q', n))
+  hashu64 = hashu64b
+  def hashbuf8(me, s): return me.hashu8(len(s)).hash(s)
+  def hashbuf16l(me, s): return me.hashu16l(len(s)).hash(s)
+  def hashbuf16b(me, s): return me.hashu16b(len(s)).hash(s)
+  hashbuf16 = hashbuf16b
+  def hashbuf32l(me, s): return me.hashu32l(len(s)).hash(s)
+  def hashbuf32b(me, s): return me.hashu32b(len(s)).hash(s)
+  hashbuf32 = hashbuf32b
+  def hashbuf64l(me, s): return me.hashu64l(len(s)).hash(s)
+  def hashbuf64b(me, s): return me.hashu64b(len(s)).hash(s)
+  hashbuf64 = hashbuf64b
+  def hashstrz(me, s): return me.hash(s).hashu8(0)
+
+class _ShakeBase (_HashBase):
+
+  ## Python gets really confused if I try to augment `__new__' on native
+  ## classes, so wrap and delegate.  Sorry.
+  def __init__(me, perso = '', *args, **kw):
+    super(_ShakeBase, me).__init__(*args, **kw)
+    me._h = me._SHAKE(perso = perso, func = me._FUNC)
+
+  ## Delegate methods...
+  def copy(me): new = me.__class__(); new._copy(me)
+  def _copy(me, other): me._h = other._h
+  def hash(me, m): me._h.hash(m); return me
+  def xof(me): me._h.xof(); return me
+  def get(me, n): return me._h.get(n)
+  def mask(me, m): return me._h.mask(m)
+  def done(me, n): return me._h.done(n)
+  def check(me, h): return ctstreq(h, me.done(len(h)))
+  @property
+  def state(me): return me._h.state
+  @property
+  def buffered(me): return me._h.buffered
+  @property
+  def rate(me): return me._h.rate
+
+class _tmp:
+  def check(me, h):
+    return ctstreq(h, me.done(len(h)))
+  def leftenc(me, n):
+    nn = MP(n).storeb()
+    return me.hashu8(len(nn)).hash(nn)
+  def rightenc(me, n):
+    nn = MP(n).storeb()
+    return me.hash(nn).hashu8(len(nn))
+  def stringenc(me, str):
+    return me.leftenc(8*len(str)).hash(str)
+  def bytepad_before(me):
+    return me.leftenc(me.rate)
+  def bytepad_after(me):
+    if me.buffered: me.hash(me._Z[:me.rate - me.buffered])
+    return me
+  @_ctxmgr
+  def bytepad(me):
+    me.bytepad_before()
+    yield me
+    me.bytepad_after()
+_augment(Shake, _tmp)
+_augment(_ShakeBase, _tmp)
+Shake._Z = _ShakeBase._Z = ByteString(200*'\0')
+
+class KMAC (_ShakeBase):
+  _FUNC = 'KMAC'
+  def __init__(me, k, *arg, **kw):
+    super(KMAC, me).__init__(*arg, **kw)
+    with me.bytepad(): me.stringenc(k)
+  def done(me, n = -1):
+    if n < 0: n = me._TAGSZ
+    me.rightenc(8*n)
+    return super(KMAC, me).done(n)
+  def xof(me):
+    me.rightenc(0)
+    return super(KMAC, me).xof()
+
+class KMAC128 (KMAC): _SHAKE = Shake128; _TAGSZ = 16
+class KMAC256 (KMAC): _SHAKE = Shake256; _TAGSZ = 32
+
 ###--------------------------------------------------------------------------
 ### NaCl `secretbox'.
 
diff --git a/rand.c b/rand.c
index 5402737..6fe78bc 100644 (file)
--- a/rand.c
+++ b/rand.c
@@ -583,6 +583,12 @@ static PyTypeObject *gccrand_pytype, *gcrand_pytype, *gclatinrand_pytype;
 typedef grand *gcrand_func(const void *, size_t sz);
 typedef grand *gcirand_func(const void *, size_t sz, uint32);
 typedef grand *gcnrand_func(const void *, size_t sz, const void *);
+typedef grand *gcshakerand_func(const void *, size_t,
+                               const void *, size_t,
+                               const void *, size_t);
+typedef grand *gcshafuncrand_func(const void *, size_t,
+                                 const void *, size_t);
+typedef grand *gckmacrand_func(const void *, size_t, const void *, size_t);
 typedef struct gccrand_info {
   const char *name;
   const octet *keysz;
@@ -671,6 +677,34 @@ end:
   return (0);
 }
 
+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 };
+  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,
+                                      &k, &ksz, &f, &fsz, &p, &psz)
+       : !PyArg_ParseTupleAndKeywords(arg, kw, "s#|s#:new", kwlist_func,
+                                      &k, &ksz, &p, &psz))
+    goto end;
+  if (keysz(ksz, info->keysz) != ksz) VALERR("bad key length");
+  return (grand_dopywrap(ty,
+                        (info->f&RNGF_MASK) == RNG_SHAKE
+                          ? ((gcshakerand_func *)info->func)(f, fsz,
+                                                             p, psz,
+                                                             k, ksz)
+                          : ((gcshafuncrand_func *)info->func)(p, psz,
+                                                               k, ksz),
+                        f_freeme));
+end:
+  return (0);
+}
+
 static PyObject *gccrand_pywrap(const gccrand_info *info)
 {
   gccrand_pyobj *g = newtype(gccrand_pytype, 0, info->name);
@@ -689,6 +723,8 @@ static PyObject *gccrand_pywrap(const gccrand_info *info)
   switch (info->f&RNGF_MASK) {
     case RNG_LATIN: g->ty.ht_type.tp_new = gcnrand_pynew; break;
     case RNG_SEAL: g->ty.ht_type.tp_new = gcirand_pynew; break;
+    case RNG_SHAKE: case RNG_KMAC:
+      g->ty.ht_type.tp_new = gcshakyrand_pynew; break;
     default: g->ty.ht_type.tp_new = gcrand_pynew; break;
   }
   typeready(&g->ty.ht_type);