blob: 9b396e443da1255e69018855036d8acb0709a80c [file] [log] [blame] [edit]
/*
* _counter.c: Fast counter for use with CTR-mode ciphers
*
* Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
*
* ===================================================================
* The contents of this file are dedicated to the public domain. To
* the extent that dedication to the public domain is not available,
* everyone is granted a worldwide, perpetual, royalty-free,
* non-exclusive license to exercise all rights associated with the
* contents of this file for any purpose whatsoever.
* No rights are reserved.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ===================================================================
*/
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include "Python.h"
#include "pycrypto_compat.h"
#include "_counter.h"
#ifndef IS_PY3K
#define PyLong_FromLong PyInt_FromLong
#endif
/* NB: This can be called multiple times for a given object, via the __init__ method. Be careful. */
static int
CounterObject_init(PCT_CounterObject *self, PyObject *args, PyObject *kwargs)
{
#ifdef IS_PY3K
PyBytesObject *prefix=NULL, *suffix=NULL, *initval=NULL;
#else
PyStringObject *prefix=NULL, *suffix=NULL, *initval=NULL;
#endif
int allow_wraparound = 0;
int disable_shortcut = 0;
Py_ssize_t size;
static char *kwlist[] = {"prefix", "suffix", "initval", "allow_wraparound", "disable_shortcut", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "SSS|ii", kwlist, &prefix, &suffix, &initval, &allow_wraparound, &disable_shortcut))
return -1;
/* Check string size and set nbytes */
size = PyBytes_GET_SIZE(initval);
if (size < 1) {
PyErr_SetString(PyExc_ValueError, "initval length too small (must be >= 1 byte)");
return -1;
} else if (size > 0xffff) {
PyErr_SetString(PyExc_ValueError, "initval length too large (must be <= 65535 bytes)");
return -1;
}
self->nbytes = (uint16_t) size;
/* Check prefix length */
size = PyBytes_GET_SIZE(prefix);
assert(size >= 0);
if (size > 0xffff) {
PyErr_SetString(PyExc_ValueError, "prefix length too large (must be <= 65535 bytes)");
return -1;
}
/* Check suffix length */
size = PyBytes_GET_SIZE(suffix);
assert(size >= 0);
if (size > 0xffff) {
PyErr_SetString(PyExc_ValueError, "suffix length too large (must be <= 65535 bytes)");
return -1;
}
/* Set prefix, being careful to properly discard any old reference */
Py_CLEAR(self->prefix);
Py_INCREF(prefix);
self->prefix = prefix;
/* Set prefix, being careful to properly discard any old reference */
Py_CLEAR(self->suffix);
Py_INCREF(suffix);
self->suffix = suffix;
/* Free old buffer (if any) */
if (self->val) {
PyMem_Free(self->val);
self->val = self->p = NULL;
self->buf_size = 0;
}
/* Allocate new buffer */
/* buf_size won't overflow because the length of each string will always be <= 0xffff */
self->buf_size = PyBytes_GET_SIZE(prefix) + PyBytes_GET_SIZE(suffix) + self->nbytes;
self->val = self->p = PyMem_Malloc(self->buf_size);
if (self->val == NULL) {
self->buf_size = 0;
return -1;
}
self->p = self->val + PyBytes_GET_SIZE(prefix);
/* Sanity-check pointers */
assert(self->val <= self->p);
assert(self->p + self->nbytes <= self->val + self->buf_size);
assert(self->val + PyBytes_GET_SIZE(self->prefix) == self->p);
assert(PyBytes_GET_SIZE(self->prefix) + self->nbytes + PyBytes_GET_SIZE(self->suffix) == self->buf_size);
/* Copy the prefix, suffix, and initial value into the buffer. */
memcpy(self->val, PyBytes_AS_STRING(prefix), PyBytes_GET_SIZE(prefix));
memcpy(self->p, PyBytes_AS_STRING(initval), self->nbytes);
memcpy(self->p + self->nbytes, PyBytes_AS_STRING(suffix), PyBytes_GET_SIZE(suffix));
/* Set shortcut_disabled and allow_wraparound */
self->shortcut_disabled = disable_shortcut;
self->allow_wraparound = allow_wraparound;
/* Clear the carry flag */
self->carry = 0;
return 0;
}
static void
CounterObject_dealloc(PCT_CounterObject *self)
{
/* Free the buffer */
if (self->val) {
memset(self->val, 0, self->buf_size); /* wipe the buffer before freeing it */
PyMem_Free(self->val);
self->val = self->p = NULL;
self->buf_size = 0;
}
/* Deallocate the prefix and suffix, if they are present. */
Py_CLEAR(self->prefix);
Py_CLEAR(self->suffix);
/* Free this object */
PyObject_Del(self);
}
static inline PyObject *
_CounterObject_next_value(PCT_CounterObject *self, int little_endian)
{
unsigned int i;
int increment;
uint8_t *p;
PyObject *eight = NULL;
PyObject *ch = NULL;
PyObject *y = NULL;
PyObject *x = NULL;
if (self->carry && !self->allow_wraparound) {
PyErr_SetString(PyExc_OverflowError,
"counter wrapped without allow_wraparound");
goto err_out;
}
eight = PyLong_FromLong(8);
if (!eight)
goto err_out;
/* Make a new Python long integer */
x = PyLong_FromUnsignedLong(0);
if (!x)
goto err_out;
if (little_endian) {
/* little endian */
p = self->p + self->nbytes - 1;
increment = -1;
} else {
/* big endian */
p = self->p;
increment = 1;
}
for (i = 0; i < self->nbytes; i++, p += increment) {
/* Sanity check pointer */
assert(self->p <= p);
assert(p < self->p + self->nbytes);
/* ch = ord(p) */
Py_CLEAR(ch); /* delete old ch */
ch = PyLong_FromLong((long) *p);
if (!ch)
goto err_out;
/* y = x << 8 */
Py_CLEAR(y); /* delete old y */
y = PyNumber_Lshift(x, eight);
if (!y)
goto err_out;
/* x = y | ch */
Py_CLEAR(x); /* delete old x */
x = PyNumber_Or(y, ch);
}
Py_CLEAR(eight);
Py_CLEAR(ch);
Py_CLEAR(y);
return x;
err_out:
Py_CLEAR(eight);
Py_CLEAR(ch);
Py_CLEAR(y);
Py_CLEAR(x);
return NULL;
}
static PyObject *
CounterLEObject_next_value(PCT_CounterObject *self, PyObject *args)
{
return _CounterObject_next_value(self, 1);
}
static PyObject *
CounterBEObject_next_value(PCT_CounterObject *self, PyObject *args)
{
return _CounterObject_next_value(self, 0);
}
static void
CounterLEObject_increment(PCT_CounterObject *self)
{
unsigned int i, tmp, carry;
uint8_t *p;
assert(sizeof(i) >= sizeof(self->nbytes));
carry = 1;
p = self->p;
for (i = 0; i < self->nbytes; i++, p++) {
/* Sanity check pointer */
assert(self->p <= p);
assert(p < self->p + self->nbytes);
tmp = *p + carry;
carry = tmp >> 8; /* This will only ever be 0 or 1 */
*p = tmp & 0xff;
}
self->carry = carry;
}
static void
CounterBEObject_increment(PCT_CounterObject *self)
{
unsigned int i, tmp, carry;
uint8_t *p;
assert(sizeof(i) >= sizeof(self->nbytes));
carry = 1;
p = self->p + self->nbytes-1;
for (i = 0; i < self->nbytes; i++, p--) {
/* Sanity check pointer */
assert(self->p <= p);
assert(p < self->p + self->nbytes);
tmp = *p + carry;
carry = tmp >> 8; /* This will only ever be 0 or 1 */
*p = tmp & 0xff;
}
self->carry = carry;
}
static PyObject *
CounterObject_call(PCT_CounterObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *retval;
if (self->carry && !self->allow_wraparound) {
PyErr_SetString(PyExc_OverflowError,
"counter wrapped without allow_wraparound");
return NULL;
}
retval = (PyObject *)PyBytes_FromStringAndSize((const char *)self->val, self->buf_size);
self->inc_func(self);
return retval;
}
static PyMethodDef CounterLEObject_methods[] = {
{"next_value", (PyCFunction)CounterLEObject_next_value, METH_VARARGS,
"Get the numerical value of next value of the counter."},
{NULL} /* sentinel */
};
static PyMethodDef CounterBEObject_methods[] = {
{"next_value", (PyCFunction)CounterBEObject_next_value, METH_VARARGS,
"Get the numerical value of next value of the counter."},
{NULL} /* sentinel */
};
/* Python 2.1 doesn't allow us to assign methods or attributes to an object,
* so we hack it here. */
static PyObject *
#ifdef IS_PY3K
CounterLEObject_getattro(PyObject *s, PyObject *attr)
#else
CounterLEObject_getattr(PyObject *s, char *name)
#endif
{
PCT_CounterObject *self = (PCT_CounterObject *)s;
#ifdef IS_PY3K
if (!PyUnicode_Check(attr))
goto generic;
if (PyUnicode_CompareWithASCIIString(attr, "carry") == 0) {
#else
if (strcmp(name, "carry") == 0) {
#endif
return PyLong_FromLong((long)self->carry);
#ifdef IS_PY3K
} else if (!self->shortcut_disabled && PyUnicode_CompareWithASCIIString(attr, "__PCT_CTR_SHORTCUT__") == 0) {
#else
} else if (!self->shortcut_disabled && strcmp(name, "__PCT_CTR_SHORTCUT__") == 0) {
#endif
/* Shortcut hack - See block_template.c */
Py_INCREF(Py_True);
return Py_True;
}
#ifdef IS_PY3K
generic:
return PyObject_GenericGetAttr(s, attr);
#else
return Py_FindMethod(CounterLEObject_methods, (PyObject *)self, name);
#endif
}
static PyObject *
#ifdef IS_PY3K
CounterBEObject_getattro(PyObject *s, PyObject *attr)
#else
CounterBEObject_getattr(PyObject *s, char *name)
#endif
{
PCT_CounterObject *self = (PCT_CounterObject *)s;
#ifdef IS_PY3K
if (!PyUnicode_Check(attr))
goto generic;
if (PyUnicode_CompareWithASCIIString(attr, "carry") == 0) {
#else
if (strcmp(name, "carry") == 0) {
#endif
return PyLong_FromLong((long)self->carry);
#ifdef IS_PY3K
} else if (!self->shortcut_disabled && PyUnicode_CompareWithASCIIString(attr, "__PCT_CTR_SHORTCUT__") == 0) {
#else
} else if (!self->shortcut_disabled && strcmp(name, "__PCT_CTR_SHORTCUT__") == 0) {
#endif
/* Shortcut hack - See block_template.c */
Py_INCREF(Py_True);
return Py_True;
}
#ifdef IS_PY3K
generic:
return PyObject_GenericGetAttr(s, attr);
#else
return Py_FindMethod(CounterBEObject_methods, (PyObject *)self, name);
#endif
}
static PyTypeObject
my_CounterLEType = {
#ifdef IS_PY3K
PyVarObject_HEAD_INIT(NULL, 0) /* deferred type init for compilation on Windows, type will be filled in at runtime */
#else
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
#endif
"_counter.CounterLE", /* tp_name */
sizeof(PCT_CounterObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)CounterObject_dealloc, /* tp_dealloc */
0, /* tp_print */
#ifdef IS_PY3K
0, /* tp_getattr */
#else
CounterLEObject_getattr, /* tp_getattr */
#endif
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc)CounterObject_call, /* tp_call */
0, /* tp_str */
#ifdef IS_PY3K
CounterLEObject_getattro, /* tp_getattro */
#else
0, /* tp_getattro */
#endif
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Counter (little endian)", /* tp_doc */
#ifdef IS_PY3K
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
CounterLEObject_methods, /*tp_methods*/
#endif
};
static PyTypeObject
my_CounterBEType = {
#ifdef IS_PY3K
PyVarObject_HEAD_INIT(NULL, 0) /* deferred type init for compilation on Windows, type will be filled in at runtime */
#else
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
#endif
"_counter.CounterBE", /* tp_name */
sizeof(PCT_CounterObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)CounterObject_dealloc, /* tp_dealloc */
0, /* tp_print */
#ifdef IS_PY3K
0, /* tp_getattr */
#else
CounterBEObject_getattr, /* tp_getattr */
#endif
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc)CounterObject_call, /* tp_call */
0, /* tp_str */
#ifdef IS_PY3K
CounterBEObject_getattro, /* tp_getattro */
#else
0, /* tp_getattro */
#endif
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Counter (big endian)", /* tp_doc */
#ifdef IS_PY3K
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
CounterBEObject_methods, /*tp_methods*/
#endif
};
/*
* Python 2.1 doesn't seem to allow a C equivalent of the __init__ method, so
* we use the module-level functions newLE and newBE here.
*/
static PyObject *
CounterLE_new(PyObject *self, PyObject *args, PyObject *kwargs)
{
PCT_CounterObject *obj = NULL;
/* Create the new object */
obj = PyObject_New(PCT_CounterObject, &my_CounterLEType);
if (obj == NULL) {
return NULL;
}
/* Zero the custom portion of the structure */
memset(&obj->prefix, 0, sizeof(PCT_CounterObject) - offsetof(PCT_CounterObject, prefix));
/* Call the object's initializer. Delete the object if this fails. */
if (CounterObject_init(obj, args, kwargs) != 0) {
return NULL;
}
/* Set the inc_func pointer */
obj->inc_func = (void (*)(void *))CounterLEObject_increment;
/* Return the object */
return (PyObject *)obj;
}
static PyObject *
CounterBE_new(PyObject *self, PyObject *args, PyObject *kwargs)
{
PCT_CounterObject *obj = NULL;
/* Create the new object */
obj = PyObject_New(PCT_CounterObject, &my_CounterBEType);
if (obj == NULL) {
return NULL;
}
/* Zero the custom portion of the structure */
memset(&obj->prefix, 0, sizeof(PCT_CounterObject) - offsetof(PCT_CounterObject, prefix));
/* Call the object's initializer. Delete the object if this fails. */
if (CounterObject_init(obj, args, kwargs) != 0) {
return NULL;
}
/* Set the inc_func pointer */
obj->inc_func = (void (*)(void *))CounterBEObject_increment;
/* Return the object */
return (PyObject *)obj;
}
/*
* Module-level method table and module initialization function
*/
static PyMethodDef module_methods[] = {
{"_newLE", (PyCFunction) CounterLE_new, METH_VARARGS|METH_KEYWORDS, NULL},
{"_newBE", (PyCFunction) CounterBE_new, METH_VARARGS|METH_KEYWORDS, NULL},
{NULL, NULL, 0, NULL} /* end-of-list sentinel value */
};
#ifdef IS_PY3K
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_counter",
NULL,
-1,
module_methods,
NULL,
NULL,
NULL,
NULL
};
#endif
PyMODINIT_FUNC
#ifdef IS_PY3K
PyInit__counter(void)
#else
init_counter(void)
#endif
{
PyObject *m;
/* TODO - Is the error handling here correct? */
#ifdef IS_PY3K
/* PyType_Ready automatically fills in ob_type with &PyType_Type if it's not already set */
if (PyType_Ready(&my_CounterLEType) < 0)
return NULL;
if (PyType_Ready(&my_CounterBEType) < 0)
return NULL;
/* Initialize the module */
m = PyModule_Create(&moduledef);
if (m == NULL)
return NULL;
return m;
#else
m = Py_InitModule("_counter", module_methods);
if (m == NULL)
return;
my_CounterLEType.ob_type = &PyType_Type;
my_CounterBEType.ob_type = &PyType_Type;
#endif
}
/* vim:set ts=4 sw=4 sts=4 expandtab: */