ml: Add software implementation of ML-KEM
This follows FIPS 203 relatively closely but takes some ideas from the reference implementation. In particular, how to avoid potential side-channels via direct C division/modulo operations. However, it just uses Barrett reduction (no Montgomery reduction) and no negative coefficients to avoid number format conversions and keep the implementation clearer.
This commit is contained in:
@@ -152,6 +152,7 @@ ARG_DISBL_SET([kdf], [disable KDF (prf+) implementation plugin.])
|
||||
ARG_ENABL_SET([md4], [enable MD4 software implementation plugin.])
|
||||
ARG_DISBL_SET([md5], [disable MD5 software implementation plugin.])
|
||||
ARG_ENABL_SET([mgf1], [enable the MGF1 software implementation plugin.])
|
||||
ARG_ENABL_SET([ml], [enable Module-Lattice-based crypto (ML-KEM) plugin.])
|
||||
ARG_DISBL_SET([nonce], [disable nonce generation plugin.])
|
||||
ARG_ENABL_SET([openssl], [enables the OpenSSL crypto plugin.])
|
||||
ARG_ENABL_SET([wolfssl], [enables the wolfSSL crypto plugin.])
|
||||
@@ -1587,6 +1588,7 @@ ADD_PLUGIN([kdf], [s charon pki scripts nm cmd])
|
||||
ADD_PLUGIN([ctr], [s charon scripts nm cmd])
|
||||
ADD_PLUGIN([ccm], [s charon scripts nm cmd])
|
||||
ADD_PLUGIN([gcm], [s charon scripts nm cmd])
|
||||
ADD_PLUGIN([ml], [s charon scripts nm cmd])
|
||||
ADD_PLUGIN([drbg], [s charon pki scripts nm cmd])
|
||||
ADD_PLUGIN([curl], [s charon pki scripts nm cmd])
|
||||
ADD_PLUGIN([files], [s charon pki scripts nm cmd])
|
||||
@@ -1755,6 +1757,7 @@ AM_CONDITIONAL(USE_CCM, test x$ccm = xtrue)
|
||||
AM_CONDITIONAL(USE_GCM, test x$gcm = xtrue)
|
||||
AM_CONDITIONAL(USE_AF_ALG, test x$af_alg = xtrue)
|
||||
AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue)
|
||||
AM_CONDITIONAL(USE_ML, test x$ml = xtrue)
|
||||
|
||||
# charon plugins
|
||||
# ----------------
|
||||
@@ -2034,6 +2037,7 @@ AC_CONFIG_FILES([
|
||||
src/libstrongswan/plugins/gcm/Makefile
|
||||
src/libstrongswan/plugins/af_alg/Makefile
|
||||
src/libstrongswan/plugins/drbg/Makefile
|
||||
src/libstrongswan/plugins/ml/Makefile
|
||||
src/libstrongswan/plugins/test_vectors/Makefile
|
||||
src/libstrongswan/tests/Makefile
|
||||
src/libipsec/Makefile
|
||||
|
||||
@@ -683,6 +683,13 @@ if MONOLITHIC
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_ML
|
||||
SUBDIRS += plugins/ml
|
||||
if MONOLITHIC
|
||||
libstrongswan_la_LIBADD += plugins/ml/libstrongswan-ml.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_TEST_VECTORS
|
||||
SUBDIRS += plugins/test_vectors
|
||||
if MONOLITHIC
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/libstrongswan
|
||||
|
||||
AM_CFLAGS = \
|
||||
$(PLUGIN_CFLAGS)
|
||||
|
||||
if MONOLITHIC
|
||||
noinst_LTLIBRARIES = libstrongswan-ml.la
|
||||
else
|
||||
plugin_LTLIBRARIES = libstrongswan-ml.la
|
||||
endif
|
||||
|
||||
libstrongswan_ml_la_SOURCES = \
|
||||
ml_bitpacker.c ml_bitpacker.h \
|
||||
ml_kem.c ml_kem.h \
|
||||
ml_params.c ml_params.h \
|
||||
ml_plugin.h ml_plugin.c \
|
||||
ml_poly.c ml_poly.h \
|
||||
ml_utils.c ml_utils.h
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
* Copyright (C) 2014 Andreas Steffen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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 "ml_bitpacker.h"
|
||||
#include "ml_utils.h"
|
||||
|
||||
typedef struct private_ml_bitpacker_t private_ml_bitpacker_t;
|
||||
|
||||
/**
|
||||
* Private data.
|
||||
*/
|
||||
struct private_ml_bitpacker_t {
|
||||
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
ml_bitpacker_t public;
|
||||
|
||||
/**
|
||||
* Bit buffer for up to 32 bits.
|
||||
*/
|
||||
uint32_t bits_buf;
|
||||
|
||||
/**
|
||||
* Bits left in the bit buffer.
|
||||
*/
|
||||
size_t bits_left;
|
||||
|
||||
/**
|
||||
* Target buffer.
|
||||
*/
|
||||
chunk_t buf;
|
||||
|
||||
/**
|
||||
* Read/Write pointer into buffer.
|
||||
*/
|
||||
chunk_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write the bytes in the bit buffer to the output buffer.
|
||||
*/
|
||||
static void flush_buffer(private_ml_bitpacker_t *this)
|
||||
{
|
||||
size_t to_write = min(4, this->pos.len);
|
||||
|
||||
ml_write_bytes_le(this->pos.ptr, to_write, this->bits_buf);
|
||||
this->pos = chunk_skip(this->pos, to_write);
|
||||
}
|
||||
|
||||
METHOD(ml_bitpacker_t, write_bits, bool,
|
||||
private_ml_bitpacker_t *this, uint32_t value, size_t bits)
|
||||
{
|
||||
if (!bits)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if (bits > 32)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (bits < 32)
|
||||
{
|
||||
value &= (1 << bits) - 1;
|
||||
}
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
if (!this->pos.len)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
this->bits_buf |= value << (32 - this->bits_left);
|
||||
|
||||
if (bits < this->bits_left)
|
||||
{
|
||||
this->bits_left -= bits;
|
||||
return TRUE;
|
||||
}
|
||||
value >>= this->bits_left;
|
||||
bits -= this->bits_left;
|
||||
|
||||
flush_buffer(this);
|
||||
this->bits_buf = 0;
|
||||
this->bits_left = 32;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(ml_bitpacker_t, read_bits, bool,
|
||||
private_ml_bitpacker_t *this, uint32_t *value, size_t bits)
|
||||
{
|
||||
size_t to_read, written = 0;
|
||||
|
||||
if (bits > 32)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*value = 0;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
if (!this->bits_left)
|
||||
{
|
||||
if (!this->pos.len)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
to_read = min(4, this->pos.len);
|
||||
this->bits_buf = ml_read_bytes_le(this->pos.ptr, to_read);
|
||||
this->pos = chunk_skip(this->pos, to_read);
|
||||
this->bits_left = 8 * to_read;
|
||||
}
|
||||
if (bits <= this->bits_left)
|
||||
{
|
||||
*value |= (this->bits_buf & ((1 << bits) - 1)) << written;
|
||||
this->bits_buf >>= bits;
|
||||
this->bits_left -= bits;
|
||||
return TRUE;
|
||||
}
|
||||
*value |= this->bits_buf;
|
||||
written = this->bits_left;
|
||||
bits -= this->bits_left;
|
||||
this->bits_left = 0;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(ml_bitpacker_t, destroy, void,
|
||||
private_ml_bitpacker_t *this)
|
||||
{
|
||||
if (this->public.write_bits == _write_bits &&
|
||||
this->bits_left < 32)
|
||||
{
|
||||
flush_buffer(this);
|
||||
}
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
ml_bitpacker_t *ml_bitpacker_create(chunk_t dst)
|
||||
{
|
||||
private_ml_bitpacker_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.write_bits = _write_bits,
|
||||
.read_bits = (void*)return_false,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.bits_left = 32,
|
||||
.buf = dst,
|
||||
);
|
||||
|
||||
this->pos = this->buf;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
ml_bitpacker_t *ml_bitpacker_create_from_data(chunk_t data)
|
||||
{
|
||||
private_ml_bitpacker_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.write_bits = (void*)return_false,
|
||||
.read_bits = _read_bits,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.buf = data,
|
||||
);
|
||||
|
||||
this->pos = this->buf;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
* Copyright (C) 2014 Andreas Steffen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ml_bitpacker ml_bitpacker
|
||||
* @{ @ingroup ml_p
|
||||
*/
|
||||
|
||||
#ifndef ML_BITPACKER_H_
|
||||
#define ML_BITPACKER_H_
|
||||
|
||||
#include <library.h>
|
||||
|
||||
typedef struct ml_bitpacker_t ml_bitpacker_t;
|
||||
|
||||
/**
|
||||
* Reads and writes a variable number of bits in packed format
|
||||
* from and to an octet buffer.
|
||||
*/
|
||||
struct ml_bitpacker_t {
|
||||
|
||||
/**
|
||||
* Write a number of bits of the given value to the buffer.
|
||||
*
|
||||
* @param value value to be written
|
||||
* @param bits number of bits to be written
|
||||
* @result TRUE if value could be written into buffer
|
||||
*/
|
||||
bool (*write_bits)(ml_bitpacker_t *this, uint32_t value, size_t bits);
|
||||
|
||||
/**
|
||||
* Get a number of bits from the buffer.
|
||||
*
|
||||
* @param value read value
|
||||
* @param bits number of bits to be read
|
||||
* @result TRUE if value could be read from buffer
|
||||
*/
|
||||
bool (*read_bits)(ml_bitpacker_t *this, uint32_t *value, size_t bits);
|
||||
|
||||
/**
|
||||
* Destroy the object.
|
||||
*
|
||||
* Note that when writing, this flushes the internal buffer and writes the
|
||||
* remaining bits if there is still room in the destination buffer.
|
||||
*/
|
||||
void (*destroy)(ml_bitpacker_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a ml_bitpacker_t object for writing to a buffer.
|
||||
*
|
||||
* @param dst existing buffer to write bits to
|
||||
*/
|
||||
ml_bitpacker_t *ml_bitpacker_create(chunk_t dst);
|
||||
|
||||
/**
|
||||
* Create a ml_bitpacker_t object for reading.
|
||||
*
|
||||
* @param data packed array of bits
|
||||
*/
|
||||
ml_bitpacker_t *ml_bitpacker_create_from_data(chunk_t data);
|
||||
|
||||
#endif /** ML_BITPACKER_H_ @}*/
|
||||
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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 "ml_bitpacker.h"
|
||||
#include "ml_kem.h"
|
||||
#include "ml_params.h"
|
||||
#include "ml_poly.h"
|
||||
#include "ml_utils.h"
|
||||
|
||||
typedef struct private_key_exchange_t private_key_exchange_t;
|
||||
|
||||
/**
|
||||
* Private data.
|
||||
*/
|
||||
struct private_key_exchange_t {
|
||||
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
key_exchange_t public;
|
||||
|
||||
/**
|
||||
* Key exchange method.
|
||||
*/
|
||||
key_exchange_method_t method;
|
||||
|
||||
/**
|
||||
* Parameter set.
|
||||
*/
|
||||
const ml_kem_params_t *params;
|
||||
|
||||
/**
|
||||
* Decryption/private key as initiator.
|
||||
*/
|
||||
chunk_t private_key;
|
||||
|
||||
/**
|
||||
* Ciphertext as responder.
|
||||
*/
|
||||
chunk_t ciphertext;
|
||||
|
||||
/**
|
||||
* Shared secret.
|
||||
*/
|
||||
chunk_t shared_secret;
|
||||
|
||||
/**
|
||||
* SHAKE-128 instance used as XOF when generating matrix A.
|
||||
*/
|
||||
xof_t *shake128;
|
||||
|
||||
/**
|
||||
* SHAKE-256 instance used as PRF and hash function J.
|
||||
*/
|
||||
xof_t *shake256;
|
||||
|
||||
/**
|
||||
* Hash function G (SHA3-512) used throughout the algorithms.
|
||||
*/
|
||||
hasher_t *G;
|
||||
|
||||
/**
|
||||
* Hash function H (SHA3-256) used throughout the algorithms.
|
||||
*/
|
||||
hasher_t *H;
|
||||
|
||||
/**
|
||||
* DRBG used during testing.
|
||||
*/
|
||||
drbg_t *drbg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get random bytes either from a DRBG during testing or from an RNG.
|
||||
*/
|
||||
static bool get_random(private_key_exchange_t *this, size_t len, uint8_t *out)
|
||||
{
|
||||
rng_t *rng;
|
||||
|
||||
if (this->drbg)
|
||||
{
|
||||
return this->drbg->generate(this->drbg, len, out);
|
||||
}
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
|
||||
if (!rng || !rng->get_bytes(rng, len, out))
|
||||
{
|
||||
DESTROY_IF(rng);
|
||||
return FALSE;
|
||||
}
|
||||
rng->destroy(rng);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a pseudorandom element of T_q using the given XOF.
|
||||
*
|
||||
* Algorithm 7 in FIPS 203.
|
||||
*/
|
||||
static bool sample_ntt(private_key_exchange_t *this, ml_poly_t *ahat)
|
||||
{
|
||||
uint8_t C[3];
|
||||
uint16_t d1, d2;
|
||||
int j = 0;
|
||||
|
||||
while (j < ML_KEM_N)
|
||||
{
|
||||
if (!this->shake128->get_bytes(this->shake128, sizeof(C), C))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
d1 = C[0] + ((C[1] & 0xf) << 8);
|
||||
d2 = (C[1] >> 4) + (C[2] << 4);
|
||||
if (d1 < ML_KEM_Q)
|
||||
{
|
||||
ahat->f[j++] = d1;
|
||||
}
|
||||
if (d2 < ML_KEM_Q && j < ML_KEM_N)
|
||||
{
|
||||
ahat->f[j++] = d2;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate pseudorandom matrix A or its transposed version A^T.
|
||||
*/
|
||||
static bool generate_a(private_key_exchange_t *this, ml_poly_t *a,
|
||||
uint8_t *rho)
|
||||
{
|
||||
const uint8_t k = this->params->k;
|
||||
|
||||
chunk_t B = chunk_alloca(ML_KEM_SEED_LEN + 2);
|
||||
int i, j;
|
||||
|
||||
memcpy(B.ptr, rho, ML_KEM_SEED_LEN);
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
for (j = 0; j < k; j++)
|
||||
{
|
||||
B.ptr[ML_KEM_SEED_LEN] = j;
|
||||
B.ptr[ML_KEM_SEED_LEN + 1] = i;
|
||||
if (!this->shake128->set_seed(this->shake128, B) ||
|
||||
!sample_ntt(this, &a[i*k + j]))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a seed via PRF_eta(s, N) = SHAKE256(s||N, 64 * eta) from the given
|
||||
* random 32-byte seed and nonce N and outputs a pseudorandom polynomial sampled
|
||||
* from the distribution D_eta(R_q) of polynomials in R_q with small
|
||||
* coefficients, with each coefficient sampled independently from a certain
|
||||
* centered binomial distribution (CBD) on Z_q.
|
||||
*
|
||||
* Algorithm 8 in FIPS 203.
|
||||
*/
|
||||
static void sample_poly_cbd(private_key_exchange_t *this, uint8_t eta,
|
||||
uint8_t *s, uint8_t N, ml_poly_t *p)
|
||||
{
|
||||
/* this uses an optimization from the reference implementation. since eta
|
||||
* can only take two values for the current parameter sets (it's actually 2
|
||||
* for all but ML-KEM-512 that uses 3 for eta1), we can optimize this and
|
||||
* either add together 16*2 or 8*3 bits concurrently and then split the
|
||||
* result into 8 or 4 coefficients, respectively */
|
||||
const int coeffs = (eta == 2) ? 8 : 4;
|
||||
const int fetch = 2 * coeffs * eta / 8;
|
||||
/* every second or third bit is set in these masks */
|
||||
const uint32_t add_mask = (eta == 2) ? 0x55555555 : 0x00249249;
|
||||
const uint32_t mask = (eta == 2) ? 0x3 : 0x7;
|
||||
const int len = 64 * eta;
|
||||
|
||||
chunk_t seed = chunk_alloca(ML_KEM_SEED_LEN+1);
|
||||
uint8_t sample_seed[len] = {};
|
||||
uint32_t t, b;
|
||||
uint16_t x, y;
|
||||
int i, j;
|
||||
|
||||
memcpy(seed.ptr, s, ML_KEM_SEED_LEN);
|
||||
seed.ptr[ML_KEM_SEED_LEN] = N;
|
||||
|
||||
ignore_result(this->shake256->set_seed(this->shake256, seed) &&
|
||||
this->shake256->get_bytes(this->shake256, len, sample_seed));
|
||||
|
||||
for (i = 0; i < ML_KEM_N/coeffs; i++)
|
||||
{
|
||||
/* get a bit stream from the seed */
|
||||
t = ml_read_bytes_le(sample_seed + fetch*i, fetch);
|
||||
|
||||
/* add together eta consecutive bits */
|
||||
b = t & add_mask;
|
||||
b += (t >> 1) & add_mask;
|
||||
if (eta == 3)
|
||||
{
|
||||
b += (t >> 2) & add_mask;
|
||||
}
|
||||
|
||||
for (j = 0; j < coeffs; j++)
|
||||
{
|
||||
x = (b >> (2*j*eta)) & mask;
|
||||
y = (b >> (2*j*eta + eta)) & mask;
|
||||
p->f[coeffs*i + j] = ml_reduce_modq(x - y + ML_KEM_Q);
|
||||
}
|
||||
}
|
||||
|
||||
memwipe(seed.ptr, seed.len);
|
||||
memwipe(sample_seed, sizeof(sample_seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the given two (reduced) numbers, followed by a Barrett reduction to
|
||||
* get a*b mod q so i.e. a*b - [a*b*m/R]*q (where m is [R/q], R is 2^40 and
|
||||
* rounded against zero). Note that m/2^40 is close enough to 1/q that no
|
||||
* fixup is necessary after the final subtraction.
|
||||
*/
|
||||
static uint16_t mul_modq(uint16_t a, uint16_t b)
|
||||
{
|
||||
const uint64_t m = (1ULL << 40) / ML_KEM_Q;
|
||||
const uint32_t prod = (uint32_t)a * b;
|
||||
|
||||
uint32_t t;
|
||||
|
||||
t = (prod * m) >> 40;
|
||||
return prod - t * ML_KEM_Q;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number-theoretic transform (NTT) representation of the given
|
||||
* polynomial p in place.
|
||||
*
|
||||
* Algorithm 9 in FIPS 203.
|
||||
*/
|
||||
static void ntt(private_key_exchange_t *this, ml_poly_t *p)
|
||||
{
|
||||
int len, start, i = 1, j;
|
||||
uint16_t zeta, t;
|
||||
|
||||
for (len = ML_KEM_N / 2; len >= 2; len /= 2)
|
||||
{
|
||||
for (start = 0; start < ML_KEM_N; start += 2 * len)
|
||||
{
|
||||
zeta = ml_kem_zetas[i++];
|
||||
for (j = start; j < start + len; j++)
|
||||
{
|
||||
t = mul_modq(zeta, p->f[j + len]);
|
||||
p->f[j + len] = ml_reduce_modq(p->f[j] - t + ML_KEM_Q);
|
||||
p->f[j] = ml_reduce_modq(p->f[j] + t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the polynomial p that corresponds to the given number-theoretic
|
||||
* transform (NTT) representation of it in place.
|
||||
*
|
||||
* Algorithm 10 in FIPS 203.
|
||||
*/
|
||||
static void ntt_inv(private_key_exchange_t *this, ml_poly_t *p)
|
||||
{
|
||||
const uint16_t f = 3303;
|
||||
|
||||
int len, start, i = 127, j;
|
||||
uint16_t zeta, t;
|
||||
|
||||
for (len = 2; len <= 128; len *= 2)
|
||||
{
|
||||
for (start = 0; start < ML_KEM_N; start += 2 * len)
|
||||
{
|
||||
zeta = ml_kem_zetas[i--];
|
||||
for (j = start; j < start + len; j++)
|
||||
{
|
||||
t = p->f[j];
|
||||
p->f[j] = ml_reduce_modq(t + p->f[j + len]);
|
||||
p->f[j + len] = mul_modq(zeta, p->f[j + len] - t + ML_KEM_Q);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ML_KEM_N; i++)
|
||||
{
|
||||
p->f[i] = mul_modq(p->f[i], f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the two degree-one polynomials a (= a[0] + a[1]*X) and b with
|
||||
* respect to a quadratic modulus (X^2-gamma) and put the result in c.
|
||||
*
|
||||
* Algorithm 12 in FIPS 203.
|
||||
*/
|
||||
static void base_case_multiply(uint16_t *a, uint16_t *b, uint16_t *c,
|
||||
uint16_t gamma)
|
||||
{
|
||||
c[0] = mul_modq(a[1], b[1]);
|
||||
c[0] = ml_reduce_modq(mul_modq(c[0], gamma) + mul_modq(a[0], b[0]));
|
||||
c[1] = ml_reduce_modq(mul_modq(a[0], b[1]) + mul_modq(a[1], b[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply two polynomials a and b in NTT domain and put them into c.
|
||||
*
|
||||
* Algorithm 11 in FIPS 203.
|
||||
*/
|
||||
static void multiply_poly(ml_poly_t *a, ml_poly_t *b, ml_poly_t *c)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* since the Zeta^(2BitRev_7(i)+1) mod q values required here can be found
|
||||
* in the second half of the table for Zeta^BitRev_7(i) mod q, we reuse them
|
||||
* and directly handle four coefficients per iteration instead of two */
|
||||
for (i = 0; i < ML_KEM_N/4; i++)
|
||||
{
|
||||
base_case_multiply(&a->f[4*i], &b->f[4*i], &c->f[4*i],
|
||||
ml_kem_zetas[i+64]);
|
||||
base_case_multiply(&a->f[4*i+2], &b->f[4*i+2], &c->f[4*i+2],
|
||||
ML_KEM_Q - ml_kem_zetas[i+64]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the k polynomials in a with those in b and accumulate them into c.
|
||||
*
|
||||
* If transposed is TRUE, a is assumed to be a k*k matrix where polynomials from
|
||||
* a column are used (not a sequential row).
|
||||
*
|
||||
* See the note regarding the result of multiply_poly().
|
||||
*/
|
||||
static void multiply_poly_arr(uint8_t k, ml_poly_t *a, ml_poly_t *b,
|
||||
ml_poly_t *c, bool transposed)
|
||||
{
|
||||
ml_poly_t t;
|
||||
int i, f = transposed ? k : 1;
|
||||
|
||||
multiply_poly(&a[0], &b[0], c);
|
||||
for (i = 1; i < k; i++)
|
||||
{
|
||||
multiply_poly(&a[i*f], &b[i], &t);
|
||||
ml_poly_add(c, &t, c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode k polynomials to a byte array (12-bit version that packs 2
|
||||
* coefficients into 3 bytes, not using ml_bitpacker_t for performance reasons).
|
||||
*
|
||||
* Algorithm 5 in FIPS 203.
|
||||
*/
|
||||
static void encode_poly_arr(uint8_t k, ml_poly_t *a, uint8_t *out)
|
||||
{
|
||||
uint16_t f0, f1;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
for (j = 0; j < ML_KEM_N / 2; j++)
|
||||
{
|
||||
f0 = a[i].f[2*j];
|
||||
f1 = a[i].f[2*j + 1];
|
||||
out[3*j] = (uint8_t)f0;
|
||||
out[3*j+1] = (uint8_t)((f0 >> 8) | (f1 << 4));
|
||||
out[3*j+2] = (uint8_t)(f1 >> 4);
|
||||
}
|
||||
out += ML_KEM_POLY_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode k polynomials from a byte array (12-bit version that unpacks 2
|
||||
* coefficients from 3 bytes, not using ml_bitpacker_t for performance reasons).
|
||||
*
|
||||
* Algorithm 6 in FIPS 203.
|
||||
*/
|
||||
static void decode_poly_arr(uint8_t k, uint8_t *in, ml_poly_t *a)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
for (j = 0; j < ML_KEM_N / 2; j++)
|
||||
{
|
||||
a[i].f[2*j] = (in[3*j] | ((uint16_t)in[3*j+1] << 8)) & 0xfff;
|
||||
a[i].f[2*j+1] = ((in[3*j+1] >> 4) | ((uint16_t)in[3*j+2] << 4)) & 0xfff;
|
||||
}
|
||||
in += ML_KEM_POLY_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress the k 12-bit polynomials in a to d bits and encode the result as
|
||||
* bytes in out.
|
||||
*/
|
||||
static void compress_polys_arr(uint8_t k, uint8_t d, ml_poly_t *a, uint8_t *out)
|
||||
{
|
||||
/* avoid division by replacing 2^d/q with [m/2^p] where m is [2^(p+d)/q] */
|
||||
const int p = 63 - d;
|
||||
const uint64_t m = ((1ULL << (p+d)) + ML_KEM_Q/2) / ML_KEM_Q;
|
||||
const uint64_t mask = (1 << d) - 1;
|
||||
|
||||
ml_bitpacker_t *packer;
|
||||
uint64_t f;
|
||||
int i, j;
|
||||
|
||||
packer = ml_bitpacker_create(chunk_create(out, k * d * ML_KEM_N / 8));
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
for (j = 0; j < ML_KEM_N; j++)
|
||||
{
|
||||
f = a[i].f[j];
|
||||
/* calculate the compression [f * 2^d/q mod 2^d] without division */
|
||||
f = ((f * m + (1ULL << (p - 1))) >> p) & mask;
|
||||
packer->write_bits(packer, f, d);
|
||||
}
|
||||
}
|
||||
packer->destroy(packer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress the k 12-bit polynomials in a from a stream of d-bit chunks.
|
||||
*/
|
||||
static void decompress_poly_arr(uint8_t k, uint8_t d, uint8_t *in, ml_poly_t *a)
|
||||
{
|
||||
const uint16_t rounding = 1 << (d - 1);
|
||||
|
||||
ml_bitpacker_t *packer;
|
||||
uint32_t f;
|
||||
int i, j;
|
||||
|
||||
packer = ml_bitpacker_create_from_data(chunk_create(in, k * d * ML_KEM_N / 8));
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
for (j = 0; j < ML_KEM_N; j++)
|
||||
{
|
||||
/* calculate the decompression [f * q / 2^d] */
|
||||
packer->read_bits(packer, &f, d);
|
||||
a[i].f[j] = (f * ML_KEM_Q + rounding) >> d;
|
||||
}
|
||||
}
|
||||
packer->destroy(packer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates Decompress_1(ByteDecode_1()) for the given message m of length
|
||||
* ML_KEM_SEED_LEN and puts the result into p.
|
||||
*/
|
||||
static void message_to_poly(uint8_t *m, ml_poly_t *p)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ML_KEM_SEED_LEN; i++)
|
||||
{
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
/* can't use
|
||||
* p->f[8 * i + j] = (ML_KEM_Q + 1) / 2 * (m[i] >> j & 0x1);
|
||||
* or some manual masking here because after recognizing that this
|
||||
* is either 0 or a constant, some versions of clang apparently
|
||||
* optimize this with a branching instruction to just skip over the
|
||||
* assignment, creating a possible side-channel */
|
||||
p->f[8 * i + j] = 0;
|
||||
ml_assign_cond_int16(&p->f[8 * i + j], (ML_KEM_Q + 1) / 2,
|
||||
(m[i] >> j) & 0x1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates ByteEncode_1(Compress_1()) for the given polynomial p to decode
|
||||
* message m of length ML_KEM_SEED_LEN.
|
||||
*/
|
||||
static void poly_to_message(ml_poly_t *p, uint8_t *m)
|
||||
{
|
||||
/* avoid division by replacing 2/q with [n/2^k] where n is [2^(k+1)/q] */
|
||||
const int k = 30;
|
||||
const uint32_t n = ((1 << (k+1)) + ML_KEM_Q/2) / ML_KEM_Q;
|
||||
|
||||
uint32_t f;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ML_KEM_SEED_LEN; i++)
|
||||
{
|
||||
m[i] = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
f = p->f[8 * i + j];
|
||||
/* calculate the compression [f * 2/q mod 2] without division */
|
||||
f = ((f * n + (1 << (k-1))) >> k) & 0x1;
|
||||
m[i] |= f << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key pair from the given random seed d.
|
||||
*
|
||||
* Algorithm 13 in FIPS 203.
|
||||
*/
|
||||
static bool pke_keygen(private_key_exchange_t *this, chunk_t d, chunk_t *ek,
|
||||
chunk_t *dk)
|
||||
{
|
||||
const uint8_t k = this->params->k;
|
||||
const uint8_t eta1 = this->params->eta1;
|
||||
|
||||
uint8_t seeds[2 * ML_KEM_SEED_LEN];
|
||||
uint8_t *rho = seeds;
|
||||
uint8_t *sigma = seeds + ML_KEM_SEED_LEN;
|
||||
uint8_t N = 0;
|
||||
ml_poly_t a[k*k], s[k], e[k], t[k];
|
||||
int i;
|
||||
bool success = FALSE;
|
||||
|
||||
/* derive seeds for private and public key from randomness d and domain
|
||||
* parameter k */
|
||||
if (!this->G->get_hash(this->G, d, NULL) ||
|
||||
!this->G->get_hash(this->G, chunk_from_thing(k), seeds))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* generate matrix A */
|
||||
if (!generate_a(this, a, rho))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* sample s from CBD using noise seed sigma and nonce N as input */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
sample_poly_cbd(this, eta1, sigma, N++, &s[i]);
|
||||
}
|
||||
|
||||
/* sample e from CBD using noise seed sigma and nonce N as input */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
sample_poly_cbd(this, eta1, sigma, N++, &e[i]);
|
||||
}
|
||||
|
||||
/* calculate s = NTT(s) */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
ntt(this, &s[i]);
|
||||
}
|
||||
|
||||
/* calculate e = NTT(e) */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
ntt(this, &e[i]);
|
||||
}
|
||||
|
||||
/* calculate t = A * s + e to get the public key */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
multiply_poly_arr(k, &a[i*k], s, &t[i], FALSE);
|
||||
}
|
||||
ml_poly_add_arr(k, t, e, t);
|
||||
|
||||
/* pack public key and rho */
|
||||
*ek = chunk_alloc(k * ML_KEM_POLY_LEN + ML_KEM_SEED_LEN);
|
||||
encode_poly_arr(k, t, ek->ptr);
|
||||
memcpy(ek->ptr + k * ML_KEM_POLY_LEN, rho, ML_KEM_SEED_LEN);
|
||||
|
||||
/* pack private key */
|
||||
*dk = chunk_alloc(k * ML_KEM_POLY_LEN);
|
||||
encode_poly_arr(k, s, dk->ptr);
|
||||
|
||||
success = TRUE;
|
||||
|
||||
err:
|
||||
memwipe(seeds, sizeof(seeds));
|
||||
memwipe(sigma, ML_KEM_SEED_LEN);
|
||||
memwipe(s, sizeof(s));
|
||||
memwipe(e, sizeof(e));
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt randomness m using the given public key and randomness r that's
|
||||
* derived from both.
|
||||
*
|
||||
* Algorithm 14 in FIPS 203.
|
||||
*/
|
||||
static bool pke_encrypt(private_key_exchange_t *this, chunk_t ek, uint8_t *m,
|
||||
uint8_t *r, chunk_t ciphertext)
|
||||
{
|
||||
const uint8_t k = this->params->k;
|
||||
const uint8_t eta1 = this->params->eta1;
|
||||
const uint8_t eta2 = this->params->eta2;
|
||||
const uint8_t du = this->params->du;
|
||||
const uint8_t dv = this->params->dv;
|
||||
|
||||
uint8_t rho[ML_KEM_SEED_LEN];
|
||||
uint8_t N = 0;
|
||||
ml_poly_t a[k*k], t[k], y[k], e1[k], e2, u[k], mu, v;
|
||||
int i;
|
||||
bool success = FALSE;
|
||||
|
||||
/* decode polynomial t and extract seed rho from the public key */
|
||||
decode_poly_arr(k, ek.ptr, t);
|
||||
memcpy(rho, ek.ptr + k * ML_KEM_POLY_LEN, ML_KEM_SEED_LEN);
|
||||
|
||||
/* generate matrix A */
|
||||
if (!generate_a(this, a, rho))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* sample y from CBD using noise seed r and nonce N as input */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
sample_poly_cbd(this, eta1, r, N++, &y[i]);
|
||||
}
|
||||
|
||||
/* sample e_1 from CBD using noise seed r and nonce N as input */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
sample_poly_cbd(this, eta2, r, N++, &e1[i]);
|
||||
}
|
||||
|
||||
/* sample e_2 from CBD using noise seed r and nonce N as input */
|
||||
sample_poly_cbd(this, eta2, r, N++, &e2);
|
||||
|
||||
/* calculate y = NTT(y) */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
ntt(this, &y[i]);
|
||||
}
|
||||
|
||||
/* calculate u = NTT^-1(A^T * y) + e_1 */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
multiply_poly_arr(k, &a[i], y, &u[i], TRUE);
|
||||
}
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
ntt_inv(this, &u[i]);
|
||||
}
|
||||
ml_poly_add_arr(k, u, e1, u);
|
||||
|
||||
/* prepare plaintext message m */
|
||||
message_to_poly(m, &mu);
|
||||
|
||||
/* calculate v = NTT^-1(t^T * y) + e_2 + mu to encrypt the plaintext */
|
||||
multiply_poly_arr(k, t, y, &v, FALSE);
|
||||
ntt_inv(this, &v);
|
||||
ml_poly_add(&v, &e2, &v);
|
||||
ml_poly_add(&v, &mu, &v);
|
||||
|
||||
/* encode u as c1 and v as c2, the two parts of the ciphertext */
|
||||
compress_polys_arr(k, du, u, ciphertext.ptr);
|
||||
compress_polys_arr(1, dv, &v, ciphertext.ptr + k * du * ML_KEM_N / 8);
|
||||
success = TRUE;
|
||||
|
||||
err:
|
||||
memwipe(y, sizeof(y));
|
||||
memwipe(e1, sizeof(e1));
|
||||
memwipe(&e2, sizeof(e2));
|
||||
memwipe(&mu, sizeof(mu));
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt message m using the given private key and ciphertext.
|
||||
*
|
||||
* Algorithm 14 in FIPS 203.
|
||||
*/
|
||||
static bool pke_decrypt(private_key_exchange_t *this, chunk_t dk,
|
||||
chunk_t ciphertext, uint8_t *m)
|
||||
{
|
||||
const uint8_t k = this->params->k;
|
||||
const uint8_t du = this->params->du;
|
||||
const uint8_t dv = this->params->dv;
|
||||
|
||||
ml_poly_t s[k], u[k], v, w;
|
||||
int i;
|
||||
|
||||
/* decode u and v from c1 and c2, the two parts of the ciphertext */
|
||||
decompress_poly_arr(k, du, ciphertext.ptr, u);
|
||||
decompress_poly_arr(1, dv, ciphertext.ptr + k * du * ML_KEM_N / 8, &v);
|
||||
|
||||
/* decode polynomial s from private key */
|
||||
decode_poly_arr(k, dk.ptr, s);
|
||||
|
||||
/* calculate w = v - NTT^-1(s * NTT(u)) */
|
||||
for (i = 0; i < k; i++)
|
||||
{
|
||||
ntt(this, &u[i]);
|
||||
}
|
||||
multiply_poly_arr(k, s, u, &w, FALSE);
|
||||
ntt_inv(this, &w);
|
||||
ml_poly_sub(&v, &w, &w);
|
||||
|
||||
/* decode plaintext message m from polynomial w */
|
||||
poly_to_message(&w, m);
|
||||
|
||||
memwipe(s, sizeof(s));
|
||||
memwipe(&w, sizeof(w));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random seeds and generate a key pair.
|
||||
*
|
||||
* Algorithm 16/19 in FIPS 203.
|
||||
*/
|
||||
static bool generate_keypair(private_key_exchange_t *this, chunk_t *ek)
|
||||
{
|
||||
uint8_t dz[2*ML_KEM_SEED_LEN];
|
||||
chunk_t d = chunk_create(dz, ML_KEM_SEED_LEN);
|
||||
chunk_t z = chunk_create(dz + ML_KEM_SEED_LEN, ML_KEM_SEED_LEN);
|
||||
chunk_t dk = chunk_empty, Hek;
|
||||
bool success = FALSE;
|
||||
|
||||
/* get random seeds d and z */
|
||||
if (!get_random(this, sizeof(dz), dz))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* generate a key pair and store the private key, the public key, a hash
|
||||
* of the latter and seed z as our secret key */
|
||||
if (pke_keygen(this, d, ek, &dk) &&
|
||||
this->H->allocate_hash(this->H, *ek, &Hek))
|
||||
{
|
||||
this->private_key = chunk_cat("ccmc", dk, *ek, Hek, z);
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
memwipe(dz, sizeof(dz));
|
||||
chunk_clear(&dk);
|
||||
return success;
|
||||
}
|
||||
|
||||
METHOD(key_exchange_t, get_public_key, bool,
|
||||
private_key_exchange_t *this, chunk_t *value)
|
||||
{
|
||||
/* as responder, this method is called after set_public_key(), which
|
||||
* encapsulated the secret to produce this ciphertext */
|
||||
if (this->ciphertext.ptr)
|
||||
{
|
||||
*value = chunk_clone(this->ciphertext);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* as initiator, we generate a key pair and return the public key */
|
||||
return generate_keypair(this, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decapsulate a generated shared secret from the given ciphertext using our
|
||||
* private key.
|
||||
*
|
||||
* Algorithm 18 in FIPS 203.
|
||||
*/
|
||||
static bool decaps_shared_secret(private_key_exchange_t *this, chunk_t ciphertext)
|
||||
{
|
||||
const uint8_t k = this->params->k;
|
||||
|
||||
chunk_t dk, ek, Hek, z, zc, c = chunk_empty;
|
||||
chunk_t m = chunk_alloca(ML_KEM_SEED_LEN);
|
||||
uint8_t Kr[2*ML_KEM_SEED_LEN];
|
||||
uint8_t *r = Kr + ML_KEM_SEED_LEN;
|
||||
bool success = FALSE;
|
||||
|
||||
/* get the private and public keys, a hash of the latter and seed z */
|
||||
chunk_split(this->private_key, "mmmm",
|
||||
k * ML_KEM_POLY_LEN, &dk,
|
||||
k * ML_KEM_POLY_LEN + ML_KEM_SEED_LEN, &ek,
|
||||
ML_KEM_SEED_LEN, &Hek,
|
||||
ML_KEM_SEED_LEN, &z);
|
||||
/* prepare the seed to derive the implicit rejection secret */
|
||||
zc = chunk_cat("cc", z, ciphertext);
|
||||
|
||||
/* decrypt message m */
|
||||
if (!pke_decrypt(this, dk, ciphertext, m.ptr))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* calculate (K, r) = G(m||H(ek)) */
|
||||
if (!this->G->get_hash(this->G, m, NULL) ||
|
||||
!this->G->get_hash(this->G, Hek, Kr))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* encrypt the decrypted message again using the derived r */
|
||||
c = chunk_alloc(this->params->ct_len);
|
||||
if (!pke_encrypt(this, ek, m.ptr, r, c))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
this->shared_secret = chunk_alloc(ML_KEM_SEED_LEN);
|
||||
|
||||
/* calculate the rejection value K_rej = J(z||c) as fallback */
|
||||
if (!this->shake256->set_seed(this->shake256, zc) ||
|
||||
!this->shake256->get_bytes(this->shake256, this->shared_secret.len,
|
||||
this->shared_secret.ptr))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
/* replace the shared secret with K based on whether our own ciphertext
|
||||
* matches what we received (in constant time) */
|
||||
memcpy_cond(this->shared_secret.ptr, Kr, this->shared_secret.len,
|
||||
chunk_equals_const(ciphertext, c));
|
||||
|
||||
success = TRUE;
|
||||
|
||||
err:
|
||||
memwipe(m.ptr, m.len);
|
||||
memwipe(Kr, sizeof(Kr));
|
||||
chunk_clear(&zc);
|
||||
chunk_free(&c);
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulate a generated shared secret using the given public key.
|
||||
*
|
||||
* Algorithm 17 in FIPS 203.
|
||||
*/
|
||||
static bool encaps_shared_secret(private_key_exchange_t *this, chunk_t public)
|
||||
{
|
||||
chunk_t mH = chunk_alloca(2*ML_KEM_SEED_LEN);
|
||||
uint8_t Kr[2*ML_KEM_SEED_LEN];
|
||||
uint8_t *r = Kr + ML_KEM_SEED_LEN;
|
||||
bool success = FALSE;
|
||||
|
||||
/* get a random message and calculate (K, r) = G(m||H(ek)) */
|
||||
if (!get_random(this, ML_KEM_SEED_LEN, mH.ptr) ||
|
||||
!this->H->get_hash(this->H, public, mH.ptr + ML_KEM_SEED_LEN) ||
|
||||
!this->G->get_hash(this->G, mH, Kr))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* encrypt the message using the derived r */
|
||||
this->ciphertext = chunk_alloc(this->params->ct_len);
|
||||
if (pke_encrypt(this, public, mH.ptr, r, this->ciphertext))
|
||||
{
|
||||
this->shared_secret = chunk_clone(chunk_create(Kr, ML_KEM_SEED_LEN));
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
err:
|
||||
memwipe(mH.ptr, ML_KEM_SEED_LEN);
|
||||
memwipe(Kr, sizeof(Kr));
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a modulus check as required by section 7.2 of FIPS 203.
|
||||
*/
|
||||
static bool validate_public_key(private_key_exchange_t *this, chunk_t public)
|
||||
{
|
||||
const uint8_t k = this->params->k;
|
||||
|
||||
ml_poly_t p[k];
|
||||
uint8_t ek[k * ML_KEM_POLY_LEN];
|
||||
|
||||
decode_poly_arr(k, public.ptr, p);
|
||||
encode_poly_arr(k, p, ek);
|
||||
return memeq_const(public.ptr, ek, sizeof(ek));
|
||||
}
|
||||
|
||||
METHOD(key_exchange_t, set_public_key, bool,
|
||||
private_key_exchange_t *this, chunk_t value)
|
||||
{
|
||||
/* as initiator, we decapsulate the secret from the given ciphertext */
|
||||
if (this->private_key.ptr)
|
||||
{
|
||||
if (value.len != this->params->ct_len)
|
||||
{
|
||||
DBG1(DBG_LIB, "wrong %N ciphertext size of %u bytes, %u bytes expected",
|
||||
key_exchange_method_names, this->method, value.len,
|
||||
this->params->ct_len);
|
||||
return FALSE;
|
||||
}
|
||||
return decaps_shared_secret(this, value);
|
||||
}
|
||||
|
||||
/* as responder, we generate a secret and encapsulate it */
|
||||
if (value.len != this->params->pk_len)
|
||||
{
|
||||
DBG1(DBG_LIB, "wrong %N public key size of %u bytes, %u bytes expected",
|
||||
key_exchange_method_names, this->method, value.len,
|
||||
this->params->pk_len);
|
||||
return FALSE;
|
||||
}
|
||||
else if (!validate_public_key(this, value))
|
||||
{
|
||||
DBG1(DBG_LIB, "%N public key encoding invalid",
|
||||
key_exchange_method_names, this->method);
|
||||
return FALSE;
|
||||
}
|
||||
return encaps_shared_secret(this, value);
|
||||
}
|
||||
|
||||
METHOD(key_exchange_t, get_method, key_exchange_method_t,
|
||||
private_key_exchange_t *this)
|
||||
{
|
||||
return this->method;
|
||||
}
|
||||
|
||||
METHOD(key_exchange_t, get_shared_secret, bool,
|
||||
private_key_exchange_t *this, chunk_t *secret)
|
||||
{
|
||||
*secret = chunk_clone(this->shared_secret);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(key_exchange_t, set_seed, bool,
|
||||
private_key_exchange_t *this, chunk_t value, drbg_t *drbg)
|
||||
{
|
||||
DESTROY_IF(this->drbg);
|
||||
this->drbg = drbg->get_ref(drbg);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(key_exchange_t, destroy, void,
|
||||
private_key_exchange_t *this)
|
||||
{
|
||||
chunk_clear(&this->shared_secret);
|
||||
chunk_clear(&this->private_key);
|
||||
chunk_free(&this->ciphertext);
|
||||
DESTROY_IF(this->drbg);
|
||||
DESTROY_IF(this->shake128);
|
||||
DESTROY_IF(this->shake256);
|
||||
DESTROY_IF(this->G);
|
||||
DESTROY_IF(this->H);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
key_exchange_t *ml_kem_create(key_exchange_method_t method)
|
||||
{
|
||||
private_key_exchange_t *this;
|
||||
const ml_kem_params_t *params;
|
||||
|
||||
params = ml_kem_params_get(method);
|
||||
if (!params)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_method = _get_method,
|
||||
.get_public_key = _get_public_key,
|
||||
.set_public_key = _set_public_key,
|
||||
.get_shared_secret = _get_shared_secret,
|
||||
.set_seed = _set_seed,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.method = method,
|
||||
.params = params,
|
||||
.shake128 = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_128),
|
||||
.shake256 = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_256),
|
||||
.G = lib->crypto->create_hasher(lib->crypto, HASH_SHA3_512),
|
||||
.H = lib->crypto->create_hasher(lib->crypto, HASH_SHA3_256),
|
||||
);
|
||||
|
||||
if (!this->shake128 || !this->shake256 || !this->G || !this->H)
|
||||
{
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Quantum-safe key encapsulation implementation using ML-KEM.
|
||||
*
|
||||
* @defgroup ml_kem ml_kem
|
||||
* @{ @ingroup ml_p
|
||||
*/
|
||||
|
||||
#ifndef ML_KEM_H_
|
||||
#define ML_KEM_H_
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* Creates a new key_exchange_t object.
|
||||
*
|
||||
* @param method key exchange method
|
||||
* @return key_exchange_t object, NULL if not supported
|
||||
*/
|
||||
key_exchange_t *ml_kem_create(key_exchange_method_t method);
|
||||
|
||||
#endif /** ML_KEM_H_ @}*/
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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 "ml_params.h"
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
const uint16_t ml_kem_zetas[128] = {
|
||||
1, 1729, 2580, 3289, 2642, 630, 1897, 848,
|
||||
1062, 1919, 193, 797, 2786, 3260, 569, 1746,
|
||||
296, 2447, 1339, 1476, 3046, 56, 2240, 1333,
|
||||
1426, 2094, 535, 2882, 2393, 2879, 1974, 821,
|
||||
289, 331, 3253, 1756, 1197, 2304, 2277, 2055,
|
||||
650, 1977, 2513, 632, 2865, 33, 1320, 1915,
|
||||
2319, 1435, 807, 452, 1438, 2868, 1534, 2402,
|
||||
2647, 2617, 1481, 648, 2474, 3110, 1227, 910,
|
||||
17, 2761, 583, 2649, 1637, 723, 2288, 1100,
|
||||
1409, 2662, 3281, 233, 756, 2156, 3015, 3050,
|
||||
1703, 1651, 2789, 1789, 1847, 952, 1461, 2687,
|
||||
939, 2308, 2437, 2388, 733, 2337, 268, 641,
|
||||
1584, 2298, 2037, 3220, 375, 2549, 2090, 1645,
|
||||
1063, 319, 2773, 757, 2099, 561, 2466, 2594,
|
||||
2804, 1092, 403, 1026, 1143, 2150, 2775, 886,
|
||||
1722, 1212, 1874, 1029, 2110, 2935, 885, 2154,
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameter sets for ML-KEM.
|
||||
*/
|
||||
static const ml_kem_params_t ml_kem_params[] = {
|
||||
{
|
||||
.method = ML_KEM_512,
|
||||
.k = 2,
|
||||
.eta1 = 3,
|
||||
.eta2 = 2,
|
||||
.du = 10,
|
||||
.dv = 4,
|
||||
.pk_len = 800,
|
||||
.ct_len = 768,
|
||||
},
|
||||
{
|
||||
.method = ML_KEM_768,
|
||||
.k = 3,
|
||||
.eta1 = 2,
|
||||
.eta2 = 2,
|
||||
.du = 10,
|
||||
.dv = 4,
|
||||
.pk_len = 1184,
|
||||
.ct_len = 1088,
|
||||
},
|
||||
{
|
||||
.method = ML_KEM_1024,
|
||||
.k = 4,
|
||||
.eta1 = 2,
|
||||
.eta2 = 2,
|
||||
.du = 11,
|
||||
.dv = 5,
|
||||
.pk_len = 1568,
|
||||
.ct_len = 1568,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
const ml_kem_params_t *ml_kem_params_get(key_exchange_method_t method)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < countof(ml_kem_params); i++)
|
||||
{
|
||||
if (ml_kem_params[i].method == method)
|
||||
{
|
||||
return &ml_kem_params[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ml_params ml_params
|
||||
* @{ @ingroup ml_p
|
||||
*/
|
||||
|
||||
#ifndef ML_PARAMS_H_
|
||||
#define ML_PARAMS_H_
|
||||
|
||||
#include <crypto/key_exchange.h>
|
||||
|
||||
typedef struct ml_kem_params_t ml_kem_params_t;
|
||||
|
||||
/**
|
||||
* Constant n used throughout the algorithms.
|
||||
*/
|
||||
#define ML_KEM_N 256
|
||||
|
||||
/**
|
||||
* The prime q = 2^8 * 13 + 1.
|
||||
*/
|
||||
#define ML_KEM_Q 3329
|
||||
|
||||
/**
|
||||
* Length of the seeds and hashes as well as the shared secret.
|
||||
*/
|
||||
#define ML_KEM_SEED_LEN 32
|
||||
|
||||
/**
|
||||
* Length of an enoded polynomial (used for the public key).
|
||||
*/
|
||||
#define ML_KEM_POLY_LEN 384
|
||||
|
||||
/**
|
||||
* Parameters for ML-KEM.
|
||||
*/
|
||||
struct ml_kem_params_t {
|
||||
|
||||
/**
|
||||
* Key exchange method.
|
||||
*/
|
||||
const key_exchange_method_t method;
|
||||
|
||||
/**
|
||||
* Module dimension k.
|
||||
*/
|
||||
const uint8_t k;
|
||||
|
||||
/**
|
||||
* Factor eta_1 for generating s, e and y.
|
||||
*/
|
||||
const uint8_t eta1;
|
||||
|
||||
/**
|
||||
* Factor eta_2 for generating e1 and e2.
|
||||
*/
|
||||
const uint8_t eta2;
|
||||
|
||||
/**
|
||||
* Parameter du for compression/encoding.
|
||||
*/
|
||||
const uint8_t du;
|
||||
|
||||
/**
|
||||
* Parameter dv for compression/encoding.
|
||||
*/
|
||||
const uint8_t dv;
|
||||
|
||||
/**
|
||||
* Length of the public key (k * ML_KEM_POLY_LEN + ML_KEM_SEED_LEN).
|
||||
*/
|
||||
const uint16_t pk_len;
|
||||
|
||||
/**
|
||||
* Length of the ciphertext ((ML_KEM_N * (k * du + kv)) / 8).
|
||||
*/
|
||||
const uint16_t ct_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Precalculated Zeta^BitRev_7(i) mod q values for NTT (see Appendix A in
|
||||
* FIPS 203). The second half is also used as Zeta^(2*BitRev_7(i)+1) mod q
|
||||
* values.
|
||||
*/
|
||||
extern const uint16_t ml_kem_zetas[128];
|
||||
|
||||
/**
|
||||
* Get parameters fro a specific ML-KEM method.
|
||||
*
|
||||
* @param method key exchange method
|
||||
* @return parameters, NULL if not supported
|
||||
*/
|
||||
const ml_kem_params_t *ml_kem_params_get(key_exchange_method_t method);
|
||||
|
||||
#endif /** ML_PARAMS_H_ @}*/
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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 "ml_plugin.h"
|
||||
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
#include "ml_kem.h"
|
||||
|
||||
typedef struct private_plugin_t private_plugin_t;
|
||||
|
||||
/**
|
||||
* Private data.
|
||||
*/
|
||||
struct private_plugin_t {
|
||||
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
plugin_t public;
|
||||
};
|
||||
|
||||
METHOD(plugin_t, get_name, char*,
|
||||
private_plugin_t *this)
|
||||
{
|
||||
return "ml";
|
||||
}
|
||||
|
||||
METHOD(plugin_t, get_features, int,
|
||||
private_plugin_t *this, plugin_feature_t *features[])
|
||||
{
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_REGISTER(KE, ml_kem_create),
|
||||
PLUGIN_PROVIDE(KE, ML_KEM_512),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA3_256),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA3_512),
|
||||
PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
|
||||
PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
|
||||
PLUGIN_DEPENDS(RNG, RNG_STRONG),
|
||||
PLUGIN_PROVIDE(KE, ML_KEM_768),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA3_256),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA3_512),
|
||||
PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
|
||||
PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
|
||||
PLUGIN_DEPENDS(RNG, RNG_STRONG),
|
||||
PLUGIN_PROVIDE(KE, ML_KEM_1024),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA3_256),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA3_512),
|
||||
PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
|
||||
PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
|
||||
PLUGIN_DEPENDS(RNG, RNG_STRONG),
|
||||
};
|
||||
*features = f;
|
||||
return countof(f);
|
||||
}
|
||||
|
||||
METHOD(plugin_t, destroy, void,
|
||||
private_plugin_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
plugin_t *ml_plugin_create()
|
||||
{
|
||||
private_plugin_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_name = _get_name,
|
||||
.get_features = _get_features,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plugin implementing Module-Lattice-based algorithms like ML-KEM in software.
|
||||
*
|
||||
* @defgroup ml_p ml
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @defgroup ml_plugin ml_plugin
|
||||
* @{ @ingroup ml_p
|
||||
*/
|
||||
|
||||
#ifndef ML_PLUGIN_H_
|
||||
#define ML_PLUGIN_H_
|
||||
|
||||
#endif /** ML_PLUGIN_H_ @}*/
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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 "ml_poly.h"
|
||||
#include "ml_utils.h"
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void ml_poly_add(ml_poly_t *a, ml_poly_t *b, ml_poly_t *res)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ML_KEM_N; i++)
|
||||
{
|
||||
res->f[i] = ml_reduce_modq(a->f[i] + b->f[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void ml_poly_add_arr(u_int k, ml_poly_t *a, ml_poly_t *b, ml_poly_t *res)
|
||||
{
|
||||
while (k--)
|
||||
{
|
||||
ml_poly_add(&a[k], &b[k], &res[k]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void ml_poly_sub(ml_poly_t *a, ml_poly_t *b, ml_poly_t *res)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ML_KEM_N; i++)
|
||||
{
|
||||
res->f[i] = ml_reduce_modq(a->f[i] - b->f[i] + ML_KEM_Q);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ml_poly ml_poly
|
||||
* @{ @ingroup ml_p
|
||||
*/
|
||||
|
||||
#ifndef ML_POLY_H_
|
||||
#define ML_POLY_H_
|
||||
|
||||
#include "ml_params.h"
|
||||
|
||||
typedef struct ml_poly_t ml_poly_t;
|
||||
|
||||
/**
|
||||
* Represents an element in R_q = Z_q[X]/(X^n + 1) i.e. a polynomial of the
|
||||
* form f[0] + f[1]*X + ... + f[n-1]*X^n-1.
|
||||
*/
|
||||
struct ml_poly_t {
|
||||
|
||||
/**
|
||||
* Coefficients of the polynomial.
|
||||
*/
|
||||
uint16_t f[ML_KEM_N];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add two polynomials (a + b mod q).
|
||||
*
|
||||
* @param a polynomial a
|
||||
* @param b polynomial b
|
||||
* @param res result of adding a and b (can be one of the others)
|
||||
*/
|
||||
void ml_poly_add(ml_poly_t *a, ml_poly_t *b, ml_poly_t *res);
|
||||
|
||||
/**
|
||||
* Add polynomials in array a and b (a[i] + b[i] mod q for i in 0 to k-1).
|
||||
*
|
||||
* @param k number of polynomials in each array
|
||||
* @param a array of polynomials a
|
||||
* @param b array of polynomials b
|
||||
* @param res array of resulting polynomials (can be one of the others)
|
||||
*/
|
||||
void ml_poly_add_arr(u_int k, ml_poly_t *a, ml_poly_t *b, ml_poly_t *res);
|
||||
|
||||
/**
|
||||
* Subtract a polynomial from another (a - b mod q).
|
||||
*
|
||||
* @param a polynomial a
|
||||
* @param b polynomial b
|
||||
* @param res result of subtracting b from a (can be one of the others)
|
||||
*/
|
||||
void ml_poly_sub(ml_poly_t *a, ml_poly_t *b, ml_poly_t *res);
|
||||
|
||||
#endif /** ML_POLY_H_ @}*/
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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 "ml_utils.h"
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void ml_assign_cond_int16(int16_t *dst, int16_t val, uint16_t cond)
|
||||
{
|
||||
cond = -cond;
|
||||
*dst ^= cond & (val ^ *dst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
uint32_t ml_read_bytes_le(uint8_t *buf, size_t len)
|
||||
{
|
||||
uint32_t x = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
x |= (uint32_t)buf[i] << (8 * i);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void ml_write_bytes_le(uint8_t *buf, size_t len, uint32_t val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
buf[i] = val;
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Tobias Brunner
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ml_utils ml_utils
|
||||
* @{ @ingroup ml_p
|
||||
*/
|
||||
|
||||
#ifndef ML_UTILS_H_
|
||||
#define ML_UTILS_H_
|
||||
|
||||
#include "ml_params.h"
|
||||
|
||||
/**
|
||||
* Returns a mod q for a in [0,2*q) in constant time.
|
||||
*
|
||||
* @param a value to reduce mod q
|
||||
* @return reduced value
|
||||
*/
|
||||
static inline uint16_t ml_reduce_modq(uint16_t a)
|
||||
{
|
||||
const uint16_t diff = a - ML_KEM_Q;
|
||||
uint16_t mask = 0 - (diff >> 15);
|
||||
|
||||
return (mask & a) | (~mask & diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to assign the given value based on a condition in constant time and
|
||||
* without branching.
|
||||
*
|
||||
* @param dst the value to set
|
||||
* @param val value to set if condition is 1
|
||||
* @param cond 1 to set the value, 0 to leave as is
|
||||
*/
|
||||
void ml_assign_cond_int16(int16_t *dst, int16_t val, uint16_t cond);
|
||||
|
||||
/**
|
||||
* Read up to four bytes in little-endian order from the given buffer.
|
||||
*
|
||||
* @param buf byte buffer to read from
|
||||
* @param len length betweeen 0 and 4
|
||||
* @return read value
|
||||
*/
|
||||
uint32_t ml_read_bytes_le(uint8_t *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Write up to four bytes of the given value in little-endian order to a buffer.
|
||||
*
|
||||
* @param buf byte buffer to write to
|
||||
* @param len length betweeen 0 and 4
|
||||
* @param val value to write
|
||||
*/
|
||||
void ml_write_bytes_le(uint8_t *buf, size_t len, uint32_t val);
|
||||
|
||||
#endif /** ML_UTILS_H_ @}*/
|
||||
Reference in New Issue
Block a user