summaryrefslogtreecommitdiffstats
path: root/Modules/_sha3/sha3module.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_sha3/sha3module.c')
-rw-r--r--Modules/_sha3/sha3module.c583
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);
+}