A simple library to support serving https.
Dependents: oldheating gps motorhome heating
Diff: aes-gcm/gcm.c
- Revision:
- 6:819c17738dc2
diff -r ee5489ee1117 -r 819c17738dc2 aes-gcm/gcm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/aes-gcm/gcm.c Sun Sep 01 18:15:12 2019 +0000 @@ -0,0 +1,493 @@ +/****************************************************************************** +* +* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL +* +* This is a simple and straightforward implementation of AES-GCM authenticated +* encryption. The focus of this work was correctness & accuracy. It is written +* in straight 'C' without any particular focus upon optimization or speed. It +* should be endian (memory byte order) neutral since the few places that care +* are handled explicitly. +* +* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. +* +* It is intended for general purpose use, but was written in support of GRC's +* reference implementation of the SQRL (Secure Quick Reliable Login) client. +* +* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf +* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ +* gcm/gcm-revised-spec.pdf +* +* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE +* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. +* +*******************************************************************************/ + +#include "gcm.h" +#include "aes.h" + +/****************************************************************************** + * ==== IMPLEMENTATION WARNING ==== + * + * This code was developed for use within SQRL's fixed environmnent. Thus, it + * is somewhat less "general purpose" than it would be if it were designed as + * a general purpose AES-GCM library. Specifically, it bothers with almost NO + * error checking on parameter limits, buffer bounds, etc. It assumes that it + * is being invoked by its author or by someone who understands the values it + * expects to receive. Its behavior will be undefined otherwise. + * + * All functions that might fail are defined to return 'ints' to indicate a + * problem. Most do not do so now. But this allows for error propagation out + * of internal functions if robust error checking should ever be desired. + * + ******************************************************************************/ + +/* Calculating the "GHASH" + * + * There are many ways of calculating the so-called GHASH in software, each with + * a traditional size vs performance tradeoff. The GHASH (Galois field hash) is + * an intriguing construction which takes two 128-bit strings (also the cipher's + * block size and the fundamental operation size for the system) and hashes them + * into a third 128-bit result. + * + * Many implementation solutions have been worked out that use large precomputed + * table lookups in place of more time consuming bit fiddling, and this approach + * can be scaled easily upward or downward as needed to change the time/space + * tradeoff. It's been studied extensively and there's a solid body of theory and + * practice. For example, without using any lookup tables an implementation + * might obtain 119 cycles per byte throughput, whereas using a simple, though + * large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13 + * cycles per byte. + * + * And Intel's processors have, since 2010, included an instruction which does + * the entire 128x128->128 bit job in just several 64x64->128 bit pieces. + * + * Since SQRL is interactive, and only processing a few 128-bit blocks, I've + * settled upon a relatively slower but appealing small-table compromise which + * folds a bunch of not only time consuming but also bit twiddling into a simple + * 16-entry table which is attributed to Victor Shoup's 1996 work while at + * Bellcore: "On Fast and Provably Secure MessageAuthentication Based on + * Universal Hashing." See: http://www.shoup.net/papers/macs.pdf + * See, also section 4.1 of the "gcm-revised-spec" cited above. + */ + +/* + * This 16-entry table of pre-computed constants is used by the + * GHASH multiplier to improve over a strictly table-free but + * significantly slower 128x128 bit multiple within GF(2^128). + */ +static const uint64_t last4[16] = { + 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0 }; + +/* + * Platform Endianness Neutralizing Load and Store Macro definitions + * GCM wants platform-neutral Big Endian (BE) byte ordering + */ +#define GET_UINT32_BE(n,b,i) { \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); } + +#define PUT_UINT32_BE(n,b,i) { \ + (b)[(i) ] = (uchar) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uchar) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uchar) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uchar) ( (n) ); } + + +/****************************************************************************** + * + * GCM_INITIALIZE + * + * Must be called once to initialize the GCM library. + * + * At present, this only calls the AES keygen table generator, which expands + * the AES keying tables for use. This is NOT A THREAD-SAFE function, so it + * MUST be called during system initialization before a multi-threading + * environment is running. + * + ******************************************************************************/ +int gcm_initialize( void ) +{ + aes_init_keygen_tables(); + return( 0 ); +} + + +/****************************************************************************** + * + * GCM_MULT + * + * Performs a GHASH operation on the 128-bit input vector 'x', setting + * the 128-bit output vector to 'x' times H using our precomputed tables. + * 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field. + * + ******************************************************************************/ +static void gcm_mult( gcm_context *ctx, // pointer to established context + const uchar x[16], // pointer to 128-bit input vector + uchar output[16] ) // pointer to 128-bit output vector +{ + int i; + uchar lo, hi, rem; + uint64_t zh, zl; + + lo = (uchar)( x[15] & 0x0f ); + hi = (uchar)( x[15] >> 4 ); + zh = ctx->HH[lo]; + zl = ctx->HL[lo]; + + for( i = 15; i >= 0; i-- ) { + lo = (uchar) ( x[i] & 0x0f ); + hi = (uchar) ( x[i] >> 4 ); + + if( i != 15 ) { + rem = (uchar) ( zl & 0x0f ); + zl = ( zh << 60 ) | ( zl >> 4 ); + zh = ( zh >> 4 ); + zh ^= (uint64_t) last4[rem] << 48; + zh ^= ctx->HH[lo]; + zl ^= ctx->HL[lo]; + } + rem = (uchar) ( zl & 0x0f ); + zl = ( zh << 60 ) | ( zl >> 4 ); + zh = ( zh >> 4 ); + zh ^= (uint64_t) last4[rem] << 48; + zh ^= ctx->HH[hi]; + zl ^= ctx->HL[hi]; + } + PUT_UINT32_BE( zh >> 32, output, 0 ); + PUT_UINT32_BE( zh, output, 4 ); + PUT_UINT32_BE( zl >> 32, output, 8 ); + PUT_UINT32_BE( zl, output, 12 ); +} + + +/****************************************************************************** + * + * GCM_SETKEY + * + * This is called to set the AES-GCM key. It initializes the AES key + * and populates the gcm context's pre-calculated HTables. + * + ******************************************************************************/ +int gcm_setkey( gcm_context *ctx, // pointer to caller-provided gcm context + const uchar *key, // pointer to the AES encryption key + const uint keysize) // must be 128, 192 or 256 +{ + int ret, i, j; + uint64_t hi, lo; + uint64_t vl, vh; + unsigned char h[16]; + + memset( ctx, 0, sizeof(gcm_context) ); // zero caller-provided GCM context + memset( h, 0, 16 ); // initialize the block to encrypt + + // encrypt the null 128-bit block to generate a key-based value + // which is then used to initialize our GHASH lookup tables + if(( ret = aes_setkey( &ctx->aes_ctx, ENCRYPT, key, keysize )) != 0 ) + return( ret ); + if(( ret = aes_cipher( &ctx->aes_ctx, h, h )) != 0 ) + return( ret ); + + GET_UINT32_BE( hi, h, 0 ); // pack h as two 64-bit ints, big-endian + GET_UINT32_BE( lo, h, 4 ); + vh = (uint64_t) hi << 32 | lo; + + GET_UINT32_BE( hi, h, 8 ); + GET_UINT32_BE( lo, h, 12 ); + vl = (uint64_t) hi << 32 | lo; + + ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128) + ctx->HH[8] = vh; + ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128) + ctx->HL[0] = 0; + + for( i = 4; i > 0; i >>= 1 ) { + uint32_t T = (uint32_t) ( vl & 1 ) * 0xe1000000U; + vl = ( vh << 63 ) | ( vl >> 1 ); + vh = ( vh >> 1 ) ^ ( (uint64_t) T << 32); + ctx->HL[i] = vl; + ctx->HH[i] = vh; + } + for (i = 2; i < 16; i <<= 1 ) { + uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; + vh = *HiH; + vl = *HiL; + for( j = 1; j < i; j++ ) { + HiH[j] = vh ^ ctx->HH[j]; + HiL[j] = vl ^ ctx->HL[j]; + } + } + return( 0 ); +} + + +/****************************************************************************** + * + * GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH. + * + * SETKEY: + * + * START: Sets the Encryption/Decryption mode. + * Accepts the initialization vector and additional data. + * + * UPDATE: Encrypts or decrypts the plaintext or ciphertext. + * + * FINISH: Performs a final GHASH to generate the authentication tag. + * + ****************************************************************************** + * + * GCM_START + * + * Given a user-provided GCM context, this initializes it, sets the encryption + * mode, and preprocesses the initialization vector and additional AEAD data. + * + ******************************************************************************/ +int gcm_start( gcm_context *ctx, // pointer to user-provided GCM context + int mode, // GCM_ENCRYPT or GCM_DECRYPT + const uchar *iv, // pointer to initialization vector + size_t iv_len, // IV length in bytes (should == 12) + const uchar *add, // ptr to additional AEAD data (NULL if none) + size_t add_len ) // length of additional AEAD data (bytes) +{ + int ret; // our error return if the AES encrypt fails + uchar work_buf[16]; // XOR source built from provided IV if len != 16 + const uchar *p; // general purpose array pointer + size_t use_len; // byte count to process, up to 16 bytes + size_t i; // local loop iterator + + // since the context might be reused under the same key + // we zero the working buffers for this next new process + memset( ctx->y, 0x00, sizeof(ctx->y ) ); + memset( ctx->buf, 0x00, sizeof(ctx->buf) ); + ctx->len = 0; + ctx->add_len = 0; + + ctx->mode = mode; // set the GCM encryption/decryption mode + ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode + + if( iv_len == 12 ) { // GCM natively uses a 12-byte, 96-bit IV + memcpy( ctx->y, iv, iv_len ); // copy the IV to the top of the 'y' buff + ctx->y[15] = 1; // start "counting" from 1 (not 0) + } + else // if we don't have a 12-byte IV, we GHASH whatever we've been given + { + memset( work_buf, 0x00, 16 ); // clear the working buffer + PUT_UINT32_BE( iv_len * 8, work_buf, 12 ); // place the IV into buffer + + p = iv; + while( iv_len > 0 ) { + use_len = ( iv_len < 16 ) ? iv_len : 16; + for( i = 0; i < use_len; i++ ) ctx->y[i] ^= p[i]; + gcm_mult( ctx, ctx->y, ctx->y ); + iv_len -= use_len; + p += use_len; + } + for( i = 0; i < 16; i++ ) ctx->y[i] ^= work_buf[i]; + gcm_mult( ctx, ctx->y, ctx->y ); + } + if( ( ret = aes_cipher( &ctx->aes_ctx, ctx->y, ctx->base_ectr ) ) != 0 ) + return( ret ); + + ctx->add_len = add_len; + p = add; + while( add_len > 0 ) { + use_len = ( add_len < 16 ) ? add_len : 16; + for( i = 0; i < use_len; i++ ) ctx->buf[i] ^= p[i]; + gcm_mult( ctx, ctx->buf, ctx->buf ); + add_len -= use_len; + p += use_len; + } + return( 0 ); +} + +/****************************************************************************** + * + * GCM_UPDATE + * + * This is called once or more to process bulk plaintext or ciphertext data. + * We give this some number of bytes of input and it returns the same number + * of output bytes. If called multiple times (which is fine) all but the final + * invocation MUST be called with length mod 16 == 0. (Only the final call can + * have a partial block length of < 128 bits.) + * + ******************************************************************************/ +int gcm_update( gcm_context *ctx, // pointer to user-provided GCM context + size_t length, // length, in bytes, of data to process + const uchar *input, // pointer to source data + uchar *output ) // pointer to destination data +{ + int ret; // our error return if the AES encrypt fails + uchar ectr[16]; // counter-mode cipher output for XORing + size_t use_len; // byte count to process, up to 16 bytes + size_t i; // local loop iterator + + ctx->len += length; // bump the GCM context's running length count + + while( length > 0 ) { + // clamp the length to process at 16 bytes + use_len = ( length < 16 ) ? length : 16; + + // increment the context's 128-bit IV||Counter 'y' vector + for( i = 16; i > 12; i-- ) if( ++ctx->y[i - 1] != 0 ) break; + + // encrypt the context's 'y' vector under the established key + if( ( ret = aes_cipher( &ctx->aes_ctx, ctx->y, ectr ) ) != 0 ) + return( ret ); + + // encrypt or decrypt the input to the output + for( i = 0; i < use_len; i++ ) { + // XOR the cipher's ouptut vector (ectr) with our input + output[i] = (uchar) ( ectr[i] ^ input[i] ); + // now we mix in our data into the authentication hash. + // if we're ENcrypting we XOR in the post-XOR (output) results, + // but if we're DEcrypting we XOR in the input data + if( ctx->mode == ENCRYPT ) ctx->buf[i] ^= output[i]; + else ctx->buf[i] ^= input[i]; + } + gcm_mult( ctx, ctx->buf, ctx->buf ); // perform a GHASH operation + + length -= use_len; // drop the remaining byte count to process + input += use_len; // bump our input pointer forward + output += use_len; // bump our output pointer forward + } + return( 0 ); +} + +/****************************************************************************** + * + * GCM_FINISH + * + * This is called once after all calls to GCM_UPDATE to finalize the GCM. + * It performs the final GHASH to produce the resulting authentication TAG. + * + ******************************************************************************/ +int gcm_finish( gcm_context *ctx, // pointer to user-provided GCM context + uchar *tag, // pointer to buffer which receives the tag + size_t tag_len ) // length, in bytes, of the tag-receiving buf +{ + uchar work_buf[16]; + uint64_t orig_len = ctx->len * 8; + uint64_t orig_add_len = ctx->add_len * 8; + size_t i; + + if( tag_len != 0 ) memcpy( tag, ctx->base_ectr, tag_len ); + + if( orig_len || orig_add_len ) { + memset( work_buf, 0x00, 16 ); + + PUT_UINT32_BE( ( orig_add_len >> 32 ), work_buf, 0 ); + PUT_UINT32_BE( ( orig_add_len ), work_buf, 4 ); + PUT_UINT32_BE( ( orig_len >> 32 ), work_buf, 8 ); + PUT_UINT32_BE( ( orig_len ), work_buf, 12 ); + + for( i = 0; i < 16; i++ ) ctx->buf[i] ^= work_buf[i]; + gcm_mult( ctx, ctx->buf, ctx->buf ); + for( i = 0; i < tag_len; i++ ) tag[i] ^= ctx->buf[i]; + } + return( 0 ); +} + + +/****************************************************************************** + * + * GCM_CRYPT_AND_TAG + * + * This either encrypts or decrypts the user-provided data and, either + * way, generates an authentication tag of the requested length. It must be + * called with a GCM context whose key has already been set with GCM_SETKEY. + * + * The user would typically call this explicitly to ENCRYPT a buffer of data + * and optional associated data, and produce its an authentication tag. + * + * To reverse the process the user would typically call the companion + * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided + * authentication tag. The GCM_AUTH_DECRYPT function calls this function + * to perform its decryption and tag generation, which it then compares. + * + ******************************************************************************/ +int gcm_crypt_and_tag( + gcm_context *ctx, // gcm context with key already setup + int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT + const uchar *iv, // pointer to the 12-byte initialization vector + size_t iv_len, // byte length if the IV. should always be 12 + const uchar *add, // pointer to the non-ciphered additional data + size_t add_len, // byte length of the additional AEAD data + const uchar *input, // pointer to the cipher data source + uchar *output, // pointer to the cipher data destination + size_t length, // byte length of the cipher data + uchar *tag, // pointer to the tag to be generated + size_t tag_len ) // byte length of the tag to be generated +{ /* + assuming that the caller has already invoked gcm_setkey to + prepare the gcm context with the keying material, we simply + invoke each of the three GCM sub-functions in turn... + */ + gcm_start ( ctx, mode, iv, iv_len, add, add_len ); + gcm_update ( ctx, length, input, output ); + gcm_finish ( ctx, tag, tag_len ); + return( 0 ); +} + + +/****************************************************************************** + * + * GCM_AUTH_DECRYPT + * + * This DECRYPTS a user-provided data buffer with optional associated data. + * It then verifies a user-supplied authentication tag against the tag just + * re-created during decryption to verify that the data has not been altered. + * + * This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption + * and authentication tag generation. + * + ******************************************************************************/ +int gcm_auth_decrypt( + gcm_context *ctx, // gcm context with key already setup + const uchar *iv, // pointer to the 12-byte initialization vector + size_t iv_len, // byte length if the IV. should always be 12 + const uchar *add, // pointer to the non-ciphered additional data + size_t add_len, // byte length of the additional AEAD data + const uchar *input, // pointer to the cipher data source + uchar *output, // pointer to the cipher data destination + size_t length, // byte length of the cipher data + const uchar *tag, // pointer to the tag to be authenticated + size_t tag_len ) // byte length of the tag <= 16 +{ + uchar check_tag[16]; // the tag generated and returned by decryption + int diff; // an ORed flag to detect authentication errors + size_t i; // our local iterator + /* + we use GCM_DECRYPT_AND_TAG (above) to perform our decryption + (which is an identical XORing to reverse the previous one) + and also to re-generate the matching authentication tag + */ + gcm_crypt_and_tag( ctx, DECRYPT, iv, iv_len, add, add_len, + input, output, length, check_tag, tag_len ); + + // now we verify the authentication tag in 'constant time' + for( diff = 0, i = 0; i < tag_len; i++ ) + diff |= tag[i] ^ check_tag[i]; + + if( diff != 0 ) { // see whether any bits differed? + memset( output, 0, length ); // if so... wipe the output data + return( GCM_AUTH_FAILURE ); // return GCM_AUTH_FAILURE + } + return( 0 ); +} + +/****************************************************************************** + * + * GCM_ZERO_CTX + * + * The GCM context contains both the GCM context and the AES context. + * This includes keying and key-related material which is security- + * sensitive, so it MUST be zeroed after use. This function does that. + * + ******************************************************************************/ +void gcm_zero_ctx( gcm_context *ctx ) +{ + // zero the context originally provided to us + memset( ctx, 0, sizeof( gcm_context ) ); +}