strongswan/src/libstrongswan/plugins/newhope/newhope_noise.c

161 lines
3.5 KiB
C

/*
* Copyright (C) 2016 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
* Based on public domain code by Erdem Alkim, Léo Ducas, Thomas Pöppelmann,
* and Peter Schwabe.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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 General Public License
* for more details.
*/
#include "newhope_noise.h"
typedef struct private_newhope_noise_t private_newhope_noise_t;
static const int seed_len = 32; /* 256 bits */
static const int nonce_len = 12; /* 96 bits */
/**
* Private data of an newhope_noise_t object.
*/
struct private_newhope_noise_t {
/**
* Public newhope_noise_t interface.
*/
newhope_noise_t public;
/**
* 256 bit seed and 96 bit nonce (44 bytes)
*/
chunk_t seed;
/**
* ChaCha20 stream
*/
xof_t *xof;
};
METHOD(newhope_noise_t, get_uniform_bytes, uint8_t*,
private_newhope_noise_t *this, uint8_t nonce, uint16_t n)
{
uint8_t *bytes;
this->seed.ptr[seed_len] = nonce;
if (!this->xof->set_seed(this->xof, this->seed))
{
DBG1(DBG_LIB, "could not set seed of CHACHA20 XOF");
return NULL;
}
/* allocate dynamic memory for the noise polynomial */
bytes = (uint8_t*)malloc(n);
if (!this->xof->get_bytes(this->xof, n, bytes))
{
DBG1(DBG_LIB, "could not get bytes from SHAKE128 XOF");
free(bytes);
return NULL;
}
return bytes;
}
METHOD(newhope_noise_t, get_binomial_words, uint32_t*,
private_newhope_noise_t *this, uint8_t nonce, uint16_t n, uint16_t q)
{
uint32_t *np, a, b, d, t;
uint8_t x[4];
int i = 0, j;
this->seed.ptr[seed_len] = nonce;
if (!this->xof->set_seed(this->xof, this->seed))
{
DBG1(DBG_LIB, "could not set seed of CHACHA20 XOF");
return NULL;
}
/* allocate dynamic memory for the noise polynomial */
np = (uint32_t*)malloc(n * sizeof(uint32_t));
for (i = 0; i < n; i++)
{
if (!this->xof->get_bytes(this->xof, sizeof(x), x))
{
DBG1(DBG_LIB, "could not get bytes from SHAKE128 XOF");
free(np);
return NULL;
}
/* Treat x as a 32 bit unsigned little endian integer */
t = uletoh32(x);
/* Compute Psi_16 distribution */
d = 0;
for (j = 0; j < 8; j++)
{
d += (t >> j) & 0x01010101;
}
a = ((d >> 8) & 0xff) + (d & 0xff);
b = ((d >> 16) & 0xff) + (d >> 24);
np[i] = (a >= b) ? a - b : a + q - b;
}
return np;
}
METHOD(newhope_noise_t, destroy, void,
private_newhope_noise_t *this)
{
this->xof->destroy(this->xof);
chunk_free(&this->seed);
free(this);
}
/*
* Described in header.
*/
newhope_noise_t *newhope_noise_create(chunk_t seed)
{
private_newhope_noise_t *this;
xof_t *xof;
if (seed.len != seed_len)
{
DBG1(DBG_LIB, "seed for ChaCha20 stream must be 256 bits");
return NULL;
}
xof = lib->crypto->create_xof(lib->crypto, XOF_CHACHA20);
if (!xof)
{
DBG1(DBG_LIB, "could not instantiate ChaCha20 stream");
return NULL;
}
INIT(this,
.public = {
.get_uniform_bytes = _get_uniform_bytes,
.get_binomial_words = _get_binomial_words,
.destroy = _destroy,
},
.xof = xof,
.seed = chunk_alloc(seed_len + nonce_len),
);
/* initialize seed for ChaCha 20 stream */
memcpy(this->seed.ptr, seed.ptr, seed_len);
memset(this->seed.ptr + seed_len, 0x00, nonce_len);
return &this->public;
}