Ticket #15: diff-salsa20.txt

File diff-salsa20.txt, 16.8 KB (added by zooko, at 2009-03-02T22:05:50Z)

add Salsa20

Line 
1diff -rN -u old-from_zaula_new_and_improved/cryptopp/c5/aside/TestVectors/salsa.txt new-from_zaula_new_and_improved/cryptopp/c5/aside/TestVectors/salsa.txt
2--- old-from_zaula_new_and_improved/cryptopp/c5/aside/TestVectors/salsa.txt     2009-03-02 14:23:05.000000000 -0700
3+++ new-from_zaula_new_and_improved/cryptopp/c5/aside/TestVectors/salsa.txt     1969-12-31 17:00:00.000000000 -0700
4@@ -1,40 +0,0 @@
5-AlgorithmType: SymmetricCipher
6-Name: Salsa20
7-Source: http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors?rev=161&view=markup
8-Comment: Set 1, vector#  0
9-Key: 80000000000000000000000000000000
10-IV: 0000000000000000
11-Plaintext: r16 00000000
12-Seek: 0
13-Ciphertext: 4DFA5E481DA23EA09A31022050859936DA52FCEE218005164F267CB65F5CFD7F2B4F97E0FF16924A52DF269515110A07F9E460BC65EF95DA58F740B7D1DBB0AA
14-Test: Encrypt
15-Seek: 448
16-Ciphertext: B375703739DACED4DD4059FD71C3C47FC2F9939670FAD4A46066ADCC6A5645783308B90FFB72BE04A6B147CBE38CC0C3B9267C296A92A7C69873F9F263BE9703
17-Test: Encrypt
18-Seek: 192
19-Plaintext: r32 00000000
20-Ciphertext: DA9C1581F429E0A00F7D67E23B730676783B262E8EB43A25F55FB90B3E753AEF8C6713EC66C51881111593CCB3E8CB8F8DE124080501EEEB389C4BCB6977CF95\
21-7D5789631EB4554400E1E025935DFA7B3E9039D61BDC58A8697D36815BF1985CEFDF7AE112E5BB81E37ECF0616CE7147FC08A93A367E08631F23C03B00A8DA2F
22-Test: Encrypt
23-Comment: Set 3, vector#243
24-Key: F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112
25-IV: 0000000000000000
26-Plaintext: r16 00000000
27-Seek: 0
28-Ciphertext: B4C0AFA503BE7FC29A62058166D56F8F5D27DC246F75B9AD8760C8C39DFD87492D3B76D5D9637F009EADA14458A52DFB09815337E72672681DDDC24633750D83
29-Test: Encrypt
30-Seek: 448
31-Ciphertext: 5A5FB5C8F0AFEA471F0318A4A2792F7AA5C67B6D6E0F0DDB79961C34E3A564BA2EECE78D9AFF45E510FEAB1030B102D39DFCECB77F5798F7D2793C0AB09C7A04
32-Test: Encrypt
33-Seek: 192
34-Plaintext: r32 00000000
35-Ciphertext: DBBA0683DF48C335A9802EEF0252256354C9F763C3FDE19131A6BB7B85040624B1D6CD4BF66D16F7482236C8602A6D58505EEDCCA0B77AED574AB583115124B9\
36-F0C5F98BAE05E019764EF6B65E0694A904CB9EC9C10C297B1AB1A6052365BB78E55D3C6CB9F06184BA7D425A92E7E987757FC5D9AFD7082418DD64125CA6F2B6
37-Test: Encrypt
38-Comment: Set 6, vector#  3
39-Seek: 0
40-Key: 0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C
41-IV: 288FF65DC42B92F9
42-Plaintext: r131072 00
43-CiphertextXorDigest: E00EBCCD70D69152725F9987982178A2E2E139C7BCBE04CA8A0E99E318D9AB76F988C8549F75ADD790BA4F81C176DA653C1A043F11A958E169B6D2319F4EEC1A
44-Test: EncryptXorDigest
45diff -rN -u old-from_zaula_new_and_improved/pycryptopp/cipher/salsa20module.cpp new-from_zaula_new_and_improved/pycryptopp/cipher/salsa20module.cpp
46--- old-from_zaula_new_and_improved/pycryptopp/cipher/salsa20module.cpp 1969-12-31 17:00:00.000000000 -0700
47+++ new-from_zaula_new_and_improved/pycryptopp/cipher/salsa20module.cpp 2009-03-02 14:23:09.000000000 -0700
48@@ -0,0 +1,195 @@
49+/**
50+ * salsa20module.cpp -- Python wrappers around Crypto++'s Salsa20-CTR
51+ */
52+
53+#include <Python.h>
54+
55+#if (PY_VERSION_HEX < 0x02050000)
56+typedef int Py_ssize_t;
57+#endif
58+
59+/* from Crypto++ */
60+#include "modes.h"
61+#include "salsa.h"
62+
63+static char salsa20__doc__[] = "\
64+salsa20 counter mode cipher\
65+";
66+
67+static PyObject *salsa20_error;
68+
69+typedef struct {
70+    PyObject_HEAD
71+
72+    /* internal */
73+    CryptoPP::Salsa20::Encryption * e;
74+} Salsa20;
75+
76+PyDoc_STRVAR(Salsa20__doc__,
77+"An Salsa20 cipher object.\n\
78+\n\
79+This object encrypts/decrypts in CTR mode, using a counter that is initialized\n\
80+to zero when you instantiate the object.  Successive calls to .process() will\n\
81+use the current counter value and increment it.\n\
82+\n\
83+Note that you must never encrypt different data with the same key, or you\n\
84+will leak information about your data.  Therefore the only safe way to use\n\
85+this class is to use a different Salsa20 key every time you are going to encrypt\n\
86+different data.  A good way to generate a different Salsa20 key is using Salsa20, like\n\
87+this:\n\
88+\n\
89+    onetimekey = Salsa20(key=masterkey).process(nonce)\n\
90+\n\
91+Where 'masterkey' is a secret key used only for generating onetimekeys this\
92+way, and 'nonce' is a value that is guaranteed to never repeat.\
93+\n\
94+@param key: the symmetric encryption key; a string of exactly 16 bytes\
95+");
96+
97+static PyObject *
98+Salsa20_process(Salsa20* self, PyObject* msgobj) {
99+    if (!PyString_CheckExact(msgobj)) {
100+        PyStringObject* typerepr = reinterpret_cast<PyStringObject*>(PyObject_Repr(reinterpret_cast<PyObject*>(msgobj->ob_type)));
101+        if (typerepr) {
102+            PyErr_Format(salsa20_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)));
103+            Py_DECREF(typerepr);
104+        } else
105+            PyErr_Format(salsa20_error, "Precondition violation: you are required to pass a Python string object (not a unicode, a subclass of string, or anything else).");
106+        return NULL;
107+    }
108+
109+    const char *msg;
110+    Py_ssize_t msgsize;
111+    if (PyString_AsStringAndSize(msgobj, const_cast<char**>(&msg), &msgsize))
112+        return NULL;
113+    assert (msgsize >= 0);
114+
115+    PyStringObject* result = reinterpret_cast<PyStringObject*>(PyString_FromStringAndSize(NULL, msgsize));
116+    if (!result)
117+        return NULL;
118+
119+    self->e->ProcessData(reinterpret_cast<byte*>(PyString_AS_STRING(result)), reinterpret_cast<const byte*>(msg), msgsize);
120+    return reinterpret_cast<PyObject*>(result);
121+}
122+
123+PyDoc_STRVAR(Salsa20_process__doc__,
124+"Encrypt or decrypt the next bytes, returning the result.");
125+
126+static PyMethodDef Salsa20_methods[] = {
127+    {"process", reinterpret_cast<PyCFunction>(Salsa20_process), METH_O, Salsa20_process__doc__},
128+    {NULL},
129+};
130+
131+static PyObject *
132+Salsa20_new(PyTypeObject* type, PyObject *args, PyObject *kwdict) {
133+    Salsa20* self = reinterpret_cast<Salsa20*>(type->tp_alloc(type, 0));
134+    if (!self)
135+        return NULL;
136+    self->e = NULL;
137+    return reinterpret_cast<PyObject*>(self);
138+}
139+
140+static void
141+Salsa20_dealloc(PyObject* self) {
142+    if (reinterpret_cast<Salsa20*>(self)->e)
143+        delete reinterpret_cast<Salsa20*>(self)->e;
144+    self->ob_type->tp_free(self);
145+}
146+
147+static int
148+Salsa20_init(PyObject* self, PyObject *args, PyObject *kwdict) {
149+    static const char *kwlist[] = { "key", "iv", NULL };
150+    const char *key = NULL;
151+    Py_ssize_t keysize = 0;
152+    const char *iv = NULL;
153+    const char defaultiv[CryptoPP::Salsa20::IV_LENGTH] = {0};
154+    Py_ssize_t ivsize = 0;
155+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#|t#:Salsa20.__init__", const_cast<char**>(kwlist), &key, &keysize, &iv, &ivsize))
156+        return -1;
157+    assert (keysize >= 0);
158+    assert (ivsize >= 0);
159+
160+    if (!iv)
161+        iv = defaultiv;
162+    try {
163+        reinterpret_cast<Salsa20*>(self)->e = new CryptoPP::Salsa20::Encryption(reinterpret_cast<const byte*>(key), keysize, reinterpret_cast<const byte*>(iv));
164+    } catch (CryptoPP::InvalidKeyLength le) {
165+        PyErr_Format(salsa20_error, "Precondition violation: you are required to pass a valid key size.  Crypto++ gave this exception: %s", le.what());
166+        return -1;
167+    }
168+    if (!reinterpret_cast<Salsa20*>(self)->e) {
169+        PyErr_NoMemory();
170+        return -1;
171+    }
172+    return 0;
173+}
174+
175+static PyTypeObject Salsa20_type = {
176+    PyObject_HEAD_INIT(NULL)
177+    0,                         /*ob_size*/
178+    "salsa20.Salsa20", /*tp_name*/
179+    sizeof(Salsa20),             /*tp_basicsize*/
180+    0,                         /*tp_itemsize*/
181+    Salsa20_dealloc, /*tp_dealloc*/
182+    0,                         /*tp_print*/
183+    0,                         /*tp_getattr*/
184+    0,                         /*tp_setattr*/
185+    0,                         /*tp_compare*/
186+    0,                         /*tp_repr*/
187+    0,                         /*tp_as_number*/
188+    0,                         /*tp_as_sequence*/
189+    0,                         /*tp_as_mapping*/
190+    0,                         /*tp_hash */
191+    0,                         /*tp_call*/
192+    0,                         /*tp_str*/
193+    0,                         /*tp_getattro*/
194+    0,                         /*tp_setattro*/
195+    0,                         /*tp_as_buffer*/
196+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
197+    Salsa20__doc__,           /* tp_doc */
198+    0,                        /* tp_traverse */
199+    0,                        /* tp_clear */
200+    0,                        /* tp_richcompare */
201+    0,                        /* tp_weaklistoffset */
202+    0,                        /* tp_iter */
203+    0,                        /* tp_iternext */
204+    Salsa20_methods,      /* tp_methods */
205+    0,                         /* tp_members */
206+    0,                         /* tp_getset */
207+    0,                         /* tp_base */
208+    0,                         /* tp_dict */
209+    0,                         /* tp_descr_get */
210+    0,                         /* tp_descr_set */
211+    0,                         /* tp_dictoffset */
212+    Salsa20_init,               /* tp_init */
213+    0,                         /* tp_alloc */
214+    Salsa20_new,                /* tp_new */
215+};
216+
217+static struct PyMethodDef salsa20_functions[] = {
218+    {NULL,     NULL}            /* Sentinel */
219+};
220+
221+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
222+#define PyMODINIT_FUNC void
223+#endif
224+PyMODINIT_FUNC
225+initsalsa20(void) {
226+    PyObject *module;
227+    PyObject *module_dict;
228+
229+    if (PyType_Ready(&Salsa20_type) < 0)
230+        return;
231+
232+    module = Py_InitModule3("salsa20", salsa20_functions, salsa20__doc__);
233+    if (!module)
234+      return;
235+
236+    Py_INCREF(&Salsa20_type);
237+
238+    PyModule_AddObject(module, "Salsa20", (PyObject *)&Salsa20_type);
239+
240+    module_dict = PyModule_GetDict(module);
241+    salsa20_error = PyErr_NewException(const_cast<char*>("salsa20.Error"), NULL, NULL);
242+    PyDict_SetItemString(module_dict, "Error", salsa20_error);
243+}
244diff -rN -u old-from_zaula_new_and_improved/pycryptopp/hash/sha256module.cpp new-from_zaula_new_and_improved/pycryptopp/hash/sha256module.cpp
245--- old-from_zaula_new_and_improved/pycryptopp/test/test_salsa20.py     1969-12-31 17:00:00.000000000 -0700
246+++ new-from_zaula_new_and_improved/pycryptopp/test/test_salsa20.py     2009-03-02 14:23:09.000000000 -0700
247@@ -0,0 +1,91 @@
248+#!/usr/bin/env python
249+
250+import os, random, re
251+
252+import unittest
253+
254+from binascii import a2b_hex, b2a_hex
255+
256+global VERBOSE
257+VERBOSE=False
258+
259+from pycryptopp.cipher import salsa20
260+
261+from base64 import b32encode
262+def ab(x): # debuggery
263+    if len(x) >= 3:
264+        return "%s:%s" % (len(x), b32encode(x[-3:]),)
265+    elif len(x) == 2:
266+        return "%s:%s" % (len(x), b32encode(x[-2:]),)
267+    elif len(x) == 1:
268+        return "%s:%s" % (len(x), b32encode(x[-1:]),)
269+    elif len(x) == 0:
270+        return "%s:%s" % (len(x), "--empty--",)
271+
272+def randstr(n):
273+    return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
274+
275+class Salsa20(unittest.TestCase):
276+    enc0 = "dc95c078a2408989ad48a21492842087530f8afbc74536b9a963b4f1c4cb738b"
277+
278+    def test_encrypt_zeroes(self):
279+        cryptor = salsa20.Salsa20(key="\x00"*32)
280+        ct = cryptor.process("\x00"*32)
281+        self.failUnlessEqual(self.enc0, b2a_hex(ct))
282+
283+    def test_init_type_check(self):
284+        self.failUnlessRaises(TypeError, salsa20.Salsa20, None)
285+        self.failUnlessRaises(salsa20.Error, salsa20.Salsa20, "a"*1) # too short
286+        self.failUnlessRaises(salsa20.Error, salsa20.Salsa20, "a"*17) # not one of the valid key sizes for Salsa20 (16, 24, 32)
287+
288+    def test_encrypt_zeroes_in_two_parts(self):
289+        cryptor = salsa20.Salsa20(key="\x00"*32)
290+        ct1 = cryptor.process("\x00"*15)
291+        ct2 = cryptor.process("\x00"*17)
292+        self.failUnlessEqual(self.enc0, b2a_hex(ct1+ct2))
293+
294+def fake_ecb_using_ctr(k, p):
295+    return salsa20.Salsa20(key=k, iv=p).process('\x00'*16)
296+
297+NIST_KAT_VECTS_RE=re.compile("\nCOUNT = ([0-9]+)\nKEY = ([0-9a-f]+)\nPLAINTEXT = ([0-9a-f]+)\nCIPHERTEXT = ([0-9a-f]+)")
298+
299+class AES_from_NIST_KAT(unittest.TestCase):
300+    def test_NIST_KAT(self):
301+        katdir=os.path.join('pycryptopp', 'test', 'vectors', 'KAT_AES')
302+        for fn in os.listdir(katdir):
303+            f = open(os.path.join(katdir, fn), 'rU')
304+            self._test_KAT_file(f)
305+
306+    def _test_KAT_file(self, f):
307+        vects_str =  f.read()
308+        for mo in NIST_KAT_VECTS_RE.finditer(vects_str):
309+            key = a2b_hex(mo.group(2))
310+            plaintext = a2b_hex(mo.group(3))
311+            ciphertext = a2b_hex(mo.group(4))
312+
313+            computedciphertext = fake_ecb_using_ctr(key, plaintext)
314+            self.failUnlessEqual(computedciphertext, ciphertext, "computedciphertext: %s, ciphertext: %s, key: %s, plaintext: %s" % (b2a_hex(computedciphertext), b2a_hex(ciphertext), b2a_hex(key), b2a_hex(plaintext)))
315+
316+class AES_from_Niels_Ferguson(unittest.TestCase):
317+    # http://blogs.msdn.com/si_team/archive/2006/05/19/salsa20-test-vectors.aspx
318+    def _test_from_Niels_AES(self, keysize, result):
319+        E = fake_ecb_using_ctr
320+        b = 16
321+        k = keysize
322+        S = '\x00' * (k+b)
323+        for i in range(1000):
324+            n = len(S)
325+            K = S[-k:]
326+            P = S[-k-b:-k]
327+            S += E(K, E(K, P))
328+
329+        self.failUnlessEqual(S[-b:], a2b_hex(result))
330+
331+    def test_from_Niels_AES128(self):
332+        return self._test_from_Niels_AES(16, 'bd883f01035e58f42f9d812f2dacbcd8')
333+
334+    def test_from_Niels_AES256(self):
335+        return self._test_from_Niels_AES(32, 'c84b0f3a2c76dd9871900b07f09bdd3e')
336+
337+if __name__ == "__main__":
338+    unittest.main()
339diff -rN -u old-from_zaula_new_and_improved/pycryptopp/test/test_sha256.py new-from_zaula_new_and_improved/pycryptopp/test/test_sha256.py
340--- old-from_zaula_new_and_improved/pycryptopp/test/vectors/salsa.txt   1969-12-31 17:00:00.000000000 -0700
341+++ new-from_zaula_new_and_improved/pycryptopp/test/vectors/salsa.txt   2009-03-02 14:23:09.000000000 -0700
342@@ -0,0 +1,40 @@
343+AlgorithmType: SymmetricCipher
344+Name: Salsa20
345+Source: http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors?rev=161&view=markup
346+Comment: Set 1, vector#  0
347+Key: 80000000000000000000000000000000
348+IV: 0000000000000000
349+Plaintext: r16 00000000
350+Seek: 0
351+Ciphertext: 4DFA5E481DA23EA09A31022050859936DA52FCEE218005164F267CB65F5CFD7F2B4F97E0FF16924A52DF269515110A07F9E460BC65EF95DA58F740B7D1DBB0AA
352+Test: Encrypt
353+Seek: 448
354+Ciphertext: B375703739DACED4DD4059FD71C3C47FC2F9939670FAD4A46066ADCC6A5645783308B90FFB72BE04A6B147CBE38CC0C3B9267C296A92A7C69873F9F263BE9703
355+Test: Encrypt
356+Seek: 192
357+Plaintext: r32 00000000
358+Ciphertext: DA9C1581F429E0A00F7D67E23B730676783B262E8EB43A25F55FB90B3E753AEF8C6713EC66C51881111593CCB3E8CB8F8DE124080501EEEB389C4BCB6977CF95\
359+7D5789631EB4554400E1E025935DFA7B3E9039D61BDC58A8697D36815BF1985CEFDF7AE112E5BB81E37ECF0616CE7147FC08A93A367E08631F23C03B00A8DA2F
360+Test: Encrypt
361+Comment: Set 3, vector#243
362+Key: F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112
363+IV: 0000000000000000
364+Plaintext: r16 00000000
365+Seek: 0
366+Ciphertext: B4C0AFA503BE7FC29A62058166D56F8F5D27DC246F75B9AD8760C8C39DFD87492D3B76D5D9637F009EADA14458A52DFB09815337E72672681DDDC24633750D83
367+Test: Encrypt
368+Seek: 448
369+Ciphertext: 5A5FB5C8F0AFEA471F0318A4A2792F7AA5C67B6D6E0F0DDB79961C34E3A564BA2EECE78D9AFF45E510FEAB1030B102D39DFCECB77F5798F7D2793C0AB09C7A04
370+Test: Encrypt
371+Seek: 192
372+Plaintext: r32 00000000
373+Ciphertext: DBBA0683DF48C335A9802EEF0252256354C9F763C3FDE19131A6BB7B85040624B1D6CD4BF66D16F7482236C8602A6D58505EEDCCA0B77AED574AB583115124B9\
374+F0C5F98BAE05E019764EF6B65E0694A904CB9EC9C10C297B1AB1A6052365BB78E55D3C6CB9F06184BA7D425A92E7E987757FC5D9AFD7082418DD64125CA6F2B6
375+Test: Encrypt
376+Comment: Set 6, vector#  3
377+Seek: 0
378+Key: 0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C
379+IV: 288FF65DC42B92F9
380+Plaintext: r131072 00
381+CiphertextXorDigest: E00EBCCD70D69152725F9987982178A2E2E139C7BCBE04CA8A0E99E318D9AB76F988C8549F75ADD790BA4F81C176DA653C1A043F11A958E169B6D2319F4EEC1A
382+Test: EncryptXorDigest
383diff -rN -u old-from_zaula_new_and_improved/setup.py new-from_zaula_new_and_improved/setup.py
384--- old-from_zaula_new_and_improved/setup.py    2009-03-02 14:23:06.000000000 -0700
385+++ new-from_zaula_new_and_improved/setup.py    2009-03-02 14:23:09.000000000 -0700
386@@ -100,9 +100,17 @@
387     )
388 
389 ext_modules.append(
390     Extension('pycryptopp.cipher.aes', cryptopp_src + ['pycryptopp/cipher/aesmodule.cpp',], include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_link_args=extra_link_args, extra_compile_args=extra_compile_args, define_macros=define_macros, undef_macros=undef_macros)
391     )
392 
393+ext_modules.append(
394+    Extension('pycryptopp.cipher.salsa20', cryptopp_src + ['pycryptopp/cipher/salsa20module.cpp',], include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_link_args=extra_link_args, extra_compile_args=extra_compile_args, define_macros=define_macros, undef_macros=undef_macros)
395+    )
396+
397 miscdeps=os.path.join(os.getcwd(), 'misc', 'dependencies')
398 dependency_links=[os.path.join(miscdeps, t) for t in os.listdir(miscdeps) if t.endswith(".tar")]
399 setup_requires = []
400