Commit d331b3ac authored by davxy's avatar davxy

GCM block cipher mode of operation

parent 1bc69443
......@@ -27,8 +27,9 @@ Unreleased
### Added
- CBC block cipher mode of operation
- AES block cipher
- DES block cipher
- CBC block cipher mode of operation
- GCM block cipher mode of operation
- Base64 encoder/decoder
......@@ -11,7 +11,8 @@ OBJS := src/cry_version.o \
src/cry_base64.o \
src/cry_des.o \
src/cry_aes.o \
src/cry_cbc.o
src/cry_cbc.o \
src/cry_gcm.o
.PHONY: all clean
......
......@@ -13,6 +13,7 @@ Block ciphers
- ECB
- CBC
- GCM
Utilities
---------
......
/*
* Copyright (c) 2013, Davide Galassi. All rights reserved.
* Copyright (c) 2013-2014, Davide Galassi. All rights reserved.
*
* This file is part of CRY software.
*
......@@ -30,8 +30,9 @@
#include "cry_base64.h"
#include "cry_des.h"
#include "cry_aes.h"
#include "cry_cbc.h"
#include "cry_ciph.h"
#include "cry_cbc.h"
#include "cry_gcm.h"
#endif /* _CRY_H_ */
......@@ -30,7 +30,7 @@
/** CTR block size. */
#define CRY_CBC_BLOCK_SIZE 16
/** Convenience macro to initialize a CBC context. */
/** Initialization helper macro */
#define CRY_CBC_INIT(ctx, _crypto_ctx, _crypto_itf) do { \
memset((ctx), 0U, sizeof(struct cry_cbc_ctx)); \
(ctx)->ciph_ctx = (_crypto_ctx); \
......@@ -39,9 +39,12 @@
/** CBC context structure. */
struct cry_cbc_ctx {
const struct cry_ciph_itf *ciph_itf; /** Block cipher interface. */
void *ciph_ctx; /** Block cipher context. */
unsigned char v[CRY_CBC_BLOCK_SIZE]; /* Counter. */
/** Block cipher interface. */
const struct cry_ciph_itf *ciph_itf;
/** Block cipher context. */
void *ciph_ctx;
/** Counter */
unsigned char v[CRY_CBC_BLOCK_SIZE];
};
typedef struct cry_cbc_ctx cry_cbc_ctx;
......
/*
* Copyright (c) 2013-2014, Davide Galassi. All rights reserved.
*
* This file is part of CRY software.
*
* CRY is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with CRY; if not, see <http://www.gnu/licenses/>.
*/
/**
* @file cry_gcm.h
* @brief GCM block cipher mode of operation.
*/
#ifndef _CRY_GCM_H_
#define _CRY_GCM_H_
#include "cry_ciph.h"
/** Size of a GCM block. */
#define CRY_GCM_BLOCK_SIZE 16
/* Initialization helper macro */
#define CRY_GCM_INIT(ctx, _ciph_ctx, _ciph_itf) do { \
memset((ctx), 0U, sizeof(struct cry_gcm_ctx)); \
(ctx)->ciph_ctx = (_ciph_ctx); \
(ctx)->ciph_itf = (_ciph_itf); \
} while(0)
/** GCM context. */
struct cry_gcm_ctx {
/** Block cipher. */
void *ciph_ctx;
/** Crypto context interface. */
const struct cry_ciph_itf *ciph_itf;
/** Original counter block */
unsigned char iv[CRY_GCM_BLOCK_SIZE];
/** Updated for each block. */
unsigned char ctr[CRY_GCM_BLOCK_SIZE];
/** Hashing state */
unsigned char x[CRY_GCM_BLOCK_SIZE];
/** Hashing subkey. */
unsigned char key[CRY_GCM_BLOCK_SIZE];
/** Auth data size. */
unsigned int auth_size;
/** Cipher data size. */
unsigned int ciph_size;
};
#ifdef __cplusplus
extern "C"{
#endif
/**
* Set the key in the gcm and in the cipher context.
*
* @param ctx GCM context.
* @param key Cipher key.
* @param size Size of cipher key.
*/
void cry_gcm_key_set(struct cry_gcm_ctx *ctx, const unsigned char *key,
unsigned int size);
/**
* Set of the initialization vector.
*
* @param ctx GCM context.
* @param iv Initialization vector buffer.
* @param size Size of the initialization vector.
*/
void cry_gcm_iv_set(struct cry_gcm_ctx *ctx, const unsigned char *iv,
unsigned int size);
/**
* Encryption function.
*
* @param ctx GCM context.
* @param dst Destination buffer.
* @param src Source buffer.
* @param size Size of source/destination buffers.
*/
void cry_gcm_encrypt(struct cry_gcm_ctx *ctx, unsigned char *dst,
const unsigned char *src, unsigned int size);
/**
* Decryption function.
*
* @param ctx GCM context.
* @param dst Destination buffer.
* @param src Source buffer.
* @param size Size of source/destination buffers.
*/
void cry_gcm_decrypt(struct cry_gcm_ctx *ctx, unsigned char *dst,
const unsigned char *src, unsigned int size);
/**
* Set the additional authentication data.
*
* This must me called before the encrypt (decrypt) function (data_size == 0).
*
* @param ctx GCM context.
* @param aad Additional authentication data buffer.
* @param size Size of the additional auth data.
* Constraint: auth_size % GCM_BLOCK_SIZE == 0.
*/
void cry_gcm_update(struct cry_gcm_ctx *ctx, const unsigned char *aad,
unsigned int size);
/**
* Generate the GCM mac (GMAC).
*
* @param ctx GCM context.
* @param mac Message authentication code output buffer.
* @param size Should be <= GCM_BLOCK_SIZE (16).
*/
void cry_gcm_digest(struct cry_gcm_ctx *ctx, unsigned char *mac,
unsigned int size);
#ifdef __cplusplus
}
#endif
#endif /* _CRY_GCM_H_ */
/*
* Copyright (c) 2013-2014, Davide Galassi. All rights reserved.
*
* This file is part of CRY software.
*
* CRY is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with CRY; if not, see <http://www.gnu/licenses/>.
*/
#include "cry_gcm.h"
#include "cry_memxor.h"
#include <string.h>
#include <stdint.h>
/**
* Increments a big endian value of a give size.
* Used to directly increment a value within a buffer.
*/
#define INCREMENT_BE(val_ptr, val_size) do { \
int i = (val_size) - 1; \
if (++(val_ptr)[i] == 0) \
while (++(val_ptr)[--i] == 0 && i > 0); \
} while (0)
static void gcm_gf_add(unsigned char *r, const unsigned char *x,
const unsigned char *y)
{
uint32_t *rw = (uint32_t *) r;
const uint32_t *xw = (const uint32_t *) x;
const uint32_t *yw = (const uint32_t *) y;
rw[0] = xw[0] ^ yw[0];
rw[1] = xw[1] ^ yw[1];
rw[2] = xw[2] ^ yw[2];
rw[3] = xw[3] ^ yw[3];
}
#define RSHIFT_WORD(x) \
((((x) & 0xfefefefeUL) >> 1U) | (((x) & 0x00010101UL) << 15U))
#define GHASH_POLYNOMIAL 0xE1UL
/*
* Multiplication by 010...0; a big-endian shift right. If the bit
* shifted out is one, the defining polynomial is added to cancel it
* out. r == x is allowed.
*/
static void gcm_gf_shift(unsigned char *r, const unsigned char *x)
{
uint32_t mask;
uint32_t *rw = (uint32_t *) r;
const uint32_t *xw = (const uint32_t *) x;
/* Shift uses big-endian representation. */
#ifndef CRY_BIG_ENDIAN
mask = (~((xw[3] >> 24U) & 1U)) + 1;
rw[3] = RSHIFT_WORD(xw[3]) | ((xw[2] >> 17) & 0x80U);
rw[2] = RSHIFT_WORD(xw[2]) | ((xw[1] >> 17) & 0x80U);
rw[1] = RSHIFT_WORD(xw[1]) | ((xw[0] >> 17) & 0x80U);
rw[0] = RSHIFT_WORD(xw[0]) ^ (mask & GHASH_POLYNOMIAL);
#else
mask = (~(xw[3] & 1U)) + 1;
rw[3] = (xw[3] >> 1U) | ((xw[2] & 1) << 31U);
rw[2] = (xw[2] >> 1U) | ((xw[1] & 1) << 31U);
rw[1] = (xw[1] >> 1U) | ((xw[0] & 1) << 31U);
rw[0] = (xw[0] >> 1U) ^ (mask & (GHASH_POLYNOMIAL << 24U));
#endif
}
/*
* Sets x <- x * y mod r, using the plain bitwise algorithm from the
* specification. y may be shorter than a full block, missing bytes
* are assumed zero.
*/
static void gcm_gf_mul(unsigned char *x, const unsigned char *y)
{
unsigned char V[CRY_GCM_BLOCK_SIZE];
unsigned char Z[CRY_GCM_BLOCK_SIZE];
unsigned char b;
unsigned int i, j;
memcpy(V, x, CRY_GCM_BLOCK_SIZE);
memset(Z, 0, CRY_GCM_BLOCK_SIZE);
for (i = 0; i < CRY_GCM_BLOCK_SIZE; i++) {
b = y[i];
for (j = 0; j < 8; j++, b <<= 1) {
if (b & 0x80)
gcm_gf_add(Z, Z, V);
gcm_gf_shift(V, V);
}
}
memcpy(x, Z, CRY_GCM_BLOCK_SIZE);
}
static void cry_gcm_hash(const unsigned char *key, unsigned char *x,
unsigned int length, const unsigned char *data)
{
for (; length >= CRY_GCM_BLOCK_SIZE;
length -= CRY_GCM_BLOCK_SIZE, data += CRY_GCM_BLOCK_SIZE) {
cry_memxor(x, data, CRY_GCM_BLOCK_SIZE);
gcm_gf_mul(x, key);
}
if (length > 0) {
cry_memxor(x, data, length);
gcm_gf_mul(x, key);
}
}
#define WRITE32_BE(val, dst) do { \
((unsigned char *)(dst))[0] = (unsigned char)(((val) >> 24U) & 0xffU); \
((unsigned char *)(dst))[1] = (unsigned char)(((val) >> 16U) & 0xffU); \
((unsigned char *)(dst))[2] = (unsigned char)(((val) >> 8U) & 0xffU); \
((unsigned char *)(dst))[3] = (unsigned char) ((val) & 0xff); \
} while(0)
static void cry_gcm_hash_sizes(const unsigned char *key,
unsigned char *x,
unsigned long auth_size,
unsigned long ciph_size)
{
unsigned char buffer[CRY_GCM_BLOCK_SIZE] = {0};
/* Get the sizes in bits */
ciph_size <<= 3;
auth_size <<= 3;
WRITE32_BE(auth_size, buffer + 4);
WRITE32_BE(ciph_size, buffer + 12);
cry_gcm_hash(key, x, CRY_GCM_BLOCK_SIZE, buffer);
}
void cry_gcm_key_set(struct cry_gcm_ctx *gcm, const unsigned char *key,
unsigned int size)
{
void *ciph = gcm->ciph_ctx;
cry_ciph_encrypt_f encrypt = gcm->ciph_itf->encrypt;
cry_ciph_key_set_f key_set = gcm->ciph_itf->key_set;
memset(gcm->x, 0, CRY_GCM_BLOCK_SIZE);
memset(gcm->ctr, 0, CRY_GCM_BLOCK_SIZE);
memset(gcm->iv, 0, CRY_GCM_BLOCK_SIZE);
memset(gcm->key, 0, CRY_GCM_BLOCK_SIZE);
gcm->auth_size = 0;
gcm->ciph_size = 0;
key_set(ciph, key, size);
memset(gcm->key, 0, CRY_GCM_BLOCK_SIZE);
encrypt(ciph, gcm->key, gcm->key, CRY_GCM_BLOCK_SIZE);
}
void cry_gcm_iv_set(struct cry_gcm_ctx *ctx, const unsigned char *iv,
unsigned int size)
{
if (size == (CRY_GCM_BLOCK_SIZE-4)) {
memcpy(ctx->iv, iv, CRY_GCM_BLOCK_SIZE-4);
ctx->iv[CRY_GCM_BLOCK_SIZE-4] = 0;
ctx->iv[CRY_GCM_BLOCK_SIZE-3] = 0;
ctx->iv[CRY_GCM_BLOCK_SIZE-2] = 0;
ctx->iv[CRY_GCM_BLOCK_SIZE-1] = 1;
} else {
memset(ctx->iv, 0, CRY_GCM_BLOCK_SIZE);
cry_gcm_hash(ctx->key, ctx->iv, size, iv);
cry_gcm_hash_sizes(ctx->key, ctx->iv, 0, size);
}
memcpy(ctx->ctr, ctx->iv, CRY_GCM_BLOCK_SIZE);
INCREMENT_BE(ctx->ctr + CRY_GCM_BLOCK_SIZE - 4, 4);
/* Reset the rest of the message-dependent state */
memset(ctx->x, 0, CRY_GCM_BLOCK_SIZE);
ctx->auth_size = ctx->ciph_size = 0;
}
static void cry_gcm_crypt(struct cry_gcm_ctx *ctx, unsigned char *dst,
const unsigned char *src, unsigned int size)
{
void *ciph = ctx->ciph_ctx;
cry_ciph_encrypt_f encrypt = ctx->ciph_itf->encrypt;
unsigned char buffer[CRY_GCM_BLOCK_SIZE];
if (src != dst) {
for (; size >= CRY_GCM_BLOCK_SIZE; (size -= CRY_GCM_BLOCK_SIZE,
src += CRY_GCM_BLOCK_SIZE, dst += CRY_GCM_BLOCK_SIZE)) {
encrypt(ciph, dst, ctx->ctr, CRY_GCM_BLOCK_SIZE);
cry_memxor(dst, src, CRY_GCM_BLOCK_SIZE);
INCREMENT_BE(&ctx->ctr[CRY_GCM_BLOCK_SIZE-4], 4);
}
} else {
for (; size >= CRY_GCM_BLOCK_SIZE; (size -= CRY_GCM_BLOCK_SIZE,
src += CRY_GCM_BLOCK_SIZE, dst += CRY_GCM_BLOCK_SIZE)) {
encrypt(ciph, buffer, ctx->ctr, CRY_GCM_BLOCK_SIZE);
cry_memxor3 (dst, src, buffer, CRY_GCM_BLOCK_SIZE);
INCREMENT_BE(&ctx->ctr[CRY_GCM_BLOCK_SIZE-4], 4);
}
}
if (size > 0) {
/* A final partial block */
encrypt(ciph, buffer, ctx->ctr, CRY_GCM_BLOCK_SIZE);
cry_memxor3(dst, src, buffer, size);
INCREMENT_BE(&ctx->ctr[CRY_GCM_BLOCK_SIZE-4], 4);
}
}
void cry_gcm_encrypt(struct cry_gcm_ctx *ctx, unsigned char *dst,
const unsigned char *src, unsigned int size)
{
cry_gcm_crypt(ctx, dst, src, size);
cry_gcm_hash(ctx->key, ctx->x, size, dst);
ctx->ciph_size += size;
}
void cry_gcm_decrypt(struct cry_gcm_ctx *ctx, unsigned char *dst,
const unsigned char *src, unsigned int size)
{
cry_gcm_hash(ctx->key, ctx->x, size, src);
cry_gcm_crypt(ctx, dst, src, size);
ctx->ciph_size += size;
}
void cry_gcm_update(struct cry_gcm_ctx *ctx, const unsigned char *aad,
unsigned int size)
{
cry_gcm_hash(ctx->key, ctx->x, size, aad);
ctx->auth_size += size;
}
void cry_gcm_digest(struct cry_gcm_ctx *ctx, unsigned char *mac,
unsigned int size)
{
void *ciph = ctx->ciph_ctx;
cry_ciph_encrypt_f encrypt = ctx->ciph_itf->encrypt;
unsigned char buffer[CRY_GCM_BLOCK_SIZE];
cry_gcm_hash_sizes(ctx->key, ctx->x, ctx->auth_size, ctx->ciph_size);
encrypt(ciph, buffer, ctx->iv, CRY_GCM_BLOCK_SIZE);
cry_memxor3(mac, ctx->x, buffer, size);
}
/*
* Copyright (c) 2013, Davide Galassi. All rights reserved.
* Copyright (c) 2013-2014, Davide Galassi. All rights reserved.
*
* This file is part of CRY software.
*
......
......@@ -10,7 +10,8 @@ TESTS := version_test \
base64_test \
des_test \
aes_test \
cbc_test
cbc_test \
gcm_test
.PHONY: all clean
......
/*
* Copyright (c) 2013-2014, Davide Galassi. All rights reserved.
*
* This file is part of CRY software.
*
* CRY is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with CRY; if not, see <http://www.gnu/licenses/>.
*/
#include <cry.h>
#include <stdio.h>
#include <string.h>
static const struct cry_ciph_itf aes_itf = {
.init = NULL,
.clean = NULL,
.key_set = (cry_ciph_key_set_f) cry_aes_key_set,
.encrypt = (cry_ciph_encrypt_f) cry_aes_encrypt,
.decrypt = (cry_ciph_decrypt_f) cry_aes_decrypt
};
void cry_aes_128_gcm_encrypt(unsigned char *dst,
const unsigned char *src,
const unsigned int src_size,
const unsigned char *key,
const unsigned char *iv,
unsigned char *mac,
const unsigned char *aad,
unsigned int aad_size)
{
struct cry_aes_ctx aes;
struct cry_gcm_ctx gcm;
gcm.ciph_itf = &aes_itf;
gcm.ciph_ctx = &aes;
cry_gcm_key_set(&gcm, key, 16);
cry_gcm_iv_set(&gcm, iv, 16);
cry_gcm_encrypt(&gcm, dst, src, src_size);
if (aad != NULL)
cry_gcm_update(&gcm, aad, aad_size);
if (mac != NULL)
cry_gcm_digest(&gcm, mac, 16);
}
void cry_aes_128_gcm_decrypt(unsigned char *dst,
const unsigned char *src,
const unsigned int src_size,
const unsigned char *key,
const unsigned char *iv,
unsigned char *mac,
const unsigned char *aad,
unsigned int aad_size)
{
struct cry_aes_ctx aes;
struct cry_gcm_ctx gcm;
gcm.ciph_itf = &aes_itf;
gcm.ciph_ctx = &aes;
cry_gcm_key_set(&gcm, key, 16);
cry_gcm_iv_set(&gcm, iv, 16);
cry_gcm_decrypt(&gcm, dst, src, src_size);
if (aad != NULL)
cry_gcm_update(&gcm, aad, aad_size);
if (mac != NULL)
cry_gcm_digest(&gcm, mac, 16);
}
int main(void)
{
char buf[128];
char *msg = "CRY is free software: you can redistribute it and/or modify";
char key[] = { 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13,14,15 };
char iv[] = { 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13,14,15 };
char *aad = "Hello";
int msglen = strlen(msg);
int aadlen = strlen(aad);
printf("Msg len: %d\n", msglen);
printf("AES-128-GCM\n");
memset(buf, 0, sizeof(buf));
cry_aes_128_gcm_encrypt(buf, msg, msglen, key, iv,
buf+msglen, aad, aadlen);
cry_aes_128_gcm_decrypt(buf, buf, msglen, key, iv,
buf+msglen+16, aad, aadlen);
printf("%.*s\n", msglen, buf);
if (memcmp(buf+msglen, buf+msglen+16, 16) != 0)
printf("Auth failure\n");
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment