MastersThesis/PQ_TIIGER_TLS/sal/miracl-ubuntu22-11-04-24/includes/gcm.cpp
2024-04-19 14:16:07 +02:00

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");
}
*/