Skip to content

Commit 7b6a52e

Browse files
author
James Cleveland
committed
Merge pull request #1 from oohlaf/master
Update whirlpool to use Python's hashlib interface
2 parents ce71d18 + aad902b commit 7b6a52e

File tree

5 files changed

+430
-20
lines changed

5 files changed

+430
-20
lines changed

AUTHORS.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
James Cleveland <[email protected]>
2+
Olaf Conradi <[email protected]>

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
1-
# python-whirlpool
1+
python-whirlpool
2+
================
23

4+
The Whirlpool algorithm is designed by Vincent Rijmen and Paulo S.L.M. Barreto.
35
This is just a wrapper to the Whirlpool C reference implementation.
46
The Whirlpool reference implementations are public domain, as is this code.
57

68
Wrapper written by James Cleveland with help from #python on irc.freenode.net.
79

8-
USAGE:
10+
Wrapper modified by Olaf Conradi to use the hashlib interface.
11+
12+
Usage
13+
-----
14+
15+
This is the same interface as provided by the other digest algorithms in
16+
Python's hashlib.
917

1018
import whirlpool
1119

12-
hashed_string = whirlpool.hash("Mystring")
20+
wp = whirlpool.new("My String")
21+
hashed_string = wp.hexdigest()
22+
23+
wp.update("My Salt")
24+
hashed_string = wp.hexdigest()
25+
26+
Deprecated usage
27+
----------------
28+
29+
For backward compatibility the old interface remains available.
30+
31+
import whirlpool
1332

33+
hashed_string = whirlpool.hash("My String")
1434

main.c

Lines changed: 307 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,286 @@
77
* on irc.freenode.net
88
*/
99

10-
#define PY_SSIZE_T_CLEAN 1
1110
#include <Python.h>
1211
#include "Whirlpool.c"
12+
13+
typedef struct {
14+
PyObject_HEAD
15+
NESSIEstruct whirlpool; /* the context holder */
16+
} whirlpoolobject;
17+
18+
static PyTypeObject Whirlpooltype;
19+
20+
#define is_whirlpoolobject(v) ((v)->ob_type == &Whirlpooltype)
21+
22+
static whirlpoolobject *
23+
newwhirlpoolobject(void)
24+
{
25+
whirlpoolobject *wpp;
26+
27+
wpp = PyObject_New(whirlpoolobject, &Whirlpooltype);
28+
if (wpp == NULL)
29+
return NULL;
30+
31+
NESSIEinit(&wpp->whirlpool); /* actual initialisation */
32+
return wpp;
33+
}
34+
35+
/* Whirlpool methods */
36+
37+
static void
38+
whirlpool_dealloc(whirlpoolobject *wpp)
39+
{
40+
PyObject_Del(wpp);
41+
}
42+
43+
/* Whirlpool methods-as-attributes */
44+
45+
static PyObject *
46+
whirlpool_update(whirlpoolobject *self, PyObject *args)
47+
{
48+
Py_buffer view;
49+
50+
if (!PyArg_ParseTuple(args, "s*:update", &view))
51+
return NULL;
52+
53+
NESSIEadd((unsigned char*)view.buf,
54+
Py_SAFE_DOWNCAST(view.len, Py_ssize_t, unsigned int) * 8,
55+
&self->whirlpool);
56+
57+
PyBuffer_Release(&view);
58+
Py_RETURN_NONE;
59+
}
60+
61+
PyDoc_STRVAR(update_doc,
62+
"update (arg)\n\
63+
\n\
64+
Update the whirlpool object with the string arg. Repeated calls are\n\
65+
equivalent to a single call with the concatenation of all the\n\
66+
arguments.");
67+
68+
69+
static PyObject *
70+
whirlpool_digest(whirlpoolobject *self)
71+
{
72+
NESSIEstruct wpContext;
73+
unsigned char digest[DIGESTBYTES];
74+
75+
/* Make a temporary copy, and perform the final */
76+
wpContext = self->whirlpool;
77+
NESSIEfinalize(&wpContext, digest);
78+
79+
return PyString_FromStringAndSize((const char *)digest, sizeof(digest));
80+
}
81+
82+
PyDoc_STRVAR(digest_doc,
83+
"digest() -> string\n\
84+
\n\
85+
Return the digest of the strings passed to the update() method so\n\
86+
far. This is a binary string which may contain non-ASCII characters,\n\
87+
including null bytes.");
88+
89+
90+
static PyObject *
91+
whirlpool_hexdigest(whirlpoolobject *self)
92+
{
93+
NESSIEstruct wpContext;
94+
PyObject *retval;
95+
unsigned char digest[DIGESTBYTES];
96+
char *hexdigest;
97+
int i, j;
98+
99+
/* Get the raw (binary) digest value */
100+
wpContext = self->whirlpool;
101+
NESSIEfinalize(&wpContext, digest);
102+
103+
/* Create a new string */
104+
retval = PyString_FromStringAndSize(NULL, sizeof(digest) * 2);
105+
if (!retval)
106+
return NULL;
107+
hexdigest = PyString_AsString(retval);
108+
if (!hexdigest) {
109+
Py_DECREF(retval);
110+
return NULL;
111+
}
112+
113+
/* Make hex version of the digest */
114+
for(i=j=0; i<sizeof(digest); i++) {
115+
char c;
116+
c = (digest[i] >> 4) & 0xf;
117+
c = (c>9) ? c+'a'-10 : c+'0';
118+
hexdigest[j++] = c;
119+
c = (digest[i] & 0xf);
120+
c = (c>9) ? c+'a'-10 : c+'0';
121+
hexdigest[j++] = c;
122+
}
123+
return retval;
124+
}
125+
126+
PyDoc_STRVAR(hexdigest_doc,
127+
"hexdigest() -> string\n\
128+
\n\
129+
Like digest(), but returns the digest as a string of hexadecimal digits.");
130+
131+
132+
static PyObject *
133+
whirlpool_copy(whirlpoolobject *self)
134+
{
135+
whirlpoolobject *wpp;
136+
137+
if ((wpp = newwhirlpoolobject()) == NULL)
138+
return NULL;
139+
140+
wpp->whirlpool = self->whirlpool;
141+
return (PyObject *)wpp;
142+
}
143+
144+
PyDoc_STRVAR(copy_doc,
145+
"copy() -> whirlpool object\n\
146+
\n\
147+
Return a copy (``clone'') of the whirlpool object.");
148+
149+
150+
PyMethodDef whirlpool_methods[] = {
151+
{"update", (PyCFunction)whirlpool_update, METH_VARARGS, update_doc},
152+
{"digest", (PyCFunction)whirlpool_digest, METH_NOARGS, digest_doc},
153+
{"hexdigest", (PyCFunction)whirlpool_hexdigest, METH_NOARGS, hexdigest_doc},
154+
{"copy", (PyCFunction)whirlpool_copy, METH_NOARGS, copy_doc},
155+
{NULL, NULL} /* sentinel */
156+
};
157+
158+
159+
static PyObject *
160+
whirlpool_get_block_size(PyObject *self, void *closure)
161+
{
162+
return PyInt_FromLong(WBLOCKBYTES);
163+
}
164+
165+
static PyObject *
166+
whirlpool_get_digest_size(PyObject *self, void *closure)
167+
{
168+
return PyInt_FromLong(DIGESTBYTES);
169+
}
170+
171+
static PyObject *
172+
whirlpool_get_name(PyObject *self, void *closure)
173+
{
174+
return PyString_FromStringAndSize("WHIRLPOOL", 9);
175+
}
176+
177+
static PyGetSetDef whirlpool_getseters[] = {
178+
{"digest_size",
179+
(getter)whirlpool_get_digest_size, NULL,
180+
NULL,
181+
NULL},
182+
{"block_size",
183+
(getter)whirlpool_get_block_size, NULL,
184+
NULL,
185+
NULL},
186+
{"name",
187+
(getter)whirlpool_get_name, NULL,
188+
NULL,
189+
NULL},
190+
{NULL} /* sentinel */
191+
};
192+
193+
194+
PyDoc_STRVAR(module_doc,
195+
"This module implements the interface to the whirlpool message digest\n\
196+
algorithm. It operates on messages less than 2^256 bits in length,\n\
197+
and produces a message digest of 512 bits. Its use is quite straighforward:\n\
198+
use new() to create a whirlpool object. You can now feed this object with\n\
199+
arbitrary strings using the update() method. At any point you can ask it for\n\
200+
the digest of the concatenation of the strings fed to it so far.\n\
201+
\n\
202+
Functions:\n\
203+
new([arg]) -- return a new whirlpool object, initialized with arg if provided\n\
204+
hash(arg) -- DEPRECATED, returns a whirlpool digest of arg, for backward \
205+
compatibility\n\
206+
\n\
207+
Special Objects:\n\
208+
\n\
209+
WhirlpoolType -- type object for whirlpool objects");
210+
211+
PyDoc_STRVAR(whirlpooltype_doc,
212+
"A whirlpool represents the object used to calculate the WHIRLPOOL checksum of\n\
213+
a string of information.\n\
214+
\n\
215+
Methods:\n\
216+
\n\
217+
update(arg) -- updates the current digest with an additional string\n\
218+
digest() -- return the current digest value\n\
219+
hexdigest() -- return the current digest as a string of hexadecimal digits\n\
220+
copy() -- return a copy of the current whirlpool object");
221+
222+
static PyTypeObject Whirlpooltype = {
223+
PyVarObject_HEAD_INIT(NULL, 0)
224+
"whirlpool.whirlpool", /*tp_name*/
225+
sizeof(whirlpoolobject), /*tp_size*/
226+
0, /*tp_itemsize*/
227+
/* methods */
228+
(destructor)whirlpool_dealloc, /*tp_dealloc*/
229+
0, /*tp_print*/
230+
0, /*tp_getattr*/
231+
0, /*tp_setattr*/
232+
0, /*tp_compare*/
233+
0, /*tp_repr*/
234+
0, /*tp_as_number*/
235+
0, /*tp_as_sequence*/
236+
0, /*tp_as_mapping*/
237+
0, /*tp_hash*/
238+
0, /*tp_call*/
239+
0, /*tp_str*/
240+
0, /*tp_getattro*/
241+
0, /*tp_setattro*/
242+
0, /*tp_as_buffer*/
243+
Py_TPFLAGS_DEFAULT, /*tp_flags*/
244+
whirlpooltype_doc, /*tp_doc*/
245+
0, /*tp_traverse*/
246+
0, /*tp_clear*/
247+
0, /*tp_richcompare*/
248+
0, /*tp_weaklistoffset*/
249+
0, /*tp_iter*/
250+
0, /*tp_iternext*/
251+
whirlpool_methods, /*tp_methods */
252+
0, /*tp_members */
253+
whirlpool_getseters, /*tp_getset */
254+
};
255+
256+
257+
/* Whirlpool functions */
258+
259+
static PyObject *
260+
whirlpool_new(PyObject *self, PyObject *args)
261+
{
262+
whirlpoolobject *wpp;
263+
Py_buffer view = { 0 };
264+
265+
if (!PyArg_ParseTuple(args, "|s*:new", &view))
266+
return NULL;
267+
268+
if ((wpp = newwhirlpoolobject()) == NULL) {
269+
PyBuffer_Release(&view);
270+
return NULL;
271+
}
272+
273+
if (view.len > 0) {
274+
NESSIEadd((unsigned char*)view.buf,
275+
Py_SAFE_DOWNCAST(view.len, Py_ssize_t, unsigned int) * 8,
276+
&wpp->whirlpool);
277+
}
278+
PyBuffer_Release(&view);
279+
280+
return (PyObject *)wpp;
281+
}
282+
283+
PyDoc_STRVAR(new_doc,
284+
"new([arg]) -> whirlpool object\n\
285+
\n\
286+
Return a new whirlpool object. If arg is present, the method call update(arg)\n\
287+
is made.");
288+
289+
13290
static PyObject *
14291
whirlpool_hash(PyObject *self, PyObject *args) {
15292
struct NESSIEstruct w;
@@ -28,13 +305,36 @@ whirlpool_hash(PyObject *self, PyObject *args) {
28305
return Py_BuildValue("s#", digest, DIGESTBYTES);
29306
}
30307

31-
PyMethodDef methods[] = {
32-
{"hash", whirlpool_hash, METH_VARARGS,
33-
"Hash with whirlpool algorithm."},
34-
{NULL, NULL, 0, NULL}
308+
PyDoc_STRVAR(hash_doc,
309+
"Returns a hash of argument using the whirlpool algorithm.\n\
310+
This function is deprecated. Please use new() and hexdigest().");
311+
312+
313+
/* List of functions exported by this module */
314+
315+
static PyMethodDef whirlpool_functions[] = {
316+
{"new", (PyCFunction)whirlpool_new, METH_VARARGS, new_doc},
317+
{"hash", (PyCFunction)whirlpool_hash, METH_VARARGS, hash_doc},
318+
{NULL, NULL} /* sentinel */
35319
};
36320

321+
322+
/* Initialize this module */
323+
37324
PyMODINIT_FUNC
38-
initwhirlpool() {
39-
(void) Py_InitModule("whirlpool", methods);
325+
initwhirlpool(void)
326+
{
327+
PyObject *m, *d;
328+
329+
Py_TYPE(&Whirlpooltype) = &PyType_Type;
330+
if (PyType_Ready(&Whirlpooltype) < 0)
331+
return;
332+
m = Py_InitModule3("whirlpool", whirlpool_functions, module_doc);
333+
if (m == NULL)
334+
return;
335+
PyModule_AddIntConstant(m, "digest_size", DIGESTBYTES);
336+
PyModule_AddIntConstant(m, "block_size", WBLOCKBYTES);
337+
d = PyModule_GetDict(m);
338+
PyDict_SetItemString(d, "WhirlpoolType", (PyObject *)&Whirlpooltype);
40339
}
340+

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
implementation which is fairly simple to wrap with a Python extension, which is much
77
faster than re-implementation in pure Python.
88
"""
9-
from distutils.core import setup, Extension
9+
from setuptools import setup, Extension
1010

1111

1212
doclines = __doc__.split("\n")
1313

1414
setup(name = "Whirlpool",
15-
version = "0.1",
15+
version = "0.2",
1616
description = doclines[0],
1717
long_description = "\n".join(doclines[2:]),
1818
url = "https://github.com/radiosilence/python-whirlpool",
@@ -21,6 +21,7 @@
2121
license = "Public Domain",
2222
platforms = ["any"],
2323
ext_modules = [Extension("whirlpool", ["main.c"])],
24-
data_files = [("whirlpool", ['nessie.h', "Whirlpool.c"])]
24+
data_files = [("whirlpool", ['nessie.h', "Whirlpool.c"])],
25+
test_suite = "tests"
2526
)
2627

0 commit comments

Comments
 (0)