A simple library to support serving https.
Dependents: oldheating gps motorhome heating
aes-gcm/aes.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 the AES Rijndael |
andrewboyson | 6:819c17738dc2 | 6 | * 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus |
andrewboyson | 6:819c17738dc2 | 7 | * of this work was correctness & accuracy. It is written in 'C' without any |
andrewboyson | 6:819c17738dc2 | 8 | * particular focus upon optimization or speed. It should be endian (memory |
andrewboyson | 6:819c17738dc2 | 9 | * byte order) neutral since the few places that care are handled explicitly. |
andrewboyson | 6:819c17738dc2 | 10 | * |
andrewboyson | 6:819c17738dc2 | 11 | * This implementation of Rijndael 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/archive/aes/rijndael/wsdindex.html |
andrewboyson | 6:819c17738dc2 | 17 | * |
andrewboyson | 6:819c17738dc2 | 18 | * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE |
andrewboyson | 6:819c17738dc2 | 19 | * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. |
andrewboyson | 6:819c17738dc2 | 20 | * |
andrewboyson | 6:819c17738dc2 | 21 | *******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 22 | |
andrewboyson | 6:819c17738dc2 | 23 | #include "aes.h" |
andrewboyson | 6:819c17738dc2 | 24 | |
andrewboyson | 6:819c17738dc2 | 25 | static int aes_tables_inited = 0; // run-once flag for performing key |
andrewboyson | 6:819c17738dc2 | 26 | // expasion table generation (see below) |
andrewboyson | 6:819c17738dc2 | 27 | /* |
andrewboyson | 6:819c17738dc2 | 28 | * The following static local tables must be filled-in before the first use of |
andrewboyson | 6:819c17738dc2 | 29 | * the GCM or AES ciphers. They are used for the AES key expansion/scheduling |
andrewboyson | 6:819c17738dc2 | 30 | * and once built are read-only and thread safe. The "gcm_initialize" function |
andrewboyson | 6:819c17738dc2 | 31 | * must be called once during system initialization to populate these arrays |
andrewboyson | 6:819c17738dc2 | 32 | * for subsequent use by the AES key scheduler. If they have not been built |
andrewboyson | 6:819c17738dc2 | 33 | * before attempted use, an error will be returned to the caller. |
andrewboyson | 6:819c17738dc2 | 34 | * |
andrewboyson | 6:819c17738dc2 | 35 | * NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since |
andrewboyson | 6:819c17738dc2 | 36 | * GCM uses AES in counter-mode, where the AES cipher output is XORed with |
andrewboyson | 6:819c17738dc2 | 37 | * the GCM input, we ONLY NEED AES encryption. Thus, to save space AES |
andrewboyson | 6:819c17738dc2 | 38 | * decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h. |
andrewboyson | 6:819c17738dc2 | 39 | */ |
andrewboyson | 6:819c17738dc2 | 40 | // We always need our forward tables |
andrewboyson | 6:819c17738dc2 | 41 | static uchar FSb[256]; // Forward substitution box (FSb) |
andrewboyson | 6:819c17738dc2 | 42 | static uint32_t FT0[256]; // Forward key schedule assembly tables |
andrewboyson | 6:819c17738dc2 | 43 | static uint32_t FT1[256]; |
andrewboyson | 6:819c17738dc2 | 44 | static uint32_t FT2[256]; |
andrewboyson | 6:819c17738dc2 | 45 | static uint32_t FT3[256]; |
andrewboyson | 6:819c17738dc2 | 46 | |
andrewboyson | 6:819c17738dc2 | 47 | #if AES_DECRYPTION // We ONLY need reverse for decryption |
andrewboyson | 6:819c17738dc2 | 48 | static uchar RSb[256]; // Reverse substitution box (RSb) |
andrewboyson | 6:819c17738dc2 | 49 | static uint32_t RT0[256]; // Reverse key schedule assembly tables |
andrewboyson | 6:819c17738dc2 | 50 | static uint32_t RT1[256]; |
andrewboyson | 6:819c17738dc2 | 51 | static uint32_t RT2[256]; |
andrewboyson | 6:819c17738dc2 | 52 | static uint32_t RT3[256]; |
andrewboyson | 6:819c17738dc2 | 53 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 54 | |
andrewboyson | 6:819c17738dc2 | 55 | static uint32_t RCON[10]; // AES round constants |
andrewboyson | 6:819c17738dc2 | 56 | |
andrewboyson | 6:819c17738dc2 | 57 | /* |
andrewboyson | 6:819c17738dc2 | 58 | * Platform Endianness Neutralizing Load and Store Macro definitions |
andrewboyson | 6:819c17738dc2 | 59 | * AES wants platform-neutral Little Endian (LE) byte ordering |
andrewboyson | 6:819c17738dc2 | 60 | */ |
andrewboyson | 6:819c17738dc2 | 61 | #define GET_UINT32_LE(n,b,i) { \ |
andrewboyson | 6:819c17738dc2 | 62 | (n) = ( (uint32_t) (b)[(i) ] ) \ |
andrewboyson | 6:819c17738dc2 | 63 | | ( (uint32_t) (b)[(i) + 1] << 8 ) \ |
andrewboyson | 6:819c17738dc2 | 64 | | ( (uint32_t) (b)[(i) + 2] << 16 ) \ |
andrewboyson | 6:819c17738dc2 | 65 | | ( (uint32_t) (b)[(i) + 3] << 24 ); } |
andrewboyson | 6:819c17738dc2 | 66 | |
andrewboyson | 6:819c17738dc2 | 67 | #define PUT_UINT32_LE(n,b,i) { \ |
andrewboyson | 6:819c17738dc2 | 68 | (b)[(i) ] = (uchar) ( (n) ); \ |
andrewboyson | 6:819c17738dc2 | 69 | (b)[(i) + 1] = (uchar) ( (n) >> 8 ); \ |
andrewboyson | 6:819c17738dc2 | 70 | (b)[(i) + 2] = (uchar) ( (n) >> 16 ); \ |
andrewboyson | 6:819c17738dc2 | 71 | (b)[(i) + 3] = (uchar) ( (n) >> 24 ); } |
andrewboyson | 6:819c17738dc2 | 72 | |
andrewboyson | 6:819c17738dc2 | 73 | /* |
andrewboyson | 6:819c17738dc2 | 74 | * AES forward and reverse encryption round processing macros |
andrewboyson | 6:819c17738dc2 | 75 | */ |
andrewboyson | 6:819c17738dc2 | 76 | #define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ |
andrewboyson | 6:819c17738dc2 | 77 | { \ |
andrewboyson | 6:819c17738dc2 | 78 | X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 79 | FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 80 | FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 81 | FT3[ ( Y3 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 82 | \ |
andrewboyson | 6:819c17738dc2 | 83 | X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 84 | FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 85 | FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 86 | FT3[ ( Y0 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 87 | \ |
andrewboyson | 6:819c17738dc2 | 88 | X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 89 | FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 90 | FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 91 | FT3[ ( Y1 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 92 | \ |
andrewboyson | 6:819c17738dc2 | 93 | X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 94 | FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 95 | FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 96 | FT3[ ( Y2 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 97 | } |
andrewboyson | 6:819c17738dc2 | 98 | |
andrewboyson | 6:819c17738dc2 | 99 | #define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ |
andrewboyson | 6:819c17738dc2 | 100 | { \ |
andrewboyson | 6:819c17738dc2 | 101 | X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 102 | RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 103 | RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 104 | RT3[ ( Y1 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 105 | \ |
andrewboyson | 6:819c17738dc2 | 106 | X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 107 | RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 108 | RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 109 | RT3[ ( Y2 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 110 | \ |
andrewboyson | 6:819c17738dc2 | 111 | X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 112 | RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 113 | RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 114 | RT3[ ( Y3 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 115 | \ |
andrewboyson | 6:819c17738dc2 | 116 | X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 117 | RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 118 | RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ |
andrewboyson | 6:819c17738dc2 | 119 | RT3[ ( Y0 >> 24 ) & 0xFF ]; \ |
andrewboyson | 6:819c17738dc2 | 120 | } |
andrewboyson | 6:819c17738dc2 | 121 | |
andrewboyson | 6:819c17738dc2 | 122 | /* |
andrewboyson | 6:819c17738dc2 | 123 | * These macros improve the readability of the key |
andrewboyson | 6:819c17738dc2 | 124 | * generation initialization code by collapsing |
andrewboyson | 6:819c17738dc2 | 125 | * repetitive common operations into logical pieces. |
andrewboyson | 6:819c17738dc2 | 126 | */ |
andrewboyson | 6:819c17738dc2 | 127 | #define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) |
andrewboyson | 6:819c17738dc2 | 128 | #define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) |
andrewboyson | 6:819c17738dc2 | 129 | #define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) |
andrewboyson | 6:819c17738dc2 | 130 | #define MIX(x,y) { y = ( (y << 1) | (y >> 7) ) & 0xFF; x ^= y; } |
andrewboyson | 6:819c17738dc2 | 131 | #define CPY128 { *RK++ = *SK++; *RK++ = *SK++; \ |
andrewboyson | 6:819c17738dc2 | 132 | *RK++ = *SK++; *RK++ = *SK++; } |
andrewboyson | 6:819c17738dc2 | 133 | |
andrewboyson | 6:819c17738dc2 | 134 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 135 | * |
andrewboyson | 6:819c17738dc2 | 136 | * AES_INIT_KEYGEN_TABLES |
andrewboyson | 6:819c17738dc2 | 137 | * |
andrewboyson | 6:819c17738dc2 | 138 | * Fills the AES key expansion tables allocated above with their static |
andrewboyson | 6:819c17738dc2 | 139 | * data. This is not "per key" data, but static system-wide read-only |
andrewboyson | 6:819c17738dc2 | 140 | * table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once |
andrewboyson | 6:819c17738dc2 | 141 | * at system initialization to setup the tables for all subsequent use. |
andrewboyson | 6:819c17738dc2 | 142 | * |
andrewboyson | 6:819c17738dc2 | 143 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 144 | void aes_init_keygen_tables( void ) |
andrewboyson | 6:819c17738dc2 | 145 | { |
andrewboyson | 6:819c17738dc2 | 146 | int i, x, y, z; // general purpose iteration and computation locals |
andrewboyson | 6:819c17738dc2 | 147 | int pow[256]; |
andrewboyson | 6:819c17738dc2 | 148 | int log[256]; |
andrewboyson | 6:819c17738dc2 | 149 | |
andrewboyson | 6:819c17738dc2 | 150 | // fill the 'pow' and 'log' tables over GF(2^8) |
andrewboyson | 6:819c17738dc2 | 151 | for( i = 0, x = 1; i < 256; i++ ) { |
andrewboyson | 6:819c17738dc2 | 152 | pow[i] = x; |
andrewboyson | 6:819c17738dc2 | 153 | log[x] = i; |
andrewboyson | 6:819c17738dc2 | 154 | x = ( x ^ XTIME( x ) ) & 0xFF; |
andrewboyson | 6:819c17738dc2 | 155 | } |
andrewboyson | 6:819c17738dc2 | 156 | // compute the round constants |
andrewboyson | 6:819c17738dc2 | 157 | for( i = 0, x = 1; i < 10; i++ ) { |
andrewboyson | 6:819c17738dc2 | 158 | RCON[i] = (uint32_t) x; |
andrewboyson | 6:819c17738dc2 | 159 | x = XTIME( x ) & 0xFF; |
andrewboyson | 6:819c17738dc2 | 160 | } |
andrewboyson | 6:819c17738dc2 | 161 | // fill the forward and reverse substitution boxes |
andrewboyson | 6:819c17738dc2 | 162 | FSb[0x00] = 0x63; |
andrewboyson | 6:819c17738dc2 | 163 | #if AES_DECRYPTION // whether AES decryption is supported |
andrewboyson | 6:819c17738dc2 | 164 | RSb[0x63] = 0x00; |
andrewboyson | 6:819c17738dc2 | 165 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 166 | |
andrewboyson | 6:819c17738dc2 | 167 | for( i = 1; i < 256; i++ ) { |
andrewboyson | 6:819c17738dc2 | 168 | x = y = pow[255 - log[i]]; |
andrewboyson | 6:819c17738dc2 | 169 | MIX(x,y); |
andrewboyson | 6:819c17738dc2 | 170 | MIX(x,y); |
andrewboyson | 6:819c17738dc2 | 171 | MIX(x,y); |
andrewboyson | 6:819c17738dc2 | 172 | MIX(x,y); |
andrewboyson | 6:819c17738dc2 | 173 | FSb[i] = (uchar) ( x ^= 0x63 ); |
andrewboyson | 6:819c17738dc2 | 174 | #if AES_DECRYPTION // whether AES decryption is supported |
andrewboyson | 6:819c17738dc2 | 175 | RSb[x] = (uchar) i; |
andrewboyson | 6:819c17738dc2 | 176 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 177 | |
andrewboyson | 6:819c17738dc2 | 178 | } |
andrewboyson | 6:819c17738dc2 | 179 | // generate the forward and reverse key expansion tables |
andrewboyson | 6:819c17738dc2 | 180 | for( i = 0; i < 256; i++ ) { |
andrewboyson | 6:819c17738dc2 | 181 | x = FSb[i]; |
andrewboyson | 6:819c17738dc2 | 182 | y = XTIME( x ) & 0xFF; |
andrewboyson | 6:819c17738dc2 | 183 | z = ( y ^ x ) & 0xFF; |
andrewboyson | 6:819c17738dc2 | 184 | |
andrewboyson | 6:819c17738dc2 | 185 | FT0[i] = ( (uint32_t) y ) ^ ( (uint32_t) x << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 186 | ( (uint32_t) x << 16 ) ^ ( (uint32_t) z << 24 ); |
andrewboyson | 6:819c17738dc2 | 187 | |
andrewboyson | 6:819c17738dc2 | 188 | FT1[i] = ROTL8( FT0[i] ); |
andrewboyson | 6:819c17738dc2 | 189 | FT2[i] = ROTL8( FT1[i] ); |
andrewboyson | 6:819c17738dc2 | 190 | FT3[i] = ROTL8( FT2[i] ); |
andrewboyson | 6:819c17738dc2 | 191 | |
andrewboyson | 6:819c17738dc2 | 192 | #if AES_DECRYPTION // whether AES decryption is supported |
andrewboyson | 6:819c17738dc2 | 193 | x = RSb[i]; |
andrewboyson | 6:819c17738dc2 | 194 | |
andrewboyson | 6:819c17738dc2 | 195 | RT0[i] = ( (uint32_t) MUL( 0x0E, x ) ) ^ |
andrewboyson | 6:819c17738dc2 | 196 | ( (uint32_t) MUL( 0x09, x ) << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 197 | ( (uint32_t) MUL( 0x0D, x ) << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 198 | ( (uint32_t) MUL( 0x0B, x ) << 24 ); |
andrewboyson | 6:819c17738dc2 | 199 | |
andrewboyson | 6:819c17738dc2 | 200 | RT1[i] = ROTL8( RT0[i] ); |
andrewboyson | 6:819c17738dc2 | 201 | RT2[i] = ROTL8( RT1[i] ); |
andrewboyson | 6:819c17738dc2 | 202 | RT3[i] = ROTL8( RT2[i] ); |
andrewboyson | 6:819c17738dc2 | 203 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 204 | } |
andrewboyson | 6:819c17738dc2 | 205 | aes_tables_inited = 1; // flag that the tables have been generated |
andrewboyson | 6:819c17738dc2 | 206 | } // to permit subsequent use of the AES cipher |
andrewboyson | 6:819c17738dc2 | 207 | |
andrewboyson | 6:819c17738dc2 | 208 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 209 | * |
andrewboyson | 6:819c17738dc2 | 210 | * AES_SET_ENCRYPTION_KEY |
andrewboyson | 6:819c17738dc2 | 211 | * |
andrewboyson | 6:819c17738dc2 | 212 | * This is called by 'aes_setkey' when we're establishing a key for |
andrewboyson | 6:819c17738dc2 | 213 | * subsequent encryption. We give it a pointer to the encryption |
andrewboyson | 6:819c17738dc2 | 214 | * context, a pointer to the key, and the key's length in bytes. |
andrewboyson | 6:819c17738dc2 | 215 | * Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits). |
andrewboyson | 6:819c17738dc2 | 216 | * |
andrewboyson | 6:819c17738dc2 | 217 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 218 | int aes_set_encryption_key( aes_context *ctx, |
andrewboyson | 6:819c17738dc2 | 219 | const uchar *key, |
andrewboyson | 6:819c17738dc2 | 220 | uint keysize ) |
andrewboyson | 6:819c17738dc2 | 221 | { |
andrewboyson | 6:819c17738dc2 | 222 | uint i; // general purpose iteration local |
andrewboyson | 6:819c17738dc2 | 223 | uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer |
andrewboyson | 6:819c17738dc2 | 224 | |
andrewboyson | 6:819c17738dc2 | 225 | for( i = 0; i < (keysize >> 2); i++ ) { |
andrewboyson | 6:819c17738dc2 | 226 | GET_UINT32_LE( RK[i], key, i << 2 ); |
andrewboyson | 6:819c17738dc2 | 227 | } |
andrewboyson | 6:819c17738dc2 | 228 | |
andrewboyson | 6:819c17738dc2 | 229 | switch( ctx->rounds ) |
andrewboyson | 6:819c17738dc2 | 230 | { |
andrewboyson | 6:819c17738dc2 | 231 | case 10: |
andrewboyson | 6:819c17738dc2 | 232 | for( i = 0; i < 10; i++, RK += 4 ) { |
andrewboyson | 6:819c17738dc2 | 233 | RK[4] = RK[0] ^ RCON[i] ^ |
andrewboyson | 6:819c17738dc2 | 234 | ( (uint32_t) FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 235 | ( (uint32_t) FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 236 | ( (uint32_t) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 237 | ( (uint32_t) FSb[ ( RK[3] ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 238 | |
andrewboyson | 6:819c17738dc2 | 239 | RK[5] = RK[1] ^ RK[4]; |
andrewboyson | 6:819c17738dc2 | 240 | RK[6] = RK[2] ^ RK[5]; |
andrewboyson | 6:819c17738dc2 | 241 | RK[7] = RK[3] ^ RK[6]; |
andrewboyson | 6:819c17738dc2 | 242 | } |
andrewboyson | 6:819c17738dc2 | 243 | break; |
andrewboyson | 6:819c17738dc2 | 244 | |
andrewboyson | 6:819c17738dc2 | 245 | case 12: |
andrewboyson | 6:819c17738dc2 | 246 | for( i = 0; i < 8; i++, RK += 6 ) { |
andrewboyson | 6:819c17738dc2 | 247 | RK[6] = RK[0] ^ RCON[i] ^ |
andrewboyson | 6:819c17738dc2 | 248 | ( (uint32_t) FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 249 | ( (uint32_t) FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 250 | ( (uint32_t) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 251 | ( (uint32_t) FSb[ ( RK[5] ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 252 | |
andrewboyson | 6:819c17738dc2 | 253 | RK[7] = RK[1] ^ RK[6]; |
andrewboyson | 6:819c17738dc2 | 254 | RK[8] = RK[2] ^ RK[7]; |
andrewboyson | 6:819c17738dc2 | 255 | RK[9] = RK[3] ^ RK[8]; |
andrewboyson | 6:819c17738dc2 | 256 | RK[10] = RK[4] ^ RK[9]; |
andrewboyson | 6:819c17738dc2 | 257 | RK[11] = RK[5] ^ RK[10]; |
andrewboyson | 6:819c17738dc2 | 258 | } |
andrewboyson | 6:819c17738dc2 | 259 | break; |
andrewboyson | 6:819c17738dc2 | 260 | |
andrewboyson | 6:819c17738dc2 | 261 | case 14: |
andrewboyson | 6:819c17738dc2 | 262 | for( i = 0; i < 7; i++, RK += 8 ) { |
andrewboyson | 6:819c17738dc2 | 263 | RK[8] = RK[0] ^ RCON[i] ^ |
andrewboyson | 6:819c17738dc2 | 264 | ( (uint32_t) FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 265 | ( (uint32_t) FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 266 | ( (uint32_t) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 267 | ( (uint32_t) FSb[ ( RK[7] ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 268 | |
andrewboyson | 6:819c17738dc2 | 269 | RK[9] = RK[1] ^ RK[8]; |
andrewboyson | 6:819c17738dc2 | 270 | RK[10] = RK[2] ^ RK[9]; |
andrewboyson | 6:819c17738dc2 | 271 | RK[11] = RK[3] ^ RK[10]; |
andrewboyson | 6:819c17738dc2 | 272 | |
andrewboyson | 6:819c17738dc2 | 273 | RK[12] = RK[4] ^ |
andrewboyson | 6:819c17738dc2 | 274 | ( (uint32_t) FSb[ ( RK[11] ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 275 | ( (uint32_t) FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 276 | ( (uint32_t) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 277 | ( (uint32_t) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 278 | |
andrewboyson | 6:819c17738dc2 | 279 | RK[13] = RK[5] ^ RK[12]; |
andrewboyson | 6:819c17738dc2 | 280 | RK[14] = RK[6] ^ RK[13]; |
andrewboyson | 6:819c17738dc2 | 281 | RK[15] = RK[7] ^ RK[14]; |
andrewboyson | 6:819c17738dc2 | 282 | } |
andrewboyson | 6:819c17738dc2 | 283 | break; |
andrewboyson | 6:819c17738dc2 | 284 | } |
andrewboyson | 6:819c17738dc2 | 285 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 286 | } |
andrewboyson | 6:819c17738dc2 | 287 | |
andrewboyson | 6:819c17738dc2 | 288 | #if AES_DECRYPTION // whether AES decryption is supported |
andrewboyson | 6:819c17738dc2 | 289 | |
andrewboyson | 6:819c17738dc2 | 290 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 291 | * |
andrewboyson | 6:819c17738dc2 | 292 | * AES_SET_DECRYPTION_KEY |
andrewboyson | 6:819c17738dc2 | 293 | * |
andrewboyson | 6:819c17738dc2 | 294 | * This is called by 'aes_setkey' when we're establishing a |
andrewboyson | 6:819c17738dc2 | 295 | * key for subsequent decryption. We give it a pointer to |
andrewboyson | 6:819c17738dc2 | 296 | * the encryption context, a pointer to the key, and the key's |
andrewboyson | 6:819c17738dc2 | 297 | * length in bits. Valid lengths are: 128, 192, or 256 bits. |
andrewboyson | 6:819c17738dc2 | 298 | * |
andrewboyson | 6:819c17738dc2 | 299 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 300 | int aes_set_decryption_key( aes_context *ctx, |
andrewboyson | 6:819c17738dc2 | 301 | const uchar *key, |
andrewboyson | 6:819c17738dc2 | 302 | uint keysize ) |
andrewboyson | 6:819c17738dc2 | 303 | { |
andrewboyson | 6:819c17738dc2 | 304 | int i, j; |
andrewboyson | 6:819c17738dc2 | 305 | aes_context cty; // a calling aes context for set_encryption_key |
andrewboyson | 6:819c17738dc2 | 306 | uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer |
andrewboyson | 6:819c17738dc2 | 307 | uint32_t *SK; |
andrewboyson | 6:819c17738dc2 | 308 | int ret; |
andrewboyson | 6:819c17738dc2 | 309 | |
andrewboyson | 6:819c17738dc2 | 310 | cty.rounds = ctx->rounds; // initialize our local aes context |
andrewboyson | 6:819c17738dc2 | 311 | cty.rk = cty.buf; // round count and key buf pointer |
andrewboyson | 6:819c17738dc2 | 312 | |
andrewboyson | 6:819c17738dc2 | 313 | if (( ret = aes_set_encryption_key( &cty, key, keysize )) != 0 ) |
andrewboyson | 6:819c17738dc2 | 314 | return( ret ); |
andrewboyson | 6:819c17738dc2 | 315 | |
andrewboyson | 6:819c17738dc2 | 316 | SK = cty.rk + cty.rounds * 4; |
andrewboyson | 6:819c17738dc2 | 317 | |
andrewboyson | 6:819c17738dc2 | 318 | CPY128 // copy a 128-bit block from *SK to *RK |
andrewboyson | 6:819c17738dc2 | 319 | |
andrewboyson | 6:819c17738dc2 | 320 | for( i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8 ) { |
andrewboyson | 6:819c17738dc2 | 321 | for( j = 0; j < 4; j++, SK++ ) { |
andrewboyson | 6:819c17738dc2 | 322 | *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ |
andrewboyson | 6:819c17738dc2 | 323 | RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ |
andrewboyson | 6:819c17738dc2 | 324 | RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ |
andrewboyson | 6:819c17738dc2 | 325 | RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; |
andrewboyson | 6:819c17738dc2 | 326 | } |
andrewboyson | 6:819c17738dc2 | 327 | } |
andrewboyson | 6:819c17738dc2 | 328 | CPY128 // copy a 128-bit block from *SK to *RK |
andrewboyson | 6:819c17738dc2 | 329 | memset( &cty, 0, sizeof( aes_context ) ); // clear local aes context |
andrewboyson | 6:819c17738dc2 | 330 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 331 | } |
andrewboyson | 6:819c17738dc2 | 332 | |
andrewboyson | 6:819c17738dc2 | 333 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 334 | |
andrewboyson | 6:819c17738dc2 | 335 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 336 | * |
andrewboyson | 6:819c17738dc2 | 337 | * AES_SETKEY |
andrewboyson | 6:819c17738dc2 | 338 | * |
andrewboyson | 6:819c17738dc2 | 339 | * Invoked to establish the key schedule for subsequent encryption/decryption |
andrewboyson | 6:819c17738dc2 | 340 | * |
andrewboyson | 6:819c17738dc2 | 341 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 342 | int aes_setkey( aes_context *ctx, // AES context provided by our caller |
andrewboyson | 6:819c17738dc2 | 343 | int mode, // ENCRYPT or DECRYPT flag |
andrewboyson | 6:819c17738dc2 | 344 | const uchar *key, // pointer to the key |
andrewboyson | 6:819c17738dc2 | 345 | uint keysize ) // key length in bytes |
andrewboyson | 6:819c17738dc2 | 346 | { |
andrewboyson | 6:819c17738dc2 | 347 | // since table initialization is not thread safe, we could either add |
andrewboyson | 6:819c17738dc2 | 348 | // system-specific mutexes and init the AES key generation tables on |
andrewboyson | 6:819c17738dc2 | 349 | // demand, or ask the developer to simply call "gcm_initialize" once during |
andrewboyson | 6:819c17738dc2 | 350 | // application startup before threading begins. That's what we choose. |
andrewboyson | 6:819c17738dc2 | 351 | if( !aes_tables_inited ) return ( 0 ); // fail the call when not inited. |
andrewboyson | 6:819c17738dc2 | 352 | |
andrewboyson | 6:819c17738dc2 | 353 | ctx->mode = mode; // capture the key type we're creating |
andrewboyson | 6:819c17738dc2 | 354 | ctx->rk = ctx->buf; // initialize our round key pointer |
andrewboyson | 6:819c17738dc2 | 355 | |
andrewboyson | 6:819c17738dc2 | 356 | switch( keysize ) // set the rounds count based upon the keysize |
andrewboyson | 6:819c17738dc2 | 357 | { |
andrewboyson | 6:819c17738dc2 | 358 | case 16: ctx->rounds = 10; break; // 16-byte, 128-bit key |
andrewboyson | 6:819c17738dc2 | 359 | case 24: ctx->rounds = 12; break; // 24-byte, 192-bit key |
andrewboyson | 6:819c17738dc2 | 360 | case 32: ctx->rounds = 14; break; // 32-byte, 256-bit key |
andrewboyson | 6:819c17738dc2 | 361 | } |
andrewboyson | 6:819c17738dc2 | 362 | |
andrewboyson | 6:819c17738dc2 | 363 | #if AES_DECRYPTION |
andrewboyson | 6:819c17738dc2 | 364 | if( mode == DECRYPT ) // expand our key for encryption or decryption |
andrewboyson | 6:819c17738dc2 | 365 | return( aes_set_decryption_key( ctx, key, keysize ) ); |
andrewboyson | 6:819c17738dc2 | 366 | else /* ENCRYPT */ |
andrewboyson | 6:819c17738dc2 | 367 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 368 | return( aes_set_encryption_key( ctx, key, keysize ) ); |
andrewboyson | 6:819c17738dc2 | 369 | } |
andrewboyson | 6:819c17738dc2 | 370 | |
andrewboyson | 6:819c17738dc2 | 371 | /****************************************************************************** |
andrewboyson | 6:819c17738dc2 | 372 | * |
andrewboyson | 6:819c17738dc2 | 373 | * AES_CIPHER |
andrewboyson | 6:819c17738dc2 | 374 | * |
andrewboyson | 6:819c17738dc2 | 375 | * Perform AES encryption and decryption. |
andrewboyson | 6:819c17738dc2 | 376 | * The AES context will have been setup with the encryption mode |
andrewboyson | 6:819c17738dc2 | 377 | * and all keying information appropriate for the task. |
andrewboyson | 6:819c17738dc2 | 378 | * |
andrewboyson | 6:819c17738dc2 | 379 | ******************************************************************************/ |
andrewboyson | 6:819c17738dc2 | 380 | int aes_cipher( aes_context *ctx, |
andrewboyson | 6:819c17738dc2 | 381 | const uchar input[16], |
andrewboyson | 6:819c17738dc2 | 382 | uchar output[16] ) |
andrewboyson | 6:819c17738dc2 | 383 | { |
andrewboyson | 6:819c17738dc2 | 384 | int i; |
andrewboyson | 6:819c17738dc2 | 385 | uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals |
andrewboyson | 6:819c17738dc2 | 386 | |
andrewboyson | 6:819c17738dc2 | 387 | RK = ctx->rk; |
andrewboyson | 6:819c17738dc2 | 388 | |
andrewboyson | 6:819c17738dc2 | 389 | GET_UINT32_LE( X0, input, 0 ); X0 ^= *RK++; // load our 128-bit |
andrewboyson | 6:819c17738dc2 | 390 | GET_UINT32_LE( X1, input, 4 ); X1 ^= *RK++; // input buffer in a storage |
andrewboyson | 6:819c17738dc2 | 391 | GET_UINT32_LE( X2, input, 8 ); X2 ^= *RK++; // memory endian-neutral way |
andrewboyson | 6:819c17738dc2 | 392 | GET_UINT32_LE( X3, input, 12 ); X3 ^= *RK++; |
andrewboyson | 6:819c17738dc2 | 393 | |
andrewboyson | 6:819c17738dc2 | 394 | #if AES_DECRYPTION // whether AES decryption is supported |
andrewboyson | 6:819c17738dc2 | 395 | |
andrewboyson | 6:819c17738dc2 | 396 | if( ctx->mode == DECRYPT ) |
andrewboyson | 6:819c17738dc2 | 397 | { |
andrewboyson | 6:819c17738dc2 | 398 | for( i = (ctx->rounds >> 1) - 1; i > 0; i-- ) |
andrewboyson | 6:819c17738dc2 | 399 | { |
andrewboyson | 6:819c17738dc2 | 400 | AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); |
andrewboyson | 6:819c17738dc2 | 401 | AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); |
andrewboyson | 6:819c17738dc2 | 402 | } |
andrewboyson | 6:819c17738dc2 | 403 | |
andrewboyson | 6:819c17738dc2 | 404 | AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); |
andrewboyson | 6:819c17738dc2 | 405 | |
andrewboyson | 6:819c17738dc2 | 406 | X0 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 407 | ( (uint32_t) RSb[ ( Y0 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 408 | ( (uint32_t) RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 409 | ( (uint32_t) RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 410 | ( (uint32_t) RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 411 | |
andrewboyson | 6:819c17738dc2 | 412 | X1 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 413 | ( (uint32_t) RSb[ ( Y1 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 414 | ( (uint32_t) RSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 415 | ( (uint32_t) RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 416 | ( (uint32_t) RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 417 | |
andrewboyson | 6:819c17738dc2 | 418 | X2 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 419 | ( (uint32_t) RSb[ ( Y2 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 420 | ( (uint32_t) RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 421 | ( (uint32_t) RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 422 | ( (uint32_t) RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 423 | |
andrewboyson | 6:819c17738dc2 | 424 | X3 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 425 | ( (uint32_t) RSb[ ( Y3 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 426 | ( (uint32_t) RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 427 | ( (uint32_t) RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 428 | ( (uint32_t) RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 429 | } |
andrewboyson | 6:819c17738dc2 | 430 | else /* ENCRYPT */ |
andrewboyson | 6:819c17738dc2 | 431 | { |
andrewboyson | 6:819c17738dc2 | 432 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 433 | |
andrewboyson | 6:819c17738dc2 | 434 | for( i = (ctx->rounds >> 1) - 1; i > 0; i-- ) |
andrewboyson | 6:819c17738dc2 | 435 | { |
andrewboyson | 6:819c17738dc2 | 436 | AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); |
andrewboyson | 6:819c17738dc2 | 437 | AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); |
andrewboyson | 6:819c17738dc2 | 438 | } |
andrewboyson | 6:819c17738dc2 | 439 | |
andrewboyson | 6:819c17738dc2 | 440 | AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); |
andrewboyson | 6:819c17738dc2 | 441 | |
andrewboyson | 6:819c17738dc2 | 442 | X0 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 443 | ( (uint32_t) FSb[ ( Y0 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 444 | ( (uint32_t) FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 445 | ( (uint32_t) FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 446 | ( (uint32_t) FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 447 | |
andrewboyson | 6:819c17738dc2 | 448 | X1 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 449 | ( (uint32_t) FSb[ ( Y1 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 450 | ( (uint32_t) FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 451 | ( (uint32_t) FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 452 | ( (uint32_t) FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 453 | |
andrewboyson | 6:819c17738dc2 | 454 | X2 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 455 | ( (uint32_t) FSb[ ( Y2 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 456 | ( (uint32_t) FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 457 | ( (uint32_t) FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 458 | ( (uint32_t) FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 459 | |
andrewboyson | 6:819c17738dc2 | 460 | X3 = *RK++ ^ \ |
andrewboyson | 6:819c17738dc2 | 461 | ( (uint32_t) FSb[ ( Y3 ) & 0xFF ] ) ^ |
andrewboyson | 6:819c17738dc2 | 462 | ( (uint32_t) FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ |
andrewboyson | 6:819c17738dc2 | 463 | ( (uint32_t) FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ |
andrewboyson | 6:819c17738dc2 | 464 | ( (uint32_t) FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); |
andrewboyson | 6:819c17738dc2 | 465 | |
andrewboyson | 6:819c17738dc2 | 466 | #if AES_DECRYPTION // whether AES decryption is supported |
andrewboyson | 6:819c17738dc2 | 467 | } |
andrewboyson | 6:819c17738dc2 | 468 | #endif /* AES_DECRYPTION */ |
andrewboyson | 6:819c17738dc2 | 469 | |
andrewboyson | 6:819c17738dc2 | 470 | PUT_UINT32_LE( X0, output, 0 ); |
andrewboyson | 6:819c17738dc2 | 471 | PUT_UINT32_LE( X1, output, 4 ); |
andrewboyson | 6:819c17738dc2 | 472 | PUT_UINT32_LE( X2, output, 8 ); |
andrewboyson | 6:819c17738dc2 | 473 | PUT_UINT32_LE( X3, output, 12 ); |
andrewboyson | 6:819c17738dc2 | 474 | |
andrewboyson | 6:819c17738dc2 | 475 | return( 0 ); |
andrewboyson | 6:819c17738dc2 | 476 | } |
andrewboyson | 6:819c17738dc2 | 477 | /* end of aes.c */ |