kalyna-python.c, setup.py: Actually implement the bindings.
[kalyna-python] / kalyna-python.c
CommitLineData
50edf34f
MW
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
65f8a434
MW
31#include "kalyna.h"
32
50edf34f
MW
33/*----- Utilities ---------------------------------------------------------*/
34
65f8a434
MW
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
50edf34f
MW
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
65f8a434
MW
47#define FREEOBJ(obj) \
48 (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj)))
49
50int 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);
68end:
69 return (0);
70}
71
72static 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);
81end:
82 return (0);
83}
84
85static 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
95static 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
107static PyTypeObject kalyna_pytype;
108
109typedef struct kalyna_pyobj {
110 PyObject_HEAD
111 kalyna_t *k;
112} kalyna_pyobj;
113
114static 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;
140end:
141 return ((PyObject *)rc);
142}
143
144static void kalyna_pydealloc(PyObject *me)
145{
146 kalyna_pyobj *kobj = (kalyna_pyobj *)me;
147 KalynaDelete(kobj->k);
148 FREEOBJ(kobj);
149}
150
151static 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]);
166end:
167 return (rc);
168}
169
170static 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]);
185end:
186 return (rc);
187}
188
189static 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
197static 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
50edf34f
MW
245/*----- Main code ---------------------------------------------------------*/
246
247void initkalyna(void)
248{
249 PyObject *mod = Py_InitModule("kalyna", 0);
65f8a434 250 if (PyType_Ready(&kalyna_pytype) < 0) return;
50edf34f 251 INSERT("version", PyString_FromString(VERSION));
65f8a434 252 INSERT("Kalyna", &kalyna_pytype);
50edf34f
MW
253}
254
255/*----- That's all, folks -------------------------------------------------*/