2350 lines
88 KiB
C
2350 lines
88 KiB
C
/*
|
||
==============================================================================
|
||
Name : micro_aes.c
|
||
Author : polfosol
|
||
Version : 10
|
||
Copyright : copyright © 2022 - polfosol
|
||
Description : ANSI-C compatible implementation of µAES ™ library.
|
||
==============================================================================
|
||
*/
|
||
|
||
#include "micro_aes.h"
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Constants and MACROs
|
||
\*----------------------------------------------------------------------------*/
|
||
enum basic_constants
|
||
{
|
||
BLOCKSIZE = (128) / 8, /* Block length in AES is 'always' 128-bits */
|
||
KEYSIZE = AES_KEY_SIZE,
|
||
Nb = BLOCKSIZE / 4, /* number of columns comprising a AES state */
|
||
Nk = KEYSIZE / 4, /* number of 32 bit words in a key. */
|
||
ROUNDS = Nk + 6, /* number of rounds in AES Cipher. */
|
||
LAST = BLOCKSIZE - 1, /* index of last element (LSB) in a block. */
|
||
HB = BLOCKSIZE / 2, /* length of a half-block unit. */
|
||
SP = 17 /* length of data blocks in poly-1305 mode. */
|
||
};
|
||
|
||
#define IMPLEMENT(x) (x) > 0
|
||
|
||
#define INCREASE_SECURITY 0 /* see the comments at the bottom of header */
|
||
#define DISCARD_SUBROUTINES 0
|
||
#define SMALL_CIPHER 0
|
||
|
||
/** Lookup-tables are "static constant", so that they can be placed in read-only
|
||
* storage instead of RAM. They can be computed dynamically trading ROM for RAM.
|
||
* This may be useful in (embedded) bootloader applications, where ROM is often
|
||
* limited. Note that sbox[y] = x, if and only if rsbox[x] = y. For more details
|
||
* on dynamic sbox computation, see https://en.wikipedia.org/wiki/Rijndael_S-box
|
||
*/
|
||
static const char sbox[256] =
|
||
"c|w{\362ko\3050\01g+\376\327\253v\312\202\311}""\372YG\360\255\324\242\257"
|
||
"\234\244r\300\267\375\223&6\?\367\3144\245\345\361q\3301\25\4\307#\303\030"
|
||
"\226\5\232\a\22\200\342\353\'\262u\t\203,\32\33nZ\240R;\326\263)\343/\204S"
|
||
"\321\0\355 \374\261[j\313\2769JLX\317\320\357\252\373CM3\205E\371\02\177P<"
|
||
"\237\250Q\243@\217\222\2358\365\274\266\332!\20\377\363\322\315\f\023\354_"
|
||
"\227D\27\304\247~=d]\31s`\201O\334\"*\220\210F\356\270\24\336^\v\333\3402:"
|
||
"\nI\06$\\\302\323\254b\221\225\344y\347\3107m\215\325N\251lV\364\352ez\256"
|
||
"\b\272x%.\034\246\264\306\350\335t\37K\275\213\212p>\265fH\3\366\16a5W\271"
|
||
"\206\301\035\236\341\370\230\21i\331\216\224\233\036\207\351\316U(\337\214"
|
||
"\241\211\r\277\346BhA\231-\17\260T\273\26";
|
||
|
||
#if DECRYPTION
|
||
static const char rsbox[256] =
|
||
"R\tj\32506\2458\277@\243\236\201\363\327\373|\3439\202\233/\377\2074\216CD"
|
||
"\304\336\351\313T{\2242\246\302#=\356L\225\vB\372\303N\b.\241f(\331$\262v["
|
||
"\242Im\213\321%r\370\366d\206h\230\026\324\244\\\314]e\266\222lpHP\375\355"
|
||
"\271\332^\25FW\247\215\235\204\220\330\253\0\214\274\323\n\367\344X\05\270"
|
||
"\263E\6\320,\036\217\312?\17\2\301\257\275\3\1\023\212k:\221\21AOg\334\352"
|
||
"\227\362\317\316\360\264\346s\226\254t\"\347\2555\205\342\3717\350\34u\337"
|
||
"nG\361\32q\35)\305\211o\267b\16\252\30\276\33\374V>K\306\322y \232\333\300"
|
||
"\376x\315Z\364\037\335\2503\210\a\3071\261\22\20Y\'\200\354_`Q\177\251\031"
|
||
"\265J\r-\345z\237\223\311\234\357\240\340;M\256*\365\260\310\353\273<\203S"
|
||
"\231a\027+\004~\272w\326&\341i\024cU!\f}";
|
||
#endif
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Data types and private variables
|
||
\*----------------------------------------------------------------------------*/
|
||
|
||
/** The array that stores all round keys during the AES key-expansion process */
|
||
static uint8_t RoundKey[BLOCKSIZE * ROUNDS + KEYSIZE];
|
||
|
||
/** block_t indicates fixed-size memory blocks, and state_t represents the state
|
||
* matrix. note that state[i][j] means the i-th COLUMN and j-th ROW of matrix */
|
||
typedef uint8_t block_t[BLOCKSIZE];
|
||
typedef uint8_t state_t[Nb][4];
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Auxiliary functions for the Rijndael algorithm
|
||
\*----------------------------------------------------------------------------*/
|
||
|
||
#define SBoxValue(x) ( sbox[x])
|
||
#define InvSBoxValue(x) (rsbox[x]) /* omitted dynamic s-box calculation */
|
||
|
||
#define COPYDWORD(x, y) *(int32_t*) &y = *(int32_t*) &x
|
||
#define XOR32BITS(x, y) *(int32_t*) &y ^= *(int32_t*) &x
|
||
|
||
#if DISCARD_SUBROUTINES
|
||
|
||
/** note: 'long long' type is NOT supported in C89. so this may throw errors: */
|
||
#define xorBlock(x, y) \
|
||
( \
|
||
*(long long*) &(y)[0] ^= *(long long const*) &(x)[0], \
|
||
*(long long*) &(y)[8] ^= *(long long const*) &(x)[8] \
|
||
)
|
||
|
||
#define xtime(x) ((x) & 0x80 ? (x) * 2 ^ 0x11b : (x) << 1)
|
||
|
||
#define mixG8(a, b, c, d) b ^ c ^ d ^ \
|
||
xtime(a ^ b ^ xtime(a ^ c ^ xtime(a ^ b ^ c ^ d )))
|
||
#else
|
||
|
||
/** XOR two 128bit numbers (blocks) called src and dest, so that: dest ^= src */
|
||
static void xorBlock( const block_t src, block_t dest )
|
||
{
|
||
uint8_t i;
|
||
for (i = 0; i < BLOCKSIZE; ++i) /* many CPUs have single instruction */
|
||
{ /* such as XORPS for 128-bit-xor. */
|
||
dest[i] ^= src[i]; /* see the file: x86-improvements */
|
||
}
|
||
}
|
||
|
||
/** doubling in GF(2^8): left-shift and if carry bit is set, xor it with 0x1b */
|
||
static uint8_t xtime( uint8_t x )
|
||
{
|
||
return (x > 0x7f) * 0x1b ^ (x << 1);
|
||
}
|
||
|
||
#if DECRYPTION
|
||
|
||
/** inverse multiply in 8bit GF: mul(a,14) ^ mul(b,11) ^ mul(c,13) ^ mul(d,9) */
|
||
static uint8_t mixG8( uint8_t a, uint8_t b, uint8_t c, uint8_t d )
|
||
{
|
||
b ^= a;
|
||
d ^= b ^ c;
|
||
c ^= a;
|
||
a ^= d;
|
||
c ^= xtime( d );
|
||
b ^= xtime( c );
|
||
a ^= xtime( b );
|
||
return a; /* or use (9 11 13 14) lookup tables */
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Main functions for the Rijndael encryption algorithm
|
||
\*----------------------------------------------------------------------------*/
|
||
|
||
/** This function produces (ROUNDS+1) round keys, which are used in each round
|
||
* to encrypt/decrypt the intermediate states. First round key is the main key
|
||
* itself, and other rounds are constructed from the previous ones as follows */
|
||
static void KeyExpansion( const uint8_t* key )
|
||
{
|
||
uint8_t rcon = 1, i;
|
||
memcpy( RoundKey, key, KEYSIZE );
|
||
|
||
for (i = KEYSIZE; i < BLOCKSIZE * (ROUNDS + 1); i += 4)
|
||
{
|
||
switch (i % KEYSIZE)
|
||
{
|
||
case 0:
|
||
memcpy( &RoundKey[i], &RoundKey[i - KEYSIZE], KEYSIZE );
|
||
|
||
/* RCON reaches 0 only in AES-128, otherwise the line is ignored. */
|
||
if (4 / Nk && rcon == 0) rcon = 0x1b;
|
||
|
||
RoundKey[i ] ^= SBoxValue( RoundKey[i - 3] ) ^ rcon;
|
||
RoundKey[i + 1] ^= SBoxValue( RoundKey[i - 2] );
|
||
RoundKey[i + 2] ^= SBoxValue( RoundKey[i - 1] );
|
||
RoundKey[i + 3] ^= SBoxValue( RoundKey[i - 4] );
|
||
rcon <<= 1;
|
||
break;
|
||
#if AES___== 256
|
||
case 48 - KEYSIZE:
|
||
RoundKey[i ] ^= SBoxValue( RoundKey[i - 4] );
|
||
RoundKey[i + 1] ^= SBoxValue( RoundKey[i - 3] );
|
||
RoundKey[i + 2] ^= SBoxValue( RoundKey[i - 2] );
|
||
RoundKey[i + 3] ^= SBoxValue( RoundKey[i - 1] );
|
||
break;
|
||
#endif
|
||
default:
|
||
XOR32BITS( RoundKey[ i - 4 ], RoundKey[ i ] );
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/** Add the round keys to the rijndael state matrix (adding in GF means XOR). */
|
||
static void AddRoundKey( const uint8_t round, block_t state )
|
||
{
|
||
xorBlock( RoundKey + BLOCKSIZE * round, state );
|
||
}
|
||
|
||
/** Substitute values in the state matrix with associated values in the S-box */
|
||
static void SubBytes( block_t state )
|
||
{
|
||
uint8_t i;
|
||
for (i = 0; i < BLOCKSIZE; ++i)
|
||
{
|
||
state[i] = SBoxValue( state[i] );
|
||
}
|
||
}
|
||
|
||
/** Shift/rotate the rows of the state matrix to the left. Each row is shifted
|
||
* with a different offset (= Row number). So the "zeroth" row is not shifted */
|
||
static void ShiftRows( state_t state )
|
||
{
|
||
uint8_t tmp = state[0][1];
|
||
state[0][1] = state[1][1];
|
||
state[1][1] = state[2][1];
|
||
state[2][1] = state[3][1];
|
||
state[3][1] = tmp; /* the first row rotates 1 column(s) to left */
|
||
|
||
tmp = state[0][2];
|
||
state[0][2] = state[2][2];
|
||
state[2][2] = tmp;
|
||
tmp = state[1][2];
|
||
state[1][2] = state[3][2];
|
||
state[3][2] = tmp; /* the second row rotates 2 columns to left, */
|
||
|
||
tmp = state[0][3];
|
||
state[0][3] = state[3][3];
|
||
state[3][3] = state[2][3];
|
||
state[2][3] = state[1][3];
|
||
state[1][3] = tmp; /* and the 3rd row rotates 3 columns to left */
|
||
}
|
||
|
||
/** Use matrix multiplication in Galois field to mix the columns of the state */
|
||
static void MixColumns( state_t state )
|
||
{
|
||
uint8_t C[4], i;
|
||
for (i = 0; i < Nb; ++i) /*-> https://crypto.stackexchange.com/q/2402 */
|
||
{
|
||
COPYDWORD( state[i], C[0] );
|
||
C[3] ^= C[1];
|
||
C[1] ^= C[0];
|
||
C[0] ^= C[2];
|
||
C[2] = xtime( C[0] );
|
||
C[0] ^= C[3]; /* C[0] = xor of all elements in i-th column */
|
||
C[0] ^= xtime( C[1] );
|
||
C[1] = xtime( C[3] );
|
||
|
||
state[i][0] ^= C[0];
|
||
state[i][1] ^= C[0] ^= C[2];
|
||
state[i][2] ^= C[0] ^= C[1];
|
||
state[i][3] ^= C[0] ^= C[2];
|
||
}
|
||
}
|
||
|
||
/** Encrypt a plaintext input block and save the result/ciphertext as output. */
|
||
static void rijndaelEncrypt( const block_t input, block_t output )
|
||
{
|
||
uint8_t r;
|
||
state_t* mat = (void*) output;
|
||
|
||
/* copy plaintext into the state matrix, and beware of undefined behavior */
|
||
if (input != output) memcpy( mat, input, BLOCKSIZE );
|
||
|
||
/* The encryption is carried out in #ROUNDS iterations, of which the first
|
||
* #ROUNDS-1 are identical. The last round doesn't involve mixing columns */
|
||
for (r = 0; r != ROUNDS; )
|
||
{
|
||
AddRoundKey( r, output );
|
||
SubBytes( output );
|
||
ShiftRows( *mat );
|
||
++r != ROUNDS ? MixColumns( *mat ) : AddRoundKey( ROUNDS, output );
|
||
}
|
||
}
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Block-decryption part of the Rijndael algorithm
|
||
\*----------------------------------------------------------------------------*/
|
||
|
||
#if IMPLEMENT(DECRYPTION)
|
||
|
||
/** Substitutes the values in state matrix with values of the inverted S-box. */
|
||
static void InvSubBytes( block_t state )
|
||
{
|
||
uint8_t i;
|
||
for (i = 0; i < BLOCKSIZE; ++i)
|
||
{
|
||
state[i] = InvSBoxValue( state[i] );
|
||
}
|
||
}
|
||
|
||
/** This function shifts (i.e rotates) the rows of the state matrix to right. */
|
||
static void InvShiftRows( state_t state )
|
||
{
|
||
uint8_t tmp = state[3][1];
|
||
state[3][1] = state[2][1];
|
||
state[2][1] = state[1][1];
|
||
state[1][1] = state[0][1];
|
||
state[0][1] = tmp; /* the first row rotates 1 columns to right, */
|
||
|
||
tmp = state[0][2];
|
||
state[0][2] = state[2][2];
|
||
state[2][2] = tmp;
|
||
tmp = state[1][2];
|
||
state[1][2] = state[3][2];
|
||
state[3][2] = tmp; /* the second row rotates 2 columns to right */
|
||
|
||
tmp = state[0][3];
|
||
state[0][3] = state[1][3];
|
||
state[1][3] = state[2][3];
|
||
state[2][3] = state[3][3];
|
||
state[3][3] = tmp; /* the third row rotates 3 columns to right. */
|
||
}
|
||
|
||
/** Reverse the process of mixing columns by matrix multiplication in GF(2^8) */
|
||
static void InvMixColumns( state_t state )
|
||
{
|
||
uint8_t C[4], i;
|
||
for (i = 0; i < Nb; ++i) /*-> https://crypto.stackexchange.com/q/2569 */
|
||
{
|
||
COPYDWORD( state[i], C[0] );
|
||
state[i][0] = mixG8( C[0], C[1], C[2], C[3] );
|
||
state[i][1] = mixG8( C[1], C[2], C[3], C[0] );
|
||
state[i][2] = mixG8( C[2], C[3], C[0], C[1] );
|
||
state[i][3] = mixG8( C[3], C[0], C[1], C[2] );
|
||
}
|
||
}
|
||
|
||
/** Decrypt a ciphertext input block and save the result/plaintext to output. */
|
||
static void rijndaelDecrypt( const block_t input, block_t output )
|
||
{
|
||
uint8_t r;
|
||
state_t* mat = (void*) output;
|
||
|
||
/* copy input to the state matrix, i.e initialize the state by ciphertext */
|
||
if (input != output) memcpy( mat, input, BLOCKSIZE );
|
||
|
||
/* Decryption is carried out in #ROUNDS iterations. The rounds are similar
|
||
* except for the first one which doesn't involve [reverse]mixing columns */
|
||
for (r = ROUNDS; r != 0; )
|
||
{
|
||
r-- != ROUNDS ? InvMixColumns( *mat ) : AddRoundKey( ROUNDS, output );
|
||
InvShiftRows( *mat );
|
||
InvSubBytes( output );
|
||
AddRoundKey( r, output );
|
||
}
|
||
}
|
||
#endif /* DECRYPTION */
|
||
|
||
#if MICRO_RJNDL
|
||
/**
|
||
* @brief encrypt or decrypt a single block with a given key
|
||
* @param key a byte array with a fixed size of KEYSIZE
|
||
* @param mode mode of operation: 'E' (1) to encrypt, 'D' (0) to decrypt
|
||
* @param x input byte array with BLOCKSIZE bytes
|
||
* @param y output byte array with BLOCKSIZE bytes
|
||
*/
|
||
void AES_Cipher( const uint8_t* key, const char mode, const block_t x, block_t y )
|
||
{
|
||
KeyExpansion( key );
|
||
mode & 1 ? rijndaelEncrypt( x, y ) : rijndaelDecrypt( x, y );
|
||
}
|
||
#endif
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
* Implementation of different block ciphers modes *
|
||
* Definitions & Auxiliary Functions *
|
||
\*----------------------------------------------------------------------------*/
|
||
|
||
/** function-pointer types, indicating functions that take fixed-size blocks: */
|
||
typedef void (*fmix_t)( const block_t, block_t ) SDCC_REENT;
|
||
typedef void (*fdouble_t)( block_t );
|
||
|
||
#define AES_setkey(key) KeyExpansion( key )
|
||
|
||
#if INCREASE_SECURITY
|
||
#define BURN(key) memset( key, 0, sizeof key )
|
||
#define SABOTAGE(buf, len) memset( buf, 0, len )
|
||
#define MISMATCH constmemcmp /* a.k.a secure memcmp */
|
||
#else
|
||
#define MISMATCH memcmp
|
||
#define SABOTAGE(buf, len) (void) buf
|
||
#define BURN(key) (void) key /* the line will be ignored */
|
||
#endif
|
||
|
||
#if INCREASE_SECURITY && AEAD_MODES
|
||
|
||
/** for constant-time comparison of memory blocks, to avoid "timing attacks". */
|
||
static uint8_t constmemcmp( const uint8_t* src, const uint8_t* dst, uint8_t n )
|
||
{
|
||
uint8_t cmp = 0;
|
||
while (n--)
|
||
{
|
||
cmp |= src[n] ^ dst[n];
|
||
}
|
||
return cmp;
|
||
}
|
||
#endif
|
||
|
||
#if SMALL_CIPHER
|
||
typedef uint8_t count_t;
|
||
|
||
#define incBlock(block, index) ++block[index]
|
||
#define xorBEint(buf, num, pos) buf[pos - 1] ^= (num) >> 8, buf[pos] ^= num
|
||
#define copyLint(buf, num, pos) buf[pos + 1] = (num) >> 8, buf[pos] = num
|
||
|
||
#else
|
||
typedef size_t count_t;
|
||
|
||
#if XTS || GCM_SIV
|
||
|
||
/** copy a little endian integer to the block, with LSB at specified position */
|
||
static void copyLint( block_t block, size_t num, uint8_t pos )
|
||
{
|
||
do
|
||
block[pos++] = (uint8_t) num;
|
||
while (num >>= 8);
|
||
}
|
||
#endif
|
||
|
||
#if CTR || KWA || FPE
|
||
|
||
/** xor a byte array with a big-endian integer, whose LSB is at specified pos */
|
||
static void xorBEint( uint8_t* buff, size_t num, uint8_t pos )
|
||
{
|
||
do
|
||
buff[pos--] ^= (uint8_t) num;
|
||
while (num >>= 8);
|
||
}
|
||
#endif
|
||
|
||
#if CTR
|
||
|
||
/** increment the value of a 128-bit counter block, regarding its endian-ness */
|
||
static void incBlock( block_t block, uint8_t index )
|
||
{
|
||
do /* increment the LSBs, */
|
||
if (++block[index]) /* ..until no overflow */
|
||
break;
|
||
while ((index < 4 && ++index < 4) || --index > 8);
|
||
}
|
||
#endif
|
||
#endif /* SMALL CIPHER */
|
||
|
||
#if EAX && !EAXP || SIV || OCB || CMAC
|
||
|
||
/** Multiply a block by two in Galois bit field GF(2^128): big-endian version */
|
||
static void doubleBblock( block_t array )
|
||
{
|
||
int c = 0, i;
|
||
for (i = BLOCKSIZE; i > 0; c >>= 8) /* from last byte (LSB) to */
|
||
{ /* first: left-shift, then */
|
||
c |= array[--i] << 1; /* append the previous MSBit */
|
||
array[i] = (uint8_t) c;
|
||
} /* if first MSBit is carried */
|
||
array[LAST] ^= c * 0x87; /* .. B ^= 10000111b (B.E.) */
|
||
}
|
||
#endif
|
||
|
||
#if XTS || EAXP
|
||
|
||
/** Multiply a block by two in Galois field GF(2^128): little-endian version. */
|
||
static void doubleLblock( block_t array )
|
||
{
|
||
int i, c = 0;
|
||
for (i = 0; i < BLOCKSIZE; c >>= 8) /* the same as doubleBblock */
|
||
{ /* ..but with reversed bytes */
|
||
c |= array[i] << 1;
|
||
array[i++] = (uint8_t) c;
|
||
}
|
||
array[0] ^= c * 0x87; /* B ^= 10000111b (L.E.) */
|
||
}
|
||
#endif
|
||
|
||
#if GCM
|
||
|
||
/** Divide a 128-bit big-endian integer by two in Galois bit field GF(2^128). */
|
||
static void divideBblock( block_t array )
|
||
{
|
||
unsigned i, c = 0;
|
||
for (i = 0; i < BLOCKSIZE; ++i) /* from first to last byte, */
|
||
{ /* prepend the previous LSB */
|
||
c = c << 8 | array[i]; /* then shift it to right. */
|
||
array[i] = c >> 1;
|
||
} /* if block is odd (LSB = 1) */
|
||
if (c & 1) array[0] ^= 0xe1; /* .. B ^= 11100001b << 120 */
|
||
}
|
||
|
||
/** Multiply two 128-bit numbers (big-endian blocks) in the Galois bit field. */
|
||
static void mulGF128( const block_t x, block_t y )
|
||
{
|
||
uint8_t b, i;
|
||
block_t result = { 0 }; /* working memory */
|
||
|
||
for (i = 0; i < BLOCKSIZE; ++i)
|
||
{
|
||
for (b = 0x80; b; b >>= 1) /* check all the bits of X, */
|
||
{
|
||
if (x[i] & b) /* ..and if any bit is set, */
|
||
{
|
||
xorBlock( y, result ); /* ..add Y to the result */
|
||
}
|
||
divideBblock( y ); /* Y_next = (Y / 2) in GF */
|
||
}
|
||
}
|
||
memcpy( y, result, sizeof result ); /* result is saved into y */
|
||
}
|
||
#endif /* GCM */
|
||
|
||
#if GCM_SIV
|
||
|
||
/** Divide a block by two in 128-bit Galois field: the little-endian version. */
|
||
static void divideLblock( block_t array )
|
||
{
|
||
unsigned c = 0, i;
|
||
for (i = BLOCKSIZE; i--; ) /* similar to divideBblock ↑ */
|
||
{ /* ..but with reversed bytes */
|
||
c = c << 8 | array[i];
|
||
array[i] = c >> 1;
|
||
}
|
||
if (c & 1) array[LAST] ^= 0xe1; /* B ^= LE. 11100001b << 120 */
|
||
}
|
||
|
||
/** The so-called "dot multiplying" in GF(2^128), used in POLYVAL calculation */
|
||
static void dotGF128( const block_t x, block_t y )
|
||
{
|
||
uint8_t b, i;
|
||
block_t result = { 0 };
|
||
|
||
for (i = BLOCKSIZE; i--; )
|
||
{
|
||
for (b = 0x80; b; b >>= 1) /* pretty much the same as */
|
||
{ /* ..(reversed) mulGF128 */
|
||
divideLblock( y );
|
||
if (x[i] & b)
|
||
{
|
||
xorBlock( y, result );
|
||
}
|
||
}
|
||
}
|
||
memcpy( y, result, sizeof result ); /* result is saved into y */
|
||
}
|
||
#endif /* GCM-SIV */
|
||
|
||
#if CTR || CFB || OFB || CTS || OCB
|
||
|
||
/** mix/cipher the block B and then xor the result with n bytes of X to get Y */
|
||
static void mixThenXor( fmix_t mix, const block_t B, block_t f,
|
||
const uint8_t* X, uint8_t n, uint8_t* Y )
|
||
{
|
||
if (n != 0)
|
||
{
|
||
mix( B, f ); /* Y = f(B) ^ X */
|
||
while (n--)
|
||
{
|
||
Y[n] = f[n] ^ X[n];
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#if AEAD_MODES || FPE
|
||
|
||
/** xor the result with input data and then apply the digest/mixing function.
|
||
* repeat this process for each block of data, until all blocks are digested. */
|
||
static void xMac( const void* data, const size_t dataSize,
|
||
const block_t seed, fmix_t mix, block_t result )
|
||
{
|
||
uint8_t const* x;
|
||
count_t n = dataSize / BLOCKSIZE; /* number of full blocks */
|
||
|
||
for (x = data; n--; x += BLOCKSIZE)
|
||
{
|
||
xorBlock( x, result ); /* M_next = mix(seed, M ^ X) */
|
||
mix( seed, result );
|
||
}
|
||
if ((n = dataSize % BLOCKSIZE) != 0) /* if any partial block left */
|
||
{
|
||
while (n--)
|
||
{
|
||
result[n] ^= x[n];
|
||
}
|
||
mix( seed, result );
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#if CMAC || SIV || EAX || OCB
|
||
|
||
/** calculate CMAC of input data using pre-calculated keys: K1 (D) and K2 (Q) */
|
||
static void cMac( const block_t K1, const block_t K2,
|
||
const void* data, const size_t dataSize, block_t mac )
|
||
{
|
||
const uint8_t s = dataSize ? (dataSize - 1) % BLOCKSIZE + 1 : 0;
|
||
const uint8_t *k = K1, *ps = s ? (uint8_t*) data + dataSize - s : &s;
|
||
|
||
xMac( data, dataSize - s, mac, &rijndaelEncrypt, mac );
|
||
if (s < BLOCKSIZE)
|
||
{
|
||
mac[s] ^= 0x80; /* pad( M_last; K1, K2 ) */
|
||
k = K2;
|
||
}
|
||
xorBlock( k, mac );
|
||
xMac( ps, s + !s, mac, &rijndaelEncrypt, mac );
|
||
}
|
||
|
||
/** calculate key-dependent constants D and Q using a given doubling function */
|
||
static void getSubkeys( fdouble_t gfdouble, const char quad,
|
||
const uint8_t* key, block_t D, block_t Q )
|
||
{
|
||
AES_setkey( key );
|
||
rijndaelEncrypt( D, D ); /* H or L_* = Enc(zeros) */
|
||
if (quad)
|
||
{
|
||
gfdouble( D ); /* D or L_$ = double(L_*) */
|
||
}
|
||
memcpy( Q, D, BLOCKSIZE );
|
||
gfdouble( Q ); /* Q or L_0 = double(L_$) */
|
||
}
|
||
#endif
|
||
|
||
#ifdef AES_PADDING
|
||
|
||
/** in ECB mode & CBC without CTS, the last (partial) block has to be padded. */
|
||
static char padBlock( const uint8_t len, block_t block )
|
||
{
|
||
uint8_t n = BLOCKSIZE - len, *p = &block[len];
|
||
|
||
#if AES_PADDING
|
||
memset( p, n * (AES_PADDING != 2), n );
|
||
*p ^= (0x80) * (AES_PADDING == 2); /* either PKCS#7 / IEC7816-4 */
|
||
#else
|
||
memset( p, 0, n % BLOCKSIZE ); /* default (zero) padding */
|
||
#endif
|
||
return len || AES_PADDING;
|
||
}
|
||
#endif
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
ECB-AES (electronic codebook mode) functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(ECB)
|
||
/**
|
||
* @brief encrypt the input plaintext using ECB-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param pntxt input plaintext buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param crtxt resulting cipher-text buffer
|
||
*/
|
||
void AES_ECB_encrypt( const uint8_t* key,
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
uint8_t* y;
|
||
count_t n = ptextLen / BLOCKSIZE; /* number of full blocks */
|
||
memcpy( crtxt, pntxt, ptextLen ); /* copy plaintext to output */
|
||
|
||
AES_setkey( key );
|
||
for (y = crtxt; n--; y += BLOCKSIZE)
|
||
{
|
||
rijndaelEncrypt( y, y ); /* C = Enc(P) */
|
||
}
|
||
if (padBlock( ptextLen % BLOCKSIZE, y ))
|
||
{
|
||
rijndaelEncrypt( y, y );
|
||
}
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using ECB-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param crtxt input ciphertext buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return error if the ciphertext has a partial block
|
||
*/
|
||
char AES_ECB_decrypt( const uint8_t* key,
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
uint8_t* y;
|
||
count_t n = crtxtLen / BLOCKSIZE;
|
||
memcpy( pntxt, crtxt, crtxtLen ); /* do in-place decryption */
|
||
|
||
AES_setkey( key );
|
||
for (y = pntxt; n--; y += BLOCKSIZE)
|
||
{
|
||
rijndaelDecrypt( y, y ); /* P = Dec(C) */
|
||
}
|
||
BURN( RoundKey );
|
||
|
||
/* if padding is enabled, check whether the result is properly padded. error
|
||
* must be thrown if it's not. here we skip that and just check the size. */
|
||
return crtxtLen % BLOCKSIZE ? M_DECRYPTION_ERROR : M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* ECB */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
CBC-AES (cipher block chaining) functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(CBC)
|
||
/**
|
||
* @brief encrypt the input plaintext using CBC-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param pntxt input plaintext buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param crtxt resulting cipher-text buffer
|
||
* @return error in CTS mode, if plaintext is a single partial block
|
||
*/
|
||
char AES_CBC_encrypt( const uint8_t* key, const block_t iVec,
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
uint8_t const* iv = iVec;
|
||
uint8_t r = ptextLen % BLOCKSIZE, *y;
|
||
count_t n = ptextLen / BLOCKSIZE;
|
||
|
||
#if CTS
|
||
if (n > 1 && !r && --n) r = BLOCKSIZE; /* CS3 ciphertext stealing */
|
||
|
||
if (n == 0) return M_ENCRYPTION_ERROR; /* data size >= BLOCKSIZE */
|
||
#endif
|
||
memcpy( crtxt, pntxt, ptextLen ); /* do in-place encryption */
|
||
|
||
AES_setkey( key );
|
||
for (y = crtxt; n--; y += BLOCKSIZE)
|
||
{
|
||
xorBlock( iv, y ); /* C = Enc(IV ^ P) */
|
||
rijndaelEncrypt( y, y ); /* IV_next = C */
|
||
iv = y;
|
||
}
|
||
#if CTS
|
||
if (r)
|
||
{
|
||
block_t L = { 0 };
|
||
memcpy( L, y, r ); /* backup the last chunk */
|
||
memcpy( y, y - BLOCKSIZE, r ); /* 'steal' the cipher-text */
|
||
y -= BLOCKSIZE; /* ..to fill the last chunk */
|
||
iv = L;
|
||
#else
|
||
if (padBlock( r, y ))
|
||
{
|
||
#endif
|
||
xorBlock( iv, y );
|
||
rijndaelEncrypt( y, y );
|
||
}
|
||
BURN( RoundKey );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using CBC-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param crtxt input ciphertext buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return error if the size of ciphertext is not a valid value
|
||
*/
|
||
char AES_CBC_decrypt( const uint8_t* key, const block_t iVec,
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
uint8_t const *x = crtxt, *iv = iVec;
|
||
uint8_t r = crtxtLen % BLOCKSIZE, *y;
|
||
count_t n = crtxtLen / BLOCKSIZE;
|
||
|
||
#if CTS
|
||
if (n > 1 && !r && --n) r = BLOCKSIZE;
|
||
|
||
if (n == 0) return M_DECRYPTION_ERROR;
|
||
#else
|
||
if (r != 0) return M_DECRYPTION_ERROR;
|
||
#endif
|
||
n -= r > 0; /* hold last 2 blocks in CTS */
|
||
|
||
AES_setkey( key );
|
||
for (y = pntxt; n--; y += BLOCKSIZE)
|
||
{
|
||
rijndaelDecrypt( x, y ); /* P = Dec(C) ^ IV */
|
||
xorBlock( iv, y ); /* IV_next = C */
|
||
iv = x;
|
||
x += BLOCKSIZE;
|
||
#if CTS
|
||
}
|
||
if (r)
|
||
{ /* last two blocks: {X, Z} */
|
||
const uint8_t* z = x + BLOCKSIZE;
|
||
mixThenXor( &rijndaelDecrypt, x, y, z, r, y + BLOCKSIZE );
|
||
memcpy( y, z, r );
|
||
rijndaelDecrypt( y, y ); /* P2 = Z ^ Dec(X) = Z ^ Y */
|
||
xorBlock( iv, y ); /* P1 = IV ^ Dec(Z | *Y) */
|
||
#endif
|
||
}
|
||
BURN( RoundKey );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* CBC */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
CFB-AES (cipher feedback) functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(CFB)
|
||
/**
|
||
* @brief the general scheme of CFB-AES block-ciphering algorithm
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param mode mode of operation: (1) to encrypt, (0) to decrypt
|
||
* @param input buffer of the input plain/cipher-text
|
||
* @param dataSize size of input in bytes
|
||
* @param output buffer of the resulting cipher/plain-text
|
||
*/
|
||
static void CFB_cipher( const uint8_t* key, const block_t iVec, const char mode,
|
||
const void* input, const size_t dataSize, void* output )
|
||
{
|
||
uint8_t const *iv = iVec, *x = input;
|
||
uint8_t* y;
|
||
block_t tmp;
|
||
count_t n = dataSize / BLOCKSIZE; /* number of full blocks */
|
||
|
||
AES_setkey( key );
|
||
for (y = output; n--; y += BLOCKSIZE)
|
||
{
|
||
rijndaelEncrypt( iv, y ); /* both in en[de]cryption: */
|
||
xorBlock( x, y ); /* Y = Enc(IV) ^ X */
|
||
iv = mode ? y : x; /* IV_next = Ciphertext */
|
||
x += BLOCKSIZE;
|
||
}
|
||
mixThenXor( &rijndaelEncrypt, iv, tmp, x, dataSize % BLOCKSIZE, y );
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using CFB-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param pntxt input plaintext buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param crtxt resulting cipher-text buffer
|
||
*/
|
||
void AES_CFB_encrypt( const uint8_t* key, const block_t iVec,
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
CFB_cipher( key, iVec, 1, pntxt, ptextLen, crtxt );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using CFB-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param crtxt input ciphertext buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
*/
|
||
void AES_CFB_decrypt( const uint8_t* key, const block_t iVec,
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
CFB_cipher( key, iVec, 0, crtxt, crtxtLen, pntxt );
|
||
}
|
||
#endif /* CFB */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
OFB-AES (output feedback) functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(OFB)
|
||
/**
|
||
* @brief encrypt the input plaintext using OFB-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param pntxt input plaintext buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param crtxt resulting cipher-text buffer
|
||
*/
|
||
void AES_OFB_encrypt( const uint8_t* key, const block_t iVec,
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
count_t n = ptextLen / BLOCKSIZE;
|
||
uint8_t* y;
|
||
block_t iv;
|
||
|
||
memcpy( iv, iVec, sizeof iv );
|
||
memcpy( crtxt, pntxt, ptextLen ); /* i.e. in-place encryption */
|
||
|
||
AES_setkey( key );
|
||
for (y = crtxt; n--; y += BLOCKSIZE)
|
||
{
|
||
rijndaelEncrypt( iv, iv ); /* IV_next = Enc(IV) */
|
||
xorBlock( iv, y ); /* C = IV_next ^ P */
|
||
}
|
||
mixThenXor( &rijndaelEncrypt, iv, iv, y, ptextLen % BLOCKSIZE, y );
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using OFB-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param iVec initialization vector
|
||
* @param crtxt input ciphertext buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
*/
|
||
void AES_OFB_decrypt( const uint8_t* key, const block_t iVec,
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
AES_OFB_encrypt( key, iVec, crtxt, crtxtLen, pntxt );
|
||
}
|
||
#endif /* OFB */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Parallelizable, counter-based modes of AES: demonstrating the main idea
|
||
+ How to use it in a simple, non-authenticated API
|
||
\*----------------------------------------------------------------------------*/
|
||
#if CTR
|
||
/**
|
||
* @brief the general scheme of operation in block-counter mode
|
||
* @param iCtr initialized counter block
|
||
* @param mode specifies the "counter based" block-cipher or AEAD mode
|
||
* @param input buffer of the input plain/cipher-text
|
||
* @param dataSize size of input in bytes
|
||
* @param output buffer of the resulting cipher/plain-text
|
||
*/
|
||
static void CTR_cipher( const block_t iCtr, const char mode,
|
||
const void* input, const size_t dataSize, void* output )
|
||
{
|
||
block_t c, enc;
|
||
count_t n = dataSize / BLOCKSIZE;
|
||
uint8_t index = LAST, *y;
|
||
|
||
memcpy( output, input, dataSize ); /* do in-place en/decryption */
|
||
memcpy( c, iCtr, sizeof c );
|
||
|
||
switch (mode)
|
||
{
|
||
case 2:
|
||
incBlock( c, index ); /* pre-increment in CCM/GCM */
|
||
break;
|
||
case 4:
|
||
c[+8] &= 0x7F; /* SIV mode: clear two bits */
|
||
c[12] &= 0x7F;
|
||
break;
|
||
case 8: /* GCM-SIV: set one bit */
|
||
c[index] |= 0x80;
|
||
index = 0;
|
||
break;
|
||
}
|
||
for (y = output; n--; y += BLOCKSIZE)
|
||
{
|
||
rijndaelEncrypt( c, enc ); /* both in en[de]cryption: */
|
||
xorBlock( enc, y ); /* Y = Enc(Ctr) ^ X */
|
||
incBlock( c, index ); /* Ctr_next = Ctr + 1 */
|
||
}
|
||
mixThenXor( &rijndaelEncrypt, c, c, y, dataSize % BLOCKSIZE, y );
|
||
}
|
||
#endif
|
||
|
||
#if IMPLEMENT(CTR_NA)
|
||
/**
|
||
* @brief encrypt the input plaintext using CTR-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param iv initialization vector a.k.a. nonce
|
||
* @param pntxt input plaintext buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param crtxt resulting cipher-text buffer
|
||
*/
|
||
void AES_CTR_encrypt( const uint8_t* key, const uint8_t* iv,
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
#if CTR_IV_LENGTH == 16
|
||
uint8_t const* ctr = iv; /* block is pre-initialized */
|
||
#else
|
||
block_t ctr = { 0 };
|
||
memcpy( ctr, iv, CTR_IV_LENGTH );
|
||
|
||
xorBEint( ctr, CTR_STARTVALUE, LAST ); /* initialize the counter */
|
||
#endif
|
||
AES_setkey( key );
|
||
CTR_cipher( ctr, 0, pntxt, ptextLen, crtxt );
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using CTR-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param iv initialization vector a.k.a. nonce
|
||
* @param crtxt input ciphertext buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
*/
|
||
void AES_CTR_decrypt( const uint8_t* key, const uint8_t* iv,
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
AES_CTR_encrypt( key, iv, crtxt, crtxtLen, pntxt );
|
||
}
|
||
#endif /* CTR */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
XEX-AES based modes (xor-encrypt-xor): the basic idea
|
||
+ main functions of XTS-AES (XEX Tweaked-codebook with ciphertext Stealing)
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(XTS)
|
||
/**
|
||
* @brief encrypt or decrypt a data unit with XTS method
|
||
* @param keypair pair of encryption keys, each one has KEYSIZE bytes
|
||
* @param mode mode of operation: encrypting (1) or decrypting (0)
|
||
* @param tweak data unit identifier block, similar to nonce in CTR mode
|
||
* @param sectid sector id: in case of a null tweak, use this instead
|
||
* @param dataSize size of input data, to be encrypted/decrypted
|
||
* @param storage result of encryption/decryption process
|
||
*/
|
||
static void XTS_cipher( const uint8_t* keypair, const char mode,
|
||
const block_t tweak, const size_t sectid,
|
||
const size_t dataSize, void* storage )
|
||
{
|
||
fmix_t cipher = mode ? &rijndaelEncrypt : &rijndaelDecrypt;
|
||
uint8_t r = dataSize % BLOCKSIZE, *y;
|
||
count_t n = dataSize / BLOCKSIZE - (r > 0);
|
||
block_t T;
|
||
|
||
if (tweak == NULL)
|
||
{ /* the `i` block is either */
|
||
memset( T, 0, sizeof T ); /* ..a little-endian number */
|
||
copyLint( T, sectid, 0 ); /* ..or a byte array (tweak) */
|
||
}
|
||
else
|
||
{
|
||
memcpy( T, tweak, sizeof T );
|
||
}
|
||
AES_setkey( keypair + KEYSIZE ); /* T = encrypt `i` with key2 */
|
||
rijndaelEncrypt( T, T );
|
||
|
||
AES_setkey( keypair ); /* now key1 is cipher key */
|
||
for (y = storage; n--; y += BLOCKSIZE)
|
||
{ /* XEX: xor-encrypt-xor */
|
||
xorBlock( T, y );
|
||
cipher( y, y );
|
||
xorBlock( T, y ); /* Y = T ^ Cipher( T ^ X ) */
|
||
doubleLblock( T ); /* T_next = alpha · T */
|
||
}
|
||
if (r)
|
||
{ /* XTS: ciphertext stealing */
|
||
block_t L;
|
||
memcpy( L, T, sizeof L );
|
||
doubleLblock( mode ? T : L ); /* T = α·L or L = α·T */
|
||
|
||
xorBlock( L, y );
|
||
cipher( y, y );
|
||
xorBlock( L, y );
|
||
memcpy( L, y, sizeof L );
|
||
memcpy( y, y + BLOCKSIZE, r ); /* 'steal' the cipher-text */
|
||
memcpy( y + BLOCKSIZE, L, r ); /* ..to fill the last chunk */
|
||
|
||
xorBlock( T, y );
|
||
cipher( y, y ); /* encrypt or decrypt the */
|
||
xorBlock( T, y ); /* ..block we stole from */
|
||
}
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using XTS-AES block-cipher method
|
||
* @param keys two-part encryption key with a fixed size of 2*KEYSIZE
|
||
* @param tweak tweak bytes of data unit, a.k.a sector ID (little-endian)
|
||
* @param pntxt input plaintext buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param crtxt resulting cipher-text buffer
|
||
* @return error if plaintext is a single partial block
|
||
*/
|
||
char AES_XTS_encrypt( const uint8_t* keys, const uint8_t* tweak,
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
if (ptextLen < BLOCKSIZE) return M_ENCRYPTION_ERROR;
|
||
|
||
memcpy( crtxt, pntxt, ptextLen ); /* do in-place encryption */
|
||
XTS_cipher( keys, 1, tweak, 0, ptextLen, crtxt );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input ciphertext using XTS-AES block-cipher method
|
||
* @param keys two-part encryption key with a fixed size of 2*KEYSIZE
|
||
* @param tweak tweak bytes of data unit, a.k.a sector ID (little-endian)
|
||
* @param crtxt input ciphertext buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return error if ciphertext is a single partial block
|
||
*/
|
||
char AES_XTS_decrypt( const uint8_t* keys, const uint8_t* tweak,
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
if (crtxtLen < BLOCKSIZE) return M_DECRYPTION_ERROR;
|
||
|
||
memcpy( pntxt, crtxt, crtxtLen ); /* in-place decryption */
|
||
XTS_cipher( keys, 0, tweak, 0, crtxtLen, pntxt );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* XTS */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
CMAC-AES (cipher-based message authentication code): main function
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(CMAC)
|
||
/**
|
||
* @brief derive the AES-CMAC of input data using an encryption key
|
||
* @param key AES encryption key
|
||
* @param data buffer of input data
|
||
* @param dataSize size of data in bytes
|
||
* @param mac calculated CMAC hash
|
||
*/
|
||
void AES_CMAC( const uint8_t* key,
|
||
const void* data, const size_t dataSize, block_t mac )
|
||
{
|
||
block_t K1 = { 0 }, K2;
|
||
memcpy( mac, K1, sizeof K1 ); /* initialize mac */
|
||
|
||
getSubkeys( &doubleBblock, 1, key, K1, K2 );
|
||
cMac( K1, K2, data, dataSize, mac );
|
||
BURN( RoundKey );
|
||
}
|
||
#endif /* CMAC */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
GCM-AES (Galois counter mode): authentication with GMAC & main functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(GCM)
|
||
|
||
/** calculate G-Hash of ciphertext and AAD using an authentication subkey `H` */
|
||
static void Ghash( const block_t H, const void* aData, const void* crtxt,
|
||
const size_t aDataLen, const size_t crtxtLen, block_t gh )
|
||
{
|
||
block_t len = { 0 };
|
||
xorBEint( len, aDataLen * 8, LAST / 2 );
|
||
xorBEint( len, crtxtLen * 8, LAST ); /* save bit-sizes into len */
|
||
|
||
xMac( aData, aDataLen, H, &mulGF128, gh ); /* first digest AAD, then */
|
||
xMac( crtxt, crtxtLen, H, &mulGF128, gh ); /* ..ciphertext, and then */
|
||
xMac( len, sizeof len, H, &mulGF128, gh ); /* ..bit sizes into GHash */
|
||
}
|
||
|
||
/** encrypt zeros to get authentication subkey H, and prepare the IV for GCM. */
|
||
static void GCMinit( const uint8_t* key,
|
||
const uint8_t* nonce, block_t auKey, block_t iv )
|
||
{
|
||
AES_setkey( key );
|
||
rijndaelEncrypt( auKey, auKey ); /* auKey = Enc( zero block ) */
|
||
#if GCM_NONCE_LEN != 12
|
||
Ghash( auKey, NULL, nonce, 0, GCM_NONCE_LEN, iv );
|
||
#else
|
||
memcpy( iv, nonce, 12 );
|
||
iv[LAST] = 1;
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using GCM-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a initialization vector with fixed size: GCM_NONCE_LEN
|
||
* @param pntxt input plain-text buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param crtxt resulting cipher-text buffer
|
||
* @param auTag message authentication tag. buffer must be 16-bytes long
|
||
*/
|
||
void AES_GCM_encrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* pntxt, const size_t ptextLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
uint8_t* crtxt, block_t auTag )
|
||
{
|
||
block_t gh = { 0 }, H = { 0 }, iv = { 0 };
|
||
GCMinit( key, nonce, H, iv ); /* get IV & auth. subkey H */
|
||
|
||
CTR_cipher( iv, 2, pntxt, ptextLen, crtxt );
|
||
rijndaelEncrypt( iv, iv );
|
||
BURN( RoundKey );
|
||
|
||
Ghash( H, aData, crtxt, aDataLen, ptextLen, gh );
|
||
xorBlock( iv, gh );
|
||
memcpy( auTag, gh, BLOCKSIZE ); /* GMAC = Enc(iv) ^ G-HASH */
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using GCM-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a initialization vector with fixed size: GCM_NONCE_LEN
|
||
* @param crtxt input cipher-text buffer + appended authentication tag
|
||
* @param crtxtLen size of ciphertext, excluding tag
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param tagLen length of authentication tag
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return whether message authentication/decryption was successful
|
||
*/
|
||
char AES_GCM_decrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* crtxt, const size_t crtxtLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
const uint8_t tagLen, uint8_t* pntxt )
|
||
{
|
||
block_t gh = { 0 }, H = { 0 }, iv = { 0 };
|
||
GCMinit( key, nonce, H, iv );
|
||
|
||
Ghash( H, aData, crtxt, aDataLen, crtxtLen, gh );
|
||
rijndaelEncrypt( iv, H );
|
||
xorBlock( gh, H ); /* tag = Enc(iv) ^ G-HASH */
|
||
|
||
if (MISMATCH( H, crtxt + crtxtLen, tagLen ))
|
||
{ /* compare tags and */
|
||
BURN( RoundKey ); /* ..proceed if they match */
|
||
return M_AUTHENTICATION_ERROR;
|
||
}
|
||
CTR_cipher( iv, 2, crtxt, crtxtLen, pntxt );
|
||
BURN( RoundKey );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* GCM */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
CCM-AES (counter with CBC-MAC): CBC-MAC authentication & main functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(CCM)
|
||
|
||
/** this function calculates the CBC-MAC of plaintext and authentication data */
|
||
static void CCMtag( const block_t iv, const void* aData, const void* pntxt,
|
||
const size_t aDataLen, const size_t ptextLen, block_t M )
|
||
{
|
||
block_t A = { 0 };
|
||
uint8_t s = aDataLen < LAST ? aDataLen : sizeof A - 2;
|
||
|
||
memcpy( M, iv, BLOCKSIZE ); /* initialize CBC-MAC */
|
||
M[0] |= (CCM_TAG_LEN - 2) << 2; /* set some flags on M_* */
|
||
xorBEint( M, ptextLen, LAST ); /* copy data size into M_* */
|
||
|
||
if (aDataLen) /* construct the 'A' block */
|
||
{
|
||
M[0] |= 0x40;
|
||
rijndaelEncrypt( M, M ); /* flag M_* and encrypt it */
|
||
if (aDataLen > 0xFEFF)
|
||
{ /* assuming aDataLen < 2^32 */
|
||
s -= 4;
|
||
A[0] = 0xFF, A[1] = 0xFE; /* prepend FFFE to aDataLen */
|
||
}
|
||
xorBEint( A, aDataLen, LAST - s ); /* copy aDataLen into A, */
|
||
memcpy( A + sizeof A - s, aData, s ); /* ..and append aData */
|
||
}
|
||
|
||
/* digest the first s bytes of aData, the rest of it, and then plaintext: */
|
||
xMac( A, sizeof A, M, &rijndaelEncrypt, M );
|
||
if (aDataLen > s)
|
||
{
|
||
xMac( (char*) aData + s, aDataLen - s, M, &rijndaelEncrypt, M );
|
||
}
|
||
xMac( pntxt, ptextLen, M, &rijndaelEncrypt, M );
|
||
|
||
rijndaelEncrypt( iv, A ); /* tag = Enc(iv) ^ CBCMac */
|
||
xorBlock( A, M );
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using CCM-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a initialization vector with fixed size: CCM_NONCE_LEN
|
||
* @param pntxt input plain-text buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param crtxt resulting cipher-text buffer
|
||
* @param auTag message authentication tag
|
||
*/
|
||
void AES_CCM_encrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* pntxt, const size_t ptextLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
uint8_t* crtxt, uint8_t* auTag )
|
||
{
|
||
block_t iv = { 14 - CCM_NONCE_LEN, 0 }, tag;
|
||
memcpy( iv + 1, nonce, CCM_NONCE_LEN );
|
||
|
||
AES_setkey( key );
|
||
CCMtag( iv, aData, pntxt, aDataLen, ptextLen, tag );
|
||
CTR_cipher( iv, 2, pntxt, ptextLen, crtxt );
|
||
BURN( RoundKey );
|
||
memcpy( auTag, tag, CCM_TAG_LEN );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using CCM-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a initialization vector with fixed size: CCM_NONCE_LEN
|
||
* @param crtxt input cipher-text buffer + appended authentication tag
|
||
* @param crtxtLen size of ciphertext, excluding tag
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param tagLen length of authentication tag
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return whether message decryption/authentication was successful
|
||
*/
|
||
char AES_CCM_decrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* crtxt, const size_t crtxtLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
const uint8_t tagLen, uint8_t* pntxt )
|
||
{
|
||
block_t iv = { 14 - CCM_NONCE_LEN, 0 }, tag;
|
||
memcpy( iv + 1, nonce, CCM_NONCE_LEN );
|
||
|
||
if (tagLen != CCM_TAG_LEN) return M_DECRYPTION_ERROR;
|
||
|
||
AES_setkey( key );
|
||
CTR_cipher( iv, 2, crtxt, crtxtLen, pntxt );
|
||
CCMtag( iv, aData, pntxt, aDataLen, crtxtLen, tag );
|
||
BURN( RoundKey );
|
||
|
||
if (MISMATCH( tag, crtxt + crtxtLen, tagLen ))
|
||
{ /* invalid tag: clear pntxt */
|
||
SABOTAGE( pntxt, crtxtLen );
|
||
return M_AUTHENTICATION_ERROR;
|
||
}
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* CCM */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
SIV-AES (synthetic init-vector): nonce synthesis & main functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(SIV)
|
||
|
||
/** calculate the CMAC* of AAD unit(s), then plaintext, and synthesize the IV */
|
||
static void S2V( const uint8_t* key,
|
||
const void* aData, const void* pntxt,
|
||
const size_t aDataLen, const size_t ptextLen, block_t IV )
|
||
{
|
||
block_t K[2] = { { 0 } }, Y;
|
||
uint8_t r = ptextLen % BLOCKSIZE, *Q = K[1];
|
||
|
||
memcpy( IV, *K, BLOCKSIZE ); /* initialize/clear IV */
|
||
getSubkeys( &doubleBblock, 1, key, *K, Q );
|
||
rijndaelEncrypt( *K, Y ); /* Y_0 = CMAC(zero block) */
|
||
|
||
/* in case of multiple AAD units, each one must be handled in a similar way.
|
||
* for example, let aData be a 2-D array and aDataLen a null-terminated one.
|
||
* then, instead of `if (aDataLen) { cMac( *K, Q, aData,...` we could write:
|
||
* for (int i = 0; *aDataLen; ){ cMac(*K, Q, aData[i++], *aDataLen++, IV) */
|
||
if (aDataLen)
|
||
{
|
||
cMac( *K, Q, aData, aDataLen, IV );
|
||
doubleBblock( Y ); /* Y_$ = double( Y_{i-1} ) */
|
||
xorBlock( IV, Y ); /* Y_i = Y_$ ^ CMAC(AAD_i) */
|
||
memset( IV, 0, BLOCKSIZE );
|
||
}
|
||
if (ptextLen < sizeof Y)
|
||
{ /* for short messages: */
|
||
doubleBblock( Y ); /* Y = double( Y_n ) */
|
||
r = 0;
|
||
}
|
||
if (r)
|
||
{
|
||
memset( *K, 0, BLOCKSIZE );
|
||
}
|
||
xorBlock( Y, *K + r );
|
||
cMac( *K, *K, pntxt, ptextLen - r, IV ); /* CMAC*( Y xor_end M ) */
|
||
if (r)
|
||
{
|
||
cMac( NULL, Q, (char*) pntxt + ptextLen - r, r, IV );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using SIV-AES block-cipher method
|
||
* @param keys two-part encryption key with a fixed size of 2*KEYSIZE
|
||
* @param pntxt input plain-text buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param iv synthesized I.V block, typically prepended to ciphertext
|
||
* @param crtxt resulting cipher-text buffer
|
||
*/
|
||
void AES_SIV_encrypt( const uint8_t* keys,
|
||
const void* pntxt, const size_t ptextLen,
|
||
const void* aData, const size_t aDataLen,
|
||
block_t iv, void* crtxt )
|
||
{
|
||
S2V( keys, aData, pntxt, aDataLen, ptextLen, iv );
|
||
AES_setkey( keys + KEYSIZE );
|
||
CTR_cipher( iv, 4, pntxt, ptextLen, crtxt );
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using SIV-AES block-cipher method
|
||
* @param keys two-part encryption key with a fixed size of 2*KEYSIZE
|
||
* @param iv provided I.V block to validate
|
||
* @param crtxt input cipher-text buffer
|
||
* @param crtxtLen size of ciphertext in bytes
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return whether message decryption/authentication was successful
|
||
*/
|
||
char AES_SIV_decrypt( const uint8_t* keys, const block_t iv,
|
||
const void* crtxt, const size_t crtxtLen,
|
||
const void* aData, const size_t aDataLen,
|
||
void* pntxt )
|
||
{
|
||
block_t IV;
|
||
AES_setkey( keys + KEYSIZE );
|
||
CTR_cipher( iv, 4, crtxt, crtxtLen, pntxt );
|
||
S2V( keys, aData, pntxt, aDataLen, crtxtLen, IV );
|
||
BURN( RoundKey );
|
||
|
||
if (MISMATCH( IV, iv, sizeof IV )) /* verify the synthesized IV */
|
||
{
|
||
SABOTAGE( pntxt, crtxtLen );
|
||
return M_AUTHENTICATION_ERROR;
|
||
}
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* SIV */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
SIV-GCM-AES (Galois counter mode with synthetic i.v)
|
||
authentication & i.v synthesis with polyval + main functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(GCM_SIV)
|
||
|
||
/** calculate the POLYVAL of plaintext and AAD using authentication subkey H. */
|
||
static void Polyval( const block_t H, const void* aData, const void* pntxt,
|
||
const size_t aDataLen, const size_t ptextLen, block_t pv )
|
||
{
|
||
block_t len = { 0 }; /* save bit-sizes into len */
|
||
copyLint( len, aDataLen * 8, 0 );
|
||
copyLint( len, ptextLen * 8, 8 );
|
||
|
||
xMac( aData, aDataLen, H, &dotGF128, pv ); /* first digest AAD, then */
|
||
xMac( pntxt, ptextLen, H, &dotGF128, pv ); /* ..plaintext, and then */
|
||
xMac( len, sizeof len, H, &dotGF128, pv ); /* ..bit sizes into POLYVAL */
|
||
}
|
||
|
||
/** derive the pair of authentication-encryption-keys from main key and nonce */
|
||
static void GCMSIVinit( const uint8_t* key, const uint8_t* nonce, block_t AK )
|
||
{
|
||
uint8_t iv[5 * HB + KEYSIZE], *h, *k;
|
||
k = h = iv + BLOCKSIZE;
|
||
memcpy( iv + 4, nonce, 12 );
|
||
|
||
AES_setkey( key );
|
||
for (*(int32_t*) iv = 0; *iv < 2 + Nk / 2; ++*iv)
|
||
{
|
||
rijndaelEncrypt( iv, k ); /* encrypt & take half, then */
|
||
k += HB; /* ..increment iv's LSB */
|
||
}
|
||
AES_setkey( k - KEYSIZE ); /* set the main cipher-key */
|
||
memcpy( AK, h, BLOCKSIZE ); /* take authentication key */
|
||
}
|
||
|
||
/** get the tag in GCM-SIV mode, given the nonce and calculated POLYVAL block */
|
||
static void GSIVtag( const uint8_t* nonce, block_t polyval, block_t tag )
|
||
{
|
||
XOR32BITS( nonce[0], polyval[0] );
|
||
XOR32BITS( nonce[4], polyval[4] ); /* xor POLYVAL with nonce */
|
||
XOR32BITS( nonce[8], polyval[8] );
|
||
polyval[LAST] &= 0x7F; /* clear one bit & encrypt, */
|
||
rijndaelEncrypt( polyval, tag );
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using SIV-GCM-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce provided 96-bit nonce
|
||
* @param pntxt input plain-text buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param crtxt resulting cipher-text buffer,
|
||
* @param auTag appended authentication tag. must be 16-bytes long
|
||
*/
|
||
void GCM_SIV_encrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* pntxt, const size_t ptextLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
uint8_t* crtxt, block_t auTag )
|
||
{
|
||
block_t H, P = { 0 };
|
||
GCMSIVinit( key, nonce, H ); /* get authentication subkey */
|
||
|
||
Polyval( H, aData, pntxt, aDataLen, ptextLen, P );
|
||
GSIVtag( nonce, P, auTag );
|
||
CTR_cipher( auTag, 8, pntxt, ptextLen, crtxt );
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using SIV-GCM-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce provided 96-bit nonce
|
||
* @param crtxt input cipher-text buffer + appended authentication tag
|
||
* @param crtxtLen size of ciphertext, excluding tag
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param tagLen length of authentication tag; MUST be 16 bytes
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return whether message decryption/authentication was successful
|
||
*/
|
||
char GCM_SIV_decrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* crtxt, const size_t crtxtLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
const uint8_t tagLen, uint8_t* pntxt )
|
||
{
|
||
uint8_t const* tag = crtxt + crtxtLen;
|
||
block_t H, P = { 0 };
|
||
|
||
if (tagLen != sizeof P) return M_DECRYPTION_ERROR;
|
||
|
||
GCMSIVinit( key, nonce, H ); /* get authentication subkey */
|
||
CTR_cipher( tag, 8, crtxt, crtxtLen, pntxt );
|
||
Polyval( H, aData, pntxt, aDataLen, crtxtLen, P );
|
||
GSIVtag( nonce, P, P );
|
||
BURN( RoundKey );
|
||
|
||
if (MISMATCH( P, tag, sizeof P ))
|
||
{ /* tag verification failed */
|
||
SABOTAGE( pntxt, crtxtLen );
|
||
return M_AUTHENTICATION_ERROR;
|
||
}
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* GCM-SIV */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
EAX-AES (encrypt-then-authenticate-then-translate): OMAC & main functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(EAX)
|
||
|
||
/** this function calculates the OMAC of a data array using D (K1) and Q (K2) */
|
||
static void OMac( const uint8_t t, const block_t D, const block_t Q,
|
||
const void* data, const size_t dataSize, block_t mac )
|
||
{
|
||
#if EAXP
|
||
const uint8_t zero_mac = t && !dataSize, *K = t ? Q : D;
|
||
|
||
zero_mac ? memset( mac, 0, BLOCKSIZE ) : memcpy( mac, K, BLOCKSIZE );
|
||
|
||
if (zero_mac) return; /* ignoring null ciphertext */
|
||
#else
|
||
dataSize ? memset( mac, 0, BLOCKSIZE ) : memcpy( mac, D, BLOCKSIZE );
|
||
|
||
mac[LAST] ^= t;
|
||
rijndaelEncrypt( mac, mac );
|
||
|
||
if (dataSize == 0) return; /* then OMAC = CMAC( [t]_n ) */
|
||
#endif
|
||
cMac( D, Q, data, dataSize, mac );
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input plaintext using EAX-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a init-vector with EAX_NONCE_LEN bytes unless EAX'
|
||
* @param pntxt input plain-text buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param nonceLen size of the nonce byte array; should be non-zero in EAX'
|
||
* @param aData additional authentication data; for EAX only, not EAX'
|
||
* @param aDataLen size of additional authentication data
|
||
* @param crtxt resulting cipher-text buffer; 4 bytes mac appended in EAX'
|
||
* @param auTag authentication tag; buffer must be 16 bytes long in EAX
|
||
*/
|
||
void AES_EAX_encrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* pntxt, const size_t ptextLen,
|
||
#if EAXP
|
||
#define FDOUBLE_T &doubleLblock
|
||
const size_t nonceLen, uint8_t* crtxt )
|
||
#else
|
||
#define FDOUBLE_T &doubleBblock
|
||
#define nonceLen EAX_NONCE_LEN
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
uint8_t* crtxt, block_t auTag )
|
||
#endif
|
||
{
|
||
block_t D = { 0 }, Q, mac;
|
||
|
||
getSubkeys( FDOUBLE_T, 1, key, D, Q );
|
||
OMac( 0, D, Q, nonce, nonceLen, mac ); /* N = OMAC(0; nonce) */
|
||
#if EAXP
|
||
COPYDWORD( mac[12], crtxt[ptextLen] );
|
||
mac[12] &= 0x7F;
|
||
mac[14] &= 0x7F; /* clear 2 bits to get N' */
|
||
|
||
CTR_cipher( mac, 0, pntxt, ptextLen, crtxt );
|
||
OMac( 2, D, Q, crtxt, ptextLen, mac ); /* C' = CMAC'( ciphertext ) */
|
||
XOR32BITS( mac[12], crtxt[ptextLen] ); /* tag (a.k.a mac) = N ^ C' */
|
||
#else
|
||
OMac( 1, D, Q, aData, aDataLen, auTag ); /* H = OMAC(1; adata) */
|
||
xorBlock( mac, auTag );
|
||
|
||
CTR_cipher( mac, 0, pntxt, ptextLen, crtxt );
|
||
OMac( 2, D, Q, crtxt, ptextLen, mac ); /* C = OMAC(2; ciphertext) */
|
||
xorBlock( mac, auTag ); /* tag = N ^ H ^ C */
|
||
#endif
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input ciphertext using EAX-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a init-vector with EAX_NONCE_LEN bytes unless EAX'
|
||
* @param crtxt input cipher-text buffer + appended authentication tag
|
||
* @param crtxtLen size of cipher-text; excluding tag / 4-bytes mac in EAX'
|
||
* @param nonceLen size of the nonce byte array; should be non-zero in EAX'
|
||
* @param aData additional authentication data; for EAX only, not EAX'
|
||
* @param aDataLen size of additional authentication data
|
||
* @param tagLen length of authentication tag; mandatory 4 bytes in EAX'
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return whether message authentication/decryption was successful
|
||
*/
|
||
char AES_EAX_decrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* crtxt, const size_t crtxtLen,
|
||
#if EAXP
|
||
const size_t nonceLen,
|
||
#else
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
const uint8_t tagLen,
|
||
#endif
|
||
uint8_t* pntxt )
|
||
{
|
||
block_t D = { 0 }, Q, mac, tag;
|
||
|
||
getSubkeys( FDOUBLE_T, 1, key, D, Q );
|
||
OMac( 2, D, Q, crtxt, crtxtLen, tag ); /* C = OMAC(2; ciphertext) */
|
||
#if EAXP
|
||
OMac( 0, D, Q, nonce, nonceLen, mac ); /* N = CMAC'( nonce ) */
|
||
XOR32BITS( crtxt[crtxtLen], tag[12] );
|
||
XOR32BITS( mac[12], tag[12] );
|
||
mac[12] &= 0x7F;
|
||
mac[14] &= 0x7F; /* clear 2 bits to get N' */
|
||
|
||
if (*(int32_t*) &tag[12] != 0) /* result of mac validation */
|
||
#else
|
||
OMac( 1, D, Q, aData, aDataLen, mac ); /* H = OMAC(1; adata) */
|
||
xorBlock( mac, tag );
|
||
OMac( 0, D, Q, nonce, nonceLen, mac ); /* N = OMAC(0; nonce) */
|
||
xorBlock( mac, tag ); /* tag = N ^ H ^ C */
|
||
#undef nonceLen
|
||
|
||
if (MISMATCH( tag, crtxt + crtxtLen, tagLen ))
|
||
#endif
|
||
{ /* authenticate then decrypt */
|
||
BURN( RoundKey );
|
||
return M_AUTHENTICATION_ERROR;
|
||
}
|
||
CTR_cipher( mac, 0, crtxt, crtxtLen, pntxt );
|
||
BURN( RoundKey );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* EAX */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
OCB-AES (offset codebook mode): auxiliary and main functions
|
||
+ how to parallelize it by independent calculation of the offset blocks
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(OCB)
|
||
|
||
static void nop( const block_t x, block_t y ) {}
|
||
|
||
/** Calculate the offset block (Δ_i) at a specified index, given the initial Δ_0
|
||
* and L$ blocks. This method has minimum memory usage, but it might be slow. To
|
||
* make it faster, pre-calculate all L_{i}s (avoid doubling inside the loop). */
|
||
static void getDelta( const count_t index,
|
||
const block_t Ld, const block_t delta0, block_t delta )
|
||
{
|
||
count_t r, b = 1;
|
||
block_t L;
|
||
|
||
memcpy( L, Ld, sizeof L ); /* initialize L_$ and Δ */
|
||
memcpy( delta, delta0, BLOCKSIZE );
|
||
|
||
while ((r = index - b) < index) /* Δ_i = Δ_{i-1} ^ L_ntz(i) */
|
||
{ /* where: */
|
||
doubleBblock( L ); /* L_0 = double( L_$ ) */
|
||
b *= 2; /* L_k = double( L_{k-1} ) */
|
||
|
||
if (r & b) continue;
|
||
|
||
xorBlock( L, delta );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt or decrypt the data using OCB method
|
||
* @param key encryption or decryption key
|
||
* @param nonce byte array with a fixed size of OCB_NONCE_LEN
|
||
* @param ptLen size of plaintext. ptLen = dataSize, only in encryption
|
||
* @param aDataLen size of additional authentication data
|
||
* @param aData additional authentication data
|
||
* @param dataSize size of input data, to be encrypted/decrypted
|
||
* @param data result of encryption/decryption process
|
||
* @param tag calculated tag from plaintext checksum, and PMAC of aData
|
||
*/
|
||
static void OCB_cipher( const uint8_t* key, const uint8_t* nonce,
|
||
const size_t ptLen,
|
||
const size_t aDataLen, const void* aData,
|
||
const size_t dataSize, void* data, block_t tag )
|
||
{
|
||
fmix_t cipher = ptLen ? &rijndaelEncrypt : &rijndaelDecrypt;
|
||
|
||
block_t offset[4] = { { 0 } }; /* [L_$] [L_*] [Ktop] [Δ_n] */
|
||
uint8_t *y = data;
|
||
uint8_t *Ld = offset[0], *Ls = offset[1], *kt = offset[2], *del = offset[3];
|
||
count_t i, n = nonce[OCB_NONCE_LEN - 1] % 64;
|
||
uint8_t const s = 8 - n % 8, *a = aData;
|
||
|
||
memcpy( kt + BLOCKSIZE - OCB_NONCE_LEN, nonce, OCB_NONCE_LEN );
|
||
kt[0] = OCB_TAG_LEN << 4 & 0xFF;
|
||
kt[LAST - OCB_NONCE_LEN] |= 1; /* set one and clear last 6 */
|
||
kt[LAST] &= 0xC0; /* .. bits of nonce (kt) */
|
||
|
||
getSubkeys( &doubleBblock, 0, key, Ls, Ld );
|
||
rijndaelEncrypt( kt, kt ); /* construct K_top */
|
||
memcpy( del, kt + 1, 8 ); /* stretch K_top */
|
||
xorBlock( kt, del );
|
||
|
||
n /= 8;
|
||
for (i = 0; i < BLOCKSIZE; ++i, ++n) /* shift the stretched K_top */
|
||
{
|
||
kt[i] = (kt[n] << 8 | kt[n + 1]) >> s;
|
||
}
|
||
|
||
xMac( data, ptLen, NULL, &nop, tag ); /* get plaintext? checksum */
|
||
|
||
if ((n = dataSize / BLOCKSIZE) == 0)
|
||
{
|
||
del = kt; /* Δ_N = Δ_0 = K_top */
|
||
kt = offset[3];
|
||
}
|
||
for (i = 0; i++ < n; y += BLOCKSIZE)
|
||
{ /* calculate Δ_i using */
|
||
getDelta( i, Ld, kt, del ); /* .. my 'magic' algorithm */
|
||
xorBlock( del, y );
|
||
cipher( y, y );
|
||
xorBlock( del, y ); /* Y = Δ_i ^ Cipher(Δ_i ^ X) */
|
||
}
|
||
if ((i = dataSize % BLOCKSIZE) != 0)
|
||
{ /* Y_* = Enc(L_* ^ Δ_N) ^ X */
|
||
tag[i] ^= 0x80; /* and pad X_* or checksum */
|
||
xorBlock( Ls, del );
|
||
mixThenXor( &rijndaelEncrypt, del, kt, y, i, y );
|
||
}
|
||
|
||
xMac( data, dataSize - ptLen, NULL, &nop, tag );
|
||
cMac( Ld, NULL, del, BLOCKSIZE, tag );
|
||
|
||
/*- tag = Enc( checksum ^ Δ_* ^ L_$ ) so far. next, add "PMAC" of aData: **/
|
||
n = aDataLen / BLOCKSIZE;
|
||
|
||
for (i = 0; i < n; a += BLOCKSIZE)
|
||
{
|
||
getDelta( ++i, Ld, a, del );
|
||
rijndaelEncrypt( del, del ); /* Δ = Enc( A_i ^ Δ_i ) */
|
||
xorBlock( del, tag ); /* add Δ to the tag */
|
||
}
|
||
if ((i = aDataLen % BLOCKSIZE) != 0)
|
||
{
|
||
memset( kt, 0, BLOCKSIZE );
|
||
getDelta( n, Ld, kt, del );
|
||
cMac( NULL, Ls, a, i, del ); /* Δ = Enc(L_* ^ A_* ^ Δ_N) */
|
||
xorBlock( del, tag ); /* add Δ to the tag */
|
||
}
|
||
BURN( RoundKey );
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input stream using OCB-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a initialization vector with fixed size: OCB_NONCE_LEN
|
||
* @param pntxt input plain-text buffer
|
||
* @param ptextLen size of plaintext in bytes
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param crtxt resulting cipher-text buffer
|
||
* @param auTag message authentication tag
|
||
*/
|
||
void AES_OCB_encrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* pntxt, const size_t ptextLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
uint8_t* crtxt, uint8_t* auTag )
|
||
{
|
||
block_t tag = { 0 };
|
||
memcpy( crtxt, pntxt, ptextLen ); /* doing in-place encryption */
|
||
|
||
OCB_cipher( key, nonce, ptextLen, aDataLen, aData, ptextLen, crtxt, tag );
|
||
memcpy( auTag, tag, OCB_TAG_LEN );
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt the input stream using OCB-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param nonce a.k.a initialization vector with fixed size: OCB_NONCE_LEN
|
||
* @param crtxt input cipher-text buffer + appended authentication tag
|
||
* @param crtxtLen size of ciphertext, excluding tag
|
||
* @param aData additional authentication data
|
||
* @param aDataLen size of additional authentication data
|
||
* @param tagLen length of authentication tag
|
||
* @param pntxt resulting plaintext buffer
|
||
* @return whether message decryption/authentication was successful
|
||
*/
|
||
char AES_OCB_decrypt( const uint8_t* key, const uint8_t* nonce,
|
||
const uint8_t* crtxt, const size_t crtxtLen,
|
||
const uint8_t* aData, const size_t aDataLen,
|
||
const uint8_t tagLen, uint8_t* pntxt )
|
||
{
|
||
block_t tag = { 0 };
|
||
|
||
if (tagLen != OCB_TAG_LEN) return M_DECRYPTION_ERROR;
|
||
|
||
memcpy( pntxt, crtxt, crtxtLen ); /* in-place decryption */
|
||
OCB_cipher( key, nonce, 0, aDataLen, aData, crtxtLen, pntxt, tag );
|
||
|
||
if (MISMATCH( tag, crtxt + crtxtLen, tagLen ))
|
||
{
|
||
SABOTAGE( pntxt, crtxtLen );
|
||
return M_AUTHENTICATION_ERROR;
|
||
}
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* OCB */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
KW-AES: Main functions for AES key-wrapping (RFC-3394)
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(KWA)
|
||
/**
|
||
* @brief wrap the input secret whose size is a multiple of 8 and >= 16
|
||
* @param kek key-encryption-key a.k.a master key
|
||
* @param secret input plain text secret
|
||
* @param secretLen size of input, must be a multiple of HB (half-block size)
|
||
* @param wrapped wrapped secret, prepended with an additional half-block
|
||
* @return error if size is not a multiple of HB, or size < BLOCKSIZE
|
||
*/
|
||
char AES_KEY_wrap( const uint8_t* kek,
|
||
const void* secret, const size_t secretLen, uint8_t* wrapped )
|
||
{
|
||
size_t i = 0, n = secretLen / HB; /* number of semi-blocks */
|
||
block_t A;
|
||
uint8_t *r = wrapped + HB, *endpt = wrapped + secretLen;
|
||
|
||
if (n < 2 || secretLen % HB) return M_ENCRYPTION_ERROR;
|
||
|
||
memset( A, 0xA6, HB ); /* initialization vector */
|
||
memcpy( r, secret, secretLen ); /* copy input to the output */
|
||
AES_setkey( kek );
|
||
|
||
for (n *= 6; i++ < n; )
|
||
{
|
||
memcpy( A + HB, r, HB );
|
||
rijndaelEncrypt( A, A ); /* A = Enc( V | R_{k-1} ) */
|
||
memcpy( r, A + HB, HB ); /* R_{k} = LSB(64, A) */
|
||
xorBEint( A, i, HB - 1 ); /* V = MSB(64, A) ^ i */
|
||
r = (r == endpt ? wrapped : r) + HB;
|
||
}
|
||
BURN( RoundKey );
|
||
|
||
memcpy( wrapped, A, HB ); /* authentication vector */
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief unwrap a wrapped key input, whose size is a multiple of 8 and >= 24
|
||
* @param kek key-encryption-key a.k.a master key
|
||
* @param wrapped cipher-text input, the wrapped secret
|
||
* @param wrapLen size of ciphertext/wrapped input in bytes
|
||
* @param secret unwrapped secret, which is a half block shorter than input
|
||
* @return a value indicating whether decryption was successful
|
||
*/
|
||
char AES_KEY_unwrap( const uint8_t* kek,
|
||
const void* wrapped, const size_t wrapLen, uint8_t* secret )
|
||
{
|
||
size_t n = 0, i = wrapLen / HB; /* number of semi-blocks */
|
||
block_t A;
|
||
uint8_t *r = secret, *endpt = secret + wrapLen - HB;
|
||
|
||
if (i-- < 3 || wrapLen % HB) return M_DECRYPTION_ERROR;
|
||
|
||
memcpy( A, wrapped, HB );
|
||
memcpy( r, (char*) wrapped + HB, wrapLen - HB );
|
||
AES_setkey( kek );
|
||
|
||
for (i *= 6; i; --i)
|
||
{
|
||
r = (r == secret ? endpt : r) - HB;
|
||
xorBEint( A, i, HB - 1 ); /* V = MSB(64, A) ^ i */
|
||
memcpy( A + HB, r, HB ); /* A = Dec( V | R_{k} ) */
|
||
rijndaelDecrypt( A, A ); /* R_{k-1} = LSB(64, A) */
|
||
memcpy( r, A + HB, HB );
|
||
}
|
||
BURN( RoundKey );
|
||
|
||
while (i < HB) /* authenticate/error check */
|
||
{
|
||
n |= A[i++] ^ 0xA6;
|
||
}
|
||
return n ? M_AUTHENTICATION_ERROR : M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* KWA */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
Poly1305-AES message authentication: auxiliary functions and main API
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(POLY1305)
|
||
|
||
/** add two little-endian blocks x and y up to length len, so that: y = y + x */
|
||
static void addLblocks( const uint8_t* x, const uint8_t len, uint8_t* y )
|
||
{
|
||
int a, i;
|
||
for (i = a = 0; i < len; a >>= 8)
|
||
{
|
||
a += x[i] + y[i];
|
||
y[i++] = (uint8_t) a;
|
||
}
|
||
}
|
||
|
||
/** derive modulo(2^130-5) for a large integer saved in a little-endian block */
|
||
static void modP1305( uint8_t* block, int32_t q )
|
||
{
|
||
if (q < 4) return; /* q is block's MSBs: B>>128 */
|
||
|
||
block[SP - 1] &= 3; /* get rid of excess bits. */
|
||
|
||
for (q = (q >> 2) * 5; q; q >>= 8) /* suppose Q = B / (2 ^ 130) */
|
||
{ /* ..then "almost" always: */
|
||
q += *block; /* mod = B - Q * (2^130-5) */
|
||
*block++ = (uint8_t) q; /* so subtract Q * 2^130 and */
|
||
} /* ..then add (Q * 5) := q */
|
||
}
|
||
|
||
/** modular multiplication of two little-endian poly1305 blocks: y *= x mod P */
|
||
static void mulLblocks( const uint8_t* x, uint8_t* y )
|
||
{
|
||
uint8_t n = SP, result[SP] = { 0 };
|
||
while (n--) /* Y = [Y_0][Y_1]...[Y_n], */
|
||
{
|
||
uint8_t s = 8 * (n != 0), i; /* multiply X by MSB of Y */
|
||
int32_t m = 0; /* ..and add to the result */
|
||
|
||
for (i = 0; i < sizeof result; ++i) /* if Y has another byte in */
|
||
{ /* ..queue, shift the result */
|
||
m >>= 8; /* ..but don't shift for Y_0 */
|
||
m += (y[n] * x[i] + result[i]) << s;
|
||
result[i] = (uint8_t) m;
|
||
}
|
||
modP1305( result, m ); /* modular multiplication */
|
||
}
|
||
memcpy( y, result, sizeof result );
|
||
}
|
||
|
||
/**
|
||
* @brief derive the Poly1305-AES mac of message using a nonce and key pair.
|
||
* @param keys pair of encryption/mixing keys (k, r); size = KEYSIZE + 16
|
||
* @param nonce a 128 bit string which is encrypted by AES_k
|
||
* @param data buffer of input data
|
||
* @param dataSize size of data in bytes
|
||
* @param mac calculated Poly1305-AES mac
|
||
*/
|
||
void AES_Poly1305( const uint8_t* keys, const block_t nonce,
|
||
const void* data, const size_t dataSize, block_t mac )
|
||
{
|
||
uint8_t r[SP], rk[SP] = { 1 }, c[SP] = { 0 }, poly[SP] = { 0 }, s = SP - 1;
|
||
count_t q = (dataSize - 1) / BLOCKSIZE;
|
||
const char* pos = (const char*) data + dataSize;
|
||
|
||
AES_setkey( keys );
|
||
rijndaelEncrypt( nonce, mac ); /* derive AES_k(nonce) */
|
||
BURN( RoundKey );
|
||
|
||
if (!dataSize) return;
|
||
|
||
memcpy( r, keys + KEYSIZE, s ); /* extract r from (K,r) pair */
|
||
for (r[s] = 0; s > 3; s -= 3)
|
||
{
|
||
r[s--] &= 0xFC; /* clear bottom 2 bits */
|
||
r[s ] &= 0x0F; /* clear top 4 bits */
|
||
}
|
||
s = dataSize - BLOCKSIZE * q; /* size of last chunk */
|
||
do
|
||
{
|
||
memcpy( c, pos -= s, s ); /* copy message to chunk */
|
||
c[s] = 1; /* append 1 to each chunk */
|
||
mulLblocks( r, rk ); /* r^k = r^{k-1} * r */
|
||
mulLblocks( rk, c ); /* calculate c_{q-k} * r^k */
|
||
addLblocks( c, SP, poly ); /* ..and add it to poly, */
|
||
s = SP - 1; /* ..then take mod(2^130-5) */
|
||
modP1305( poly, poly[s] );
|
||
|
||
} while (q--);
|
||
|
||
q = poly[s] * 4; /* still, in some rare cases */
|
||
if (poly[0] > 0xFA && q == 12) /* ..poly may be >= 2^130-5 */
|
||
{ /* ..if so, set q=16 */
|
||
for (q = 1; poly[q] == 0xFF; ++q);
|
||
}
|
||
modP1305( poly, q / 4 );
|
||
|
||
addLblocks( poly, BLOCKSIZE, mac ); /* add AES_k(nonce) to poly */
|
||
}
|
||
#endif /* POLY1305 */
|
||
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
FPE-AES (format-preserving encryption): definitions & auxiliary functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#if IMPLEMENT(FPE)
|
||
|
||
#if CUSTOM_ALPHABET
|
||
#include "micro_fpe.h"
|
||
#else
|
||
#define ALPHABET "0123456789"
|
||
#define string_t char* /* string pointer type */
|
||
#define RADIX 10 /* strlen (ALPHABET) */
|
||
#define LOGRDX 3.32192809488736 /* log2 (RADIX) */
|
||
#define MINLEN 6 /* ceil (6 / log10 (RADIX)) */
|
||
#define MAXLEN 0x38 /* only if FF_X == 3 */
|
||
#endif
|
||
|
||
#if RADIX - 1 <= UCHAR_MAX
|
||
typedef unsigned char rbase_t; /* digit type in base-radix */
|
||
#else
|
||
typedef unsigned short rbase_t;
|
||
#endif
|
||
|
||
#if FF_X != 3 /* FF1 method: */
|
||
|
||
static size_t bb; /* the b constant in FF1 */
|
||
|
||
/** convert a string `s` in base-RADIX to a big-endian number, denoted by num */
|
||
static void numRadix( const rbase_t* s, size_t len, uint8_t* num, size_t bytes )
|
||
{
|
||
memset( num, 0, bytes );
|
||
while (len--)
|
||
{
|
||
size_t i, y = *s++;
|
||
for (i = bytes; i--; y >>= 8)
|
||
{
|
||
y += num[i] * RADIX; /* num = num * RADIX + y */
|
||
num[i] = (uint8_t) y;
|
||
}
|
||
}
|
||
}
|
||
|
||
/** convert a big-endian number to its base-RADIX representation string: `s`. */
|
||
static void strRadix( const uint8_t* num, size_t bytes, rbase_t* s, size_t len )
|
||
{
|
||
memset( s, 0, sizeof (rbase_t) * len );
|
||
while (bytes--)
|
||
{
|
||
size_t i, x = *num++;
|
||
for (i = len; i--; x /= RADIX)
|
||
{
|
||
x += s[i] << 8; /* numstr = numstr << 8 + x */
|
||
s[i] = x % RADIX;
|
||
}
|
||
}
|
||
}
|
||
|
||
/** add two numbers in base-RADIX represented by q and p, so that: p = p + q; */
|
||
static void addRadix( const rbase_t* q, const size_t len, rbase_t* p )
|
||
{
|
||
size_t i, a = 0;
|
||
for (i = len; i--; a /= RADIX) /* big-endian addition */
|
||
{
|
||
a += p[i] + q[i]; /* a /= RADIX is equivalent */
|
||
p[i] = a % RADIX; /* ..to: a = (a >= RADIX) */
|
||
}
|
||
}
|
||
|
||
/** subtract two numbers in base-RADIX represented by q and p, so that p -= q */
|
||
static void subRadix( const rbase_t* q, const size_t len, rbase_t* p )
|
||
{
|
||
size_t i, s = 1;
|
||
for (i = len; i--; s /= RADIX) /* big-endian subtraction */
|
||
{
|
||
s += RADIX - 1 + p[i] - q[i];
|
||
p[i] = s % RADIX;
|
||
}
|
||
}
|
||
|
||
/** derive C at step i of FF1 rounds, given the values: u, v and PRF_init = P */
|
||
static void FF1round( const uint8_t i, const block_t P,
|
||
const size_t u, const size_t v, rbase_t* C )
|
||
{
|
||
uint8_t* num = (void*) (C + u); /* use pre-allocated memory */
|
||
block_t R = { 0 };
|
||
size_t k = bb % sizeof R, ext = (i & 1) * u;
|
||
|
||
numRadix( C - v - ext, v, num, bb ); /* ..to get NUM_radix(B) */
|
||
R[LAST - k] = i;
|
||
|
||
memcpy( R - k + sizeof R, num, k ); /* feed NUM_radix to the PRF */
|
||
xMac( P, BLOCKSIZE, R, &rijndaelEncrypt, R );
|
||
xMac( num + k, bb - k, R, &rijndaelEncrypt, R );
|
||
|
||
memcpy( num, R, sizeof R ); /* R = PRF(P || Q) */
|
||
k = (bb + 3L) / sizeof R; /* total additional blocks */
|
||
|
||
for (ext = 0; k; ext = k--)
|
||
{ /* S = R | Enc(R ^[k]) | ... */
|
||
ext ^= k;
|
||
xorBEint( R, ext, LAST );
|
||
rijndaelEncrypt( R, num + k * sizeof R );
|
||
}
|
||
strRadix( num, (bb + 7) & ~3L, C, u ); /* take first 'd' bytes of S */
|
||
}
|
||
|
||
/** encrypt/decrypt a base-RADIX string X with length len using FF1 algorithm */
|
||
static void FF1_cipher( const uint8_t* key, const char mode, const size_t len,
|
||
const uint8_t* tweak, const size_t tweakLen,
|
||
rbase_t* X )
|
||
{
|
||
size_t u = (len + !mode) / 2, t = tweakLen;
|
||
rbase_t* Xc = X + len;
|
||
block_t P = { 1, 2, 1, RADIX >> 16, RADIX >> 8 & 0xFF, RADIX & 0xFF, 10 };
|
||
uint8_t i = t % BLOCKSIZE;
|
||
|
||
i *= i <= (uint8_t) ~bb % BLOCKSIZE; /* if i > (t+bb)%16 : i = 0 */
|
||
AES_setkey( key );
|
||
|
||
P[7] ^= len / 2; /* initializing P block */
|
||
xorBEint( P, len, 11 );
|
||
xorBEint( P, t, LAST );
|
||
rijndaelEncrypt( P, P ); /* P -> PRF(P || tweak) */
|
||
xMac( tweak, t - i, P, &rijndaelEncrypt, P );
|
||
|
||
while (i)
|
||
{
|
||
P[--i] ^= tweak[--t];
|
||
}
|
||
for (; i < 10 * mode; u = len - u) /* Feistel procedure */
|
||
{ /* encryption rounds */
|
||
FF1round( i++, P, u, len - u, Xc );
|
||
addRadix( Xc, u, i & 1 ? X : Xc - u ); /* add C to A then swap A,B */
|
||
}
|
||
for (i ^= 10; i != 0; u = len - u) /* decryption rounds */
|
||
{
|
||
FF1round( --i, P, u, len - u, Xc );
|
||
subRadix( Xc, u, i & 1 ? Xc - u : X ); /* subtract C from A */
|
||
}
|
||
BURN( RoundKey );
|
||
}
|
||
#else /* FF3/FF3-1 method: */
|
||
|
||
/** converts a string in base-RADIX to a little-endian number, denoted by num */
|
||
static void numRadix( const rbase_t* s, uint8_t len, uint8_t* num, uint8_t bytes )
|
||
{
|
||
memset( num, 0, bytes );
|
||
while (len--)
|
||
{
|
||
size_t i, d = s[len];
|
||
for (i = 0; i < bytes; d >>= 8)
|
||
{
|
||
d += num[i] * RADIX; /* num = num * RADIX + d */
|
||
num[i++] = (uint8_t) d;
|
||
}
|
||
}
|
||
}
|
||
|
||
/** convert a little-endian number to its base-RADIX representation string: s */
|
||
static void strRadix( const uint8_t* num, uint8_t bytes, rbase_t* s, uint8_t len )
|
||
{
|
||
memset( s, 0, sizeof (rbase_t) * len );
|
||
while (bytes--)
|
||
{
|
||
size_t i, b = num[bytes];
|
||
for (i = 0; i < len; b /= RADIX)
|
||
{
|
||
b += s[i] << 8; /* numstr = numstr << 8 + b */
|
||
s[i++] = b % RADIX;
|
||
}
|
||
}
|
||
}
|
||
|
||
/** add two numbers in base-RADIX represented by q and p, so that: p = p + q; */
|
||
static void addRadix( const rbase_t* q, const uint8_t len, rbase_t* p )
|
||
{
|
||
size_t i, a = 0;
|
||
for (i = 0; i < len; a /= RADIX) /* little-endian addition */
|
||
{
|
||
a += p[i] + q[i];
|
||
p[i++] = a % RADIX;
|
||
}
|
||
}
|
||
|
||
/** subtract two numbers in base-RADIX represented by q and p, so that p -= q */
|
||
static void subRadix( const rbase_t* q, const uint8_t len, rbase_t* p )
|
||
{
|
||
size_t i, s = 1;
|
||
for (i = 0; i < len; s /= RADIX) /* little-endian subtraction */
|
||
{
|
||
s += RADIX - 1 + p[i] - q[i];
|
||
p[i++] = s % RADIX;
|
||
}
|
||
}
|
||
|
||
/** calculate C at step i of FF3 rounds, given the values: u, v and tweak (T) */
|
||
static void FF3round( const uint8_t i, const uint8_t* T,
|
||
const uint8_t u, const uint8_t v, rbase_t* C )
|
||
{
|
||
uint8_t w = (i & 1) * 4, ext = (i & 1) * u;
|
||
block_t P;
|
||
COPYDWORD( T[w], P[12] ); /* W = (i is odd) ? TR : TL */
|
||
P[12] ^= i;
|
||
|
||
numRadix( C - v - ext, v, P, 12 ); /* get REV. NUM_radix( B ) */
|
||
rijndaelEncrypt( P, P );
|
||
strRadix( P, sizeof P, C, u ); /* C = REV. STR_m( c ) */
|
||
}
|
||
|
||
/** encrypt/decrypt a base-RADIX string X with size len using FF3-1 algorithm */
|
||
static void FF3_cipher( const uint8_t* key, const char mode, const uint8_t len,
|
||
const uint8_t* tweak, rbase_t* X )
|
||
{
|
||
rbase_t* Xc = X + len;
|
||
uint8_t T[8], u = (len + mode) / 2, *k = (void*) Xc, i;
|
||
|
||
memcpy( k, tweak, FF3_TWEAK_LEN );
|
||
#if FF3_TWEAK_LEN == 7
|
||
k[7] = (uint8_t) (k[3] << 4); /* see the comments at the */
|
||
k[3] &= 0xF0; /* ..bottom of header file */
|
||
#endif
|
||
|
||
for (i = 8; i --> 0; ) T[7 - i] = k[i];
|
||
for (i = KEYSIZE; i; ) k[--i] = *key++; /* key/tweak are reversed */
|
||
|
||
AES_setkey( k );
|
||
SABOTAGE( k, KEYSIZE );
|
||
|
||
for (; i < 8 * mode; u = len - u) /* Feistel procedure */
|
||
{ /* encryption rounds */
|
||
FF3round( i++, T, u, len - u, Xc );
|
||
addRadix( Xc, u, i & 1 ? X : Xc - u ); /* add C to A then swap A,B */
|
||
}
|
||
for (i ^= 8; i != 0; u = len - u) /* decryption rounds */
|
||
{
|
||
FF3round( --i, T, u, len - u, Xc );
|
||
subRadix( Xc, u, i & 1 ? Xc - u : X );
|
||
}
|
||
BURN( RoundKey );
|
||
}
|
||
#endif /* FF_X */
|
||
|
||
/*----------------------------------------------------------------------------*\
|
||
FPE-AES: main functions
|
||
\*----------------------------------------------------------------------------*/
|
||
#include <stdlib.h>
|
||
|
||
/** allocate the required memory and validate the input string in FPE mode... */
|
||
static char FPEinit( const string_t str, const size_t len, rbase_t** idx )
|
||
{
|
||
const string_t alpha = ALPHABET;
|
||
size_t u = (len + 1) / 2;
|
||
size_t n = (len + u) * sizeof (rbase_t);
|
||
|
||
#if FF_X != 3 /* extra memory is needed.. */
|
||
bb = (size_t) (LOGRDX * u + 8 - 1e-14) / 8; /* to store NUM_radix and.. */
|
||
n += (bb + 4 + LAST) & ~LAST; /* mix it in Feistel rounds */
|
||
#else
|
||
u *= len > MAXLEN ? 0 : sizeof (rbase_t);
|
||
n += u >= KEYSIZE ? 0 : KEYSIZE - u;
|
||
#endif
|
||
|
||
if (!(len >= MINLEN && u)) return 'L'; /* invalid string-length */
|
||
|
||
if (!(*idx = malloc( n ))) return 'M'; /* memory allocation failed */
|
||
|
||
for (n = len; n--; )
|
||
{
|
||
for (u = 0; alpha[u] != str[n]; )
|
||
{
|
||
if (++u == RADIX)
|
||
{
|
||
free( *idx ); /* invalid character found */
|
||
return 'C';
|
||
}
|
||
}
|
||
(*idx)[n] = (rbase_t) u;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/** make the output string after completing the process of FPE en/decryption. */
|
||
static void FPEfinalize( const rbase_t* index, size_t n, void* output )
|
||
{
|
||
const string_t alpha = ALPHABET;
|
||
string_t str = output;
|
||
str[n] = 0; /* null-terminated strings? */
|
||
|
||
while (n--) str[n] = alpha[index[n]];
|
||
}
|
||
|
||
/**
|
||
* @brief encrypt the input string using FPE-AES block-cipher method
|
||
* @param key encryption key with a fixed size specified by KEYSIZE
|
||
* @param tweak tweak byte array; similar to nonce in other schemes
|
||
* @param tweakLen size of tweak. must be exactly 7 in FF3-1
|
||
* @param pntxt input plaintext string, consisted of ALPHABET characters
|
||
* @param ptextLen size of plaintext string, or number of characters
|
||
* @param crtxt resulting ciphertext string
|
||
* @return whether all conditions of the algorithm were satisfied
|
||
*/
|
||
char AES_FPE_encrypt( const uint8_t* key, const uint8_t* tweak,
|
||
#if FF_X != 3
|
||
const size_t tweakLen,
|
||
#endif
|
||
const void* pntxt, const size_t ptextLen, void* crtxt )
|
||
{
|
||
rbase_t* index = NULL;
|
||
|
||
if (FPEinit( pntxt, ptextLen, &index ) != 0) return M_ENCRYPTION_ERROR;
|
||
|
||
#if FF_X == 3
|
||
FF3_cipher( key, 1, ptextLen, tweak, index );
|
||
#else
|
||
FF1_cipher( key, 1, ptextLen, tweak, tweakLen, index );
|
||
#endif
|
||
FPEfinalize( index, ptextLen, crtxt );
|
||
free( index );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief decrypt a ciphertext string using FPE-AES block-cipher method
|
||
* @param key decryption key with a fixed size specified by KEYSIZE
|
||
* @param tweak tweak byte array; similar to nonce in other schemes
|
||
* @param tweakLen size of tweak. must be exactly 7 in FF3-1
|
||
* @param crtxt input ciphertext string, consisted of ALPHABET characters
|
||
* @param crtxtLen size of ciphertext string, or number of characters
|
||
* @param pntxt resulting plaintext string
|
||
* @return whether all conditions of the algorithm were satisfied
|
||
*/
|
||
char AES_FPE_decrypt( const uint8_t* key, const uint8_t* tweak,
|
||
#if FF_X != 3
|
||
const size_t tweakLen,
|
||
#endif
|
||
const void* crtxt, const size_t crtxtLen, void* pntxt )
|
||
{
|
||
rbase_t* index = NULL;
|
||
|
||
if (FPEinit( crtxt, crtxtLen, &index ) != 0) return M_DECRYPTION_ERROR;
|
||
|
||
#if FF_X == 3
|
||
FF3_cipher( key, 0, crtxtLen, tweak, index );
|
||
#else
|
||
FF1_cipher( key, 0, crtxtLen, tweak, tweakLen, index );
|
||
#endif
|
||
FPEfinalize( index, crtxtLen, pntxt );
|
||
free( index );
|
||
return M_RESULT_SUCCESS;
|
||
}
|
||
#endif /* FPE */
|