chiark / gitweb /
kalyna-python.c, setup.py: Actually implement the bindings.
[kalyna-python] / kalyna-python.c
1 /* -*-c-*-
2  *
3  * Python binding to Kalyna reference implementation
4  *
5  * (c) 2017 Mark Wooding
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  */
24
25 /*----- Header files ------------------------------------------------------*/
26
27 #define PY_SSIZE_T_CLEAN
28
29 #include "Python.h"
30
31 #include "kalyna.h"
32
33 /*----- Utilities ---------------------------------------------------------*/
34
35 #define EXCERR(exc, str) do {                                           \
36   PyErr_SetString(exc, str);                                            \
37   goto end;                                                             \
38 } while (0)
39 #define VALERR(str) EXCERR(PyExc_ValueError, str)
40
41 #define INSERT(name, ob) do {                                           \
42   PyObject *_o = (PyObject *)(ob);                                      \
43   Py_INCREF(_o);                                                        \
44   PyModule_AddObject(mod, name, _o);                                    \
45 } while (0)
46
47 #define FREEOBJ(obj)                                                    \
48   (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj)))
49
50 int convulong(PyObject *o, void *pp)
51 {
52   long i;
53   unsigned long *p = pp;
54   PyObject *t;
55
56   if (!o) VALERR("can't delete");
57   if (PyInt_Check(o)) {
58     i = PyInt_AS_LONG(o);
59     if (i < 0) VALERR("must be nonnegative");
60     *p = i;
61   } else {
62     if ((t = PyNumber_Long(o)) == 0) goto end;
63     *p = PyLong_AsUnsignedLong(t);
64     Py_DECREF(t);
65     if (PyErr_Occurred()) goto end;
66   }
67   return (1);
68 end:
69   return (0);
70 }
71
72 static int convszt(PyObject *o, void *pp)
73 {
74   unsigned long u;
75   size_t *p = pp;
76
77   if (!convulong(o, &u)) goto end;
78   if (u > ~(size_t)0) VALERR("out of range");
79   *p = u;
80   return (1);
81 end:
82   return (0);
83 }
84
85 static inline void store64(void *p, uint64_t x)
86 {
87   unsigned char *pp = p;
88
89   pp[0] = (x >>  0)&0xff; pp[1] = (x >>  8)&0xff;
90   pp[2] = (x >> 16)&0xff; pp[3] = (x >> 24)&0xff;
91   pp[4] = (x >> 32)&0xff; pp[5] = (x >> 40)&0xff;
92   pp[6] = (x >> 48)&0xff; pp[7] = (x >> 56)&0xff;
93 }
94
95 static inline uint64_t load64(const void *p)
96 {
97   const unsigned char *pp = p;
98
99   return ((((uint64_t )pp[0]&0xff) <<  0) | (((uint64_t )pp[1]&0xff) <<  8) |
100           (((uint64_t )pp[2]&0xff) << 16) | (((uint64_t )pp[3]&0xff) << 24) |
101           (((uint64_t )pp[4]&0xff) << 32) | (((uint64_t )pp[5]&0xff) << 40) |
102           (((uint64_t )pp[6]&0xff) << 48) | (((uint64_t )pp[7]&0xff) << 56));
103 }
104
105 /*----- The Kalyna block cipher -------------------------------------------*/
106
107 static PyTypeObject kalyna_pytype;
108
109 typedef struct kalyna_pyobj {
110   PyObject_HEAD
111   kalyna_t *k;
112 } kalyna_pyobj;
113
114 static PyObject *kalyna_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
115 {
116   char *kwlist[] = { "key", "blksz", 0 };
117   size_t blksz;
118   char *k;
119   Py_ssize_t ksz;
120   kalyna_t *kk;
121   kalyna_pyobj *rc = 0;
122   size_t i;
123   uint64_t k64[8];
124
125   if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#O&:new", kwlist,
126                                    &k, &ksz, convszt, &blksz))
127     goto end;
128   switch (blksz) {
129     case 16: case 32: case 64: break;
130     default: VALERR("bad block size");
131   }
132   if ((ksz != blksz && ksz != 2*blksz) || ksz > 64)
133     VALERR("bad key length");
134
135   kk = KalynaInit(8*blksz, 8*ksz); if (!kk) VALERR("unknown error");
136   for (i = 0; i < kk->nk; i++) k64[i] = load64(k + 8*i);
137   KalynaKeyExpand(k64, kk);
138   rc = PyObject_NEW(kalyna_pyobj, ty);
139   rc->k = kk;
140 end:
141   return ((PyObject *)rc);
142 }
143
144 static void kalyna_pydealloc(PyObject *me)
145 {
146   kalyna_pyobj *kobj = (kalyna_pyobj *)me;
147   KalynaDelete(kobj->k);
148   FREEOBJ(kobj);
149 }
150
151 static PyObject *kalynameth_encrypt(PyObject *me, PyObject *arg)
152 {
153   char *p, *q;
154   Py_ssize_t psz;
155   PyObject *rc = 0;
156   kalyna_pyobj *kobj = (kalyna_pyobj *)me;
157   size_t i;
158   uint64_t p64[8];
159
160   if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &psz)) goto end;
161   if (psz != 8*kobj->k->nb) VALERR("incorrect block size");
162   rc = PyString_FromStringAndSize(0, psz); q = PyString_AS_STRING(rc);
163   for (i = 0; i < kobj->k->nb; i++) p64[i] = load64(p + 8*i);
164   KalynaEncipher(p64, kobj->k, p64);
165   for (i = 0; i < kobj->k->nb; i++) store64(q + 8*i, p64[i]);
166 end:
167   return (rc);
168 }
169
170 static PyObject *kalynameth_decrypt(PyObject *me, PyObject *arg)
171 {
172   char *p, *q;
173   Py_ssize_t psz;
174   PyObject *rc = 0;
175   kalyna_pyobj *kobj = (kalyna_pyobj *)me;
176   size_t i;
177   uint64_t p64[8];
178
179   if (!PyArg_ParseTuple(arg, "s#:encrypt", &p, &psz)) goto end;
180   if (psz != 8*kobj->k->nb) VALERR("incorrect block size");
181   rc = PyString_FromStringAndSize(0, psz); q = PyString_AS_STRING(rc);
182   for (i = 0; i < kobj->k->nb; i++) p64[i] = load64(p + 8*i);
183   KalynaDecipher(p64, kobj->k, p64);
184   for (i = 0; i < kobj->k->nb; i++) store64(q + 8*i, p64[i]);
185 end:
186   return (rc);
187 }
188
189 static PyMethodDef kalyna_pymethods[] = {
190   { "encrypt",  kalynameth_encrypt,     METH_VARARGS,
191     "encrypt(PTBLK) -> CTBLK" },
192   { "decrypt",  kalynameth_decrypt,     METH_VARARGS,
193     "decrypt(CTBLK) -> PTBLK" },
194   { 0 }
195 };
196
197 static PyTypeObject kalyna_pytype = {
198   PyObject_HEAD_INIT(0) 0,              /* Header */
199   "Kalyna",                             /* @tp_name@ */
200   sizeof(kalyna_pyobj),                 /* @tp_basicsize@ */
201   0,                                    /* @tp_itemsize@ */
202
203   kalyna_pydealloc,                     /* @tp_dealloc@ */
204   0,                                    /* @tp_print@ */
205   0,                                    /* @tp_getattr@ */
206   0,                                    /* @tp_setattr@ */
207   0,                                    /* @tp_compare@ */
208   0,                                    /* @tp_repr@ */
209   0,                                    /* @tp_as_number@ */
210   0,                                    /* @tp_as_sequence@ */
211   0,                                    /* @tp_as_mapping@ */
212   0,                                    /* @tp_hash@ */
213   0,                                    /* @tp_call@ */
214   0,                                    /* @tp_str@ */
215   0,                                    /* @tp_getattro@ */
216   0,                                    /* @tp_setattro@ */
217   0,                                    /* @tp_as_buffer@ */
218   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
219     Py_TPFLAGS_BASETYPE,
220
221   /* @tp_doc@ */
222 "Kalyna block cipher instance.",
223
224   0,                                    /* @tp_traverse@ */
225   0,                                    /* @tp_clear@ */
226   0,                                    /* @tp_richcompare@ */
227   0,                                    /* @tp_weaklistoffset@ */
228   0,                                    /* @tp_iter@ */
229   0,                                    /* @tp_iternext@ */
230   kalyna_pymethods,                     /* @tp_methods@ */
231   0,                                    /* @tp_members@ */
232   0,                                    /* @tp_getset@ */
233   0,                                    /* @tp_base@ */
234   0,                                    /* @tp_dict@ */
235   0,                                    /* @tp_descr_get@ */
236   0,                                    /* @tp_descr_set@ */
237   0,                                    /* @tp_dictoffset@ */
238   0,                                    /* @tp_init@ */
239   PyType_GenericAlloc,                  /* @tp_alloc@ */
240   kalyna_pynew,                         /* @tp_new@ */
241   0,                                    /* @tp_free@ */
242   0                                     /* @tp_is_gc@ */
243 };
244
245 /*----- Main code ---------------------------------------------------------*/
246
247 void initkalyna(void)
248 {
249   PyObject *mod = Py_InitModule("kalyna", 0);
250   if (PyType_Ready(&kalyna_pytype) < 0) return;
251   INSERT("version", PyString_FromString(VERSION));
252   INSERT("Kalyna", &kalyna_pytype);
253 }
254
255 /*----- That's all, folks -------------------------------------------------*/