319 lines
8.2 KiB
C
319 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "inner.h"
|
|
|
|
/*
|
|
* Implementation Notes
|
|
* ====================
|
|
*
|
|
* Since CTR and GHASH implementations can handle only full blocks, a
|
|
* 16-byte buffer (buf[]) is maintained in the context:
|
|
*
|
|
* - When processing AAD, buf[] contains the 0-15 unprocessed bytes.
|
|
*
|
|
* - When doing CTR encryption / decryption, buf[] contains the AES output
|
|
* for the last partial block, to be used with the next few bytes of
|
|
* data, as well as the already encrypted bytes. For instance, if the
|
|
* processed data length so far is 21 bytes, then buf[0..4] contains
|
|
* the five last encrypted bytes, and buf[5..15] contains the next 11
|
|
* AES output bytes to be XORed with the next 11 bytes of input.
|
|
*
|
|
* The recorded AES output bytes are used to complete the block when
|
|
* the corresponding bytes are obtained. Note that buf[] always
|
|
* contains the _encrypted_ bytes, whether we apply encryption or
|
|
* decryption: these bytes are used as input to GHASH when the block
|
|
* is complete.
|
|
*
|
|
* In both cases, the low bits of the data length counters (count_aad,
|
|
* count_ctr) are used to work out the current situation.
|
|
*/
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_init(br_gcm_context *ctx, const br_block_ctr_class **bctx, br_ghash gh)
|
|
{
|
|
unsigned char iv[12];
|
|
|
|
ctx->vtable = &br_gcm_vtable;
|
|
ctx->bctx = bctx;
|
|
ctx->gh = gh;
|
|
|
|
/*
|
|
* The GHASH key h[] is the raw encryption of the all-zero
|
|
* block. Since we only have a CTR implementation, we use it
|
|
* with an all-zero IV and a zero counter, to CTR-encrypt an
|
|
* all-zero block.
|
|
*/
|
|
memset(ctx->h, 0, sizeof ctx->h);
|
|
memset(iv, 0, sizeof iv);
|
|
(*bctx)->run(bctx, iv, 0, ctx->h, sizeof ctx->h);
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len)
|
|
{
|
|
/*
|
|
* If the provided nonce is 12 bytes, then this is the initial
|
|
* IV for CTR mode; it will be used with a counter that starts
|
|
* at 2 (value 1 is for encrypting the GHASH output into the tag).
|
|
*
|
|
* If the provided nonce has any other length, then it is hashed
|
|
* (with GHASH) into a 16-byte value that will be the IV for CTR
|
|
* (both 12-byte IV and 32-bit counter).
|
|
*/
|
|
if (len == 12) {
|
|
memcpy(ctx->j0_1, iv, 12);
|
|
ctx->j0_2 = 1;
|
|
} else {
|
|
unsigned char ty[16], tmp[16];
|
|
|
|
memset(ty, 0, sizeof ty);
|
|
ctx->gh(ty, ctx->h, iv, len);
|
|
memset(tmp, 0, 8);
|
|
br_enc64be(tmp + 8, (uint64_t)len << 3);
|
|
ctx->gh(ty, ctx->h, tmp, 16);
|
|
memcpy(ctx->j0_1, ty, 12);
|
|
ctx->j0_2 = br_dec32be(ty + 12);
|
|
}
|
|
ctx->jc = ctx->j0_2 + 1;
|
|
memset(ctx->y, 0, sizeof ctx->y);
|
|
ctx->count_aad = 0;
|
|
ctx->count_ctr = 0;
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len)
|
|
{
|
|
size_t ptr, dlen;
|
|
|
|
ptr = (size_t)ctx->count_aad & (size_t)15;
|
|
if (ptr != 0) {
|
|
/*
|
|
* If there is a partial block, then we first try to
|
|
* complete it.
|
|
*/
|
|
size_t clen;
|
|
|
|
clen = 16 - ptr;
|
|
if (len < clen) {
|
|
memcpy(ctx->buf + ptr, data, len);
|
|
ctx->count_aad += (uint64_t)len;
|
|
return;
|
|
}
|
|
memcpy(ctx->buf + ptr, data, clen);
|
|
ctx->gh(ctx->y, ctx->h, ctx->buf, 16);
|
|
data = (const unsigned char *)data + clen;
|
|
len -= clen;
|
|
ctx->count_aad += (uint64_t)clen;
|
|
}
|
|
|
|
/*
|
|
* Now AAD is aligned on a 16-byte block (with regards to GHASH).
|
|
* We process all complete blocks, and save the last partial
|
|
* block.
|
|
*/
|
|
dlen = len & ~(size_t)15;
|
|
ctx->gh(ctx->y, ctx->h, data, dlen);
|
|
memcpy(ctx->buf, (const unsigned char *)data + dlen, len - dlen);
|
|
ctx->count_aad += (uint64_t)len;
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_flip(br_gcm_context *ctx)
|
|
{
|
|
/*
|
|
* We complete the GHASH computation if there is a partial block.
|
|
* The GHASH implementation automatically applies padding with
|
|
* zeros.
|
|
*/
|
|
size_t ptr;
|
|
|
|
ptr = (size_t)ctx->count_aad & (size_t)15;
|
|
if (ptr != 0) {
|
|
ctx->gh(ctx->y, ctx->h, ctx->buf, ptr);
|
|
}
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len)
|
|
{
|
|
unsigned char *buf;
|
|
size_t ptr, dlen;
|
|
|
|
buf = data;
|
|
ptr = (size_t)ctx->count_ctr & (size_t)15;
|
|
if (ptr != 0) {
|
|
/*
|
|
* If we have a partial block, then we try to complete it.
|
|
*/
|
|
size_t u, clen;
|
|
|
|
clen = 16 - ptr;
|
|
if (len < clen) {
|
|
clen = len;
|
|
}
|
|
for (u = 0; u < clen; u ++) {
|
|
unsigned x, y;
|
|
|
|
x = buf[u];
|
|
y = x ^ ctx->buf[ptr + u];
|
|
ctx->buf[ptr + u] = encrypt ? y : x;
|
|
buf[u] = y;
|
|
}
|
|
ctx->count_ctr += (uint64_t)clen;
|
|
buf += clen;
|
|
len -= clen;
|
|
if (ptr + clen < 16) {
|
|
return;
|
|
}
|
|
ctx->gh(ctx->y, ctx->h, ctx->buf, 16);
|
|
}
|
|
|
|
/*
|
|
* Process full blocks.
|
|
*/
|
|
dlen = len & ~(size_t)15;
|
|
if (!encrypt) {
|
|
ctx->gh(ctx->y, ctx->h, buf, dlen);
|
|
}
|
|
ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->jc, buf, dlen);
|
|
if (encrypt) {
|
|
ctx->gh(ctx->y, ctx->h, buf, dlen);
|
|
}
|
|
buf += dlen;
|
|
len -= dlen;
|
|
ctx->count_ctr += (uint64_t)dlen;
|
|
|
|
if (len > 0) {
|
|
/*
|
|
* There is a partial block.
|
|
*/
|
|
size_t u;
|
|
|
|
memset(ctx->buf, 0, sizeof ctx->buf);
|
|
ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1,
|
|
ctx->jc, ctx->buf, 16);
|
|
for (u = 0; u < len; u ++) {
|
|
unsigned x, y;
|
|
|
|
x = buf[u];
|
|
y = x ^ ctx->buf[u];
|
|
ctx->buf[u] = encrypt ? y : x;
|
|
buf[u] = y;
|
|
}
|
|
ctx->count_ctr += (uint64_t)len;
|
|
}
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_get_tag(br_gcm_context *ctx, void *tag)
|
|
{
|
|
size_t ptr;
|
|
unsigned char tmp[16];
|
|
|
|
ptr = (size_t)ctx->count_ctr & (size_t)15;
|
|
if (ptr > 0) {
|
|
/*
|
|
* There is a partial block: encrypted/decrypted data has
|
|
* been produced, but the encrypted bytes must still be
|
|
* processed by GHASH.
|
|
*/
|
|
ctx->gh(ctx->y, ctx->h, ctx->buf, ptr);
|
|
}
|
|
|
|
/*
|
|
* Final block for GHASH: the AAD and plaintext lengths (in bits).
|
|
*/
|
|
br_enc64be(tmp, ctx->count_aad << 3);
|
|
br_enc64be(tmp + 8, ctx->count_ctr << 3);
|
|
ctx->gh(ctx->y, ctx->h, tmp, 16);
|
|
|
|
/*
|
|
* Tag is the GHASH output XORed with the encryption of the
|
|
* nonce with the initial counter value.
|
|
*/
|
|
memcpy(tag, ctx->y, 16);
|
|
(*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->j0_2, tag, 16);
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
void
|
|
br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len)
|
|
{
|
|
unsigned char tmp[16];
|
|
|
|
br_gcm_get_tag(ctx, tmp);
|
|
memcpy(tag, tmp, len);
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
uint32_t
|
|
br_gcm_check_tag_trunc(br_gcm_context *ctx, const void *tag, size_t len)
|
|
{
|
|
unsigned char tmp[16];
|
|
size_t u;
|
|
int x;
|
|
|
|
br_gcm_get_tag(ctx, tmp);
|
|
x = 0;
|
|
for (u = 0; u < len; u ++) {
|
|
x |= tmp[u] ^ ((const unsigned char *)tag)[u];
|
|
}
|
|
return EQ0(x);
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
uint32_t
|
|
br_gcm_check_tag(br_gcm_context *ctx, const void *tag)
|
|
{
|
|
return br_gcm_check_tag_trunc(ctx, tag, 16);
|
|
}
|
|
|
|
/* see bearssl_aead.h */
|
|
const br_aead_class br_gcm_vtable = {
|
|
16,
|
|
(void (*)(const br_aead_class **, const void *, size_t))
|
|
&br_gcm_reset,
|
|
(void (*)(const br_aead_class **, const void *, size_t))
|
|
&br_gcm_aad_inject,
|
|
(void (*)(const br_aead_class **))
|
|
&br_gcm_flip,
|
|
(void (*)(const br_aead_class **, int, void *, size_t))
|
|
&br_gcm_run,
|
|
(void (*)(const br_aead_class **, void *))
|
|
&br_gcm_get_tag,
|
|
(uint32_t (*)(const br_aead_class **, const void *))
|
|
&br_gcm_check_tag,
|
|
(void (*)(const br_aead_class **, void *, size_t))
|
|
&br_gcm_get_tag_trunc,
|
|
(uint32_t (*)(const br_aead_class **, const void *, size_t))
|
|
&br_gcm_check_tag_trunc
|
|
};
|