1 | /** |
---|
2 | * ciphercomb.cpp -- Python wrappers around Crypto++'s AES-CTR and XSalsa20 |
---|
3 | */ |
---|
4 | |
---|
5 | #define PY_SSIZE_T_CLEAN |
---|
6 | #include <Python.h> |
---|
7 | #if (PY_VERSION_HEX < 0x02050000) |
---|
8 | typedef int Py_ssize_t; |
---|
9 | #endif |
---|
10 | |
---|
11 | #include "ciphercomb.hpp" |
---|
12 | #include <iostream> |
---|
13 | /* from Crypto++ */ |
---|
14 | #ifdef USE_NAME_CRYPTO_PLUS_PLUS |
---|
15 | #include <crypto++/modes.h> |
---|
16 | #include <crypto++/aes.h> |
---|
17 | #include <crypto++/salsa.h> |
---|
18 | #include <crypto++/sha.h> |
---|
19 | #include <crypto++/hmac.h> |
---|
20 | #else |
---|
21 | #include <cryptopp/modes.h> |
---|
22 | #include <cryptopp/aes.h> |
---|
23 | #include <cryptopp/salsa.h> |
---|
24 | #include <cryptopp/sha.h> |
---|
25 | #include <cryptopp/hmac.h> |
---|
26 | #endif |
---|
27 | using namespace std; |
---|
28 | |
---|
29 | static const char*const ciphercomb___doc__ = "_ciphercomb mode\n\ |
---|
30 | You are advised to run aes.start_up_self_test() after importing this module."; |
---|
31 | |
---|
32 | static PyObject *ciphercomb_error; |
---|
33 | |
---|
34 | typedef struct { |
---|
35 | PyObject_HEAD |
---|
36 | |
---|
37 | /* internal */ |
---|
38 | CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption * e1; |
---|
39 | CryptoPP::XSalsa20::Encryption *e2; |
---|
40 | } CipherComb; |
---|
41 | |
---|
42 | PyDoc_STRVAR(CipherComb__doc__, |
---|
43 | "A CipherComb cipher object.\n\ |
---|
44 | \n\ |
---|
45 | This object encrypts/decrypts in Combiner mode.\n\ |
---|
46 | \n\ |
---|
47 | "); |
---|
48 | |
---|
49 | static PyObject * |
---|
50 | CipherComb_process(CipherComb* self, PyObject* msgobj) { |
---|
51 | if (!PyString_CheckExact(msgobj)) { |
---|
52 | PyStringObject* typerepr = reinterpret_cast<PyStringObject*>(PyObject_Repr(reinterpret_cast<PyObject*>(msgobj->ob_type))); |
---|
53 | if (typerepr) { |
---|
54 | PyErr_Format(ciphercomb_error, "Precondition violation: you are required to pass a Python string object (not a unicode, a subclass of string, or anything else), but you passed %s.", PyString_AS_STRING(reinterpret_cast<PyObject*>(typerepr))); |
---|
55 | Py_DECREF(typerepr); |
---|
56 | } else |
---|
57 | PyErr_Format(ciphercomb_error, "Precondition violation: you are required to pass a Python string object (not a unicode, a subclass of string, or anything else)."); |
---|
58 | return NULL; |
---|
59 | } |
---|
60 | |
---|
61 | const char *msg; |
---|
62 | Py_ssize_t msgsize; |
---|
63 | if (PyString_AsStringAndSize(msgobj, const_cast<char**>(&msg), &msgsize)) |
---|
64 | return NULL; |
---|
65 | assert (msgsize >= 0); |
---|
66 | |
---|
67 | PyStringObject* result1 = reinterpret_cast<PyStringObject*>(PyString_FromStringAndSize(NULL, msgsize)); |
---|
68 | if (!result1) |
---|
69 | return NULL; |
---|
70 | |
---|
71 | self->e1->ProcessData(reinterpret_cast<byte*>(PyString_AS_STRING(result1)), reinterpret_cast<const byte*>(msg), msgsize); |
---|
72 | |
---|
73 | PyStringObject* result2 = reinterpret_cast<PyStringObject*>(PyString_FromStringAndSize(NULL, msgsize)); |
---|
74 | if (!result2) |
---|
75 | return NULL; |
---|
76 | self->e2->ProcessString(reinterpret_cast<byte*>(PyString_AS_STRING(result2)), reinterpret_cast<const byte*>(PyString_AS_STRING(result1)), msgsize); |
---|
77 | |
---|
78 | return reinterpret_cast<PyObject*>(result2); |
---|
79 | } |
---|
80 | |
---|
81 | PyDoc_STRVAR(CipherComb_process__doc__, |
---|
82 | "Encrypt or decrypt the next bytes, returning the result."); |
---|
83 | |
---|
84 | static PyMethodDef CipherComb_methods[] = { |
---|
85 | {"process", reinterpret_cast<PyCFunction>(CipherComb_process), METH_O, CipherComb_process__doc__}, |
---|
86 | {NULL}, |
---|
87 | }; |
---|
88 | |
---|
89 | static PyObject * |
---|
90 | CipherComb_new(PyTypeObject* type, PyObject *args, PyObject *kwdict) { |
---|
91 | CipherComb* self = reinterpret_cast<CipherComb*>(type->tp_alloc(type, 0)); |
---|
92 | if (!self) |
---|
93 | return NULL; |
---|
94 | self->e1 = NULL; |
---|
95 | self->e2 = NULL; |
---|
96 | return reinterpret_cast<PyObject*>(self); |
---|
97 | } |
---|
98 | |
---|
99 | static void |
---|
100 | CipherComb_dealloc(PyObject* self) { |
---|
101 | if (reinterpret_cast<CipherComb*>(self)->e1) |
---|
102 | delete reinterpret_cast<CipherComb*>(self)->e1; |
---|
103 | if (reinterpret_cast<CipherComb*>(self)->e2) |
---|
104 | delete reinterpret_cast<CipherComb*>(self)->e2; |
---|
105 | self->ob_type->tp_free(self); |
---|
106 | } |
---|
107 | |
---|
108 | static int |
---|
109 | CipherComb_init(PyObject* self, PyObject *args, PyObject *kwdict) { |
---|
110 | static const char *kwlist[] = { "key", "iv", NULL }; |
---|
111 | const char *key = NULL; |
---|
112 | Py_ssize_t keysize = 0; |
---|
113 | const char *iv = NULL; |
---|
114 | const char defaultiv[40] = {0}; |
---|
115 | Py_ssize_t ivsize = 0; |
---|
116 | if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#|t#:CipherComb.__init__", const_cast<char**>(kwlist), &key, &keysize, &iv, &ivsize)) |
---|
117 | return -1; |
---|
118 | assert (keysize >= 0); |
---|
119 | assert (ivsize >= 0); |
---|
120 | |
---|
121 | char prk[32]; |
---|
122 | char* salt=""; |
---|
123 | CryptoPP::HMAC<CryptoPP::SHA256>(reinterpret_cast<byte*>(salt), 0).CalculateDigest(reinterpret_cast<byte*>(prk), reinterpret_cast<const byte*>(key), keysize); |
---|
124 | |
---|
125 | char t1[32]; |
---|
126 | char t2[32]; |
---|
127 | char ext1[1] = {0x01}; |
---|
128 | CryptoPP::HMAC<CryptoPP::SHA256>(reinterpret_cast<byte*>(prk), 32).CalculateDigest(reinterpret_cast<byte*>(t1), reinterpret_cast<const byte*>(ext1), 1); |
---|
129 | char ext2[33]; |
---|
130 | memcpy(ext2, t1, 32); |
---|
131 | ext2[32] = 0x02; |
---|
132 | CryptoPP::HMAC<CryptoPP::SHA256>(reinterpret_cast<byte*>(prk), 32).CalculateDigest(reinterpret_cast<byte*>(t2), reinterpret_cast<const byte*>(ext2), 33); |
---|
133 | |
---|
134 | char aeskey[16]; |
---|
135 | int aeskeysize = 16; |
---|
136 | char xsalsakey[32]; |
---|
137 | int xskeysize = 32; |
---|
138 | memcpy(aeskey, t1, 16); |
---|
139 | memcpy(xsalsakey, t1+16, 16); |
---|
140 | memcpy(xsalsakey+16, t2, 16); |
---|
141 | |
---|
142 | char aesiv[16]; |
---|
143 | char xsalsaiv[24]; |
---|
144 | if (!iv) |
---|
145 | iv = defaultiv; |
---|
146 | memcpy(aesiv, iv, 16); |
---|
147 | memcpy(xsalsaiv, iv+16, 24); |
---|
148 | try { |
---|
149 | reinterpret_cast<CipherComb*>(self)->e1 = new CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption(reinterpret_cast<const byte*>(aeskey), aeskeysize, reinterpret_cast<const byte*>(aesiv)); |
---|
150 | reinterpret_cast<CipherComb*>(self)->e2 = new CryptoPP::XSalsa20::Encryption(reinterpret_cast<const byte*>(xsalsakey), xskeysize, reinterpret_cast<const byte*>(xsalsaiv)); |
---|
151 | |
---|
152 | } catch (CryptoPP::InvalidKeyLength le) { |
---|
153 | PyErr_Format(ciphercomb_error, "Precondition violation: you are required to pass a valid key size. Crypto++ gave this exception: %s", le.what()); |
---|
154 | return -1; |
---|
155 | } |
---|
156 | if (!reinterpret_cast<CipherComb*>(self)->e1 || !reinterpret_cast<CipherComb*>(self)->e2 ) { |
---|
157 | PyErr_NoMemory(); |
---|
158 | return -1; |
---|
159 | } |
---|
160 | return 0; |
---|
161 | } |
---|
162 | |
---|
163 | static PyTypeObject CipherComb_type = { |
---|
164 | PyObject_HEAD_INIT(NULL) |
---|
165 | 0, /*ob_size*/ |
---|
166 | "_ciphercomb.CipherComb", /*tp_name*/ |
---|
167 | sizeof(CipherComb), /*tp_basicsize*/ |
---|
168 | 0, /*tp_itemsize*/ |
---|
169 | CipherComb_dealloc, /*tp_dealloc*/ |
---|
170 | 0, /*tp_print*/ |
---|
171 | 0, /*tp_getattr*/ |
---|
172 | 0, /*tp_setattr*/ |
---|
173 | 0, /*tp_compare*/ |
---|
174 | 0, /*tp_repr*/ |
---|
175 | 0, /*tp_as_number*/ |
---|
176 | 0, /*tp_as_sequence*/ |
---|
177 | 0, /*tp_as_mapping*/ |
---|
178 | 0, /*tp_hash */ |
---|
179 | 0, /*tp_call*/ |
---|
180 | 0, /*tp_str*/ |
---|
181 | 0, /*tp_getattro*/ |
---|
182 | 0, /*tp_setattro*/ |
---|
183 | 0, /*tp_as_buffer*/ |
---|
184 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ |
---|
185 | CipherComb__doc__, /* tp_doc */ |
---|
186 | 0, /* tp_traverse */ |
---|
187 | 0, /* tp_clear */ |
---|
188 | 0, /* tp_richcompare */ |
---|
189 | 0, /* tp_weaklistoffset */ |
---|
190 | 0, /* tp_iter */ |
---|
191 | 0, /* tp_iternext */ |
---|
192 | CipherComb_methods, /* tp_methods */ |
---|
193 | 0, /* tp_members */ |
---|
194 | 0, /* tp_getset */ |
---|
195 | 0, /* tp_base */ |
---|
196 | 0, /* tp_dict */ |
---|
197 | 0, /* tp_descr_get */ |
---|
198 | 0, /* tp_descr_set */ |
---|
199 | 0, /* tp_dictoffset */ |
---|
200 | CipherComb_init, /* tp_init */ |
---|
201 | 0, /* tp_alloc */ |
---|
202 | CipherComb_new, /* tp_new */ |
---|
203 | }; |
---|
204 | |
---|
205 | void init_ciphercomb(PyObject*const module) { |
---|
206 | if (PyType_Ready(&CipherComb_type) < 0) |
---|
207 | return; |
---|
208 | Py_INCREF(&CipherComb_type); |
---|
209 | PyModule_AddObject(module, "ciphercomb_CipherComb", (PyObject *)&CipherComb_type); |
---|
210 | |
---|
211 | ciphercomb_error = PyErr_NewException(const_cast<char*>("_ciphercomb.Error"), NULL, NULL); |
---|
212 | PyModule_AddObject(module, "ciphercomb_Error", ciphercomb_error); |
---|
213 | |
---|
214 | PyModule_AddStringConstant(module, "ciphercomb___doc__", const_cast<char*>(ciphercomb___doc__)); |
---|
215 | } |
---|
216 | |
---|
217 | |
---|