/* * Copyright (c) 2012-2020 MIRACL UK Ltd. * * This file is part of MIRACL Core * (see https://github.com/miracl/core). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Implementation of the AES-GCM Encryption/Authentication * * Some restrictions.. * 1. Only for use with AES * 2. Returned tag is always 128-bits. Truncate at your own risk. * 3. The order of function calls must follow some rules * * Typical sequence of calls.. * 1. call GCM_init * 2. call GCM_add_header any number of times, as long as length of header is multiple of 16 bytes (block size) * 3. call GCM_add_header one last time with any length of header * 4. call GCM_add_cipher any number of times, as long as length of cipher/plaintext is multiple of 16 bytes * 5. call GCM_add_cipher one last time with any length of cipher/plaintext * 6. call GCM_finish to extract the tag. * * See http://www.mindspring.com/~dmcgrew/gcm-nist-6.pdf */ /* SU=m, m is Stack Usage */ #include #include #include "arch.h" #include "core.h" using namespace core; #define NB 4 #define MR_TOBYTE(x) ((uchar)((x))) static unsign32 pack(const uchar *b) { /* pack bytes into a 32-bit Word */ return ((unsign32)b[0] << 24) | ((unsign32)b[1] << 16) | ((unsign32)b[2] << 8) | (unsign32)b[3]; } static void unpack(unsign32 a, uchar *b) { /* unpack bytes from a word */ b[3] = MR_TOBYTE(a); b[2] = MR_TOBYTE(a >> 8); b[1] = MR_TOBYTE(a >> 16); b[0] = MR_TOBYTE(a >> 24); } static void precompute(gcm *g, uchar *H) { /* precompute small 2k bytes gf2m table of x^n.H */ int i, j; unsign32 *last, *next, b; for (i = j = 0; i < NB; i++, j += 4) g->table[0][i] = pack((uchar *)&H[j]); for (i = 1; i < 128; i++) { next = g->table[i]; last = g->table[i - 1]; b = 0; for (j = 0; j < NB; j++) { next[j] = b | (last[j]) >> 1; b = last[j] << 31; } if (b) next[0] ^= 0xE1000000; /* irreducible polynomial */ } } /* SU= 32 */ static void gf2mul(gcm *g) { /* gf2m mul - Z=H*X mod 2^128 */ int i, j, m, k; unsign32 P[4]; unsign32 b; P[0] = P[1] = P[2] = P[3] = 0; j = 8; m = 0; for (i = 0; i < 128; i++) { b = (unsign32)(g->stateX[m] >> (--j)) & 1; b = ~b + 1; for (k = 0; k < NB; k++) P[k] ^= (g->table[i][k] & b); if (j == 0) { j = 8; m++; if (m == 16) break; } } for (i = j = 0; i < NB; i++, j += 4) unpack(P[i], (uchar *)&g->stateX[j]); } /* SU= 32 */ static void GCM_wrap(gcm *g) { /* Finish off GHASH */ int i, j; unsign32 F[4]; uchar L[16]; /* convert lengths from bytes to bits */ F[0] = (g->lenA[0] << 3) | (g->lenA[1] & 0xE0000000) >> 29; F[1] = g->lenA[1] << 3; F[2] = (g->lenC[0] << 3) | (g->lenC[1] & 0xE0000000) >> 29; F[3] = g->lenC[1] << 3; for (i = j = 0; i < NB; i++, j += 4) unpack(F[i], (uchar *)&L[j]); for (i = 0; i < 16; i++) g->stateX[i] ^= L[i]; gf2mul(g); } static int GCM_ghash(gcm *g, char *plain, int len) { int i, j = 0; if (g->status == GCM_ACCEPTING_HEADER) g->status = GCM_ACCEPTING_CIPHER; if (g->status != GCM_ACCEPTING_CIPHER) return 0; while (j < len) { for (i = 0; i < 16 && j < len; i++) { g->stateX[i] ^= plain[j++]; g->lenC[1]++; if (g->lenC[1] == 0) g->lenC[0]++; } gf2mul(g); } if (len % 16 != 0) g->status = GCM_NOT_ACCEPTING_MORE; return 1; } /* SU= 48 */ /* Initialize GCM mode */ void core::GCM_init(gcm* g, int nk, char *key, int niv, char *iv) { /* iv size niv is usually 12 bytes (96 bits). AES key size nk can be 16,24 or 32 bytes */ int i; uchar H[16]; for (i = 0; i < 16; i++) { H[i] = 0; g->stateX[i] = 0; } AES_init(&(g->a), ECB, nk, key, iv); AES_ecb_encrypt(&(g->a), H); /* E(K,0) */ precompute(g, H); g->lenA[0] = g->lenC[0] = g->lenA[1] = g->lenC[1] = 0; if (niv == 12) { for (i = 0; i < 12; i++) g->a.f[i] = iv[i]; unpack((unsign32)1, (uchar *) & (g->a.f[12])); /* initialise IV */ for (i = 0; i < 16; i++) g->Y_0[i] = g->a.f[i]; } else { g->status = GCM_ACCEPTING_CIPHER; GCM_ghash(g, iv, niv); /* GHASH(H,0,IV) */ GCM_wrap(g); for (i = 0; i < 16; i++) { g->a.f[i] = g->stateX[i]; g->Y_0[i] = g->a.f[i]; g->stateX[i] = 0; } g->lenA[0] = g->lenC[0] = g->lenA[1] = g->lenC[1] = 0; } g->status = GCM_ACCEPTING_HEADER; } /* SU= 24 */ /* Add Header data - included but not encrypted */ int core::GCM_add_header(gcm* g, char *header, int len) { /* Add some header. Won't be encrypted, but will be authenticated. len is length of header */ int i, j = 0; if (g->status != GCM_ACCEPTING_HEADER) return 0; while (j < len) { for (i = 0; i < 16 && j < len; i++) { g->stateX[i] ^= header[j++]; g->lenA[1]++; if (g->lenA[1] == 0) g->lenA[0]++; } gf2mul(g); } if (len % 16 != 0) g->status = GCM_ACCEPTING_CIPHER; return 1; } /* SU= 48 */ /* Add Plaintext - included and encrypted */ int core::GCM_add_plain(gcm *g, char *cipher, char *plain, int len) { /* Add plaintext to extract ciphertext, len is length of plaintext. */ int i, j = 0; unsign32 counter; uchar B[16]; if (g->status == GCM_ACCEPTING_HEADER) g->status = GCM_ACCEPTING_CIPHER; if (g->status != GCM_ACCEPTING_CIPHER) return 0; while (j < len) { counter = pack((uchar *) & (g->a.f[12])); counter++; unpack(counter, (uchar *) & (g->a.f[12])); /* increment counter */ for (i = 0; i < 16; i++) B[i] = g->a.f[i]; AES_ecb_encrypt(&(g->a), B); /* encrypt it */ for (i = 0; i < 16 && j < len; i++) { cipher[j] = plain[j] ^ B[i]; g->stateX[i] ^= cipher[j++]; g->lenC[1]++; if (g->lenC[1] == 0) g->lenC[0]++; } gf2mul(g); } if (len % 16 != 0) g->status = GCM_NOT_ACCEPTING_MORE; return 1; } /* SU= 48 */ /* Add Ciphertext - decrypts to plaintext */ int core::GCM_add_cipher(gcm *g, char *plain, char *cipher, int len) { /* Add ciphertext to extract plaintext, len is length of ciphertext. */ int i, j = 0; unsign32 counter; char oc; uchar B[16]; if (g->status == GCM_ACCEPTING_HEADER) g->status = GCM_ACCEPTING_CIPHER; if (g->status != GCM_ACCEPTING_CIPHER) return 0; while (j < len) { counter = pack((uchar *) & (g->a.f[12])); counter++; unpack(counter, (uchar *) & (g->a.f[12])); /* increment counter */ for (i = 0; i < 16; i++) B[i] = g->a.f[i]; AES_ecb_encrypt(&(g->a), B); /* encrypt it */ for (i = 0; i < 16 && j < len; i++) { oc = cipher[j]; plain[j] = cipher[j] ^ B[i]; g->stateX[i] ^= oc; j++; g->lenC[1]++; if (g->lenC[1] == 0) g->lenC[0]++; } gf2mul(g); } if (len % 16 != 0) g->status = GCM_NOT_ACCEPTING_MORE; return 1; } /* SU= 16 */ /* Finish and extract Tag */ void core::GCM_finish(gcm *g, char *tag) { /* Finish off GHASH and extract tag (MAC) */ int i; GCM_wrap(g); /* extract tag */ if (tag != NULL) { AES_ecb_encrypt(&(g->a), g->Y_0); /* E(K,Y0) */ for (i = 0; i < 16; i++) g->Y_0[i] ^= g->stateX[i]; for (i = 0; i < 16; i++) { tag[i] = g->Y_0[i]; g->Y_0[i] = g->stateX[i] = 0; } } g->status = GCM_FINISHED; AES_end(&(g->a)); } /* AES-GCM Encryption of octets, K is key, H is header, P is plaintext, C is ciphertext, T is authentication tag */ void core::AES_GCM_ENCRYPT(octet *K, octet *IV, octet *H, octet *P, octet *C, octet *T) { gcm g; GCM_init(&g, K->len, K->val, IV->len, IV->val); GCM_add_header(&g, H->val, H->len); GCM_add_plain(&g, C->val, P->val, P->len); C->len = P->len; GCM_finish(&g, T->val); T->len = 16; } /* AES-GCM Decryption of octets, K is key, H is header, P is plaintext, C is ciphertext, T is authentication tag */ void core::AES_GCM_DECRYPT(octet *K, octet *IV, octet *H, octet *C, octet *P, octet *T) { gcm g; GCM_init(&g, K->len, K->val, IV->len, IV->val); GCM_add_header(&g, H->val, H->len); GCM_add_cipher(&g, P->val, C->val, C->len); P->len = C->len; GCM_finish(&g, T->val); T->len = 16; } // Compile with // gcc -O2 gcm.c aes.c -o gcm.exe /* SU= 16 */ /* static void hex2bytes(char *hex,char *bin) */ /* { */ /* int i; */ /* char v; */ /* int len=strlen(hex); */ /* for (i = 0; i < len/2; i++) { */ /* char c = hex[2*i]; */ /* if (c >= '0' && c <= '9') { */ /* v = c - '0'; */ /* } else if (c >= 'A' && c <= 'F') { */ /* v = c - 'A' + 10; */ /* } else if (c >= 'a' && c <= 'f') { */ /* v = c - 'a' + 10; */ /* } else { */ /* v = 0; */ /* } */ /* v <<= 4; */ /* c = hex[2*i + 1]; */ /* if (c >= '0' && c <= '9') { */ /* v += c - '0'; */ /* } else if (c >= 'A' && c <= 'F') { */ /* v += c - 'A' + 10; */ /* } else if (c >= 'a' && c <= 'f') { */ /* v += c - 'a' + 10; */ /* } else { */ /* v = 0; */ /* } */ /* bin[i] = v; */ /* } */ /* } */ /* int main() { int i; // char* KT="feffe9928665731c6d6a8f9467308308"; // char* MT="d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"; // char* HT="feedfacedeadbeeffeedfacedeadbeefabaddad2"; // char* NT="cafebabefacedbaddecaf888"; // Tag should be 5bc94fbc3221a5db94fae95ae7121a47 // char* NT="9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b"; // Tag should be 619cc5aefffe0bfa462af43c1699d050 char* KT="6dfb5dc68af6ae2f3242e9184f100918"; char* MT="47809d16c2c6ec685962c90e53fe1bba"; char* HT="dd0fa6e494031139d71ee45f00d56fa4"; char* NT="37d36f5c54d53479d4745dd1"; int len=strlen(MT)/2; int lenH=strlen(HT)/2; int lenK=strlen(KT)/2; int lenIV=strlen(NT)/2; char T[16]; // Tag char K[16]; // AES Key char H[64]; // Header - to be included in Authentication, but not encrypted char N[100]; // IV - Initialisation vector char M[100]; // Plaintext to be encrypted/authenticated char C[100]; // Ciphertext char P[100]; // Recovered Plaintext gcm g; hex2bytes(MT, M); hex2bytes(HT, H); hex2bytes(NT, N); hex2bytes(KT, K); printf("lenK= %d\n",lenK); printf("Plaintext=\n"); for (i=0;i