blob: 80d2d9b5294bcb9c5476e97133a7000ac7930e4b [file]
/*
* galois.c: arithmetic in Galois Fields
*
* ===================================================================
* 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 "pycrypto_common.h"
#include <stddef.h>
#include <assert.h>
#include <string.h>
#define ALIGNMENT 32
/**
* A V table is a 4096 bytes table that will contain the expanded
* GHASH key (H). It is used to speed up the GF(128) multiplication Z = X*H.
*
* The table contains 128 entries, one for each bit of X.
* Each entry takes 32 bytes and can fit into the cache line of a modern
* processor. If we assume that access to memory mapped to the same
* cache line is somewhat constant, we can make GHASH robust again
* cache timing attacks.
*/
typedef uint64_t t_v_tables[128][2][2];
/**
* To ensure that the V table is aligned to a 32-byte memory boundary,
* we allocate a larger piece of memory and carve the V table from there.
*/
typedef struct {
uint8_t buffer[sizeof(t_v_tables)+ALIGNMENT];
int offset;
} t_exp_key;
/**
* Big Endian to word conversions
*/
static uint64_t be_to_word(const uint8_t fb[8])
{
uint64_t tmp;
int i;
tmp = 0;
for (i=0; i<8; i++)
tmp = tmp<<8 ^ *fb++;
return tmp;
}
/**
* Word to Big Endian conversions
*/
static void word_to_be(uint8_t fb[8], uint64_t w)
{
int i;
for (i=0; i<8; i++) {
fb[7-i] = (uint8_t) w;
w >>= 8;
}
}
/**
* Create a V table. V[i] is the value H*x^i (i=0..127).
* \param h The 16 byte GHASH key
* \param tables A pointer to an allocated V table
*/
static void make_v_tables(const uint8_t h[16], t_v_tables *tables)
{
uint64_t (*cur)[2];
int i;
memset(tables, 0, sizeof(t_v_tables));
cur = &((*tables)[0][1]);
(*cur)[0] = be_to_word(&h[0]);
(*cur)[1] = be_to_word(&h[8]);
for (i=1; i<128; i++) {
uint64_t c;
uint64_t (*next)[2];
next = &((*tables)[i][1]);
/** v = (v&1)*0xE1000000000000000000000000000000L ^ (v>>1) **/
c = (*cur)[1]&1 ? 0xE100000000000000 : 0;
(*next)[1] = (*cur)[1]>>1 | (*cur)[0]<<63;
(*next)[0] = (*cur)[0]>>1 ^ c;
cur = next;
}
}
/**
* Multiply two elements of GF(2**128) using the reducing polynomial
* (x^128 + x^7 + x^2 + x + 1).
*
* \param out The 16 byte buffer that will receive the result
* \param key_tables One factor, expanded into a V table
* \param x The other factor (16 bytes)
*/
static void gcm_mult2(uint8_t out[16], const t_v_tables *key_tables, const uint8_t x[16])
{
int i, bit_scan_128;
uint64_t z[2];
z[0] = z[1] = 0;
bit_scan_128 = 0;
for (i=0; i<16; i++) {
uint8_t xi;
int j;
xi = x[i];
for (j=0; j<8; j++) {
int bit;
bit = xi>>7 & 1; /** Constant time */
z[0] ^= (*key_tables)[bit_scan_128][bit][0];
z[1] ^= (*key_tables)[bit_scan_128][bit][1];
xi <<= 1;
bit_scan_128++;
}
}
word_to_be(out, z[0]);
word_to_be(out+8, z[1]);
}
/**
* Compute the GHASH of a piece of data given an arbitrary Y_0,
* as specified in NIST SP 800 38D.
*
* \param y_out The resulting GHASH (16 bytes).
* \param block_data Pointer to the data to hash.
* \param len Length of the data to hash (multiple of 16).
* \param y_in The initial Y (Y_0, 16 bytes).
* \param key_tables The expanded hash key (16*256*16 bytes).
*/
static void ghash(
uint8_t y_out[16],
const uint8_t block_data[],
int len,
const uint8_t y_in[16],
const t_v_tables *key_tables
)
{
int i;
memcpy(y_out, y_in, 16);
for (i=0; i<len; i+=16) {
int j;
uint8_t x[16];
for (j=0; j<16; j++) {
x[j] = y_out[j] ^ block_data[i+j];
}
gcm_mult2(y_out, key_tables, x);
}
}
static void realign_v_tables(t_exp_key *exp_key)
{
int new_offset;
new_offset = ALIGNMENT - ((uintptr_t)exp_key->buffer & (ALIGNMENT-1));
if (new_offset != exp_key->offset) {
memmove(exp_key->buffer+new_offset,
exp_key->buffer+exp_key->offset,
sizeof(t_v_tables));
exp_key->offset = new_offset;
}
}
static char ghash_expand__doc__[] =
"ghash_expand(h:str) -> str\n"
"\n"
"Return an expanded GHASH key.\n";
/**
* Expand the AES key into a Python (byte) string object.
*/
static PyObject *
ghash_expand_function(PyObject *self, PyObject *args)
{
PyObject *h;
PyObject *retval = NULL;
Py_ssize_t len_h;
t_exp_key *exp_key;
if (!PyArg_ParseTuple(args, "S", &h)) {
goto out;
}
len_h = PyBytes_GET_SIZE(h);
if (len_h!=16) {
PyErr_SetString(PyExc_ValueError, "Length of h must be 16 bytes.");
goto out;
}
exp_key = calloc(1, sizeof(t_exp_key));
if (!exp_key) {
goto out;
}
Py_BEGIN_ALLOW_THREADS;
exp_key->offset = ALIGNMENT - ((uintptr_t)exp_key->buffer & (ALIGNMENT-1));
make_v_tables((uint8_t*)PyBytes_AS_STRING(h),
(t_v_tables*)(exp_key->buffer + exp_key->offset));
Py_END_ALLOW_THREADS;
retval = PyBytes_FromStringAndSize((const char*)exp_key, sizeof *exp_key);
free(exp_key);
out:
return retval;
}
static char ghash__doc__[] =
"ghash(data:str, y:str, exp_key:str) -> str\n"
"\n"
"Return a GHASH.\n";
static PyObject *
ghash_function(PyObject *self, PyObject *args)
{
PyObject *data, *y, *exp_key_serial;
PyObject *retval = NULL;
Py_ssize_t len_data, len_y, len_exp_key_serial;
const t_v_tables *v_tables;
t_exp_key *exp_key;
if (!PyArg_ParseTuple(args, "SSS", &data, &y, &exp_key_serial)) {
goto out;
}
len_data = PyBytes_GET_SIZE(data);
len_y = PyBytes_GET_SIZE(y);
len_exp_key_serial = PyBytes_GET_SIZE(exp_key_serial);
if (len_data%16!=0) {
PyErr_SetString(PyExc_ValueError, "Length of data must be a multiple of 16 bytes.");
goto out;
}
if (len_y!=16) {
PyErr_SetString(PyExc_ValueError, "Length of y must be 16 bytes.");
goto out;
}
if (len_exp_key_serial!=sizeof(t_exp_key)) {
PyErr_SetString(PyExc_ValueError, "Length of expanded key is incorrect.");
goto out;
}
/* Create return string */
retval = PyBytes_FromStringAndSize(NULL, 16);
if (!retval) {
goto out;
}
Py_BEGIN_ALLOW_THREADS;
#define PyBytes_Buffer(a) (uint8_t*)PyBytes_AS_STRING(a)
exp_key = (t_exp_key *)PyBytes_AS_STRING(exp_key_serial);
realign_v_tables(exp_key);
v_tables = (const t_v_tables*)(exp_key->buffer + exp_key->offset);
ghash( PyBytes_Buffer(retval), PyBytes_Buffer(data), len_data,
PyBytes_Buffer(y), v_tables);
#undef PyBytes_Buffer
Py_END_ALLOW_THREADS;
out:
return retval;
}
/*
* Module-level method table and module initialization function
*/
static PyMethodDef galois_methods[] = {
{"ghash_expand", ghash_expand_function, METH_VARARGS, ghash_expand__doc__},
{"ghash", ghash_function, METH_VARARGS, ghash__doc__},
{NULL, NULL, 0, NULL} /* end-of-list sentinel value */
};
#ifdef IS_PY3K
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_galois",
"Arithmetic in Galois Fields",
-1,
galois_methods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit__galois(void)
{
PyObject *m;
/* Initialize the module */
m = PyModule_Create(&moduledef);
if (m == NULL)
return NULL;
return m;
}
#else
PyMODINIT_FUNC
init_galois(void)
{
PyObject *m;
/* Initialize the module */
m = Py_InitModule("_galois", galois_methods);
if (m == NULL)
return;
}
#endif