added GCM-SIV

This commit is contained in:
polfosol
2022-10-09 22:42:28 +03:30
parent 710fd53f84
commit 08e307af8c
7 changed files with 303 additions and 200 deletions

View File

@@ -3,7 +3,7 @@
**A minimalist ANSI-C compatible code for most of the AES-related algorithms**.
[![GitHub release](https://img.shields.io/static/v1?message=%C2%B5AES&logo=github&labelColor=gray&color=blue&logoColor=white&label=%20)](https://github.com/polfosol/micro-AES) ![C](https://img.shields.io/badge/langauge-C-blue.svg) [![Build Status](https://img.shields.io/badge/1.0-alpha-blue)](https://github.com/polfosol/micro-AES) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![GitHub release](https://img.shields.io/static/v1?message=%C2%B5AES&logo=github&labelColor=gray&color=blue&logoColor=white&label=%20)](https://github.com/polfosol/micro-AES) ![C](https://img.shields.io/badge/langauge-C-blue.svg) [![Build Status](https://img.shields.io/badge/1.0-beta2-blue)](https://github.com/polfosol/micro-AES/releases) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
This library is a highly flexible, all-in-one implementation of different AES encryption schemes and block ciphers modes. Before you continue, please keep in mind that, most security experts strongly warn *against* implementing your own version of AES—or other ciphering algorithms; AND THEY ARE ABSOLUTELY RIGHT!
@@ -41,7 +41,7 @@ With that in mind, I shall say that the main purpose of developing µAES was pur
## Remarks
For the sake of simplicity, it is mostly assumed that the input parameters of the functions are well defined, and the user knows what they're doing. As a result, many error checks are just skipped. Obviously, this is a naive and sometimes dangerous assumption. One must be aware that in a serious application, anything can be fed into the functions and they must take all the necessary precautions for erroneous parameters.
For the sake of simplicity, it is often assumed that the input parameters of the functions are well defined, and the user knows what they're doing. As a result, a bunch of error checks are just skipped. Obviously, this is a naive and sometimes dangerous assumption. One must be aware that in a serious application, anything can be fed into the functions and they must take all the necessary precautions for erroneous parameters.
µAES is palpably influenced by [kokke's tiny-AES](https://github.com/kokke/tiny-AES-c) library, but I have made some modifications which makes it a bit smaller and faster. I shall give kudos to their great effort which paved the way for many other branches.
@@ -49,6 +49,6 @@ All the contents of this repository (except the ones that I didn't write!) are s
Copyright © 2022 - polfosol
$In$ $loving$ $memory$ $of$ [**_Mahsa Amini_**](https://en.wikipedia.org/wiki/Death_of_Mahsa_Amini) :black_heart:
$In$ $sorrowful$ $memory$ $of$ [**_Mahsa Amini_**](https://en.wikipedia.org/wiki/Death_of_Mahsa_Amini) :black_heart:
![The foot-shooting prevention agreement taken from Jeff Moser's blog](https://i.stack.imgur.com/SoY7x.png)

85
main.c
View File

@@ -2,7 +2,7 @@
==============================================================================
Name : main.c
Author : polfosol
Version : 8.9.5.0
Version : 9.0.0.0
Copyright : copyright © 2022 - polfosol
Description : test vectors for µAES ™ library, mostly generated by Crypto++ ®
==============================================================================
@@ -42,13 +42,9 @@ static const char
#endif
*xtscipher = "10f9301a157bfceb 3eb9e7bd38500b7e 959e21ba3cc1179a d7f7d7d99460e695\
5e8bcb177571c719 6de58ff28c381913 e7c82d0adfd90c45 ca",
*cmac_hash = "b887df1fd8c239c3 e8a64d9822e21128",
*ccmcipher = "d2575123438338d7 0b2955537fdfcf41 729870884e85af15 f0a74975a72b337d\
04d426de87594b9a be3e6dcf07f21c99 db3999f81299d302 ad1e5ba683e9039a\
5483685f1bd2c3fa 3b", /* <---- with 16 bytes tag */
*sivcipher = "f6d8137b17d58d13 af040e8abadd965b 9bae3a3de90ca6f7 049c2528767da2cf\
ef17de85b1d07b59 d26b0595071ae428 3015840928e2c7f5 9abf06003b14b9ee\
25111d34bb2bfcc2 25", /* 16 bytes i.v. PREPENDED */
*gcmcipher = "5ceab5b7c2d6dede 555a23c7e3e63274 4075a51df482730b a31485ec987ddcc8\
73acdcfc6759a47b a424d838e7c0cb71 b9a4d8f4572e2141 18c8ab284ca845c1\
4394618703cddf3a fb", /* <---- with 16 bytes tag */
@@ -63,9 +59,13 @@ static const char
50661c618335a005 47cca55a8f22fbd5 ed5ab4b4a17d0aa3 29febd14ef271bae\
986810a504f01ec6 02", /* <---- with 16 bytes tag */
#endif
*gsvcipher = "XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX\
XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX\
XXXXXXXXXXXXXXXX XX",
*gsvcipher = "2f1488496ada3f70 9760420ac72e5acf a977f6add4c55ac6 85f1b9dff8f381e0\
2a64bbdd64cdd778 525462949bb0b141 db908c5cfa365750 3666f879ac879fcb\
f25c15d496a1e6f7 f8", /* <---- with 16 bytes tag */
*sivcipher = "f6d8137b17d58d13 af040e8abadd965b 9bae3a3de90ca6f7 049c2528767da2cf\
ef17de85b1d07b59 d26b0595071ae428 3015840928e2c7f5 9abf06003b14b9ee\
25111d34bb2bfcc2 25", /* 16 bytes i.v. PREPENDED */
*cmac_hash = "b887df1fd8c239c3 e8a64d9822e21128",
*wrapped = "1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5";
#elif AES_KEY_LENGTH == 24 /* ↓↓↓↓ PKCS#7 is enabled */
*ecbcipher = "af1893f0fbb09a43 7f6b0fd4f4977890 7bb85cccf1e9d2e3 ebe5bae935107868\
@@ -200,6 +200,13 @@ int main()
*output ^= AES_SIV_decrypt(key, test, test + 16, st, a, sa, output);
check("SIV decryption", output, input, st);
#endif
#if GCM_SIV && AES_KEY_LENGTH == 16
str2bytes(gsvcipher, test);
GCM_SIV_encrypt(key, iv, input, st, a, sa, output);
check("GCMSIV encrypt", output, test, st + 16);
*output ^= GCM_SIV_decrypt(key, iv, test, st + 16, a, sa, output);
check("GCMSIV decrypt", output, input, st);
#endif
#if EAX && AES_KEY_LENGTH == 16
str2bytes(eaxcipher, test);
#if EAXP
@@ -213,13 +220,6 @@ int main()
#endif
check("EAX decryption", output, input, st);
#endif
#if GCM_SIV && AES_KEY_LENGTH == 16
str2bytes(gsvcipher, test);
GCM_SIV_encrypt(key, iv, input, st, a, sa, output, output + st);
check("GCMSIV encrypt", output, test, st + 16);
*output ^= GCM_SIV_decrypt(key, iv, test, st, a, sa, test + st, 16, output);
check("GCMSIV decrypt", output, input, st);
#endif
#if KWA
str2bytes(wrapped, test);
AES_KEY_wrap(mainKey, key + 32, AES_KEY_LENGTH, output);
@@ -244,6 +244,16 @@ int main()
*output ^= AES_OCB_decrypt(key, iv, test, st, a, sa, test + st, OCB_TAG_LEN, output);
check("OCB decryption", output, input, st);
st = 11; sa = 7; /* taken from RFC 8452: */
str2bytes("ee8e1ed9ff2540ae8f2ba9f50bc2f27c", key);
str2bytes("752abad3e0afb5f434dc4310", iv);
str2bytes("6578616d706c65", a);
str2bytes("48656c6c6f20776f726c64", input);
str2bytes("5d349ead175ef6b1def6fd4fbcdeb7e4793f4a1d7e4faa70100af1", test);
GCM_SIV_encrypt(key, iv, input, st, a, sa, output);
check("GCMSIV encrypt", output, test, st + 16);
*output ^= GCM_SIV_decrypt(key, iv, test, st + 16, a, sa, output);
check("GCMSIV decrypt", output, input, st);
st = 12; sa = 1; /* taken from RFC 8452: */
str2bytes("01000000000000000000000000000000", key);
str2bytes("030000000000000000000000", iv);
@@ -251,31 +261,10 @@ int main()
str2bytes("020000000000000000000000", input);
str2bytes("296c7889fd99f41917f4462008299c51\
02745aaa3a0c469fad9e075a", test);
GCM_SIV_encrypt(key, iv, input, st, a, sa, output, output + st);
GCM_SIV_encrypt(key, iv, input, st, a, sa, output);
check("GCMSIV encrypt", output, test, st + 16);
*output ^= GCM_SIV_decrypt(key, iv, test, st, a, sa, test + st, 16, output);
*output ^= GCM_SIV_decrypt(key, iv, test, st + 16, a, sa, output);
check("GCMSIV decrypt", output, input, st);
st = 14; sa = 24; /* taken from RFC 5297: */
str2bytes("fffefdfc fbfaf9f8 f7f6f5f4 f3f2f1f0\
f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff", key);
str2bytes("10111213 14151617 18191a1b 1c1d1e1f\
20212223 24252627", a);
str2bytes("11223344 55667788 99aabbcc ddee", input);
str2bytes("85632d07 c6e8f37f 950acd32 0a2ecc93\
40c02b96 90c4dc04 daef7f6a fe5c", test);
AES_SIV_encrypt(key, input, st, a, sa, output, output + 16);
check("SIV encryption", output, test, st + 16);
*output ^= AES_SIV_decrypt(key, test, test + 16, st, a, sa, output);
check("SIV decryption", output, input, st);
st = 16; sa = 0; /* from miscreant: https://bit.ly/3yc2GBs */
str2bytes("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", key);
str2bytes("00112233445566778899aabbccddeeff", input);
str2bytes("f304f912863e303d5b540e5057c7010c942ffaf45b0e5ca5fb9a56a5263bb065", test);
AES_SIV_encrypt(key, input, st, a, sa, output, output + 16);
check("SIV encryption", output, test, st + 16);
*output ^= AES_SIV_decrypt(key, test, test + 16, st, a, sa, output);
check("SIV decryption", output, input, st);
#if EAXP
st = 0; sa = 50; /* from Annex G of the IEEE Std 1703-2012 */
str2bytes("01020304050607080102030405060708", mainKey);
@@ -315,5 +304,25 @@ int main()
#endif
check("EAX decryption", output, input, st);
#endif
st = 16; sa = 0; /* from miscreant: https://bit.ly/3yc2GBs */
str2bytes("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", key);
str2bytes("00112233445566778899aabbccddeeff", input);
str2bytes("f304f912863e303d5b540e5057c7010c942ffaf45b0e5ca5fb9a56a5263bb065", test);
AES_SIV_encrypt(key, input, st, a, sa, output, output + 16);
check("SIV encryption", output, test, st + 16);
*output ^= AES_SIV_decrypt(key, test, test + 16, st, a, sa, output);
check("SIV decryption", output, input, st);
st = 14; sa = 24; /* taken from RFC 5297: */
str2bytes("fffefdfc fbfaf9f8 f7f6f5f4 f3f2f1f0\
f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff", key);
str2bytes("10111213 14151617 18191a1b 1c1d1e1f\
20212223 24252627", a);
str2bytes("11223344 55667788 99aabbcc ddee", input);
str2bytes("85632d07 c6e8f37f 950acd32 0a2ecc93\
40c02b96 90c4dc04 daef7f6a fe5c", test);
AES_SIV_encrypt(key, input, st, a, sa, output, output + 16);
check("SIV encryption", output, test, st + 16);
*output ^= AES_SIV_decrypt(key, test, test + 16, st, a, sa, output);
check("SIV decryption", output, input, st);
return 0;
}

View File

@@ -2,7 +2,7 @@
==============================================================================
Name : micro_aes.c
Author : polfosol
Version : 8.9.5.0
Version : 9.0.1.0
Copyright : copyright © 2022 - polfosol
Description : ANSI-C compatible implementation of µAES ™ library.
==============================================================================
@@ -20,10 +20,12 @@
#define Nk (KEYSIZE/4) /* The number of 32 bit words in a key. */
#define ROUNDS (Nk+6) /* The number of rounds in AES Cipher. */
/** Since the RoundKey is a static array, it might be exposed to some attacks.
* By enabling this macro, the RoundKey buffer is wiped at the end of ciphering
* operations. However, this is NOT A GUARANTEE against side-channel attacks. */
/** The rationale of these macros is explained at the bottom of header file: */
#define INCREASE_SECURITY 0
#define SMALL_CIPHER 0
#define REDUCE_CODE_SIZE 1
#define IMPLEMENT(x) (x) > 0
/** state_t represents rijndael state matrix. fixed-size memory blocks have an
* essential role in all algorithms. so it may be a good aide for readability to
@@ -38,8 +40,6 @@ typedef unsigned char count_t;
typedef size_t count_t;
#endif
#define IMPLEMENT(x) (x) > 0
/**--------------------------------------------------------------------------**\
Private variables:
\*----------------------------------------------------------------------------*/
@@ -53,7 +53,6 @@ static uint8_t RoundKey[BLOCKSIZE * ROUNDS + KEYSIZE];
* limited. Please refer to: https://en.wikipedia.org/wiki/Rijndael_S-box */
static const uint8_t sbox[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
@@ -95,7 +94,7 @@ static const uint8_t rsbox[256] =
#endif
/**--------------------------------------------------------------------------**\
Auxiliary functions for Rijndael algorithm
Auxiliary functions for the Rijndael algorithm
\*----------------------------------------------------------------------------*/
#define getSBoxValue(num) (sbox[(num)])
@@ -127,20 +126,13 @@ static void xorBlock( const block_t src, block_t dest )
}
#endif
#if INCREASE_SECURITY
#define BURN_AFTER_READ memset( RoundKey, 0, sizeof RoundKey );
#else
#define BURN_AFTER_READ {}
#endif
/**--------------------------------------------------------------------------**\
Main functions for the Rijndael encryption algorithm
\*----------------------------------------------------------------------------*/
/**
* @brief produces (ROUNDS+1) round keys, which are used in each round
* to encrypt/decrypt the intermediate states
* @param key encryption key with a fixed size specified by KEYSIZE
* produces (ROUNDS+1) round keys from the main encryption key, which are used
* in each round to encrypt/decrypt the intermediate states.
*/
static void KeyExpansion( const uint8_t* key )
{
@@ -440,6 +432,51 @@ static void mixThenXor( const block_t b, fmix_t mix, block_t tmp,
}
#endif
#if CTR
typedef void (*finc_t)( block_t ); /* function-ptr to increment */
/** increment value of big-endian counter block. */
static void incB( block_t block )
{
#if SMALL_CIPHER /* <-- use it with CAUTION */
++block[BLOCKSIZE - 1];
#else
uint8_t i; /* inc until no overflow */
for (i = BLOCKSIZE - 1; !++block[i] && i--; );
#endif
}
#if SMALL_CIPHER
#define putValueB(block, pos, val) block[pos - 1] = val >> 8; block[pos] = val
#else
/** copy big endian value to the block, starting at the specified position... */
static void putValueB( block_t block, uint8_t pos, size_t val )
{
do
block[pos--] = (uint8_t) val;
while (val >>= 8);
}
#endif
#endif /* CTR */
#if XTS || GCM_SIV
#if SMALL_CIPHER
#define putValueL(block, pos, val) block[pos + 1] = val >> 8; block[pos] = val
#else
/** copy little endian value to the block, starting at the specified position */
static void putValueL( block_t block, uint8_t pos, size_t val )
{
do
block[pos++] = (uint8_t) val;
while (val >>= 8);
}
#endif
#endif /* XTS */
#if EAX && !EAXP || SIV || OCB || CMAC
/** Multiply a block by two in Galois bit field GF(2^128): big-endian version */
@@ -513,6 +550,53 @@ static void MulGf128( const block_t x, block_t y )
}
#endif /* GCM */
#if GCM_SIV
/** Divide a block by two in GF(2^128) field: the little-endian version (duh) */
static void halveGf128L( block_t block )
{
uint8_t c = 0, l, i;
for (i = BLOCKSIZE; i--; ) /* see the explanations for */
{ /* ..the above-defined */
l = block[i] << 7; /* ..halveGf128B function */
block[i] >>= 1;
block[i] |= c;
c = l;
}
if (c) block[BLOCKSIZE - 1] ^= 0xe1; /* 0xe1 = 11100001b */
}
/** increase the value of a counter block. this is the little-endian version. */
static void incL( block_t block )
{
#if SMALL_CIPHER /* <-- use it with CAUTION */
++block[0];
#else
uint8_t i; /* inc until no overflow */
for (i = 0; !++block[i] && i < 4; ++i);
#endif
}
/** Dot multiplication in GF(2^128) field: used in POLYVAL hash for GCM-SIV.. */
static void DotGf128( const block_t x, block_t y )
{
uint8_t i, j, result[BLOCKSIZE] = { 0 }; /* working memory */
for (i = BLOCKSIZE; i--; )
{
for (j = 0x80; j != 0; j >>= 1) /* check all the bits of X, */
{
halveGf128L( y ); /* Y_next = (Y / 2) in GF */
if (x[i] & j)
{ /* if any bit is set: */
xorBlock( y, result ); /* M ^= (Y / 2) */
}
}
}
memcpy( y, result, sizeof result ); /* result is saved into y */
}
#endif /* GCM-SIV */
#if OCB
static void nop( const block_t x, block_t y ) {}
@@ -593,80 +677,16 @@ static void cMac( const block_t D, const block_t Q,
#endif
#endif /* AEAD */
#if CTR
typedef void (*finc_t)( block_t ); /* function-ptr to increment */
/** increment value of big-endian counter block. */
static void incB( block_t block )
{
#if SMALL_CIPHER /* <-- use it with CAUTION */
++block[BLOCKSIZE - 1];
#else
uint8_t i; /* inc until no overflow */
for (i = BLOCKSIZE - 1; !++block[i] && i--; );
#endif
}
#if SMALL_CIPHER
#define putValueB(block, pos, val) block[pos - 1] = val >> 8; block[pos] = val
#else
/** copy big endian value to the block, starting at the specified position... */
static void putValueB( block_t block, uint8_t pos, size_t val )
{
do
block[pos--] = (uint8_t) val;
while (val >>= 8);
}
#endif
#endif /* CTR */
#if XTS || GCM_SIV
#if SMALL_CIPHER
#define putValueL(block, pos, val) block[pos + 1] = val >> 8; block[pos] = val
#else
/** copy little endian value to the block, starting at the specified position */
static void putValueL( block_t block, uint8_t pos, size_t val )
{
do
block[pos++] = (uint8_t) val;
while (val >>= 8);
}
#endif
#if GCM_SIV
/** increase the value of a counter block. this is the little-endian version. */
static void incL( block_t block )
{
#if SMALL_CIPHER /* <-- use it with CAUTION */
++block[0];
#else
uint8_t i; /* inc until no overflow */
for (i = 0; !++block[i] && i < BLOCKSIZE; ++i);
#endif
}
#endif
/** Multiply a block by 2 in GF(2^128) field: the POLYVAL version for GCM_SIV */
static void doubleGf128P( block_t block )
{
uint8_t c = block[BLOCKSIZE - 1] >> 7, m, i;
for (i = 0; i < BLOCKSIZE; ++i)
{ /* somehow a combination of */
m = block[i] >> 7; /* ..doubleGF128L and */
block[i] <<= 1; /* ..halveGF128B (that are */
block[i] |= c; /* ..introduced above) */
c = m; /* ¯\_(●_o)_/¯ .. m(-_-)m */
}
if (c) block[BLOCKSIZE - 1] ^= 0xc2; /* 0xc2 = 11100001b << 1 */
}
#endif /* XTS */
#define GOTO_NEXT_BLOCK x += BLOCKSIZE; y += BLOCKSIZE;
#if INCREASE_SECURITY
#define BURN_AFTER_READ memset( RoundKey, 0, sizeof RoundKey );
#define SABOTAGE_RESULT(len) memset( pText, 0, len )
#else
#define BURN_AFTER_READ
#define SABOTAGE_RESULT(len) (void) (len)
#endif
/**--------------------------------------------------------------------------**\
ECB-AES (electronic codebook mode) functions
@@ -1235,11 +1255,11 @@ char AES_GCM_decrypt( const uint8_t* key, const uint8_t* nonce,
GHash( H, cText, aData, cTextLen, aDataLen, gsh );
RijndaelEncrypt( iv, H );
xorBlock( H, gsh ); /* tag = Enc(iv) ^ GHASH */
if (memcmp( gsh, auTag, tagSize ) != 0) /* compare tags and proceed */
{ /* ..if they match. */
BURN_AFTER_READ
return AUTHENTICATION_FAILURE;
xorBlock( H, gsh ); /* tag = Enc(iv) ^ GHASH */
if (memcmp( gsh, auTag, tagSize ) != 0) /* compare tags and proceed */
{ /* ..if they match. it is */
BURN_AFTER_READ /* ..recommended to use a */
return AUTHENTICATION_FAILURE; /* ..'secure' compare method */
}
CTR_Cipher( iv, 1, &incB, cText, cTextLen, pText );
BURN_AFTER_READ
@@ -1343,8 +1363,9 @@ char AES_CCM_decrypt( const uint8_t* key, const uint8_t* nonce,
BURN_AFTER_READ
xorBlock( iv, cm );
if (memcmp( cm, auTag, tagSize ) != 0) /* sabotage results ↓ maybe? */
{ /* memset(pText, 0, Length); */
if (memcmp( cm, auTag, tagSize ) != 0) /* memcmp is vulnerable to */
{ /* ..timing attacks */
SABOTAGE_RESULT( cTextLen );
return AUTHENTICATION_FAILURE;
}
return ENDED_IN_SUCCESS;
@@ -1439,6 +1460,7 @@ char AES_SIV_decrypt( const uint8_t* keys, const block_t iv,
if (memcmp( IV, iv, sizeof IV ) != 0)
{
SABOTAGE_RESULT( cTextLen );
return AUTHENTICATION_FAILURE;
}
return ENDED_IN_SUCCESS;
@@ -1729,8 +1751,9 @@ char AES_OCB_decrypt( const uint8_t* key, const uint8_t* nonce,
OCB_GetTag( delta, Ls, Ld, pText, aData, cTextLen, aDataLen, delta );
BURN_AFTER_READ /* saved the tag into delta! */
if (memcmp( delta, auTag, tagSize ) != 0) /* sabotage results ↓ maybe? */
{ /* memset(pText, 0, Length); */
if (memcmp( delta, auTag, tagSize ) != 0)
{
SABOTAGE_RESULT( cTextLen );
return AUTHENTICATION_FAILURE;
}
return ENDED_IN_SUCCESS;
@@ -1742,45 +1765,110 @@ char AES_OCB_decrypt( const uint8_t* key, const uint8_t* nonce,
SIV-GCM-AES (Galois counter mode with synthetic i.v): main functions
\*----------------------------------------------------------------------------*/
#if IMPLEMENT(GCM_SIV)
/** calculates the POLYVAL of plaintext and AAD using authentication subkey H */
static void Polyval( const block_t H, const void* pText, const void* aData,
const size_t pTextLen, const size_t aDataLen, block_t pv )
{
block_t buf = { 0 }; /* save bit-sizes into buf */
putValueL( buf, 0, aDataLen * 8 );
putValueL( buf, 8, pTextLen * 8 );
MAC( aData, aDataLen, H, &DotGf128, pv ); /* first digest AAD, then */
MAC( pText, pTextLen, H, &DotGf128, pv ); /* ..plaintext, and then */
MAC( buf, sizeof buf, H, &DotGf128, pv ); /* ..bit sizes into POLYVAL */
}
/** derive the pair of authentication-encryption-keys from main key and nonce */
static void DeriveKeys( const uint8_t* key, const uint8_t* nonce, block_t AK )
{
uint8_t iv[BLOCKSIZE] = { 0 }, AEKeypair[KEYSIZE + 24];
uint8_t i, *k = AEKeypair;
memcpy( iv + 4, nonce, 12 );
AES_SetKey( key );
for (i = 0; i < KEYSIZE / 8 + 2; ++i)
{
RijndaelEncrypt( iv, k ); /* encrypt nonce & take MSB */
incL( iv ); /* increment nonce (L.E.) */
k += 8;
}
AES_SetKey( AEKeypair + BLOCKSIZE ); /* set the main cipher-key */
memcpy( AK, AEKeypair, BLOCKSIZE ); /* take authentication key */
}
/**
* @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 a.k.a initialization vector
* @param nonce provided 96-bit nonce
* @param pText input plain-text buffer
* @param pTextLen size of plaintext in bytes
* @param aData additional authentication data
* @param aDataLen size of additional authentication data
* @param cText encrypted cipher-text buffer
* @param auTag message authentication tag. buffer must be 16-bytes long
* @param cText encrypted cipher-text + 16 bytes MANDATORY tag appended
*/
void GCM_SIV_encrypt( const uint8_t* key, const uint8_t* nonce,
const uint8_t* pText, const size_t pTextLen,
const uint8_t* aData, const size_t aDataLen,
uint8_t* cText, block_t auTag )
uint8_t* cText )
{
block_t AK, S = { 0 };
DeriveKeys( key, nonce, AK ); /* get authentication subkey */
Polyval( AK, pText, aData, pTextLen, aDataLen, S );
for (*AK = 0; *AK < 12; ++*AK)
{ /* use AK[0] as counter! */
S[*AK] ^= nonce[*AK]; /* xor nonce with POLYVAL */
}
S[sizeof S - 1] &= 0x7F; /* clear one bit & encrypt, */
RijndaelEncrypt( S, S ); /* ..to get auth. tag */
memcpy( cText + pTextLen, S, sizeof S );
S[sizeof S - 1] |= 0x80; /* set 1 bit to get CTR I.V */
CTR_Cipher( S, 0, &incL, pText, pTextLen, cText );
BURN_AFTER_READ
}
/**
* @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 a.k.a initialization vector
* @param cText input cipher-text buffer
* @param cTextLen size of ciphertext in bytes
* @param nonce provided 96-bit nonce
* @param cText input cipher-text buffer + 16 bytes MANDATORY tag appended
* @param cTextLen size of ciphertext + 16
* @param aData additional authentication data
* @param aDataLen size of additional authentication data
* @param auTag message authentication tag (if any)
* @param tagSize length of authentication tag
* @param pText plain-text output buffer
* @return whether message authentication was successful
* @return whether message authentication/decryption was successful
*/
char GCM_SIV_decrypt( const uint8_t* key, const uint8_t* nonce,
const uint8_t* cText, const size_t cTextLen,
const uint8_t* aData, const size_t aDataLen,
const uint8_t* auTag, const uint8_t tagSize,
uint8_t* pText )
{
BURN_AFTER_READ
block_t AK, S;
uint8_t const *tag = cText + cTextLen - 16;
if (cTextLen < 16) return DECRYPTION_FAILURE;
DeriveKeys( key, nonce, AK ); /* get authentication subkey */
memcpy( S, tag, sizeof S ); /* tag contains counter I.V. */
S[sizeof S - 1] |= 0x80;
CTR_Cipher( S, 0, &incL, cText, cTextLen - 16, pText );
memset( S, 0, sizeof S );
Polyval( AK, pText, aData, cTextLen - 16, aDataLen, S );
for (*AK = 0; *AK < 12; ++*AK)
{ /* using AK[0] as counter! */
S[*AK] ^= nonce[*AK]; /* xor nonce with POLYVAL */
}
S[sizeof S - 1] &= 0x7F; /* clear one bit & encrypt, */
RijndaelEncrypt( S, S ); /* ..to get tag & verify it */
BURN_AFTER_READ /* rfc-8452 RECOMMENDS not */
if (memcmp( S, tag, sizeof S ) != 0) /* ..using memcmp to avoid */
{ /* ..timing attacks. e.g use */
SABOTAGE_RESULT( cTextLen - 16 ); /* ..a constant-time compare */
return AUTHENTICATION_FAILURE; /* ..method: timingsafe_bcmp */
}
return ENDED_IN_SUCCESS;
}
#endif /* GCM-SIV */
@@ -1869,10 +1957,8 @@ char AES_KEY_unwrap( const uint8_t* kek,
}
BURN_AFTER_READ
for (i = 0; i < S; ++i) /* error checking... */
{
if (A[i] != 0xA6) return DECRYPTION_FAILURE;
}
return ENDED_IN_SUCCESS;
for (i = 0; i < S; ++i) j |= A[i] ^ 0xA6; /* authenticate/error check */
return j ? DECRYPTION_FAILURE : ENDED_IN_SUCCESS;
}
#endif /* KWA */

View File

@@ -2,7 +2,7 @@
==============================================================================
Name : micro_aes.h
Author : polfosol
Version : 8.9.5.0
Version : 9.0.1.0
Copyright : copyright © 2022 - polfosol
Description : μAES ™ is a minimalist all-in-one library for AES encryption
==============================================================================
@@ -10,7 +10,6 @@
#ifndef _MICRO__AES_
#define _MICRO__AES_
#include <string.h>
/**----------------------------------------------------------------------------
You can use different AES algorithms by changing this macro. Default is AES-128
@@ -21,10 +20,10 @@ You can use different AES algorithms by changing this macro. Default is AES-128
AES block-cipher modes of operation. The following modes can be enabled/disabled
by setting their corresponding macros to TRUE (1) or FALSE (0).
-----------------------------------------------------------------------------*/
#define BLOCK_CIPHER_MODES 1
#define DISABLE_BLOCKCIPHER 0
#define AEAD_MODES 1 /* authenticated encryption with associated data. */
#if BLOCK_CIPHER_MODES
#if !DISABLE_BLOCKCIPHER
#define ECB 1 /* electronic code-book (NIST SP 800-38A) */
#define CBC 1 /* cipher block chaining (NIST SP 800-38A) */
#define CFB 1 /* cipher feedback (NIST SP 800-38A) */
@@ -32,10 +31,12 @@ AES block-cipher modes of operation. The following modes can be enabled/disabled
#define CTR 1 /* counter-block (NIST SP 800-38A) */
#define XEX 1 /* xor-encrypt-xor (NIST SP 800-38E) */
#define KWA 1 /* key wrap with authentication (NIST SP 800-38F) */
#define FPE 0 /* format-preserving encryption (NIST SP 800-38G) */
#define FPE 1 /* format-preserving encryption (NIST SP 800-38G) */
#endif
#if AEAD_MODES
#define CMAC 1 /* message authentication code (NIST SP 800-38B) */
#if CTR
#define CCM 1 /* counter with CBC-MAC (RFC-3610 & SP 800-38C) */
#define GCM 1 /* Galois/counter mode with GMAC (NIST SP 800-38D) */
@@ -47,8 +48,6 @@ AES block-cipher modes of operation. The following modes can be enabled/disabled
#if XEX
#define OCB 1 /* offset codebook mode (RFC-7253) */
#endif
#define CMAC 1 /* message authentication code (NIST SP 800-38B) */
#endif
#if CBC
@@ -67,7 +66,7 @@ AES block-cipher modes of operation. The following modes can be enabled/disabled
#define EAXP 0 /* EAX-prime, as specified by IEEE Std 1703 */
#endif
#define WTF! (BLOCK_CIPHER_MODES || CMAC)
#define WTF (!CMAC && DISABLE_BLOCKCIPHER)
#define MICRO_RJNDL WTF /* none of above; just rijndael API. dude.., why? */
/**----------------------------------------------------------------------------
@@ -93,7 +92,7 @@ Refer to the BOTTOM OF THIS DOCUMENT for some explanations about these macros:
#if CCM
#define CCM_NONCE_LEN 11 /* for 32-bit count (since one byte is reserved). */
#define CCM_TAG_LEN 16
#define CCM_TAG_LEN 16 /* an even number in the range of 4..16 */
#endif
#if GCM
@@ -110,10 +109,11 @@ Refer to the BOTTOM OF THIS DOCUMENT for some explanations about these macros:
/**----------------------------------------------------------------------------
Since stdint.h is not a part of ANSI-C, we used a 'trick' that should not cause
any problems. You may replace the following two lines by: #include <stdint.h>
any problems. Just replace the lines ended by uint8_T with: #include <stdint.h>
-----------------------------------------------------------------------------*/
typedef unsigned char uint8_T;
#define uint8_t uint8_T
#include <string.h>
typedef unsigned char uint8_T;
#define uint8_t uint8_T
#ifdef __cplusplus
extern "C" {
@@ -274,30 +274,6 @@ char AES_GCM_decrypt( const uint8_t* key, /* decryption key */
uint8_t* pText ); /* decrypted plain-text */
#endif /* GCM */
/**----------------------------------------------------------------------------
Main functions for GCM-SIV-AES block ciphering
-----------------------------------------------------------------------------*/
#if GCM_SIV
void GCM_SIV_encrypt( const uint8_t* key, /* encryption key */
const uint8_t* nonce, /* a.k.a initialization vector */
const uint8_t* pText, /* plain text */
const size_t pTextLen, /* length of input plain text */
const uint8_t* aData, /* added authentication data */
const size_t aDataLen, /* size of authentication data */
uint8_t* cText, /* cipher-text buffer */
uint8_t* auTag ); /* message authentication tag */
char GCM_SIV_decrypt( const uint8_t* key, /* decryption key */
const uint8_t* nonce, /* a.k.a initialization vector */
const uint8_t* cText, /* cipher text */
const size_t cTextLen, /* length of input cipher-text */
const uint8_t* aData, /* added authentication data */
const size_t aDataLen, /* size of authentication data */
const uint8_t* auTag, /* authentication tag */
const uint8_t tagSize, /* size of tag (if any) */
uint8_t* pText ); /* decrypted plain-text */
#endif /* GCM-SIV */
/**----------------------------------------------------------------------------
Main functions for CCM-AES block ciphering
-----------------------------------------------------------------------------*/
@@ -378,6 +354,27 @@ char AES_EAX_decrypt( const uint8_t* key, /* decryption key */
uint8_t* pText ); /* decrypted plain-text */
#endif /* EAX */
/**----------------------------------------------------------------------------
Main functions for GCM-SIV-AES block ciphering
-----------------------------------------------------------------------------*/
#if GCM_SIV
void GCM_SIV_encrypt( const uint8_t* key, /* encryption key */
const uint8_t* nonce, /* provided 96-bit nonce */
const uint8_t* pText, /* plain text */
const size_t pTextLen, /* length of input plain text */
const uint8_t* aData, /* added authentication data */
const size_t aDataLen, /* size of authentication data */
uint8_t* cText ); /* cipher-text + 16 bytes tag */
char GCM_SIV_decrypt( const uint8_t* key, /* decryption key */
const uint8_t* nonce, /* provided 96-bit nonce */
const uint8_t* cText, /* cipher text + 16 bytes tag */
const size_t cTextLen, /* length of cipher-text + 16 */
const uint8_t* aData, /* added authentication data */
const size_t aDataLen, /* size of authentication data */
uint8_t* pText ); /* decrypted plain-text */
#endif /* GCM-SIV */
/**----------------------------------------------------------------------------
Main functions for AES key-wrapping; more info at the bottom of this page.
-----------------------------------------------------------------------------*/
@@ -393,6 +390,13 @@ char AES_KEY_unwrap( const uint8_t* kek, /* key encryption key */
uint8_t* secret ); /* buffer for unwrapped key */
#endif /* KWA */
/**----------------------------------------------------------------------------
Main functions for FPE-AES (to be added soon)
-----------------------------------------------------------------------------*/
#if FPE
#endif /* FPE */
/**----------------------------------------------------------------------------
Main function for AES cipher-based message authentication code
-----------------------------------------------------------------------------*/
@@ -421,9 +425,6 @@ The error codes and key length should be defined here for external references:
#define AES_KEY_LENGTH 16
#endif
#define SMALL_CIPHER 0 /* explained at the bottom ↓↓ */
#define REDUCE_CODE_SIZE 1
#endif /* header guard */
/**--------------------------------------------------------------------------**\
@@ -470,12 +471,19 @@ The error codes and key length should be defined here for external references:
find some mentions of TKW which is for 3DES and irrelevant here. Anyway, the
wrapped output has an additional block, i.e. wrappedSize = secretSize + 8.
* If the length of the input cipher/plain text is 'always' less than 4KB, you
* Let me explain three extra options that are defined in the source file. The
most sensitive part of the Rijndael algorithm is the round-keys. In this
library they are defined as a static array, which might be exposed to some
attacks. By enabling INCREASE_SECURITY macro, the RoundKey buffer is wiped
at the end of ciphering operations. However, this is NOT A GUARANTEE against
side-channel attacks.
If the length of the input cipher/plain text is 'always' less than 4KB, you
can enable the SMALL_CIPHER macro to save a few bytes in the compiled code.
NOTE that for key-wrapping, this limit is 42 blocks (336 bytes) of secret
Note that for key-wrapping, this limit is 42 blocks (336 bytes) of secret
key. These assumptions are likely to be valid for some embedded systems and
small applications. Furthermore, enabling that other macro, REDUCE_CODE_SIZE
had a considerable effect on the size of the compiled code in my own tests.
Nonetheless, others might get a different result from them.
small applications.
Furthermore, enabling that other macro, REDUCE_CODE_SIZE had a considerable
effect on the size of the compiled code in my own tests. Nonetheless, others
might get a different result from them.
*/