Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

585 lines (508 sloc) 16.962 kb
/*
* _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: */
Jump to Line
Something went wrong with that request. Please try again.