1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-25 00:48:19 +00:00
trezor-firmware/crypto/aes/gf128mul.c

472 lines
14 KiB
C
Raw Permalink Normal View History

/*
---------------------------------------------------------------------------
Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation.
This software is provided 'as is' with no explicit or implied warranties
in respect of its operation, including, but not limited to, correctness
and fitness for purpose.
---------------------------------------------------------------------------
Issue Date: 20/12/2007
This file provides fast multiplication in GF(128) as required by several
cryptographic authentication modes (see gfmul128.h).
*/
/* Speed critical loops can be unrolled to gain speed but consume more memory */
#if 1
# define UNROLL_LOOPS
#endif
/* The order of these includes matters */
#include "mode_hdr.h"
#include "gf128mul.h"
#include "gf_mul_lo.h"
#if defined( GF_MODE_LL )
# define mode _ll
#elif defined( GF_MODE_BL )
# define mode _bl
#elif defined( GF_MODE_LB )
# define mode _lb
#elif defined( GF_MODE_BB )
# define mode _bb
#else
# error mode is not defined
#endif
#if defined( GF_MODE_LL) || defined( GF_MODE_LB )
# define GF_INDEX(i) (i)
#else
# define GF_INDEX(i) (15 - (i))
#endif
/* A slow field multiplier */
void gf_mul(gf_t a, const gf_t b)
{ gf_t p[8] = {0};
uint8_t *q = NULL, ch = 0;
int i = 0;
copy_block_aligned(p[0], a);
for(i = 0; i < 7; ++i)
gf_mulx1(mode)(p[i + 1], p[i]);
q = (uint8_t*)(a == b ? p[0] : b);
memset(a, 0, GF_BYTE_LEN);
for(i = 15 ; ; )
{
ch = q[GF_INDEX(i)];
if(ch & X_0)
xor_block_aligned(a, a, p[0]);
if(ch & X_1)
xor_block_aligned(a, a, p[1]);
if(ch & X_2)
xor_block_aligned(a, a, p[2]);
if(ch & X_3)
xor_block_aligned(a, a, p[3]);
if(ch & X_4)
xor_block_aligned(a, a, p[4]);
if(ch & X_5)
xor_block_aligned(a, a, p[5]);
if(ch & X_6)
xor_block_aligned(a, a, p[6]);
if(ch & X_7)
xor_block_aligned(a, a, p[7]);
if(!i--)
break;
gf_mulx8(mode)(a);
}
}
#if defined( TABLES_64K )
/* This version uses 64k bytes of table space on the stack.
An input variable field value in a[] has to be multiplied
by a key value in g[] that changes far less frequently.
To do this a[] is split up into 16 smaller field values,
each one byte in length. For the 256 values of each of
these smaller values, we can precompute the result of
mulltiplying g by this field value. We can then combine
these values to provide the full multiply. So for each
of 16 bytes we have a table of 256 field values each of
16 bytes - 64k bytes in total.
*/
void init_64k_table(const gf_t g, gf_t64k_t t)
{ int i = 0, j = 0, k = 0;
/*
depending on the representation we have to process bits
within bytes high to low (0xe1 style ) or low to high
(0x87 style). We start by producing the powers x ,x^2
.. x^7 and put them in t[0][1], t[0][2] .. t[128] or in
t[128], t[64] .. t[1] depending on the bit order in use.
*/
/* clear the element for the zero field element */
memset(t[0][0], 0, GF_BYTE_LEN);
#if defined( GF_MODE_LL ) || defined( GF_MODE_BL )
/* g -> t[0][1], generate t[0][2] ... */
memcpy(t[0][1], g, GF_BYTE_LEN);
for(j = 1; j <= 64; j <<= 1)
gf_mulx1(mode)(t[0][j + j], t[0][j]);
#else
/* g -> t[0][128], generate t[0][64] ... */
memcpy(t[0][128], g, GF_BYTE_LEN);
for(j = 64; j >= 1; j >>= 1)
gf_mulx1(mode)(t[0][j], t[0][j + j]);
#endif
for( ; ; )
{
/* if { n } stands for the field value represented by
the integer n, we can express higher multiplies in
the table as follows:
1. g * { 3} = g * {2} ^ g * {1}
2. g * { 5} = g * {4} ^ g * {1}
g * { 6} = g * {4} ^ g * {2}
g * { 7} = g * {4} ^ g * {3}
3. g * { 9} = g * {8} ^ g * {1}
g * {10} = g * {8} ^ g * {2}
....
and so on. This is what the following loops do.
*/
for(j = 2; j < 256; j += j)
for(k = 1; k < j; ++k)
xor_block_aligned(t[i][j + k], t[i][j], t[i][k]);
if(++i == GF_BYTE_LEN) /* all 16 byte positions done */
return;
/* We now move to the next byte up and set up its eight
starting values by multiplying the values in the
lower table by x^8
*/
memset(t[i][0], 0, GF_BYTE_LEN);
for(j = 128; j > 0; j >>= 1)
{
memcpy(t[i][j], t[i - 1][j], GF_BYTE_LEN);
gf_mulx8(mode)(t[i][j]);
}
}
}
#define xor_64k(i,ap,t,r) xor_block_aligned(r, r, t[i][ap[GF_INDEX(i)]])
#if defined( UNROLL_LOOPS )
void gf_mul_64k(gf_t a, const gf_t64k_t t, gf_t r)
{ uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
xor_64k(15, ap, t, r); xor_64k(14, ap, t, r);
xor_64k(13, ap, t, r); xor_64k(12, ap, t, r);
xor_64k(11, ap, t, r); xor_64k(10, ap, t, r);
xor_64k( 9, ap, t, r); xor_64k( 8, ap, t, r);
xor_64k( 7, ap, t, r); xor_64k( 6, ap, t, r);
xor_64k( 5, ap, t, r); xor_64k( 4, ap, t, r);
xor_64k( 3, ap, t, r); xor_64k( 2, ap, t, r);
xor_64k( 1, ap, t, r); xor_64k( 0, ap, t, r);
copy_block_aligned(a, r);
}
#else
void gf_mul_64k(gf_t a, const gf_t64k_t t, gf_t r)
{ int i = 0;
uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
for(i = 15; i >= 0; --i)
{
xor_64k(i,ap,t,r);
}
copy_block_aligned(a, r);
}
#endif
#endif
#if defined( TABLES_8K )
/* This version uses 8k bytes of table space on the stack.
An input field value in a[] has to be multiplied by a
key value in g[]. To do this a[] is split up into 32
smaller field values each 4-bits in length. For the
16 values of each of these smaller field values we can
precompute the result of mulltiplying g[] by the field
value in question. So for each of 32 nibbles we have a
table of 16 field values, each of 16 bytes - 8k bytes
in total.
*/
void init_8k_table(const gf_t g, gf_t8k_t t)
{ int i = 0, j = 0, k = 0;
/* do the low 4-bit nibble first - t[0][16] - and note
that the unit multiplier sits at 0x01 - t[0][1] in
the table. Then multiplies by x go at 2, 4, 8
*/
/* set the table elements for a zero multiplier */
memset(t[0][0], 0, GF_BYTE_LEN);
memset(t[1][0], 0, GF_BYTE_LEN);
#if defined( GF_MODE_LL ) || defined( GF_MODE_BL )
/* t[0][1] = g, compute t[0][2], t[0][4], t[0][8] */
memcpy(t[0][1], g, GF_BYTE_LEN);
for(j = 1; j <= 4; j <<= 1)
gf_mulx1(mode)(t[0][j + j], t[0][j]);
/* t[1][1] = t[0][1] * x^4 = t[0][8] * x */
gf_mulx1(mode)(t[1][1], t[0][8]);
for(j = 1; j <= 4; j <<= 1)
gf_mulx1(mode)(t[1][j + j], t[1][j]);
#else
/* g -> t[0][8], compute t[0][4], t[0][2], t[0][1] */
memcpy(t[1][8], g, GF_BYTE_LEN);
for(j = 4; j >= 1; j >>= 1)
gf_mulx1(mode)(t[1][j], t[1][j + j]);
/* t[1][1] = t[0][1] * x^4 = t[0][8] * x */
gf_mulx1(mode)(t[0][8], t[1][1]);
for(j = 4; j >= 1; j >>= 1)
gf_mulx1(mode)(t[0][j], t[0][j + j]);
#endif
for( ; ; )
{
for(j = 2; j < 16; j += j)
for(k = 1; k < j; ++k)
xor_block_aligned(t[i][j + k], t[i][j], t[i][k]);
if(++i == 2 * GF_BYTE_LEN)
return;
if(i > 1)
{
memset(t[i][0], 0, GF_BYTE_LEN);
for(j = 8; j > 0; j >>= 1)
{
memcpy(t[i][j], t[i - 2][j], GF_BYTE_LEN);
gf_mulx8(mode)(t[i][j]);
}
}
}
}
#define xor_8k(i,ap,t,r) \
xor_block_aligned(r, r, t[i + i][ap[GF_INDEX(i)] & 15]); \
xor_block_aligned(r, r, t[i + i + 1][ap[GF_INDEX(i)] >> 4])
#if defined( UNROLL_LOOPS )
void gf_mul_8k(gf_t a, const gf_t8k_t t, gf_t r)
{ uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
xor_8k(15, ap, t, r); xor_8k(14, ap, t, r);
xor_8k(13, ap, t, r); xor_8k(12, ap, t, r);
xor_8k(11, ap, t, r); xor_8k(10, ap, t, r);
xor_8k( 9, ap, t, r); xor_8k( 8, ap, t, r);
xor_8k( 7, ap, t, r); xor_8k( 6, ap, t, r);
xor_8k( 5, ap, t, r); xor_8k( 4, ap, t, r);
xor_8k( 3, ap, t, r); xor_8k( 2, ap, t, r);
xor_8k( 1, ap, t, r); xor_8k( 0, ap, t, r);
copy_block_aligned(a, r);
}
#else
void gf_mul_8k(gf_t a, const gf_t8k_t t, gf_t r)
{ int i = 0;
uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
for(i = 15; i >= 0; --i)
{
xor_8k(i,ap,t,r);
}
memcpy(a, r, GF_BYTE_LEN);
}
#endif
#endif
#if defined( TABLES_4K )
/* This version uses 4k bytes of table space on the stack.
A 16 byte buffer has to be multiplied by a 16 byte key
value in GF(128). If we consider a GF(128) value in a
single byte, we can construct a table of the 256 16
byte values that result from multiplying g by the 256
values of this byte. This requires 4096 bytes.
If we take the highest byte in the buffer and use this
table to multiply it by g, we then have to multiply it
by x^120 to get the final value. For the next highest
byte the result has to be multiplied by x^112 and so on.
But we can do this by accumulating the result in an
accumulator starting with the result for the top byte.
We repeatedly multiply the accumulator value by x^8 and
then add in (i.e. xor) the 16 bytes of the next lower
byte in the buffer, stopping when we reach the lowest
byte. This requires a 4096 byte table.
*/
void init_4k_table(const gf_t g, gf_t4k_t t)
{ int j = 0, k = 0;
memset(t[0], 0, GF_BYTE_LEN);
#if defined( GF_MODE_LL ) || defined( GF_MODE_BL )
memcpy(t[1], g, GF_BYTE_LEN);
for(j = 1; j <= 64; j <<= 1)
gf_mulx1(mode)(t[j + j], t[j]);
#else
memcpy(t[128], g, GF_BYTE_LEN);
for(j = 64; j >= 1; j >>= 1)
gf_mulx1(mode)(t[j], t[j + j]);
#endif
for(j = 2; j < 256; j += j)
for(k = 1; k < j; ++k)
xor_block_aligned(t[j + k], t[j], t[k]);
}
#define xor_4k(i,ap,t,r) gf_mulx8(mode)(r); xor_block_aligned(r, r, t[ap[GF_INDEX(i)]])
#if defined( UNROLL_LOOPS )
void gf_mul_4k(gf_t a, const gf_t4k_t t, gf_t r)
{ uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
xor_4k(15, ap, t, r); xor_4k(14, ap, t, r);
xor_4k(13, ap, t, r); xor_4k(12, ap, t, r);
xor_4k(11, ap, t, r); xor_4k(10, ap, t, r);
xor_4k( 9, ap, t, r); xor_4k( 8, ap, t, r);
xor_4k( 7, ap, t, r); xor_4k( 6, ap, t, r);
xor_4k( 5, ap, t, r); xor_4k( 4, ap, t, r);
xor_4k( 3, ap, t, r); xor_4k( 2, ap, t, r);
xor_4k( 1, ap, t, r); xor_4k( 0, ap, t, r);
copy_block_aligned(a, r);
}
#else
void gf_mul_4k(gf_t a, const gf_t4k_t t, gf_t r)
{ int i = 15;
uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
for(i = 15; i >=0; --i)
{
xor_4k(i, ap, t, r);
}
copy_block_aligned(a, r);
}
#endif
#endif
#if defined( TABLES_256 )
/* This version uses 256 bytes of table space on the stack.
A 16 byte buffer has to be multiplied by a 16 byte key
value in GF(128). If we consider a GF(128) value in a
single 4-bit nibble, we can construct a table of the 16
16 byte values that result from the 16 values of this
byte. This requires 256 bytes. If we take the highest
4-bit nibble in the buffer and use this table to get the
result, we then have to multiply by x^124 to get the
final value. For the next highest byte the result has to
be multiplied by x^120 and so on. But we can do this by
accumulating the result in an accumulator starting with
the result for the top nibble. We repeatedly multiply
the accumulator value by x^4 and then add in (i.e. xor)
the 16 bytes of the next lower nibble in the buffer,
stopping when we reach the lowest nibble. This uses a
256 byte table.
*/
void init_256_table(const gf_t g, gf_t256_t t)
{ int j = 0, k = 0;
memset(t[0], 0, GF_BYTE_LEN);
#if defined( GF_MODE_LL ) || defined( GF_MODE_BL )
memcpy(t[1], g, GF_BYTE_LEN);
for(j = 1; j <= 4; j <<= 1)
gf_mulx1(mode)(t[j + j], t[j]);
#else
memcpy(t[8], g, GF_BYTE_LEN);
for(j = 4; j >= 1; j >>= 1)
gf_mulx1(mode)(t[j], t[j + j]);
#endif
for(j = 2; j < 16; j += j)
for(k = 1; k < j; ++k)
xor_block_aligned(t[j + k], t[j], t[k]);
}
#define x_lo(i,ap,t,r) gf_mulx4(mode)(r); xor_block_aligned(r, r, t[ap[GF_INDEX(i)] & 0x0f])
#define x_hi(i,ap,t,r) gf_mulx4(mode)(r); xor_block_aligned(r, r, t[ap[GF_INDEX(i)] >> 4])
#if defined( GF_MODE_LL ) || defined( GF_MODE_BL )
#define xor_256(a,b,c,d) x_hi(a,b,c,d); x_lo(a,b,c,d)
#else
#define xor_256(a,b,c,d) x_lo(a,b,c,d); x_hi(a,b,c,d)
#endif
#if defined( UNROLL_LOOPS )
void gf_mul_256(gf_t a, const gf_t256_t t, gf_t r)
{ uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
xor_256(15, ap, t, r); xor_256(14, ap, t, r);
xor_256(13, ap, t, r); xor_256(12, ap, t, r);
xor_256(11, ap, t, r); xor_256(10, ap, t, r);
xor_256( 9, ap, t, r); xor_256( 8, ap, t, r);
xor_256( 7, ap, t, r); xor_256( 6, ap, t, r);
xor_256( 5, ap, t, r); xor_256( 4, ap, t, r);
xor_256( 3, ap, t, r); xor_256( 2, ap, t, r);
xor_256( 1, ap, t, r); xor_256( 0, ap, t, r);
copy_block_aligned(a, r);
}
#else
void gf_mul_256(gf_t a, const gf_t256_t t, gf_t r)
{ int i = 0;
uint8_t *ap = (uint8_t*)a;
memset(r, 0, GF_BYTE_LEN);
for(i = 15; i >= 0; --i)
{
xor_256(i, ap, t, r);
}
copy_block_aligned(a, r);
}
#endif
#endif