Ticket #14: diff-tiger.txt

File diff-tiger.txt, 12.7 KB (added by zooko, at 2009-03-02T21:49:10Z)

patch (untested) to add tiger

Line 
1--- old-from_zaula_new_and_improved/pycryptopp/hash/tigermodule.cpp     1969-12-31 17:00:00.000000000 -0700
2+++ new-from_zaula_new_and_improved/pycryptopp/hash/tigermodule.cpp     2009-03-02 14:23:09.000000000 -0700
3@@ -0,0 +1,197 @@
4+/**
5+ * tigermodule.cpp -- Python wrappers around Crypto++'s Tiger hash
6+ */
7+
8+#include <Python.h>
9+
10+#if (PY_VERSION_HEX < 0x02050000)
11+typedef int Py_ssize_t;
12+#endif
13+
14+/* from Crypto++ */
15+#include "tiger.h"
16+#include "hex.h"
17+#include "filters.h"
18+
19+static char tiger__doc__[] = "\
20+tiger hash function\
21+";
22+
23+static PyObject *tiger_error;
24+
25+typedef struct {
26+    PyObject_HEAD
27+
28+    /* internal */
29+    CryptoPP::Tiger* h;
30+    PyStringObject* digest;
31+} Tiger;
32+
33+PyDoc_STRVAR(Tiger__doc__,
34+"a Tiger hash object\n\
35+Its constructor takes an optional string, which has the same effect as\n\
36+calling .update() with that string.");
37+
38+static PyObject *
39+Tiger_update(Tiger* self, PyObject* msgobj) {
40+    if (self->digest)
41+        return PyErr_Format(tiger_error, "Precondition violation: once .digest() has been called you are required to never call .update() again.");
42+
43+    const char *msg;
44+    Py_ssize_t msgsize;
45+    if (PyString_AsStringAndSize(msgobj, const_cast<char**>(&msg), &msgsize))
46+        return NULL;
47+    self->h->Update(reinterpret_cast<const byte*>(msg), msgsize);
48+    Py_RETURN_NONE;
49+}
50+
51+PyDoc_STRVAR(Tiger_update__doc__,
52+"Update the hash object with the string msg. Repeated calls are equivalent to\n\
53+a single call with the concatenation of all the messages.");
54+
55+static PyObject *
56+Tiger_digest(Tiger* self, PyObject* dummy) {
57+    if (!self->digest) {
58+        self->digest = reinterpret_cast<PyStringObject*>(PyString_FromStringAndSize(NULL, self->h->DigestSize()));
59+        if (!self->digest)
60+            return NULL;
61+        self->h->Final(reinterpret_cast<byte*>(PyString_AS_STRING(self->digest)));
62+    }
63+
64+    Py_INCREF(self->digest);
65+    return reinterpret_cast<PyObject*>(self->digest);
66+}
67+
68+PyDoc_STRVAR(Tiger_digest__doc__,
69+"Return the binary digest of the messages that were passed to the update()\n\
70+method (including the initial message if any).");
71+
72+static PyObject *
73+Tiger_hexdigest(Tiger* self, PyObject* dummy) {
74+    PyObject* digest = Tiger_digest(self, NULL);
75+    if (!digest)
76+        return NULL;
77+    Py_ssize_t dsize = PyString_GET_SIZE(digest);
78+    PyStringObject* hexdigest = reinterpret_cast<PyStringObject*>(PyString_FromStringAndSize(NULL, dsize*2));
79+    CryptoPP::ArraySink* as = new CryptoPP::ArraySink(reinterpret_cast<byte*>(PyString_AS_STRING(hexdigest)), dsize*2);
80+    CryptoPP::HexEncoder enc;
81+    enc.Attach(as);
82+    enc.Put(reinterpret_cast<const byte*>(PyString_AS_STRING(digest)), static_cast<size_t>(dsize));
83+    Py_DECREF(digest); digest = NULL;
84+
85+    return reinterpret_cast<PyObject*>(hexdigest);
86+}
87+
88+PyDoc_STRVAR(Tiger_hexdigest__doc__,
89+"Return the hex-encoded digest of the messages that were passed to the update()\n\
90+method (including the initial message if any).");
91+
92+static PyMethodDef Tiger_methods[] = {
93+    {"update", reinterpret_cast<PyCFunction>(Tiger_update), METH_O, Tiger_update__doc__},
94+    {"digest", reinterpret_cast<PyCFunction>(Tiger_digest), METH_NOARGS, Tiger_digest__doc__},
95+    {"hexdigest", reinterpret_cast<PyCFunction>(Tiger_hexdigest), METH_NOARGS, Tiger_hexdigest__doc__},
96+    {NULL},
97+};
98+
99+static PyObject *
100+Tiger_new(PyTypeObject* type, PyObject *args, PyObject *kwdict) {
101+    Tiger* self = reinterpret_cast<Tiger*>(type->tp_alloc(type, 0));
102+    if (!self)
103+        return NULL;
104+    self->h = new CryptoPP::Tiger();
105+    if (!self->h)
106+        return PyErr_NoMemory();
107+    self->digest = NULL;
108+    return reinterpret_cast<PyObject*>(self);
109+}
110+
111+static void
112+Tiger_dealloc(Tiger* self) {
113+    Py_XDECREF(self->digest);
114+    delete self->h;
115+    self->ob_type->tp_free((PyObject*)self);
116+}
117+
118+static int
119+Tiger_init(PyObject* self, PyObject *args, PyObject *kwdict) {
120+    static const char *kwlist[] = { "msg", NULL };
121+    const char *msg = NULL;
122+    size_t msgsize = 0;
123+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|t#", const_cast<char**>(kwlist), &msg, &msgsize))
124+        return -1;
125+
126+    if (msg)
127+        reinterpret_cast<Tiger*>(self)->h->Update(reinterpret_cast<const byte*>(msg), msgsize);
128+    return 0;
129+}
130+
131+static PyTypeObject Tiger_type = {
132+    PyObject_HEAD_INIT(NULL)
133+    0,                         /*ob_size*/
134+    "tiger.Tiger", /*tp_name*/
135+    sizeof(Tiger),             /*tp_basicsize*/
136+    0,                         /*tp_itemsize*/
137+    reinterpret_cast<destructor>(Tiger_dealloc), /*tp_dealloc*/
138+    0,                         /*tp_print*/
139+    0,                         /*tp_getattr*/
140+    0,                         /*tp_setattr*/
141+    0,                         /*tp_compare*/
142+    0,                         /*tp_repr*/
143+    0,                         /*tp_as_number*/
144+    0,                         /*tp_as_sequence*/
145+    0,                         /*tp_as_mapping*/
146+    0,                         /*tp_hash */
147+    0,                         /*tp_call*/
148+    0,                         /*tp_str*/
149+    0,                         /*tp_getattro*/
150+    0,                         /*tp_setattro*/
151+    0,                         /*tp_as_buffer*/
152+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
153+    Tiger__doc__,           /* tp_doc */
154+    0,                        /* tp_traverse */
155+    0,                        /* tp_clear */
156+    0,                        /* tp_richcompare */
157+    0,                        /* tp_weaklistoffset */
158+    0,                        /* tp_iter */
159+    0,                        /* tp_iternext */
160+    Tiger_methods,      /* tp_methods */
161+    0,                         /* tp_members */
162+    0,                         /* tp_getset */
163+    0,                         /* tp_base */
164+    0,                         /* tp_dict */
165+    0,                         /* tp_descr_get */
166+    0,                         /* tp_descr_set */
167+    0,                         /* tp_dictoffset */
168+    //reinterpret_cast<initproc>(Tiger_init),               /* tp_init */
169+    Tiger_init,               /* tp_init */
170+    0,                         /* tp_alloc */
171+    Tiger_new,                /* tp_new */
172+};
173+
174+static struct PyMethodDef tiger_functions[] = {
175+    {NULL,     NULL}            /* Sentinel */
176+};
177+
178+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
179+#define PyMODINIT_FUNC void
180+#endif
181+PyMODINIT_FUNC
182+inittiger(void) {
183+    PyObject *module;
184+    PyObject *module_dict;
185+
186+    if (PyType_Ready(&Tiger_type) < 0)
187+        return;
188+
189+    module = Py_InitModule3("tiger", tiger_functions, tiger__doc__);
190+    if (!module)
191+      return;
192+
193+    Py_INCREF(&Tiger_type);
194+
195+    PyModule_AddObject(module, "Tiger", (PyObject *)&Tiger_type);
196+
197+    module_dict = PyModule_GetDict(module);
198+    tiger_error = PyErr_NewException(const_cast<char*>("tiger.Error"), NULL, NULL);
199+    PyDict_SetItemString(module_dict, "Error", tiger_error);
200+}
201diff -rN -u old-from_zaula_new_and_improved/pycryptopp/publickey/ecdsamodule.cpp new-from_zaula_new_and_improved/pycryptopp/publickey/ecdsamodule.cpp
202--- old-from_zaula_new_and_improved/pycryptopp/test/test_tiger.py       1969-12-31 17:00:00.000000000 -0700
203+++ new-from_zaula_new_and_improved/pycryptopp/test/test_tiger.py       2009-03-02 14:23:09.000000000 -0700
204@@ -0,0 +1,133 @@
205+#!/usr/bin/env python
206+
207+import os, random, re
208+
209+import unittest
210+
211+from binascii import b2a_hex, a2b_hex
212+
213+global VERBOSE
214+VERBOSE=False
215+
216+from pycryptopp.hash import tiger
217+
218+from base64 import b32encode
219+def ab(x): # debuggery
220+    if len(x) >= 3:
221+        return "%s:%s" % (len(x), b32encode(x[-3:]),)
222+    elif len(x) == 2:
223+        return "%s:%s" % (len(x), b32encode(x[-2:]),)
224+    elif len(x) == 1:
225+        return "%s:%s" % (len(x), b32encode(x[-1:]),)
226+    elif len(x) == 0:
227+        return "%s:%s" % (len(x), "--empty--",)
228+
229+def randstr(n):
230+    return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
231+
232+h0 = a2b_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
233+h_bd = a2b_hex("68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b")
234+h_5fd4 = a2b_hex("7c4fbf484498d21b487b9d61de8914b2eadaf2698712936d47c3ada2558f6788")
235+
236+class Tiger(unittest.TestCase):
237+    def test_digest(self):
238+        empty_digest = tiger.Tiger().digest()
239+        self.failUnless(isinstance(empty_digest, str))
240+        self.failUnlessEqual(len(empty_digest), 32)
241+        self.failUnlessEqual(empty_digest, h0)
242+
243+    def test_hexdigest(self):
244+        empty_hexdigest = tiger.Tiger().hexdigest()
245+        self.failUnlessEqual(a2b_hex(empty_hexdigest), h0)
246+    test_hexdigest.todo = "Not yet implemented: Tiger.hexdigest()."
247+
248+    def test_onebyte_1(self):
249+        d = tiger.Tiger("\xbd").digest()
250+        self.failUnlessEqual(d, h_bd)
251+
252+    def test_onebyte_2(self):
253+        s = tiger.Tiger()
254+        s.update("\xbd")
255+        d = s.digest()
256+        self.failUnlessEqual(d, h_bd)
257+
258+    def test_update(self):
259+        s = tiger.Tiger("\x5f")
260+        s.update("\xd4")
261+        d = s.digest()
262+        self.failUnlessEqual(d, h_5fd4)
263+
264+    def test_constructor_type_check(self):
265+        self.failUnlessRaises(TypeError, tiger.Tiger, None)
266+                             
267+    def test_update_type_check(self):
268+        h = tiger.Tiger()
269+        self.failUnlessRaises(TypeError, h.update, None)
270+
271+    def test_digest_twice(self):
272+        h = tiger.Tiger()
273+        d1 = h.digest()
274+        self.failUnless(isinstance(d1, str))
275+        d2 = h.digest()
276+        self.failUnlessEqual(d1, d2)
277+
278+    def test_digest_then_update_fail(self):
279+        h = tiger.Tiger()
280+        d1 = h.digest()
281+        try:
282+            h.update("oops")
283+        except tiger.Error, le:
284+            self.failUnless("digest() has been called" in str(le), le)
285+
286+VECTS_RE=re.compile("\nLen = ([0-9]+)\nMsg = ([0-9a-f]+)\nMD = ([0-9a-f]+)")
287+
288+class SHSVectors(unittest.TestCase):
289+    """
290+    All of the SHA-256 test vectors from the NIST SHS, in the files distributed
291+    by NIST.  (NIST distributes them in a .zip, but we expect them to be
292+    unpacked and in a subdirectory named 'vectors').
293+    """
294+    def test_short(self):
295+        return self._test_vect_file(open(os.path.join('pycryptopp', 'test', 'vectors', 'SHA256ShortMsg.txt'), 'rU'))
296+
297+    def test_long(self):
298+        return self._test_vect_file(open(os.path.join('pycryptopp', 'test', 'vectors', 'SHA256LongMsg.txt'), 'rU'))
299+
300+    def _test_vect_file(self, infile):
301+        vects_str = infile.read()
302+        for mo in VECTS_RE.finditer(vects_str):
303+            msglenbits = int(mo.group(1))
304+            assert msglenbits % 8 == 0
305+            msglen = msglenbits / 8
306+            msg = a2b_hex(mo.group(2))[:msglen] # The slice is necessary because NIST seems to think that "00" is a reasonable representation for the zero-length string.
307+            assert len(msg) == msglen, (len(msg), msglen)
308+            md = a2b_hex(mo.group(3))
309+
310+            computed_md = tiger.Tiger(msg).digest()
311+            self.failUnlessEqual(computed_md, md)
312+
313+    def test_monte(self):
314+        infile = open(os.path.join('pycryptopp', 'test', 'vectors', 'SHA256Monte.txt'), 'rU')
315+        for line in infile:
316+            line = line.strip()
317+            if line[:7] == 'Seed = ':
318+                seed = a2b_hex(line[7:])
319+                break
320+
321+        j = 0
322+        for line in infile:
323+            line = line.strip()
324+            if line[:8] == 'COUNT = ':
325+                assert int(line[8:]) == j
326+            elif line[:5] == 'MD = ':
327+                mds = []
328+                mds.append(seed);mds.append(seed);mds.append(seed);
329+                for i in range(1000):
330+                    m = mds[-3]+mds[-2]+mds[-1]
331+                    mds.append(tiger.Tiger(m).digest())
332+                seed = mds[-1]
333+                self.failUnlessEqual(line[5:], b2a_hex(seed))
334+                j += 1
335+
336+if __name__ == "__main__":
337+    unittest.main()
338diff -rN -u old-from_zaula_new_and_improved/pycryptopp/test/vectors/salsa.txt new-from_zaula_new_and_improved/pycryptopp/test/vectors/salsa.txt
339--- old-from_zaula_new_and_improved/setup.py    2009-03-02 14:23:06.000000000 -0700
340+++ new-from_zaula_new_and_improved/setup.py    2009-03-02 14:23:09.000000000 -0700
341@@ -100,9 +100,17 @@
342     )
343 
344 ext_modules.append(
345+    Extension('pycryptopp.hash.tiger', cryptopp_src + ['pycryptopp/hash/tigermodule.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)
346+    )
347+
348+ext_modules.append(
349     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)
350     )
351