From 385caa2fd0f72e17e35eb9c28b6233c1a5197e88 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 14 Oct 2019 16:27:51 +0100 Subject: [PATCH] algorithms.c: Implement KMAC in C. It's longer this way, but significantly less ugly, and it works consistently with the other algorithms. --- algorithms.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++ catacomb/__init__.py | 106 +------------------------ t/t-algorithms.py | 16 ++-- 3 files changed, 224 insertions(+), 113 deletions(-) diff --git a/algorithms.c b/algorithms.c index 57a41d7..4c8158d 100644 --- a/algorithms.c +++ b/algorithms.c @@ -3347,6 +3347,215 @@ static const PyTypeObject shake256_pytype_skel = { 0 /* @tp_is_gc@ */ }; +static PyTypeObject *kmac_pytype, *kmac128_pytype, *kmac256_pytype; + +static PyObject *kmac_dopynew(void (*initfn)(shake_ctx *, + const void *, size_t, + const void *, size_t), + PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + shake_pyobj *rc = 0; + PyObject *pobj = Py_None; + struct bin k = { 0, 0 }, p = { 0, 0 }; + static const char *const kwlist[] = { "key", "perso", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:new", KWLIST, + convbin, &k, &pobj)) + goto end; + if (pobj != Py_None && !convbin(pobj, &p)) goto end; + rc = (shake_pyobj *)ty->tp_alloc(ty, 0); + initfn(&rc->h, p.p, p.sz, k.p, k.sz); + rc->st = 0; +end: + return ((PyObject *)rc); +} + +static PyObject *kmac128_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) + { return (kmac_dopynew(kmac128_init, ty, arg, kw)); } + +static PyObject *kmac256_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) + { return (kmac_dopynew(kmac256_init, ty, arg, kw)); } + +static PyObject *kmacmeth_xof(PyObject *me) +{ + if (shake_check(me, 0)) goto end; + kmac_xof(SHAKE_H(me)); + SHAKE_ST(me) = 1; + RETURN_ME; +end: + return (0); +} + +static PyObject *kmacmeth_done(PyObject *me, PyObject *arg, PyObject *kw) +{ + PyObject *rc = 0; + size_t n = 100 - SHAKE_H(me)->h.r/2; + static const char *const kwlist[] = { "hsz", 0 }; + if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:done", KWLIST, convszt, &n)) + goto end; + if (shake_check(me, 0)) goto end; + rc = bytestring_pywrap(0, n); + kmac_done(SHAKE_H(me), BIN_PTR(rc), n); + SHAKE_ST(me) = -1; +end: + return (rc); +} + +static const PyMethodDef kmac_pymethods[] = { +#define METHNAME(func) kmacmeth_##func + NAMETH(xof, "K.xof()") + KWMETH(done, "K.done([hsz = CAP/2]) -> T") +#undef METHNAME + { 0 } +}; + +static const PyTypeObject kmac_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "KMAC", /* @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@ */ + "KMAC base class.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + PYMETHODS(kmac), /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static const PyTypeObject kmac128_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "KMAC128", /* @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@ */ + "KMAC128(KEY, [perso = STR]): KMAC XOMAC.", + + 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@ */ + kmac128_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static const PyTypeObject kmac256_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "KMAC256", /* @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@ */ + "KMAC256(KEY, [perso = STR]): KMAC XOMAC.", + + 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@ */ + kmac256_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + /*----- Pseudorandom permutations -----------------------------------------*/ static PyTypeObject *gcprp_pytype, *gprp_pytype; @@ -3640,6 +3849,9 @@ void algorithms_pyinit(void) INITTYPE(shake, root); INITTYPE(shake128, shake); INITTYPE(shake256, shake); + INITTYPE(kmac, shake); + INITTYPE(kmac128, kmac); + INITTYPE(kmac256, kmac); INITTYPE(gcprp, type); INITTYPE(gprp, root); addmethods(methods); @@ -3693,6 +3905,9 @@ void algorithms_pyinsert(PyObject *mod) INSERT("Shake", shake_pytype); INSERT("Shake128", shake128_pytype); INSERT("Shake256", shake256_pytype); + INSERT("KMAC", kmac_pytype); + INSERT("KMAC128", kmac128_pytype); + INSERT("KMAC256", kmac256_pytype); INSERT("GCPRP", gcprp_pytype); INSERT("GPRP", gprp_pytype); INSERT("gcprps", make_algtab(gprptab, sizeof(gcprp *), diff --git a/catacomb/__init__.py b/catacomb/__init__.py index 26463b4..d59d3f3 100644 --- a/catacomb/__init__.py +++ b/catacomb/__init__.py @@ -250,115 +250,13 @@ 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 _check_range(me, n, max): - if not (0 <= n <= max): raise OverflowError("out of range") - def hashu8(me, n): - me._check_range(n, 0xff) - return me.hash(_pack('B', n)) - def hashu16l(me, n): - me._check_range(n, 0xffff) - return me.hash(_pack('H', n)) - hashu16 = hashu16b - def hashu32l(me, n): - me._check_range(n, 0xffffffff) - return me.hash(_pack('L', n)) - hashu32 = hashu32b - def hashu64l(me, n): - me._check_range(n, 0xffffffffffffffff) - return me.hash(_pack('Q', n)) - hashu64 = hashu64b - def hashbuf8(me, s): return me.hashu8(len(s)).hash(s) - def hashbuf16l(me, s): return me.hashu16l(len(s)).hash(s) - 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 = _bin(''), *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__._bare_new(); new._copy(me); return new - def _copy(me, other): me._h = other._h.copy() - def hash(me, m): me._h.hash(m); return me - def xof(me): me._h.xof(); return me - def get(me, n): return me._h.get(n) - def 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 - @classmethod - def _bare_new(cls): return cls() - 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.zero(200) - -class KMAC (_ShakeBase): - _FUNC = _bin('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() - @classmethod - def _bare_new(cls): return cls(_bin("")) -class KMAC128 (KMAC): _SHAKE = Shake128; _TAGSZ = 16 -class KMAC256 (KMAC): _SHAKE = Shake256; _TAGSZ = 32 +KMAC128.keysz = KeySZAny(16); KMAC128.tagsz = 16 +KMAC256.keysz = KeySZAny(32); KMAC256.tagsz = 32 ###-------------------------------------------------------------------------- ### NaCl `secretbox'. diff --git a/t/t-algorithms.py b/t/t-algorithms.py index aa8567b..8346fad 100644 --- a/t/t-algorithms.py +++ b/t/t-algorithms.py @@ -95,7 +95,7 @@ class HashBufferTestMixin (U.TestCase): if w <= 3: n = 1 << 8*w h0, _ = makefn(w + n) - me.assertRaises((ValueError, OverflowError, TypeError), + me.assertRaises((ValueError, TypeError), hashfn, h0, C.ByteString.zero(n)) def check_hashbuffer(me, makefn): @@ -106,10 +106,9 @@ class HashBufferTestMixin (U.TestCase): me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16(n)) me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16b(n)) me.check_hashbuffer_hashn(2, False, makefn, lambda h, n: h.hashu16l(n)) - if hasattr(makefn(0)[0], "hashu24"): - me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24(n)) - me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24b(n)) - me.check_hashbuffer_hashn(3, False, makefn, lambda h, n: h.hashu24l(n)) + me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24(n)) + me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24b(n)) + me.check_hashbuffer_hashn(3, False, makefn, lambda h, n: h.hashu24l(n)) me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32(n)) me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32b(n)) me.check_hashbuffer_hashn(4, False, makefn, lambda h, n: h.hashu32l(n)) @@ -123,10 +122,9 @@ class HashBufferTestMixin (U.TestCase): me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16(x)) me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16b(x)) me.check_hashbuffer_bufn(2, False, makefn, lambda h, x: h.hashbuf16l(x)) - if hasattr(makefn(0)[0], "hashbuf24"): - me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24(x)) - me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24b(x)) - me.check_hashbuffer_bufn(3, False, makefn, lambda h, x: h.hashbuf24l(x)) + me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24(x)) + me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24b(x)) + me.check_hashbuffer_bufn(3, False, makefn, lambda h, x: h.hashbuf24l(x)) me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32(x)) me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32b(x)) me.check_hashbuffer_bufn(4, False, makefn, lambda h, x: h.hashbuf32l(x)) -- 2.11.0