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_ENABL_SET([md4], [enable MD4 software implementation plugin.])
|
||||||
ARG_DISBL_SET([md5], [disable MD5 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([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_DISBL_SET([nonce], [disable nonce generation plugin.])
|
||||||
ARG_ENABL_SET([openssl], [enables the OpenSSL crypto plugin.])
|
ARG_ENABL_SET([openssl], [enables the OpenSSL crypto plugin.])
|
||||||
ARG_ENABL_SET([wolfssl], [enables the wolfSSL 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([ctr], [s charon scripts nm cmd])
|
||||||
ADD_PLUGIN([ccm], [s charon scripts nm cmd])
|
ADD_PLUGIN([ccm], [s charon scripts nm cmd])
|
||||||
ADD_PLUGIN([gcm], [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([drbg], [s charon pki scripts nm cmd])
|
||||||
ADD_PLUGIN([curl], [s charon pki scripts nm cmd])
|
ADD_PLUGIN([curl], [s charon pki scripts nm cmd])
|
||||||
ADD_PLUGIN([files], [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_GCM, test x$gcm = xtrue)
|
||||||
AM_CONDITIONAL(USE_AF_ALG, test x$af_alg = xtrue)
|
AM_CONDITIONAL(USE_AF_ALG, test x$af_alg = xtrue)
|
||||||
AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue)
|
AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue)
|
||||||
|
AM_CONDITIONAL(USE_ML, test x$ml = xtrue)
|
||||||
|
|
||||||
# charon plugins
|
# charon plugins
|
||||||
# ----------------
|
# ----------------
|
||||||
@@ -2034,6 +2037,7 @@ AC_CONFIG_FILES([
|
|||||||
src/libstrongswan/plugins/gcm/Makefile
|
src/libstrongswan/plugins/gcm/Makefile
|
||||||
src/libstrongswan/plugins/af_alg/Makefile
|
src/libstrongswan/plugins/af_alg/Makefile
|
||||||
src/libstrongswan/plugins/drbg/Makefile
|
src/libstrongswan/plugins/drbg/Makefile
|
||||||
|
src/libstrongswan/plugins/ml/Makefile
|
||||||
src/libstrongswan/plugins/test_vectors/Makefile
|
src/libstrongswan/plugins/test_vectors/Makefile
|
||||||
src/libstrongswan/tests/Makefile
|
src/libstrongswan/tests/Makefile
|
||||||
src/libipsec/Makefile
|
src/libipsec/Makefile
|
||||||
|
|||||||
@@ -683,6 +683,13 @@ if MONOLITHIC
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if USE_ML
|
||||||
|
SUBDIRS += plugins/ml
|
||||||
|
if MONOLITHIC
|
||||||
|
libstrongswan_la_LIBADD += plugins/ml/libstrongswan-ml.la
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
if USE_TEST_VECTORS
|
if USE_TEST_VECTORS
|
||||||
SUBDIRS += plugins/test_vectors
|
SUBDIRS += plugins/test_vectors
|
||||||
if MONOLITHIC
|
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