441 lines
12 KiB
C++
441 lines
12 KiB
C++
/*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#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<len;i++) printf("%02x",(unsigned char)M[i]);
|
|
printf("\n");
|
|
|
|
GCM_init(&g,16,K,lenIV,N);
|
|
GCM_add_header(&g,H,lenH);
|
|
GCM_add_plain(&g,C,M,len);
|
|
GCM_finish(&g,T);
|
|
|
|
printf("Ciphertext=\n");
|
|
for (i=0;i<len;i++) printf("%02x",(unsigned char)C[i]);
|
|
printf("\n");
|
|
|
|
printf("Tag=\n");
|
|
for (i=0;i<16;i++) printf("%02x",(unsigned char)T[i]);
|
|
printf("\n");
|
|
|
|
GCM_init(&g,16,K,lenIV,N);
|
|
GCM_add_header(&g,H,lenH);
|
|
GCM_add_cipher(&g,P,C,len);
|
|
GCM_finish(&g,T);
|
|
|
|
printf("Plaintext=\n");
|
|
for (i=0;i<len;i++) printf("%02x",(unsigned char)P[i]);
|
|
printf("\n");
|
|
|
|
printf("Tag=\n");
|
|
for (i=0;i<16;i++) printf("%02x",(unsigned char)T[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
*/
|