From: Mark Wooding Date: Sun, 16 Jul 2017 12:04:18 +0000 (+0100) Subject: kalyna-python.c, setup.py: Actually implement the bindings. X-Git-Tag: 0.1.0^0 X-Git-Url: https://git.distorted.org.uk/~mdw/kalyna-python/commitdiff_plain/65f8a434cecc3e80ab447909453ed9fe1af93b42 kalyna-python.c, setup.py: Actually implement the bindings. --- diff --git a/kalyna-python.c b/kalyna-python.c index 3de4bfe..7def357 100644 --- a/kalyna-python.c +++ b/kalyna-python.c @@ -28,20 +28,228 @@ #include "Python.h" +#include "kalyna.h" + /*----- Utilities ---------------------------------------------------------*/ +#define EXCERR(exc, str) do { \ + PyErr_SetString(exc, str); \ + goto end; \ +} while (0) +#define VALERR(str) EXCERR(PyExc_ValueError, str) + #define INSERT(name, ob) do { \ PyObject *_o = (PyObject *)(ob); \ Py_INCREF(_o); \ PyModule_AddObject(mod, name, _o); \ } while (0) +#define FREEOBJ(obj) \ + (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj))) + +int convulong(PyObject *o, void *pp) +{ + long i; + unsigned long *p = pp; + PyObject *t; + + if (!o) VALERR("can't delete"); + if (PyInt_Check(o)) { + i = PyInt_AS_LONG(o); + if (i < 0) VALERR("must be nonnegative"); + *p = i; + } else { + if ((t = PyNumber_Long(o)) == 0) goto end; + *p = PyLong_AsUnsignedLong(t); + Py_DECREF(t); + if (PyErr_Occurred()) goto end; + } + return (1); +end: + return (0); +} + +static int convszt(PyObject *o, void *pp) +{ + unsigned long u; + size_t *p = pp; + + if (!convulong(o, &u)) goto end; + if (u > ~(size_t)0) VALERR("out of range"); + *p = u; + return (1); +end: + return (0); +} + +static inline void store64(void *p, uint64_t x) +{ + unsigned char *pp = p; + + pp[0] = (x >> 0)&0xff; pp[1] = (x >> 8)&0xff; + pp[2] = (x >> 16)&0xff; pp[3] = (x >> 24)&0xff; + pp[4] = (x >> 32)&0xff; pp[5] = (x >> 40)&0xff; + pp[6] = (x >> 48)&0xff; pp[7] = (x >> 56)&0xff; +} + +static inline uint64_t load64(const void *p) +{ + const unsigned char *pp = p; + + return ((((uint64_t )pp[0]&0xff) << 0) | (((uint64_t )pp[1]&0xff) << 8) | + (((uint64_t )pp[2]&0xff) << 16) | (((uint64_t )pp[3]&0xff) << 24) | + (((uint64_t )pp[4]&0xff) << 32) | (((uint64_t )pp[5]&0xff) << 40) | + (((uint64_t )pp[6]&0xff) << 48) | (((uint64_t )pp[7]&0xff) << 56)); +} + +/*----- The Kalyna block cipher -------------------------------------------*/ + +static PyTypeObject kalyna_pytype; + +typedef struct kalyna_pyobj { + PyObject_HEAD + kalyna_t *k; +} kalyna_pyobj; + +static PyObject *kalyna_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + char *kwlist[] = { "key", "blksz", 0 }; + size_t blksz; + char *k; + Py_ssize_t ksz; + kalyna_t *kk; + kalyna_pyobj *rc = 0; + size_t i; + uint64_t k64[8]; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&:new", kwlist, + &k, &ksz, convszt, &blksz)) + goto end; + switch (blksz) { + case 16: case 32: case 64: break; + default: VALERR("bad block size"); + } + if ((ksz != blksz && ksz != 2*blksz) || ksz > 64) + VALERR("bad key length"); + + kk = KalynaInit(8*blksz, 8*ksz); if (!kk) VALERR("unknown error"); + for (i = 0; i < kk->nk; i++) k64[i] = load64(k + 8*i); + KalynaKeyExpand(k64, kk); + rc = PyObject_NEW(kalyna_pyobj, ty); + rc->k = kk; +end: + return ((PyObject *)rc); +} + +static void kalyna_pydealloc(PyObject *me) +{ + kalyna_pyobj *kobj = (kalyna_pyobj *)me; + KalynaDelete(kobj->k); + FREEOBJ(kobj); +} + +static PyObject *kalynameth_encrypt(PyObject *me, PyObject *arg) +{ + char *p, *q; + Py_ssize_t psz; + PyObject *rc = 0; + kalyna_pyobj *kobj = (kalyna_pyobj *)me; + size_t i; + uint64_t p64[8]; + + if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &psz)) goto end; + if (psz != 8*kobj->k->nb) VALERR("incorrect block size"); + rc = PyString_FromStringAndSize(0, psz); q = PyString_AS_STRING(rc); + for (i = 0; i < kobj->k->nb; i++) p64[i] = load64(p + 8*i); + KalynaEncipher(p64, kobj->k, p64); + for (i = 0; i < kobj->k->nb; i++) store64(q + 8*i, p64[i]); +end: + return (rc); +} + +static PyObject *kalynameth_decrypt(PyObject *me, PyObject *arg) +{ + char *p, *q; + Py_ssize_t psz; + PyObject *rc = 0; + kalyna_pyobj *kobj = (kalyna_pyobj *)me; + size_t i; + uint64_t p64[8]; + + if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &psz)) goto end; + if (psz != 8*kobj->k->nb) VALERR("incorrect block size"); + rc = PyString_FromStringAndSize(0, psz); q = PyString_AS_STRING(rc); + for (i = 0; i < kobj->k->nb; i++) p64[i] = load64(p + 8*i); + KalynaDecipher(p64, kobj->k, p64); + for (i = 0; i < kobj->k->nb; i++) store64(q + 8*i, p64[i]); +end: + return (rc); +} + +static PyMethodDef kalyna_pymethods[] = { + { "encrypt", kalynameth_encrypt, METH_VARARGS, + "encrypt(PTBLK) -> CTBLK" }, + { "decrypt", kalynameth_decrypt, METH_VARARGS, + "decrypt(CTBLK) -> PTBLK" }, + { 0 } +}; + +static PyTypeObject kalyna_pytype = { + PyObject_HEAD_INIT(0) 0, /* Header */ + "Kalyna", /* @tp_name@ */ + sizeof(kalyna_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + kalyna_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@ */ +"Kalyna block cipher instance.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + kalyna_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@ */ + kalyna_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + /*----- Main code ---------------------------------------------------------*/ void initkalyna(void) { PyObject *mod = Py_InitModule("kalyna", 0); + if (PyType_Ready(&kalyna_pytype) < 0) return; INSERT("version", PyString_FromString(VERSION)); + INSERT("Kalyna", &kalyna_pytype); } /*----- That's all, folks -------------------------------------------------*/ diff --git a/setup.py b/setup.py index a315063..699cd90 100755 --- a/setup.py +++ b/setup.py @@ -3,14 +3,18 @@ import distutils.core as DC import mdwsetup as MS -cat = DC.Extension('kalyna', ['kalyna-python.c'], - extra_compile_args = ['-DVERSION="%s"' % +cat = DC.Extension('kalyna', ['kalyna-python.c', + 'ref/kalyna.c', 'ref/tables.c'], + extra_compile_args = ['-Iref/', + '-DVERSION="%s"' % MS.auto_version()]) MS.setup(name = 'kalyna-python', description = 'Python binding to Kalyna reference implementation', url = 'https://git.distorted.org.uk/~mdw/kalyna-python/', - author = 'Mark Wooding', + author = 'Mark Wooding (Python); ' + 'Ruslan Kiianchuk, Ruslan Mordvinov, Roman Oliynykov + (reference implementation)', author_email = 'mdw@distorted.org.uk', license = 'Alas unclear', ext_modules = [cat])