diff options
Diffstat (limited to 'Modules/_sha3/sha3module.c')
-rw-r--r-- | Modules/_sha3/sha3module.c | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/Modules/_sha3/sha3module.c b/Modules/_sha3/sha3module.c new file mode 100644 index 0000000..4c3c6db --- /dev/null +++ b/Modules/_sha3/sha3module.c @@ -0,0 +1,583 @@ +/* SHA3 module + * + * This module provides an interface to the SHA3 algorithm + * + * See below for information about the original code this module was + * based upon. Additional work performed by: + * + * Andrew Kuchling (amk@amk.ca) + * Greg Stein (gstein@lyra.org) + * Trevor Perrin (trevp@trevp.net) + * Gregory P. Smith (greg@krypto.org) + * + * Copyright (C) 2012 Christian Heimes (christian@python.org) + * Licensed to PSF under a Contributor Agreement. + * + */ + +#include "Python.h" +#include "../hashlib.h" + +/* ************************************************************************** + * SHA-3 (Keccak) + * + * The code is based on KeccakReferenceAndOptimized-3.2.zip from 29 May 2012. + * + * The reference implementation is altered in this points: + * - C++ comments are converted to ANSI C comments. + * - All functions and globals are declared static. + * - The typedef for UINT64 is commented out. + * - KeccakF-1600-opt[32|64]-settings.h are commented out + * - Some unused functions are commented out to silence compiler warnings. + * + * In order to avoid name clashes with other software I have to declare all + * Keccak functions and global data as static. The C code is directly + * included into this file in order to access the static functions. + * + * Keccak can be tuned with several paramenters. I try to explain all options + * as far as I understand them. The reference implementation also contains + * assembler code for ARM platforms (NEON instructions). + * + * Common + * ====== + * + * Options: + * UseBebigokimisa, Unrolling + * + * - Unrolling: loop unrolling (24, 12, 8, 6, 4, 3, 2, 1) + * - UseBebigokimisa: lane complementing + * + * 64bit platforms + * =============== + * + * Additional options: + * UseSSE, UseOnlySIMD64, UseMMX, UseXOP, UseSHLD + * + * Optimized instructions (disabled by default): + * - UseSSE: use Stream SIMD extensions + * o UseOnlySIMD64: limit to 64bit instructions, otherwise 128bit + * o w/o UseOnlySIMD64: requires compiler agument -mssse3 or -mtune + * - UseMMX: use 64bit MMX instructions + * - UseXOP: use AMD's eXtended Operations (128bit SSE extension) + * + * Other: + * - Unrolling: default 24 + * - UseBebigokimisa: default 1 + * + * When neither UseSSE, UseMMX nor UseXOP is configured, ROL64 (rotate left + * 64) is implemented as: + * - Windows: _rotl64() + * - UseSHLD: use shld (shift left) asm optimization + * - otherwise: shift and xor + * + * UseBebigokimisa can't be used in combination with UseSSE, UseMMX or + * UseXOP. UseOnlySIMD64 has no effect unless UseSSE is specified. + * + * Tests have shown that UseSSE + UseOnlySIMD64 is about three to four + * times SLOWER than UseBebigokimisa. UseSSE and UseMMX are about two times + * slower. (tested by CH and AP) + * + * 32bit platforms + * =============== + * + * Additional options: + * UseInterleaveTables, UseSchedule + * + * - Unrolling: default 2 + * - UseBebigokimisa: default n/a + * - UseSchedule: ???, (1, 2, 3; default 3) + * - UseInterleaveTables: use two 64k lookup tables for (de)interleaving + * default: n/a + * + * schedules: + * - 3: no UseBebigokimisa, Unrolling must be 2 + * - 2 + 1: ??? + * + * *************************************************************************/ + +#ifdef __sparc + /* opt64 uses un-aligned memory access that causes a BUS error with msg + * 'invalid address alignment' on SPARC. */ + #define KeccakOpt 32 +#elif SIZEOF_VOID_P == 8 && defined(PY_UINT64_T) + /* opt64 works only for 64bit platforms with unsigned int64 */ + #define KeccakOpt 64 +#else + /* opt32 is used for the remaining 32 and 64bit platforms */ + #define KeccakOpt 32 +#endif + +#if KeccakOpt == 64 && defined(PY_UINT64_T) + /* 64bit platforms with unsigned int64 */ + #define Unrolling 24 + #define UseBebigokimisa + typedef PY_UINT64_T UINT64; +#elif KeccakOpt == 32 && defined(PY_UINT64_T) + /* 32bit platforms with unsigned int64 */ + #define Unrolling 2 + #define UseSchedule 3 + typedef PY_UINT64_T UINT64; +#else + /* 32 or 64bit platforms without unsigned int64 */ + #define Unrolling 2 + #define UseSchedule 3 + #define UseInterleaveTables +#endif + +/* replacement for brg_endian.h */ +#define IS_BIG_ENDIAN 4321 +#define IS_LITTLE_ENDIAN 1234 +#if PY_BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#else +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +/* inline all Keccak dependencies */ +#include "keccak/KeccakNISTInterface.h" +#include "keccak/KeccakNISTInterface.c" +#include "keccak/KeccakSponge.c" +#if KeccakOpt == 64 + #include "keccak/KeccakF-1600-opt64.c" +#elif KeccakOpt == 32 + #include "keccak/KeccakF-1600-opt32.c" +#endif + +/* #define SHA3_BLOCKSIZE 200 // 1600 bits */ +#define SHA3_MAX_DIGESTSIZE 64 /* 512 bits */ +#define SHA3_state hashState +#define SHA3_init Init +#define SHA3_process Update +#define SHA3_done Final +#define SHA3_copystate(dest, src) memcpy(&(dest), &(src), sizeof(SHA3_state)) +#define SHA3_clearstate(state) memset(&(state), 0, sizeof(SHA3_state)) + +/* The structure for storing SHA3 info */ + +typedef struct { + PyObject_HEAD + int hashbitlen; + SHA3_state hash_state; +#ifdef WITH_THREAD + PyThread_type_lock lock; +#endif + +} SHA3object; + +static PyTypeObject SHA3type; + + +static SHA3object * +newSHA3object(int hashbitlen) +{ + SHA3object *newobj; + + /* check hashbitlen */ + switch(hashbitlen) { + /* supported hash length */ + case 224: + break; + case 256: + break; + case 384: + break; + case 512: + break; + case 0: + /* arbitrarily-long output isn't supported by this module */ + default: + /* everything else is an error */ + PyErr_SetString(PyExc_ValueError, + "hashbitlen must be one of 224, 256, 384 or 512."); + return NULL; + } + newobj = (SHA3object *)PyObject_New(SHA3object, &SHA3type); + if (newobj == NULL) { + return NULL; + } + newobj->hashbitlen = hashbitlen; +#ifdef WITH_THREAD + newobj->lock = NULL; +#endif + return newobj; +} + + +/* Internal methods for a hash object */ + +static void +SHA3_dealloc(SHA3object *self) +{ + SHA3_clearstate(self->hash_state); +#ifdef WITH_THREAD + if (self->lock) { + PyThread_free_lock(self->lock); + } +#endif + PyObject_Del(self); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(SHA3_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +SHA3_copy(SHA3object *self, PyObject *unused) +{ + SHA3object *newobj; + + if ((newobj = newSHA3object(self->hashbitlen)) == NULL) { + return NULL; + } + ENTER_HASHLIB(self); + SHA3_copystate(newobj->hash_state, self->hash_state); + LEAVE_HASHLIB(self); + return (PyObject *)newobj; +} + + +PyDoc_STRVAR(SHA3_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA3_digest(SHA3object *self, PyObject *unused) +{ + unsigned char digest[SHA3_MAX_DIGESTSIZE]; + SHA3_state temp; + HashReturn res; + + ENTER_HASHLIB(self); + SHA3_copystate(temp, self->hash_state); + LEAVE_HASHLIB(self); + res = SHA3_done(&temp, digest); + SHA3_clearstate(temp); + if (res != SUCCESS) { + PyErr_SetString(PyExc_RuntimeError, "internal error in SHA3 Final()"); + return NULL; + } + return PyBytes_FromStringAndSize((const char *)digest, + self->hashbitlen / 8); +} + + +PyDoc_STRVAR(SHA3_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA3_hexdigest(SHA3object *self, PyObject *unused) +{ + unsigned char digest[SHA3_MAX_DIGESTSIZE]; + SHA3_state temp; + HashReturn res; + PyObject *retval; + Py_UCS1 *hex_digest; + int digestlen, i, j; + + /* Get the raw (binary) digest value */ + ENTER_HASHLIB(self); + SHA3_copystate(temp, self->hash_state); + LEAVE_HASHLIB(self); + res = SHA3_done(&temp, digest); + SHA3_clearstate(temp); + if (res != SUCCESS) { + PyErr_SetString(PyExc_RuntimeError, "internal error in SHA3 Final()"); + return NULL; + } + + /* Create a new string */ + digestlen = self->hashbitlen / 8; + retval = PyUnicode_New(digestlen * 2, 127); + if (!retval) + return NULL; + hex_digest = PyUnicode_1BYTE_DATA(retval); + + /* Make hex version of the digest */ + for(i=j=0; i < digestlen; i++) { + unsigned char c; + c = (digest[i] >> 4) & 0xf; + hex_digest[j++] = Py_hexdigits[c]; + c = (digest[i] & 0xf); + hex_digest[j++] = Py_hexdigits[c]; + } + assert(_PyUnicode_CheckConsistency(retval, 1)); + return retval; +} + +PyDoc_STRVAR(SHA3_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +SHA3_update(SHA3object *self, PyObject *args) +{ + PyObject *obj; + Py_buffer buf; + HashReturn res; + + if (!PyArg_ParseTuple(args, "O:update", &obj)) + return NULL; + + GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); + + /* add new data, the function takes the length in bits not bytes */ +#ifdef WITH_THREADS + if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) { + self->lock = PyThread_allocate_lock(); + } + /* Once a lock exists all code paths must be synchronized. We have to + * release the GIL even for small buffers as acquiring the lock may take + * an unlimited amount of time when another thread updates this object + * with lots of data. */ + if (self->lock) { + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(self->lock, 1); + res = SHA3_process(&self->hash_state, buf.buf, buf.len * 8); + PyThread_release_lock(self->lock); + Py_END_ALLOW_THREADS + } + else { + res = SHA3_process(&self->hash_state, buf.buf, buf.len * 8); + } +#else + res = SHA3_process(&self->hash_state, buf.buf, buf.len * 8); +#endif + LEAVE_HASHLIB(self); + + if (res != SUCCESS) { + PyBuffer_Release(&buf); + PyErr_SetString(PyExc_RuntimeError, + "internal error in SHA3 Update()"); + return NULL; + } + + PyBuffer_Release(&buf); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA3_methods[] = { + {"copy", (PyCFunction)SHA3_copy, METH_NOARGS, + SHA3_copy__doc__}, + {"digest", (PyCFunction)SHA3_digest, METH_NOARGS, + SHA3_digest__doc__}, + {"hexdigest", (PyCFunction)SHA3_hexdigest, METH_NOARGS, + SHA3_hexdigest__doc__}, + {"update", (PyCFunction)SHA3_update, METH_VARARGS, + SHA3_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA3_get_block_size(SHA3object *self, void *closure) +{ + /* HMAC-SHA3 hasn't been specified yet and no official test vectors are + * available. Thus block_size returns NotImplemented to prevent people + * from using SHA3 with the hmac module. + */ + Py_RETURN_NOTIMPLEMENTED; +} + +static PyObject * +SHA3_get_name(SHA3object *self, void *closure) +{ + return PyUnicode_FromFormat("sha3_%i", self->hashbitlen); +} + +static PyObject * +SHA3_get_digest_size(SHA3object *self, void *closure) +{ + return PyLong_FromLong(self->hashbitlen / 8); +} + + +static PyGetSetDef SHA3_getseters[] = { + {"block_size", (getter)SHA3_get_block_size, NULL, NULL, NULL}, + {"name", (getter)SHA3_get_name, NULL, NULL, NULL}, + {"digest_size", (getter)SHA3_get_digest_size, NULL, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHA3type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_sha3.SHA3", /* tp_name */ + sizeof(SHA3object), /* tp_size */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)SHA3_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + SHA3_methods, /* tp_methods */ + NULL, /* tp_members */ + SHA3_getseters, /* tp_getset */ +}; + + +/* constructor helper */ +static PyObject * +SHA3_factory(PyObject *args, PyObject *kwdict, const char *fmt, + int hashbitlen) +{ + SHA3object *newobj = NULL; + static char *kwlist[] = {"string", NULL}; + PyObject *data_obj = NULL; + Py_buffer buf; + HashReturn res; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, fmt, kwlist, + &data_obj)) { + return NULL; + } + + if (data_obj) + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &buf); + + if ((newobj = newSHA3object(hashbitlen)) == NULL) { + goto error; + } + + if (SHA3_init(&newobj->hash_state, hashbitlen) != SUCCESS) { + PyErr_SetString(PyExc_RuntimeError, + "internal error in SHA3 Update()"); + goto error; + } + + if (data_obj) { +#ifdef WITH_THREADS + if (buf.len >= HASHLIB_GIL_MINSIZE) { + /* invariant: New objects can't be accessed by other code yet, + * thus it's safe to release the GIL without locking the object. + */ + Py_BEGIN_ALLOW_THREADS + res = SHA3_process(&newobj->hash_state, buf.buf, buf.len * 8); + Py_END_ALLOW_THREADS + } + else { + res = SHA3_process(&newobj->hash_state, buf.buf, buf.len * 8); + } +#else + res = SHA3_process(&newobj->hash_state, buf.buf, buf.len * 8); +#endif + if (res != SUCCESS) { + PyErr_SetString(PyExc_RuntimeError, + "internal error in SHA3 Update()"); + goto error; + } + PyBuffer_Release(&buf); + } + + return (PyObject *)newobj; + + error: + if (newobj) { + SHA3_dealloc(newobj); + } + if (data_obj) { + PyBuffer_Release(&buf); + } + return NULL; + +} + +PyDoc_STRVAR(sha3_224__doc__, +"sha3_224([string]) -> SHA3 object\n\ +\n\ +Return a new SHA3 hash object with a hashbit length of 28 bytes."); + +static PyObject * +sha3_224(PyObject *self, PyObject *args, PyObject *kwdict) +{ + return SHA3_factory(args, kwdict, "|O:sha3_224", 224); +} + + +PyDoc_STRVAR(sha3_256__doc__, +"sha3_256([string]) -> SHA3 object\n\ +\n\ +Return a new SHA3 hash object with a hashbit length of 32 bytes."); + +static PyObject * +sha3_256(PyObject *self, PyObject *args, PyObject *kwdict) +{ + return SHA3_factory(args, kwdict, "|O:sha3_256", 256); +} + +PyDoc_STRVAR(sha3_384__doc__, +"sha3_384([string]) -> SHA3 object\n\ +\n\ +Return a new SHA3 hash object with a hashbit length of 48 bytes."); + +static PyObject * +sha3_384(PyObject *self, PyObject *args, PyObject *kwdict) +{ + return SHA3_factory(args, kwdict, "|O:sha3_384", 384); +} + +PyDoc_STRVAR(sha3_512__doc__, +"sha3_512([string]) -> SHA3 object\n\ +\n\ +Return a new SHA3 hash object with a hashbit length of 64 bytes."); + +static PyObject * +sha3_512(PyObject *self, PyObject *args, PyObject *kwdict) +{ + return SHA3_factory(args, kwdict, "|O:sha3_512", 512); +} + + +/* List of functions exported by this module */ +static struct PyMethodDef SHA3_functions[] = { + {"sha3_224", (PyCFunction)sha3_224, METH_VARARGS|METH_KEYWORDS, + sha3_224__doc__}, + {"sha3_256", (PyCFunction)sha3_256, METH_VARARGS|METH_KEYWORDS, + sha3_256__doc__}, + {"sha3_384", (PyCFunction)sha3_384, METH_VARARGS|METH_KEYWORDS, + sha3_384__doc__}, + {"sha3_512", (PyCFunction)sha3_512, METH_VARARGS|METH_KEYWORDS, + sha3_512__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ +static struct PyModuleDef _SHA3module = { + PyModuleDef_HEAD_INIT, + "_sha3", + NULL, + -1, + SHA3_functions, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__sha3(void) +{ + Py_TYPE(&SHA3type) = &PyType_Type; + if (PyType_Ready(&SHA3type) < 0) { + return NULL; + } + + return PyModule_Create(&_SHA3module); +} |