diff --git a/README.md b/README.md index 86413f5..9f24119 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/main.c b/main.c index 2555849..99434b7 100644 --- a/main.c +++ b/main.c @@ -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; } diff --git a/micro_aes.c b/micro_aes.c index 55bb2e1..0d41717 100644 --- a/micro_aes.c +++ b/micro_aes.c @@ -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 */ diff --git a/micro_aes.h b/micro_aes.h index 0662925..69c6f42 100644 --- a/micro_aes.h +++ b/micro_aes.h @@ -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 /**---------------------------------------------------------------------------- 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 + any problems. Just replace the lines ended by uint8_T with: #include -----------------------------------------------------------------------------*/ -typedef unsigned char uint8_T; -#define uint8_t uint8_T +#include +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. */ diff --git a/codeblocks.cbp b/prj_codeblocks.cbp similarity index 100% rename from codeblocks.cbp rename to prj_codeblocks.cbp diff --git a/codelite.project b/prj_codelite.project similarity index 100% rename from codelite.project rename to prj_codelite.project diff --git a/vc.vcxproj b/prj_vc++.vcxproj similarity index 100% rename from vc.vcxproj rename to prj_vc++.vcxproj