/* drbg-aes.c */
/* Copyright (C) 2013-2018 Red Hat
*
* This file is part of GnuTLS.
*
* The GnuTLS library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The nettle library 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 this program. If not, see
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
int
drbg_aes_init(struct drbg_aes_ctx *ctx,
unsigned entropy_size, const uint8_t * entropy,
unsigned pstring_size, const uint8_t * pstring)
{
uint8_t tmp[AES256_KEY_SIZE];
assert(AES256_KEY_SIZE == DRBG_AES_KEY_SIZE);
memset(ctx, 0, sizeof(*ctx));
memset(tmp, 0, sizeof(tmp));
aes256_set_encrypt_key(&ctx->key, tmp);
return drbg_aes_reseed(ctx, entropy_size, entropy,
pstring_size, pstring);
}
/* Sets V and key based on pdata */
static void
drbg_aes_update(struct drbg_aes_ctx *ctx,
const uint8_t pdata[DRBG_AES_SEED_SIZE])
{
unsigned len = 0;
uint8_t tmp[DRBG_AES_SEED_SIZE];
uint8_t *t = tmp;
while (len < DRBG_AES_SEED_SIZE) {
INCREMENT(sizeof(ctx->v), ctx->v);
aes256_encrypt(&ctx->key, AES_BLOCK_SIZE, t, ctx->v);
t += AES_BLOCK_SIZE;
len += AES_BLOCK_SIZE;
}
memxor(tmp, pdata, DRBG_AES_SEED_SIZE);
aes256_set_encrypt_key(&ctx->key, tmp);
memcpy(ctx->v, &tmp[DRBG_AES_KEY_SIZE], AES_BLOCK_SIZE);
ctx->seeded = 1;
}
int
drbg_aes_reseed(struct drbg_aes_ctx *ctx,
unsigned entropy_size, const uint8_t * entropy,
unsigned add_size, const uint8_t * add)
{
uint8_t tmp[DRBG_AES_SEED_SIZE];
unsigned len = 0;
if (add_size > DRBG_AES_SEED_SIZE || entropy_size != DRBG_AES_SEED_SIZE)
return 0;
if (add_size <= DRBG_AES_SEED_SIZE && add_size > 0) {
memcpy(tmp, add, add_size);
len = add_size;
}
if (len != DRBG_AES_SEED_SIZE)
memset(&tmp[len], 0, DRBG_AES_SEED_SIZE - len);
memxor(tmp, entropy, entropy_size);
drbg_aes_update(ctx, tmp);
ctx->reseed_counter = 1;
return 1;
}
int drbg_aes_random(struct drbg_aes_ctx *ctx, unsigned length, uint8_t * dst)
{
unsigned p_len;
int left = length;
uint8_t *p = dst;
int ret;
while(left > 0) {
p_len = MIN(MAX_DRBG_AES_GENERATE_SIZE, left);
ret = drbg_aes_generate(ctx, p_len, p, 0, 0);
if (ret == 0)
return ret;
p += p_len;
left -= p_len;
}
return 1;
}
/* we don't use additional input */
int drbg_aes_generate(struct drbg_aes_ctx *ctx, unsigned length, uint8_t * dst,
unsigned add_size, const uint8_t *add)
{
uint8_t tmp[AES_BLOCK_SIZE];
uint8_t seed[DRBG_AES_SEED_SIZE];
unsigned left;
if (ctx->seeded == 0)
return gnutls_assert_val(0);
if (length > MAX_DRBG_AES_GENERATE_SIZE)
return gnutls_assert_val(0);
if (add_size > 0) {
if (add_size > DRBG_AES_SEED_SIZE)
return gnutls_assert_val(0);
memcpy(seed, add, add_size);
if (add_size != DRBG_AES_SEED_SIZE)
memset(&seed[add_size], 0, DRBG_AES_SEED_SIZE - add_size);
drbg_aes_update(ctx, seed);
} else {
memset(seed, 0, DRBG_AES_SEED_SIZE);
}
/* Perform the actual encryption */
for (left = length; left >= AES_BLOCK_SIZE;
left -= AES_BLOCK_SIZE, dst += AES_BLOCK_SIZE) {
INCREMENT(sizeof(ctx->v), ctx->v);
aes256_encrypt(&ctx->key, AES_BLOCK_SIZE, dst, ctx->v);
}
if (left > 0) { /* partial fill */
INCREMENT(sizeof(ctx->v), ctx->v);
aes256_encrypt(&ctx->key, AES_BLOCK_SIZE, tmp, ctx->v);
memcpy(dst, tmp, left);
}
if (ctx->reseed_counter > DRBG_AES_RESEED_TIME)
return gnutls_assert_val(0);
ctx->reseed_counter++;
drbg_aes_update(ctx, seed);
return 1;
}
int drbg_aes_is_seeded(struct drbg_aes_ctx *ctx)
{
return ctx->seeded;
}