A simple library to support serving https.
Dependents: oldheating gps motorhome heating
aes-gcm/gcm.c@6:819c17738dc2, 2019-09-01 (annotated)
- Committer:
- andrewboyson
- Date:
- Sun Sep 01 18:15:12 2019 +0000
- Revision:
- 6:819c17738dc2
Making progress - now have decryption working.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andrewboyson | 6:819c17738dc2 | 1 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 2 | * |
andrewboyson | 6:819c17738dc2 | 3 | * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL |
andrewboyson | 6:819c17738dc2 | 4 | * |
andrewboyson | 6:819c17738dc2 | 5 | * This is a simple and straightforward implementation of AES-GCM authenticated |
andrewboyson | 6:819c17738dc2 | 6 | * encryption. The focus of this work was correctness & accuracy. It is written |
andrewboyson | 6:819c17738dc2 | 7 | * in straight 'C' without any particular focus upon optimization or speed. It |
andrewboyson | 6:819c17738dc2 | 8 | * should be endian (memory byte order) neutral since the few places that care |
andrewboyson | 6:819c17738dc2 | 9 | * are handled explicitly. |
andrewboyson | 6:819c17738dc2 | 10 | * |
andrewboyson | 6:819c17738dc2 | 11 | * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. |
andrewboyson | 6:819c17738dc2 | 12 | * |
andrewboyson | 6:819c17738dc2 | 13 | * It is intended for general purpose use, but was written in support of GRC's |
andrewboyson | 6:819c17738dc2 | 14 | * reference implementation of the SQRL (Secure Quick Reliable Login) client. |
andrewboyson | 6:819c17738dc2 | 15 | * |
andrewboyson | 6:819c17738dc2 | 16 | * See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf |
andrewboyson | 6:819c17738dc2 | 17 | * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ |
andrewboyson | 6:819c17738dc2 | 18 | * gcm/gcm-revised-spec.pdf |
andrewboyson | 6:819c17738dc2 | 19 | * |
andrewboyson | 6:819c17738dc2 | 20 | * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE |
andrewboyson | 6:819c17738dc2 | 21 | * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. |
andrewboyson | 6:819c17738dc2 | 22 | * |
andrewboyson | 6:819c17738dc2 | 23 | *******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 24 | |
andrewboyson | 6:819c17738dc2 | 25 | #include "gcm.h" |
andrewboyson | 6:819c17738dc2 | 26 | #include "aes.h" |
andrewboyson | 6:819c17738dc2 | 27 | |
andrewboyson | 6:819c17738dc2 | 28 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 29 | * ==== IMPLEMENTATION WARNING ==== |
andrewboyson | 6:819c17738dc2 | 30 | * |
andrewboyson | 6:819c17738dc2 | 31 | * This code was developed for use within SQRL's fixed environmnent. Thus, it |
andrewboyson | 6:819c17738dc2 | 32 | * is somewhat less "general purpose" than it would be if it were designed as |
andrewboyson | 6:819c17738dc2 | 33 | * a general purpose AES-GCM library. Specifically, it bothers with almost NO |
andrewboyson | 6:819c17738dc2 | 34 | * error checking on parameter limits, buffer bounds, etc. It assumes that it |
andrewboyson | 6:819c17738dc2 | 35 | * is being invoked by its author or by someone who understands the values it |
andrewboyson | 6:819c17738dc2 | 36 | * expects to receive. Its behavior will be undefined otherwise. |
andrewboyson | 6:819c17738dc2 | 37 | * |
andrewboyson | 6:819c17738dc2 | 38 | * All functions that might fail are defined to return 'ints' to indicate a |
andrewboyson | 6:819c17738dc2 | 39 | * problem. Most do not do so now. But this allows for error propagation out |
andrewboyson | 6:819c17738dc2 | 40 | * of internal functions if robust error checking should ever be desired. |
andrewboyson | 6:819c17738dc2 | 41 | * |
andrewboyson | 6:819c17738dc2 | 42 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 43 | |
andrewboyson | 6:819c17738dc2 | 44 | /* Calculating the "GHASH" |
andrewboyson | 6:819c17738dc2 | 45 | * |
andrewboyson | 6:819c17738dc2 | 46 | * There are many ways of calculating the so-called GHASH in software, each with |
andrewboyson | 6:819c17738dc2 | 47 | * a traditional size vs performance tradeoff. The GHASH (Galois field hash) is |
andrewboyson | 6:819c17738dc2 | 48 | * an intriguing construction which takes two 128-bit strings (also the cipher's |
andrewboyson | 6:819c17738dc2 | 49 | * block size and the fundamental operation size for the system) and hashes them |
andrewboyson | 6:819c17738dc2 | 50 | * into a third 128-bit result. |
andrewboyson | 6:819c17738dc2 | 51 | * |
andrewboyson | 6:819c17738dc2 | 52 | * Many implementation solutions have been worked out that use large precomputed |
andrewboyson | 6:819c17738dc2 | 53 | * table lookups in place of more time consuming bit fiddling, and this approach |
andrewboyson | 6:819c17738dc2 | 54 | * can be scaled easily upward or downward as needed to change the time/space |
andrewboyson | 6:819c17738dc2 | 55 | * tradeoff. It's been studied extensively and there's a solid body of theory and |
andrewboyson | 6:819c17738dc2 | 56 | * practice. For example, without using any lookup tables an implementation |
andrewboyson | 6:819c17738dc2 | 57 | * might obtain 119 cycles per byte throughput, whereas using a simple, though |
andrewboyson | 6:819c17738dc2 | 58 | * large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13 |
andrewboyson | 6:819c17738dc2 | 59 | * cycles per byte. |
andrewboyson | 6:819c17738dc2 | 60 | * |
andrewboyson | 6:819c17738dc2 | 61 | * And Intel's processors have, since 2010, included an instruction which does |
andrewboyson | 6:819c17738dc2 | 62 | * the entire 128x128->128 bit job in just several 64x64->128 bit pieces. |
andrewboyson | 6:819c17738dc2 | 63 | * |
andrewboyson | 6:819c17738dc2 | 64 | * Since SQRL is interactive, and only processing a few 128-bit blocks, I've |
andrewboyson | 6:819c17738dc2 | 65 | * settled upon a relatively slower but appealing small-table compromise which |
andrewboyson | 6:819c17738dc2 | 66 | * folds a bunch of not only time consuming but also bit twiddling into a simple |
andrewboyson | 6:819c17738dc2 | 67 | * 16-entry table which is attributed to Victor Shoup's 1996 work while at |
andrewboyson | 6:819c17738dc2 | 68 | * Bellcore: "On Fast and Provably Secure MessageAuthentication Based on |
andrewboyson | 6:819c17738dc2 | 69 | * Universal Hashing." See: http://www.shoup.net/papers/macs.pdf |
andrewboyson | 6:819c17738dc2 | 70 | * See, also section 4.1 of the "gcm-revised-spec" cited above. |
andrewboyson | 6:819c17738dc2 | 71 | */ |
andrewboyson | 6:819c17738dc2 | 72 | |
andrewboyson | 6:819c17738dc2 | 73 | /* |
andrewboyson | 6:819c17738dc2 | 74 | * This 16-entry table of pre-computed constants is used by the |
andrewboyson | 6:819c17738dc2 | 75 | * GHASH multiplier to improve over a strictly table-free but |
andrewboyson | 6:819c17738dc2 | 76 | * significantly slower 128x128 bit multiple within GF(2^128). |
andrewboyson | 6:819c17738dc2 | 77 | */ |
andrewboyson | 6:819c17738dc2 | 78 | static const uint64_t last4[16] = { |
andrewboyson | 6:819c17738dc2 | 79 | 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, |
andrewboyson | 6:819c17738dc2 | 80 | 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0 }; |
andrewboyson | 6:819c17738dc2 | 81 | |
andrewboyson | 6:819c17738dc2 | 82 | /* |
andrewboyson | 6:819c17738dc2 | 83 | * Platform Endianness Neutralizing Load and Store Macro definitions |
andrewboyson | 6:819c17738dc2 | 84 | * GCM wants platform-neutral Big Endian (BE) byte ordering |
andrewboyson | 6:819c17738dc2 | 85 | */ |
andrewboyson | 6:819c17738dc2 | 86 | #define GET_UINT32_BE(n,b,i) { \ |
andrewboyson | 6:819c17738dc2 | 87 | (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ |
andrewboyson | 6:819c17738dc2 | 88 | | ( (uint32_t) (b)[(i) + 1] << 16 ) \ |
andrewboyson | 6:819c17738dc2 | 89 | | ( (uint32_t) (b)[(i) + 2] << 8 ) \ |
andrewboyson | 6:819c17738dc2 | 90 | | ( (uint32_t) (b)[(i) + 3] ); } |
andrewboyson | 6:819c17738dc2 | 91 | |
andrewboyson | 6:819c17738dc2 | 92 | #define PUT_UINT32_BE(n,b,i) { \ |
andrewboyson | 6:819c17738dc2 | 93 | (b)[(i) ] = (uchar) ( (n) >> 24 ); \ |
andrewboyson | 6:819c17738dc2 | 94 | (b)[(i) + 1] = (uchar) ( (n) >> 16 ); \ |
andrewboyson | 6:819c17738dc2 | 95 | (b)[(i) + 2] = (uchar) ( (n) >> 8 ); \ |
andrewboyson | 6:819c17738dc2 | 96 | (b)[(i) + 3] = (uchar) ( (n) ); } |
andrewboyson | 6:819c17738dc2 | 97 | |
andrewboyson | 6:819c17738dc2 | 98 | |
andrewboyson | 6:819c17738dc2 | 99 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 100 | * |
andrewboyson | 6:819c17738dc2 | 101 | * GCM_INITIALIZE |
andrewboyson | 6:819c17738dc2 | 102 | * |
andrewboyson | 6:819c17738dc2 | 103 | * Must be called once to initialize the GCM library. |
andrewboyson | 6:819c17738dc2 | 104 | * |
andrewboyson | 6:819c17738dc2 | 105 | * At present, this only calls the AES keygen table generator, which expands |
andrewboyson | 6:819c17738dc2 | 106 | * the AES keying tables for use. This is NOT A THREAD-SAFE function, so it |
andrewboyson | 6:819c17738dc2 | 107 | * MUST be called during system initialization before a multi-threading |
andrewboyson | 6:819c17738dc2 | 108 | * environment is running. |
andrewboyson | 6:819c17738dc2 | 109 | * |
andrewboyson | 6:819c17738dc2 | 110 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 111 | int gcm_initialize( void ) |
andrewboyson | 6:819c17738dc2 | 112 | { |
andrewboyson | 6:819c17738dc2 | 113 | aes_init_keygen_tables(); |
andrewboyson | 6:819c17738dc2 | 114 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 115 | } |
andrewboyson | 6:819c17738dc2 | 116 | |
andrewboyson | 6:819c17738dc2 | 117 | |
andrewboyson | 6:819c17738dc2 | 118 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 119 | * |
andrewboyson | 6:819c17738dc2 | 120 | * GCM_MULT |
andrewboyson | 6:819c17738dc2 | 121 | * |
andrewboyson | 6:819c17738dc2 | 122 | * Performs a GHASH operation on the 128-bit input vector 'x', setting |
andrewboyson | 6:819c17738dc2 | 123 | * the 128-bit output vector to 'x' times H using our precomputed tables. |
andrewboyson | 6:819c17738dc2 | 124 | * 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field. |
andrewboyson | 6:819c17738dc2 | 125 | * |
andrewboyson | 6:819c17738dc2 | 126 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 127 | static void gcm_mult( gcm_context *ctx, // pointer to established context |
andrewboyson | 6:819c17738dc2 | 128 | const uchar x[16], // pointer to 128-bit input vector |
andrewboyson | 6:819c17738dc2 | 129 | uchar output[16] ) // pointer to 128-bit output vector |
andrewboyson | 6:819c17738dc2 | 130 | { |
andrewboyson | 6:819c17738dc2 | 131 | int i; |
andrewboyson | 6:819c17738dc2 | 132 | uchar lo, hi, rem; |
andrewboyson | 6:819c17738dc2 | 133 | uint64_t zh, zl; |
andrewboyson | 6:819c17738dc2 | 134 | |
andrewboyson | 6:819c17738dc2 | 135 | lo = (uchar)( x[15] & 0x0f ); |
andrewboyson | 6:819c17738dc2 | 136 | hi = (uchar)( x[15] >> 4 ); |
andrewboyson | 6:819c17738dc2 | 137 | zh = ctx->HH[lo]; |
andrewboyson | 6:819c17738dc2 | 138 | zl = ctx->HL[lo]; |
andrewboyson | 6:819c17738dc2 | 139 | |
andrewboyson | 6:819c17738dc2 | 140 | for( i = 15; i >= 0; i-- ) { |
andrewboyson | 6:819c17738dc2 | 141 | lo = (uchar) ( x[i] & 0x0f ); |
andrewboyson | 6:819c17738dc2 | 142 | hi = (uchar) ( x[i] >> 4 ); |
andrewboyson | 6:819c17738dc2 | 143 | |
andrewboyson | 6:819c17738dc2 | 144 | if( i != 15 ) { |
andrewboyson | 6:819c17738dc2 | 145 | rem = (uchar) ( zl & 0x0f ); |
andrewboyson | 6:819c17738dc2 | 146 | zl = ( zh << 60 ) | ( zl >> 4 ); |
andrewboyson | 6:819c17738dc2 | 147 | zh = ( zh >> 4 ); |
andrewboyson | 6:819c17738dc2 | 148 | zh ^= (uint64_t) last4[rem] << 48; |
andrewboyson | 6:819c17738dc2 | 149 | zh ^= ctx->HH[lo]; |
andrewboyson | 6:819c17738dc2 | 150 | zl ^= ctx->HL[lo]; |
andrewboyson | 6:819c17738dc2 | 151 | } |
andrewboyson | 6:819c17738dc2 | 152 | rem = (uchar) ( zl & 0x0f ); |
andrewboyson | 6:819c17738dc2 | 153 | zl = ( zh << 60 ) | ( zl >> 4 ); |
andrewboyson | 6:819c17738dc2 | 154 | zh = ( zh >> 4 ); |
andrewboyson | 6:819c17738dc2 | 155 | zh ^= (uint64_t) last4[rem] << 48; |
andrewboyson | 6:819c17738dc2 | 156 | zh ^= ctx->HH[hi]; |
andrewboyson | 6:819c17738dc2 | 157 | zl ^= ctx->HL[hi]; |
andrewboyson | 6:819c17738dc2 | 158 | } |
andrewboyson | 6:819c17738dc2 | 159 | PUT_UINT32_BE( zh >> 32, output, 0 ); |
andrewboyson | 6:819c17738dc2 | 160 | PUT_UINT32_BE( zh, output, 4 ); |
andrewboyson | 6:819c17738dc2 | 161 | PUT_UINT32_BE( zl >> 32, output, 8 ); |
andrewboyson | 6:819c17738dc2 | 162 | PUT_UINT32_BE( zl, output, 12 ); |
andrewboyson | 6:819c17738dc2 | 163 | } |
andrewboyson | 6:819c17738dc2 | 164 | |
andrewboyson | 6:819c17738dc2 | 165 | |
andrewboyson | 6:819c17738dc2 | 166 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 167 | * |
andrewboyson | 6:819c17738dc2 | 168 | * GCM_SETKEY |
andrewboyson | 6:819c17738dc2 | 169 | * |
andrewboyson | 6:819c17738dc2 | 170 | * This is called to set the AES-GCM key. It initializes the AES key |
andrewboyson | 6:819c17738dc2 | 171 | * and populates the gcm context's pre-calculated HTables. |
andrewboyson | 6:819c17738dc2 | 172 | * |
andrewboyson | 6:819c17738dc2 | 173 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 174 | int gcm_setkey( gcm_context *ctx, // pointer to caller-provided gcm context |
andrewboyson | 6:819c17738dc2 | 175 | const uchar *key, // pointer to the AES encryption key |
andrewboyson | 6:819c17738dc2 | 176 | const uint keysize) // must be 128, 192 or 256 |
andrewboyson | 6:819c17738dc2 | 177 | { |
andrewboyson | 6:819c17738dc2 | 178 | int ret, i, j; |
andrewboyson | 6:819c17738dc2 | 179 | uint64_t hi, lo; |
andrewboyson | 6:819c17738dc2 | 180 | uint64_t vl, vh; |
andrewboyson | 6:819c17738dc2 | 181 | unsigned char h[16]; |
andrewboyson | 6:819c17738dc2 | 182 | |
andrewboyson | 6:819c17738dc2 | 183 | memset( ctx, 0, sizeof(gcm_context) ); // zero caller-provided GCM context |
andrewboyson | 6:819c17738dc2 | 184 | memset( h, 0, 16 ); // initialize the block to encrypt |
andrewboyson | 6:819c17738dc2 | 185 | |
andrewboyson | 6:819c17738dc2 | 186 | // encrypt the null 128-bit block to generate a key-based value |
andrewboyson | 6:819c17738dc2 | 187 | // which is then used to initialize our GHASH lookup tables |
andrewboyson | 6:819c17738dc2 | 188 | if(( ret = aes_setkey( &ctx->aes_ctx, ENCRYPT, key, keysize )) != 0 ) |
andrewboyson | 6:819c17738dc2 | 189 | return( ret ); |
andrewboyson | 6:819c17738dc2 | 190 | if(( ret = aes_cipher( &ctx->aes_ctx, h, h )) != 0 ) |
andrewboyson | 6:819c17738dc2 | 191 | return( ret ); |
andrewboyson | 6:819c17738dc2 | 192 | |
andrewboyson | 6:819c17738dc2 | 193 | GET_UINT32_BE( hi, h, 0 ); // pack h as two 64-bit ints, big-endian |
andrewboyson | 6:819c17738dc2 | 194 | GET_UINT32_BE( lo, h, 4 ); |
andrewboyson | 6:819c17738dc2 | 195 | vh = (uint64_t) hi << 32 | lo; |
andrewboyson | 6:819c17738dc2 | 196 | |
andrewboyson | 6:819c17738dc2 | 197 | GET_UINT32_BE( hi, h, 8 ); |
andrewboyson | 6:819c17738dc2 | 198 | GET_UINT32_BE( lo, h, 12 ); |
andrewboyson | 6:819c17738dc2 | 199 | vl = (uint64_t) hi << 32 | lo; |
andrewboyson | 6:819c17738dc2 | 200 | |
andrewboyson | 6:819c17738dc2 | 201 | ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128) |
andrewboyson | 6:819c17738dc2 | 202 | ctx->HH[8] = vh; |
andrewboyson | 6:819c17738dc2 | 203 | ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128) |
andrewboyson | 6:819c17738dc2 | 204 | ctx->HL[0] = 0; |
andrewboyson | 6:819c17738dc2 | 205 | |
andrewboyson | 6:819c17738dc2 | 206 | for( i = 4; i > 0; i >>= 1 ) { |
andrewboyson | 6:819c17738dc2 | 207 | uint32_t T = (uint32_t) ( vl & 1 ) * 0xe1000000U; |
andrewboyson | 6:819c17738dc2 | 208 | vl = ( vh << 63 ) | ( vl >> 1 ); |
andrewboyson | 6:819c17738dc2 | 209 | vh = ( vh >> 1 ) ^ ( (uint64_t) T << 32); |
andrewboyson | 6:819c17738dc2 | 210 | ctx->HL[i] = vl; |
andrewboyson | 6:819c17738dc2 | 211 | ctx->HH[i] = vh; |
andrewboyson | 6:819c17738dc2 | 212 | } |
andrewboyson | 6:819c17738dc2 | 213 | for (i = 2; i < 16; i <<= 1 ) { |
andrewboyson | 6:819c17738dc2 | 214 | uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; |
andrewboyson | 6:819c17738dc2 | 215 | vh = *HiH; |
andrewboyson | 6:819c17738dc2 | 216 | vl = *HiL; |
andrewboyson | 6:819c17738dc2 | 217 | for( j = 1; j < i; j++ ) { |
andrewboyson | 6:819c17738dc2 | 218 | HiH[j] = vh ^ ctx->HH[j]; |
andrewboyson | 6:819c17738dc2 | 219 | HiL[j] = vl ^ ctx->HL[j]; |
andrewboyson | 6:819c17738dc2 | 220 | } |
andrewboyson | 6:819c17738dc2 | 221 | } |
andrewboyson | 6:819c17738dc2 | 222 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 223 | } |
andrewboyson | 6:819c17738dc2 | 224 | |
andrewboyson | 6:819c17738dc2 | 225 | |
andrewboyson | 6:819c17738dc2 | 226 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 227 | * |
andrewboyson | 6:819c17738dc2 | 228 | * GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH. |
andrewboyson | 6:819c17738dc2 | 229 | * |
andrewboyson | 6:819c17738dc2 | 230 | * SETKEY: |
andrewboyson | 6:819c17738dc2 | 231 | * |
andrewboyson | 6:819c17738dc2 | 232 | * START: Sets the Encryption/Decryption mode. |
andrewboyson | 6:819c17738dc2 | 233 | * Accepts the initialization vector and additional data. |
andrewboyson | 6:819c17738dc2 | 234 | * |
andrewboyson | 6:819c17738dc2 | 235 | * UPDATE: Encrypts or decrypts the plaintext or ciphertext. |
andrewboyson | 6:819c17738dc2 | 236 | * |
andrewboyson | 6:819c17738dc2 | 237 | * FINISH: Performs a final GHASH to generate the authentication tag. |
andrewboyson | 6:819c17738dc2 | 238 | * |
andrewboyson | 6:819c17738dc2 | 239 | ****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 240 | * |
andrewboyson | 6:819c17738dc2 | 241 | * GCM_START |
andrewboyson | 6:819c17738dc2 | 242 | * |
andrewboyson | 6:819c17738dc2 | 243 | * Given a user-provided GCM context, this initializes it, sets the encryption |
andrewboyson | 6:819c17738dc2 | 244 | * mode, and preprocesses the initialization vector and additional AEAD data. |
andrewboyson | 6:819c17738dc2 | 245 | * |
andrewboyson | 6:819c17738dc2 | 246 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 247 | int gcm_start( gcm_context *ctx, // pointer to user-provided GCM context |
andrewboyson | 6:819c17738dc2 | 248 | int mode, // GCM_ENCRYPT or GCM_DECRYPT |
andrewboyson | 6:819c17738dc2 | 249 | const uchar *iv, // pointer to initialization vector |
andrewboyson | 6:819c17738dc2 | 250 | size_t iv_len, // IV length in bytes (should == 12) |
andrewboyson | 6:819c17738dc2 | 251 | const uchar *add, // ptr to additional AEAD data (NULL if none) |
andrewboyson | 6:819c17738dc2 | 252 | size_t add_len ) // length of additional AEAD data (bytes) |
andrewboyson | 6:819c17738dc2 | 253 | { |
andrewboyson | 6:819c17738dc2 | 254 | int ret; // our error return if the AES encrypt fails |
andrewboyson | 6:819c17738dc2 | 255 | uchar work_buf[16]; // XOR source built from provided IV if len != 16 |
andrewboyson | 6:819c17738dc2 | 256 | const uchar *p; // general purpose array pointer |
andrewboyson | 6:819c17738dc2 | 257 | size_t use_len; // byte count to process, up to 16 bytes |
andrewboyson | 6:819c17738dc2 | 258 | size_t i; // local loop iterator |
andrewboyson | 6:819c17738dc2 | 259 | |
andrewboyson | 6:819c17738dc2 | 260 | // since the context might be reused under the same key |
andrewboyson | 6:819c17738dc2 | 261 | // we zero the working buffers for this next new process |
andrewboyson | 6:819c17738dc2 | 262 | memset( ctx->y, 0x00, sizeof(ctx->y ) ); |
andrewboyson | 6:819c17738dc2 | 263 | memset( ctx->buf, 0x00, sizeof(ctx->buf) ); |
andrewboyson | 6:819c17738dc2 | 264 | ctx->len = 0; |
andrewboyson | 6:819c17738dc2 | 265 | ctx->add_len = 0; |
andrewboyson | 6:819c17738dc2 | 266 | |
andrewboyson | 6:819c17738dc2 | 267 | ctx->mode = mode; // set the GCM encryption/decryption mode |
andrewboyson | 6:819c17738dc2 | 268 | ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode |
andrewboyson | 6:819c17738dc2 | 269 | |
andrewboyson | 6:819c17738dc2 | 270 | if( iv_len == 12 ) { // GCM natively uses a 12-byte, 96-bit IV |
andrewboyson | 6:819c17738dc2 | 271 | memcpy( ctx->y, iv, iv_len ); // copy the IV to the top of the 'y' buff |
andrewboyson | 6:819c17738dc2 | 272 | ctx->y[15] = 1; // start "counting" from 1 (not 0) |
andrewboyson | 6:819c17738dc2 | 273 | } |
andrewboyson | 6:819c17738dc2 | 274 | else // if we don't have a 12-byte IV, we GHASH whatever we've been given |
andrewboyson | 6:819c17738dc2 | 275 | { |
andrewboyson | 6:819c17738dc2 | 276 | memset( work_buf, 0x00, 16 ); // clear the working buffer |
andrewboyson | 6:819c17738dc2 | 277 | PUT_UINT32_BE( iv_len * 8, work_buf, 12 ); // place the IV into buffer |
andrewboyson | 6:819c17738dc2 | 278 | |
andrewboyson | 6:819c17738dc2 | 279 | p = iv; |
andrewboyson | 6:819c17738dc2 | 280 | while( iv_len > 0 ) { |
andrewboyson | 6:819c17738dc2 | 281 | use_len = ( iv_len < 16 ) ? iv_len : 16; |
andrewboyson | 6:819c17738dc2 | 282 | for( i = 0; i < use_len; i++ ) ctx->y[i] ^= p[i]; |
andrewboyson | 6:819c17738dc2 | 283 | gcm_mult( ctx, ctx->y, ctx->y ); |
andrewboyson | 6:819c17738dc2 | 284 | iv_len -= use_len; |
andrewboyson | 6:819c17738dc2 | 285 | p += use_len; |
andrewboyson | 6:819c17738dc2 | 286 | } |
andrewboyson | 6:819c17738dc2 | 287 | for( i = 0; i < 16; i++ ) ctx->y[i] ^= work_buf[i]; |
andrewboyson | 6:819c17738dc2 | 288 | gcm_mult( ctx, ctx->y, ctx->y ); |
andrewboyson | 6:819c17738dc2 | 289 | } |
andrewboyson | 6:819c17738dc2 | 290 | if( ( ret = aes_cipher( &ctx->aes_ctx, ctx->y, ctx->base_ectr ) ) != 0 ) |
andrewboyson | 6:819c17738dc2 | 291 | return( ret ); |
andrewboyson | 6:819c17738dc2 | 292 | |
andrewboyson | 6:819c17738dc2 | 293 | ctx->add_len = add_len; |
andrewboyson | 6:819c17738dc2 | 294 | p = add; |
andrewboyson | 6:819c17738dc2 | 295 | while( add_len > 0 ) { |
andrewboyson | 6:819c17738dc2 | 296 | use_len = ( add_len < 16 ) ? add_len : 16; |
andrewboyson | 6:819c17738dc2 | 297 | for( i = 0; i < use_len; i++ ) ctx->buf[i] ^= p[i]; |
andrewboyson | 6:819c17738dc2 | 298 | gcm_mult( ctx, ctx->buf, ctx->buf ); |
andrewboyson | 6:819c17738dc2 | 299 | add_len -= use_len; |
andrewboyson | 6:819c17738dc2 | 300 | p += use_len; |
andrewboyson | 6:819c17738dc2 | 301 | } |
andrewboyson | 6:819c17738dc2 | 302 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 303 | } |
andrewboyson | 6:819c17738dc2 | 304 | |
andrewboyson | 6:819c17738dc2 | 305 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 306 | * |
andrewboyson | 6:819c17738dc2 | 307 | * GCM_UPDATE |
andrewboyson | 6:819c17738dc2 | 308 | * |
andrewboyson | 6:819c17738dc2 | 309 | * This is called once or more to process bulk plaintext or ciphertext data. |
andrewboyson | 6:819c17738dc2 | 310 | * We give this some number of bytes of input and it returns the same number |
andrewboyson | 6:819c17738dc2 | 311 | * of output bytes. If called multiple times (which is fine) all but the final |
andrewboyson | 6:819c17738dc2 | 312 | * invocation MUST be called with length mod 16 == 0. (Only the final call can |
andrewboyson | 6:819c17738dc2 | 313 | * have a partial block length of < 128 bits.) |
andrewboyson | 6:819c17738dc2 | 314 | * |
andrewboyson | 6:819c17738dc2 | 315 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 316 | int gcm_update( gcm_context *ctx, // pointer to user-provided GCM context |
andrewboyson | 6:819c17738dc2 | 317 | size_t length, // length, in bytes, of data to process |
andrewboyson | 6:819c17738dc2 | 318 | const uchar *input, // pointer to source data |
andrewboyson | 6:819c17738dc2 | 319 | uchar *output ) // pointer to destination data |
andrewboyson | 6:819c17738dc2 | 320 | { |
andrewboyson | 6:819c17738dc2 | 321 | int ret; // our error return if the AES encrypt fails |
andrewboyson | 6:819c17738dc2 | 322 | uchar ectr[16]; // counter-mode cipher output for XORing |
andrewboyson | 6:819c17738dc2 | 323 | size_t use_len; // byte count to process, up to 16 bytes |
andrewboyson | 6:819c17738dc2 | 324 | size_t i; // local loop iterator |
andrewboyson | 6:819c17738dc2 | 325 | |
andrewboyson | 6:819c17738dc2 | 326 | ctx->len += length; // bump the GCM context's running length count |
andrewboyson | 6:819c17738dc2 | 327 | |
andrewboyson | 6:819c17738dc2 | 328 | while( length > 0 ) { |
andrewboyson | 6:819c17738dc2 | 329 | // clamp the length to process at 16 bytes |
andrewboyson | 6:819c17738dc2 | 330 | use_len = ( length < 16 ) ? length : 16; |
andrewboyson | 6:819c17738dc2 | 331 | |
andrewboyson | 6:819c17738dc2 | 332 | // increment the context's 128-bit IV||Counter 'y' vector |
andrewboyson | 6:819c17738dc2 | 333 | for( i = 16; i > 12; i-- ) if( ++ctx->y[i - 1] != 0 ) break; |
andrewboyson | 6:819c17738dc2 | 334 | |
andrewboyson | 6:819c17738dc2 | 335 | // encrypt the context's 'y' vector under the established key |
andrewboyson | 6:819c17738dc2 | 336 | if( ( ret = aes_cipher( &ctx->aes_ctx, ctx->y, ectr ) ) != 0 ) |
andrewboyson | 6:819c17738dc2 | 337 | return( ret ); |
andrewboyson | 6:819c17738dc2 | 338 | |
andrewboyson | 6:819c17738dc2 | 339 | // encrypt or decrypt the input to the output |
andrewboyson | 6:819c17738dc2 | 340 | for( i = 0; i < use_len; i++ ) { |
andrewboyson | 6:819c17738dc2 | 341 | // XOR the cipher's ouptut vector (ectr) with our input |
andrewboyson | 6:819c17738dc2 | 342 | output[i] = (uchar) ( ectr[i] ^ input[i] ); |
andrewboyson | 6:819c17738dc2 | 343 | // now we mix in our data into the authentication hash. |
andrewboyson | 6:819c17738dc2 | 344 | // if we're ENcrypting we XOR in the post-XOR (output) results, |
andrewboyson | 6:819c17738dc2 | 345 | // but if we're DEcrypting we XOR in the input data |
andrewboyson | 6:819c17738dc2 | 346 | if( ctx->mode == ENCRYPT ) ctx->buf[i] ^= output[i]; |
andrewboyson | 6:819c17738dc2 | 347 | else ctx->buf[i] ^= input[i]; |
andrewboyson | 6:819c17738dc2 | 348 | } |
andrewboyson | 6:819c17738dc2 | 349 | gcm_mult( ctx, ctx->buf, ctx->buf ); // perform a GHASH operation |
andrewboyson | 6:819c17738dc2 | 350 | |
andrewboyson | 6:819c17738dc2 | 351 | length -= use_len; // drop the remaining byte count to process |
andrewboyson | 6:819c17738dc2 | 352 | input += use_len; // bump our input pointer forward |
andrewboyson | 6:819c17738dc2 | 353 | output += use_len; // bump our output pointer forward |
andrewboyson | 6:819c17738dc2 | 354 | } |
andrewboyson | 6:819c17738dc2 | 355 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 356 | } |
andrewboyson | 6:819c17738dc2 | 357 | |
andrewboyson | 6:819c17738dc2 | 358 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 359 | * |
andrewboyson | 6:819c17738dc2 | 360 | * GCM_FINISH |
andrewboyson | 6:819c17738dc2 | 361 | * |
andrewboyson | 6:819c17738dc2 | 362 | * This is called once after all calls to GCM_UPDATE to finalize the GCM. |
andrewboyson | 6:819c17738dc2 | 363 | * It performs the final GHASH to produce the resulting authentication TAG. |
andrewboyson | 6:819c17738dc2 | 364 | * |
andrewboyson | 6:819c17738dc2 | 365 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 366 | int gcm_finish( gcm_context *ctx, // pointer to user-provided GCM context |
andrewboyson | 6:819c17738dc2 | 367 | uchar *tag, // pointer to buffer which receives the tag |
andrewboyson | 6:819c17738dc2 | 368 | size_t tag_len ) // length, in bytes, of the tag-receiving buf |
andrewboyson | 6:819c17738dc2 | 369 | { |
andrewboyson | 6:819c17738dc2 | 370 | uchar work_buf[16]; |
andrewboyson | 6:819c17738dc2 | 371 | uint64_t orig_len = ctx->len * 8; |
andrewboyson | 6:819c17738dc2 | 372 | uint64_t orig_add_len = ctx->add_len * 8; |
andrewboyson | 6:819c17738dc2 | 373 | size_t i; |
andrewboyson | 6:819c17738dc2 | 374 | |
andrewboyson | 6:819c17738dc2 | 375 | if( tag_len != 0 ) memcpy( tag, ctx->base_ectr, tag_len ); |
andrewboyson | 6:819c17738dc2 | 376 | |
andrewboyson | 6:819c17738dc2 | 377 | if( orig_len || orig_add_len ) { |
andrewboyson | 6:819c17738dc2 | 378 | memset( work_buf, 0x00, 16 ); |
andrewboyson | 6:819c17738dc2 | 379 | |
andrewboyson | 6:819c17738dc2 | 380 | PUT_UINT32_BE( ( orig_add_len >> 32 ), work_buf, 0 ); |
andrewboyson | 6:819c17738dc2 | 381 | PUT_UINT32_BE( ( orig_add_len ), work_buf, 4 ); |
andrewboyson | 6:819c17738dc2 | 382 | PUT_UINT32_BE( ( orig_len >> 32 ), work_buf, 8 ); |
andrewboyson | 6:819c17738dc2 | 383 | PUT_UINT32_BE( ( orig_len ), work_buf, 12 ); |
andrewboyson | 6:819c17738dc2 | 384 | |
andrewboyson | 6:819c17738dc2 | 385 | for( i = 0; i < 16; i++ ) ctx->buf[i] ^= work_buf[i]; |
andrewboyson | 6:819c17738dc2 | 386 | gcm_mult( ctx, ctx->buf, ctx->buf ); |
andrewboyson | 6:819c17738dc2 | 387 | for( i = 0; i < tag_len; i++ ) tag[i] ^= ctx->buf[i]; |
andrewboyson | 6:819c17738dc2 | 388 | } |
andrewboyson | 6:819c17738dc2 | 389 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 390 | } |
andrewboyson | 6:819c17738dc2 | 391 | |
andrewboyson | 6:819c17738dc2 | 392 | |
andrewboyson | 6:819c17738dc2 | 393 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 394 | * |
andrewboyson | 6:819c17738dc2 | 395 | * GCM_CRYPT_AND_TAG |
andrewboyson | 6:819c17738dc2 | 396 | * |
andrewboyson | 6:819c17738dc2 | 397 | * This either encrypts or decrypts the user-provided data and, either |
andrewboyson | 6:819c17738dc2 | 398 | * way, generates an authentication tag of the requested length. It must be |
andrewboyson | 6:819c17738dc2 | 399 | * called with a GCM context whose key has already been set with GCM_SETKEY. |
andrewboyson | 6:819c17738dc2 | 400 | * |
andrewboyson | 6:819c17738dc2 | 401 | * The user would typically call this explicitly to ENCRYPT a buffer of data |
andrewboyson | 6:819c17738dc2 | 402 | * and optional associated data, and produce its an authentication tag. |
andrewboyson | 6:819c17738dc2 | 403 | * |
andrewboyson | 6:819c17738dc2 | 404 | * To reverse the process the user would typically call the companion |
andrewboyson | 6:819c17738dc2 | 405 | * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided |
andrewboyson | 6:819c17738dc2 | 406 | * authentication tag. The GCM_AUTH_DECRYPT function calls this function |
andrewboyson | 6:819c17738dc2 | 407 | * to perform its decryption and tag generation, which it then compares. |
andrewboyson | 6:819c17738dc2 | 408 | * |
andrewboyson | 6:819c17738dc2 | 409 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 410 | int gcm_crypt_and_tag( |
andrewboyson | 6:819c17738dc2 | 411 | gcm_context *ctx, // gcm context with key already setup |
andrewboyson | 6:819c17738dc2 | 412 | int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT |
andrewboyson | 6:819c17738dc2 | 413 | const uchar *iv, // pointer to the 12-byte initialization vector |
andrewboyson | 6:819c17738dc2 | 414 | size_t iv_len, // byte length if the IV. should always be 12 |
andrewboyson | 6:819c17738dc2 | 415 | const uchar *add, // pointer to the non-ciphered additional data |
andrewboyson | 6:819c17738dc2 | 416 | size_t add_len, // byte length of the additional AEAD data |
andrewboyson | 6:819c17738dc2 | 417 | const uchar *input, // pointer to the cipher data source |
andrewboyson | 6:819c17738dc2 | 418 | uchar *output, // pointer to the cipher data destination |
andrewboyson | 6:819c17738dc2 | 419 | size_t length, // byte length of the cipher data |
andrewboyson | 6:819c17738dc2 | 420 | uchar *tag, // pointer to the tag to be generated |
andrewboyson | 6:819c17738dc2 | 421 | size_t tag_len ) // byte length of the tag to be generated |
andrewboyson | 6:819c17738dc2 | 422 | { /* |
andrewboyson | 6:819c17738dc2 | 423 | assuming that the caller has already invoked gcm_setkey to |
andrewboyson | 6:819c17738dc2 | 424 | prepare the gcm context with the keying material, we simply |
andrewboyson | 6:819c17738dc2 | 425 | invoke each of the three GCM sub-functions in turn... |
andrewboyson | 6:819c17738dc2 | 426 | */ |
andrewboyson | 6:819c17738dc2 | 427 | gcm_start ( ctx, mode, iv, iv_len, add, add_len ); |
andrewboyson | 6:819c17738dc2 | 428 | gcm_update ( ctx, length, input, output ); |
andrewboyson | 6:819c17738dc2 | 429 | gcm_finish ( ctx, tag, tag_len ); |
andrewboyson | 6:819c17738dc2 | 430 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 431 | } |
andrewboyson | 6:819c17738dc2 | 432 | |
andrewboyson | 6:819c17738dc2 | 433 | |
andrewboyson | 6:819c17738dc2 | 434 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 435 | * |
andrewboyson | 6:819c17738dc2 | 436 | * GCM_AUTH_DECRYPT |
andrewboyson | 6:819c17738dc2 | 437 | * |
andrewboyson | 6:819c17738dc2 | 438 | * This DECRYPTS a user-provided data buffer with optional associated data. |
andrewboyson | 6:819c17738dc2 | 439 | * It then verifies a user-supplied authentication tag against the tag just |
andrewboyson | 6:819c17738dc2 | 440 | * re-created during decryption to verify that the data has not been altered. |
andrewboyson | 6:819c17738dc2 | 441 | * |
andrewboyson | 6:819c17738dc2 | 442 | * This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption |
andrewboyson | 6:819c17738dc2 | 443 | * and authentication tag generation. |
andrewboyson | 6:819c17738dc2 | 444 | * |
andrewboyson | 6:819c17738dc2 | 445 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 446 | int gcm_auth_decrypt( |
andrewboyson | 6:819c17738dc2 | 447 | gcm_context *ctx, // gcm context with key already setup |
andrewboyson | 6:819c17738dc2 | 448 | const uchar *iv, // pointer to the 12-byte initialization vector |
andrewboyson | 6:819c17738dc2 | 449 | size_t iv_len, // byte length if the IV. should always be 12 |
andrewboyson | 6:819c17738dc2 | 450 | const uchar *add, // pointer to the non-ciphered additional data |
andrewboyson | 6:819c17738dc2 | 451 | size_t add_len, // byte length of the additional AEAD data |
andrewboyson | 6:819c17738dc2 | 452 | const uchar *input, // pointer to the cipher data source |
andrewboyson | 6:819c17738dc2 | 453 | uchar *output, // pointer to the cipher data destination |
andrewboyson | 6:819c17738dc2 | 454 | size_t length, // byte length of the cipher data |
andrewboyson | 6:819c17738dc2 | 455 | const uchar *tag, // pointer to the tag to be authenticated |
andrewboyson | 6:819c17738dc2 | 456 | size_t tag_len ) // byte length of the tag <= 16 |
andrewboyson | 6:819c17738dc2 | 457 | { |
andrewboyson | 6:819c17738dc2 | 458 | uchar check_tag[16]; // the tag generated and returned by decryption |
andrewboyson | 6:819c17738dc2 | 459 | int diff; // an ORed flag to detect authentication errors |
andrewboyson | 6:819c17738dc2 | 460 | size_t i; // our local iterator |
andrewboyson | 6:819c17738dc2 | 461 | /* |
andrewboyson | 6:819c17738dc2 | 462 | we use GCM_DECRYPT_AND_TAG (above) to perform our decryption |
andrewboyson | 6:819c17738dc2 | 463 | (which is an identical XORing to reverse the previous one) |
andrewboyson | 6:819c17738dc2 | 464 | and also to re-generate the matching authentication tag |
andrewboyson | 6:819c17738dc2 | 465 | */ |
andrewboyson | 6:819c17738dc2 | 466 | gcm_crypt_and_tag( ctx, DECRYPT, iv, iv_len, add, add_len, |
andrewboyson | 6:819c17738dc2 | 467 | input, output, length, check_tag, tag_len ); |
andrewboyson | 6:819c17738dc2 | 468 | |
andrewboyson | 6:819c17738dc2 | 469 | // now we verify the authentication tag in 'constant time' |
andrewboyson | 6:819c17738dc2 | 470 | for( diff = 0, i = 0; i < tag_len; i++ ) |
andrewboyson | 6:819c17738dc2 | 471 | diff |= tag[i] ^ check_tag[i]; |
andrewboyson | 6:819c17738dc2 | 472 | |
andrewboyson | 6:819c17738dc2 | 473 | if( diff != 0 ) { // see whether any bits differed? |
andrewboyson | 6:819c17738dc2 | 474 | memset( output, 0, length ); // if so... wipe the output data |
andrewboyson | 6:819c17738dc2 | 475 | return( GCM_AUTH_FAILURE ); // return GCM_AUTH_FAILURE |
andrewboyson | 6:819c17738dc2 | 476 | } |
andrewboyson | 6:819c17738dc2 | 477 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 478 | } |
andrewboyson | 6:819c17738dc2 | 479 | |
andrewboyson | 6:819c17738dc2 | 480 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 481 | * |
andrewboyson | 6:819c17738dc2 | 482 | * GCM_ZERO_CTX |
andrewboyson | 6:819c17738dc2 | 483 | * |
andrewboyson | 6:819c17738dc2 | 484 | * The GCM context contains both the GCM context and the AES context. |
andrewboyson | 6:819c17738dc2 | 485 | * This includes keying and key-related material which is security- |
andrewboyson | 6:819c17738dc2 | 486 | * sensitive, so it MUST be zeroed after use. This function does that. |
andrewboyson | 6:819c17738dc2 | 487 | * |
andrewboyson | 6:819c17738dc2 | 488 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 489 | void gcm_zero_ctx( gcm_context *ctx ) |
andrewboyson | 6:819c17738dc2 | 490 | { |
andrewboyson | 6:819c17738dc2 | 491 | // zero the context originally provided to us |
andrewboyson | 6:819c17738dc2 | 492 | memset( ctx, 0, sizeof( gcm_context ) ); |
andrewboyson | 6:819c17738dc2 | 493 | } |