mirror of
https://github.com/jedisct1/libsodium.git
synced 2026-08-25 08:37:13 +09:00
Add ML-KEM768
This commit is contained in:
@@ -128,6 +128,7 @@ test/default/kdf_hkdf
|
||||
test/default/keygen
|
||||
test/default/kx
|
||||
test/default/metamorphic
|
||||
test/default/kem_mlkem768
|
||||
test/default/misuse
|
||||
test/default/onetimeauth
|
||||
test/default/onetimeauth2
|
||||
|
||||
@@ -56,6 +56,9 @@ libsodium_la_SOURCES = \
|
||||
crypto_kdf/crypto_kdf.c \
|
||||
crypto_kdf/hkdf/kdf_hkdf_sha256.c \
|
||||
crypto_kdf/hkdf/kdf_hkdf_sha512.c \
|
||||
crypto_kem/mlkem768/kem_mlkem768.c \
|
||||
crypto_kem/mlkem768/ref/kem_mlkem768_ref.c \
|
||||
crypto_kem/mlkem768/ref/kem_mlkem768_ref.h \
|
||||
crypto_kx/crypto_kx.c \
|
||||
crypto_onetimeauth/crypto_onetimeauth.c \
|
||||
crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "crypto_kem_mlkem768.h"
|
||||
#include "ref/kem_mlkem768_ref.h"
|
||||
|
||||
size_t
|
||||
crypto_kem_mlkem768_publickeybytes(void)
|
||||
{
|
||||
return crypto_kem_mlkem768_PUBLICKEYBYTES;
|
||||
}
|
||||
|
||||
size_t
|
||||
crypto_kem_mlkem768_secretkeybytes(void)
|
||||
{
|
||||
return crypto_kem_mlkem768_SECRETKEYBYTES;
|
||||
}
|
||||
|
||||
size_t
|
||||
crypto_kem_mlkem768_ciphertextbytes(void)
|
||||
{
|
||||
return crypto_kem_mlkem768_CIPHERTEXTBYTES;
|
||||
}
|
||||
|
||||
size_t
|
||||
crypto_kem_mlkem768_sharedsecretbytes(void)
|
||||
{
|
||||
return crypto_kem_mlkem768_SHAREDSECRETBYTES;
|
||||
}
|
||||
|
||||
size_t
|
||||
crypto_kem_mlkem768_seedbytes(void)
|
||||
{
|
||||
return crypto_kem_mlkem768_SEEDBYTES;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_seed_keypair(unsigned char *pk, unsigned char *sk, const unsigned char *seed)
|
||||
{
|
||||
return mlkem768_ref_seed_keypair(pk, sk, seed);
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_keypair(unsigned char *pk, unsigned char *sk)
|
||||
{
|
||||
return mlkem768_ref_keypair(pk, sk);
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk)
|
||||
{
|
||||
return mlkem768_ref_enc(ct, ss, pk);
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_enc_deterministic(unsigned char *ct, unsigned char *ss, const unsigned char *pk,
|
||||
const unsigned char *seed)
|
||||
{
|
||||
return mlkem768_ref_enc_deterministic(ct, ss, pk, seed);
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk)
|
||||
{
|
||||
return mlkem768_ref_dec(ss, ct, sk);
|
||||
}
|
||||
@@ -0,0 +1,871 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "crypto_core_keccak1600.h"
|
||||
#include "crypto_kem_mlkem768.h"
|
||||
#include "crypto_verify_32.h"
|
||||
#include "crypto_xof_shake128.h"
|
||||
#include "crypto_xof_shake256.h"
|
||||
#include "kem_mlkem768_ref.h"
|
||||
#include "private/common.h"
|
||||
#include "randombytes.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MLKEM768_Q 3329
|
||||
#define MLKEM768_N 256
|
||||
#define MLKEM768_K 3
|
||||
#define MLKEM768_ETA1 2
|
||||
#define MLKEM768_ETA2 2
|
||||
#define MLKEM768_DU 10
|
||||
#define MLKEM768_DV 4
|
||||
|
||||
#define MLKEM768_POLYBYTES 384
|
||||
#define MLKEM768_POLYVECBYTES (MLKEM768_K * MLKEM768_POLYBYTES)
|
||||
#define MLKEM768_POLYCOMPRESSEDBYTES_DU 320
|
||||
#define MLKEM768_POLYCOMPRESSEDBYTES_DV 128
|
||||
#define MLKEM768_POLYVECCOMPRESSEDBYTES_DU (MLKEM768_K * MLKEM768_POLYCOMPRESSEDBYTES_DU)
|
||||
|
||||
#define SHA3_512_RATE 72
|
||||
#define SHA3_DOMAIN 0x06
|
||||
|
||||
typedef struct poly {
|
||||
int16_t coeffs[MLKEM768_N];
|
||||
} poly;
|
||||
|
||||
typedef struct polyvec {
|
||||
poly vec[MLKEM768_K];
|
||||
} polyvec;
|
||||
|
||||
static const int16_t zetas[128] = {
|
||||
2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, 962, 2127, 1855, 1468,
|
||||
573, 2004, 264, 383, 2500, 1458, 1727, 3199, 2648, 1017, 732, 608, 1787, 411, 3124, 1758,
|
||||
1223, 652, 2777, 1015, 2036, 1491, 3047, 1785, 516, 3321, 3009, 2663, 1711, 2167, 126, 1469,
|
||||
2476, 3239, 3058, 830, 107, 1908, 3082, 2378, 2931, 961, 1821, 2604, 448, 2264, 677, 2054,
|
||||
2226, 430, 555, 843, 2078, 871, 1550, 105, 422, 587, 177, 3094, 3038, 2869, 1574, 1653,
|
||||
3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349, 418, 329, 3173, 3254,
|
||||
817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193, 1218, 1994, 2455, 220, 2142, 1670,
|
||||
2144, 1799, 2051, 794, 1819, 2475, 2459, 478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628
|
||||
};
|
||||
|
||||
static int16_t
|
||||
montgomery_reduce(int32_t a)
|
||||
{
|
||||
int16_t t;
|
||||
|
||||
t = (int16_t) ((uint16_t) a * 62209U);
|
||||
t = (int16_t) ((a - (int32_t) t * MLKEM768_Q) >> 16);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int16_t
|
||||
barrett_reduce(int16_t a)
|
||||
{
|
||||
int16_t t;
|
||||
|
||||
t = (int16_t) (((int32_t) a * 20159) >> 26);
|
||||
t = a - t * MLKEM768_Q;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int16_t
|
||||
csubq(int16_t a)
|
||||
{
|
||||
a -= MLKEM768_Q;
|
||||
a += (a >> 15) & MLKEM768_Q;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static void
|
||||
poly_ntt(poly *r)
|
||||
{
|
||||
unsigned int len, start, j, k;
|
||||
int16_t t, zeta;
|
||||
|
||||
k = 1;
|
||||
for (len = 128; len >= 2; len >>= 1) {
|
||||
for (start = 0; start < MLKEM768_N; start = j + len) {
|
||||
zeta = zetas[k++];
|
||||
for (j = start; j < start + len; j++) {
|
||||
t = montgomery_reduce((int32_t) zeta * r->coeffs[j + len]);
|
||||
r->coeffs[j + len] = r->coeffs[j] - t;
|
||||
r->coeffs[j] = r->coeffs[j] + t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_invntt(poly *r)
|
||||
{
|
||||
unsigned int start, len, j, k;
|
||||
int16_t t, zeta;
|
||||
const int16_t f = 1441;
|
||||
|
||||
k = 127;
|
||||
for (len = 2; len <= 128; len <<= 1) {
|
||||
for (start = 0; start < MLKEM768_N; start = j + len) {
|
||||
zeta = zetas[k--];
|
||||
for (j = start; j < start + len; j++) {
|
||||
t = r->coeffs[j];
|
||||
r->coeffs[j] = barrett_reduce(t + r->coeffs[j + len]);
|
||||
r->coeffs[j + len] = montgomery_reduce((int32_t) zeta * (r->coeffs[j + len] - t));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (j = 0; j < MLKEM768_N; j++) {
|
||||
r->coeffs[j] = montgomery_reduce((int32_t) f * r->coeffs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_basemul(poly *r, const poly *a, const poly *b)
|
||||
{
|
||||
unsigned int i;
|
||||
int16_t zeta;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 4; i++) {
|
||||
zeta = zetas[64 + i];
|
||||
|
||||
r->coeffs[4 * i] = montgomery_reduce((int32_t) a->coeffs[4 * i + 1] * b->coeffs[4 * i + 1]);
|
||||
r->coeffs[4 * i] = montgomery_reduce((int32_t) r->coeffs[4 * i] * zeta);
|
||||
r->coeffs[4 * i] += montgomery_reduce((int32_t) a->coeffs[4 * i] * b->coeffs[4 * i]);
|
||||
|
||||
r->coeffs[4 * i + 1] = montgomery_reduce((int32_t) a->coeffs[4 * i] * b->coeffs[4 * i + 1]);
|
||||
r->coeffs[4 * i + 1] +=
|
||||
montgomery_reduce((int32_t) a->coeffs[4 * i + 1] * b->coeffs[4 * i]);
|
||||
|
||||
r->coeffs[4 * i + 2] =
|
||||
montgomery_reduce((int32_t) a->coeffs[4 * i + 3] * b->coeffs[4 * i + 3]);
|
||||
r->coeffs[4 * i + 2] = montgomery_reduce((int32_t) r->coeffs[4 * i + 2] * (-zeta));
|
||||
r->coeffs[4 * i + 2] +=
|
||||
montgomery_reduce((int32_t) a->coeffs[4 * i + 2] * b->coeffs[4 * i + 2]);
|
||||
|
||||
r->coeffs[4 * i + 3] =
|
||||
montgomery_reduce((int32_t) a->coeffs[4 * i + 2] * b->coeffs[4 * i + 3]);
|
||||
r->coeffs[4 * i + 3] +=
|
||||
montgomery_reduce((int32_t) a->coeffs[4 * i + 3] * b->coeffs[4 * i + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_tomont(poly *r)
|
||||
{
|
||||
unsigned int i;
|
||||
const int16_t f = 1353;
|
||||
|
||||
for (i = 0; i < MLKEM768_N; i++) {
|
||||
r->coeffs[i] = montgomery_reduce((int32_t) f * r->coeffs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_reduce(poly *r)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N; i++) {
|
||||
r->coeffs[i] = barrett_reduce(r->coeffs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_add(poly *r, const poly *a, const poly *b)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N; i++) {
|
||||
r->coeffs[i] = a->coeffs[i] + b->coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_sub(poly *r, const poly *a, const poly *b)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N; i++) {
|
||||
r->coeffs[i] = a->coeffs[i] - b->coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_csubq(poly *r)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N; i++) {
|
||||
r->coeffs[i] = csubq(r->coeffs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sha3_256(unsigned char out[32], const unsigned char *in, size_t inlen)
|
||||
{
|
||||
crypto_xof_shake256_state state;
|
||||
|
||||
crypto_xof_shake256_init_with_domain(&state, SHA3_DOMAIN);
|
||||
crypto_xof_shake256_update(&state, in, inlen);
|
||||
crypto_xof_shake256_squeeze(&state, out, 32);
|
||||
}
|
||||
|
||||
static void
|
||||
sha3_512(unsigned char out[64], const unsigned char *in, size_t inlen)
|
||||
{
|
||||
unsigned char state[200];
|
||||
size_t offset = 0;
|
||||
size_t consumed = 0;
|
||||
size_t chunk_size;
|
||||
unsigned char pad;
|
||||
|
||||
crypto_core_keccak1600_init(state);
|
||||
|
||||
while (consumed < inlen) {
|
||||
if (offset == SHA3_512_RATE) {
|
||||
crypto_core_keccak1600_permute_24(state);
|
||||
offset = 0;
|
||||
}
|
||||
chunk_size = SHA3_512_RATE - offset;
|
||||
if (chunk_size > inlen - consumed) {
|
||||
chunk_size = inlen - consumed;
|
||||
}
|
||||
crypto_core_keccak1600_xor_bytes(state, &in[consumed], offset, chunk_size);
|
||||
offset += chunk_size;
|
||||
consumed += chunk_size;
|
||||
}
|
||||
|
||||
if (offset == SHA3_512_RATE - 1) {
|
||||
pad = SHA3_DOMAIN | 0x80;
|
||||
crypto_core_keccak1600_xor_bytes(state, &pad, offset, 1);
|
||||
} else {
|
||||
pad = SHA3_DOMAIN;
|
||||
crypto_core_keccak1600_xor_bytes(state, &pad, offset, 1);
|
||||
pad = 0x80;
|
||||
crypto_core_keccak1600_xor_bytes(state, &pad, SHA3_512_RATE - 1, 1);
|
||||
}
|
||||
|
||||
crypto_core_keccak1600_permute_24(state);
|
||||
crypto_core_keccak1600_extract_bytes(state, out, 0, 64);
|
||||
}
|
||||
|
||||
static void
|
||||
poly_cbd_eta2(poly *r, const unsigned char buf[128])
|
||||
{
|
||||
unsigned int i, j;
|
||||
uint32_t t, d;
|
||||
int16_t a, b;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 8; i++) {
|
||||
t = (uint32_t) buf[4 * i] | ((uint32_t) buf[4 * i + 1] << 8) |
|
||||
((uint32_t) buf[4 * i + 2] << 16) | ((uint32_t) buf[4 * i + 3] << 24);
|
||||
|
||||
d = t & 0x55555555;
|
||||
d += (t >> 1) & 0x55555555;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
a = (d >> (4 * j)) & 0x3;
|
||||
b = (d >> (4 * j + 2)) & 0x3;
|
||||
r->coeffs[8 * i + j] = a - b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_getnoise_eta2(poly *r, const unsigned char seed[32], uint8_t nonce)
|
||||
{
|
||||
unsigned char buf[MLKEM768_ETA2 * MLKEM768_N / 4];
|
||||
crypto_xof_shake256_state state;
|
||||
unsigned char extseed[33];
|
||||
|
||||
memcpy(extseed, seed, 32);
|
||||
extseed[32] = nonce;
|
||||
|
||||
crypto_xof_shake256_init(&state);
|
||||
crypto_xof_shake256_update(&state, extseed, 33);
|
||||
crypto_xof_shake256_squeeze(&state, buf, sizeof(buf));
|
||||
|
||||
poly_cbd_eta2(r, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
poly_frombytes(poly *r, const unsigned char a[MLKEM768_POLYBYTES])
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 2; i++) {
|
||||
r->coeffs[2 * i] = ((a[3 * i + 0] >> 0) | ((uint16_t) a[3 * i + 1] << 8)) & 0xFFF;
|
||||
r->coeffs[2 * i + 1] = ((a[3 * i + 1] >> 4) | ((uint16_t) a[3 * i + 2] << 4)) & 0xFFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_tobytes(unsigned char r[MLKEM768_POLYBYTES], const poly *a)
|
||||
{
|
||||
unsigned int i;
|
||||
uint16_t t0, t1;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 2; i++) {
|
||||
t0 = a->coeffs[2 * i];
|
||||
t1 = a->coeffs[2 * i + 1];
|
||||
r[3 * i + 0] = (unsigned char) (t0 >> 0);
|
||||
r[3 * i + 1] = (unsigned char) ((t0 >> 8) | (t1 << 4));
|
||||
r[3 * i + 2] = (unsigned char) (t1 >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_frommsg(poly *r, const unsigned char msg[32])
|
||||
{
|
||||
unsigned int i, j;
|
||||
int16_t mask;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 8; i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
mask = -((msg[i] >> j) & 1);
|
||||
r->coeffs[8 * i + j] = mask & ((MLKEM768_Q + 1) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_tomsg(unsigned char msg[32], const poly *a)
|
||||
{
|
||||
unsigned int i, j;
|
||||
uint32_t t;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 8; i++) {
|
||||
msg[i] = 0;
|
||||
for (j = 0; j < 8; j++) {
|
||||
t = (uint32_t) a->coeffs[8 * i + j];
|
||||
t += (uint32_t) (((int32_t) a->coeffs[8 * i + j] >> 15) & MLKEM768_Q);
|
||||
t = (((t << 1) + MLKEM768_Q / 2) * 80635) >> 28;
|
||||
t &= 1;
|
||||
msg[i] |= t << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_compress_du(unsigned char r[MLKEM768_POLYCOMPRESSEDBYTES_DU], const poly *a)
|
||||
{
|
||||
uint32_t t[4];
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
t[j] = (uint32_t) a->coeffs[4 * i + j];
|
||||
t[j] += (uint32_t) (((int32_t) a->coeffs[4 * i + j] >> 15) & MLKEM768_Q);
|
||||
t[j] = (uint32_t) ((((uint64_t) t[j] << 10) + MLKEM768_Q / 2) * 161271ULL >> 29);
|
||||
t[j] &= 0x3ff;
|
||||
}
|
||||
|
||||
r[5 * i + 0] = (unsigned char) (t[0] >> 0);
|
||||
r[5 * i + 1] = (unsigned char) ((t[0] >> 8) | (t[1] << 2));
|
||||
r[5 * i + 2] = (unsigned char) ((t[1] >> 6) | (t[2] << 4));
|
||||
r[5 * i + 3] = (unsigned char) ((t[2] >> 4) | (t[3] << 6));
|
||||
r[5 * i + 4] = (unsigned char) (t[3] >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_decompress_du(poly *r, const unsigned char a[MLKEM768_POLYCOMPRESSEDBYTES_DU])
|
||||
{
|
||||
uint16_t t[4];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 4; i++) {
|
||||
t[0] = (a[5 * i + 0] >> 0) | ((uint16_t) a[5 * i + 1] << 8);
|
||||
t[1] = (a[5 * i + 1] >> 2) | ((uint16_t) a[5 * i + 2] << 6);
|
||||
t[2] = (a[5 * i + 2] >> 4) | ((uint16_t) a[5 * i + 3] << 4);
|
||||
t[3] = (a[5 * i + 3] >> 6) | ((uint16_t) a[5 * i + 4] << 2);
|
||||
|
||||
r->coeffs[4 * i + 0] = (int16_t) (((uint32_t) (t[0] & 0x3FF) * MLKEM768_Q + 512) >> 10);
|
||||
r->coeffs[4 * i + 1] = (int16_t) (((uint32_t) (t[1] & 0x3FF) * MLKEM768_Q + 512) >> 10);
|
||||
r->coeffs[4 * i + 2] = (int16_t) (((uint32_t) (t[2] & 0x3FF) * MLKEM768_Q + 512) >> 10);
|
||||
r->coeffs[4 * i + 3] = (int16_t) (((uint32_t) (t[3] & 0x3FF) * MLKEM768_Q + 512) >> 10);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_compress_dv(unsigned char r[MLKEM768_POLYCOMPRESSEDBYTES_DV], const poly *a)
|
||||
{
|
||||
uint32_t t[8];
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 8; i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
t[j] = (uint32_t) a->coeffs[8 * i + j];
|
||||
t[j] += (uint32_t) (((int32_t) a->coeffs[8 * i + j] >> 15) & MLKEM768_Q);
|
||||
t[j] = (uint32_t) ((((uint64_t) t[j] << 4) + MLKEM768_Q / 2) * 161271ULL >> 29);
|
||||
t[j] &= 0xf;
|
||||
}
|
||||
|
||||
r[4 * i + 0] = (unsigned char) (t[0] | (t[1] << 4));
|
||||
r[4 * i + 1] = (unsigned char) (t[2] | (t[3] << 4));
|
||||
r[4 * i + 2] = (unsigned char) (t[4] | (t[5] << 4));
|
||||
r[4 * i + 3] = (unsigned char) (t[6] | (t[7] << 4));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poly_decompress_dv(poly *r, const unsigned char a[MLKEM768_POLYCOMPRESSEDBYTES_DV])
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_N / 2; i++) {
|
||||
r->coeffs[2 * i + 0] = (int16_t) ((((uint16_t) (a[i] & 15) * MLKEM768_Q) + 8) >> 4);
|
||||
r->coeffs[2 * i + 1] = (int16_t) ((((uint16_t) (a[i] >> 4) * MLKEM768_Q) + 8) >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_ntt(polyvec *r)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_ntt(&r->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_invntt(polyvec *r)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_invntt(&r->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_basemul_acc(poly *r, const polyvec *a, const polyvec *b)
|
||||
{
|
||||
poly t;
|
||||
unsigned int i;
|
||||
|
||||
poly_basemul(r, &a->vec[0], &b->vec[0]);
|
||||
for (i = 1; i < MLKEM768_K; i++) {
|
||||
poly_basemul(&t, &a->vec[i], &b->vec[i]);
|
||||
poly_add(r, r, &t);
|
||||
}
|
||||
|
||||
poly_reduce(r);
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_reduce(polyvec *r)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_reduce(&r->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_csubq(polyvec *r)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_csubq(&r->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_add(polyvec *r, const polyvec *a, const polyvec *b)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_add(&r->vec[i], &a->vec[i], &b->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_tobytes(unsigned char r[MLKEM768_POLYVECBYTES], const polyvec *a)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_tobytes(r + i * MLKEM768_POLYBYTES, &a->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_frombytes(polyvec *r, const unsigned char a[MLKEM768_POLYVECBYTES])
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_frombytes(&r->vec[i], a + i * MLKEM768_POLYBYTES);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
polyvec_is_canonical(const polyvec *a)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
for (j = 0; j < MLKEM768_N; j++) {
|
||||
if ((uint16_t) a->vec[i].coeffs[j] >= MLKEM768_Q) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_compress(unsigned char r[MLKEM768_POLYVECCOMPRESSEDBYTES_DU], const polyvec *a)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_compress_du(r + i * MLKEM768_POLYCOMPRESSEDBYTES_DU, &a->vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
polyvec_decompress(polyvec *r, const unsigned char a[MLKEM768_POLYVECCOMPRESSEDBYTES_DU])
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_decompress_du(&r->vec[i], a + i * MLKEM768_POLYCOMPRESSEDBYTES_DU);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
rej_uniform(int16_t *r, unsigned int len, const unsigned char *buf, unsigned int buflen)
|
||||
{
|
||||
unsigned int ctr, pos;
|
||||
uint16_t val0, val1;
|
||||
|
||||
ctr = pos = 0;
|
||||
while (ctr < len && pos + 3 <= buflen) {
|
||||
val0 = ((buf[pos + 0] >> 0) | ((uint16_t) buf[pos + 1] << 8)) & 0xFFF;
|
||||
val1 = ((buf[pos + 1] >> 4) | ((uint16_t) buf[pos + 2] << 4)) & 0xFFF;
|
||||
pos += 3;
|
||||
|
||||
if (val0 < MLKEM768_Q) {
|
||||
r[ctr++] = (int16_t) val0;
|
||||
}
|
||||
if (ctr < len && val1 < MLKEM768_Q) {
|
||||
r[ctr++] = (int16_t) val1;
|
||||
}
|
||||
}
|
||||
|
||||
return ctr;
|
||||
}
|
||||
|
||||
#define GEN_MATRIX_NBLOCKS \
|
||||
((12 * MLKEM768_N / 8 * (1 << 12) / MLKEM768_Q + crypto_xof_shake128_BLOCKBYTES) / \
|
||||
crypto_xof_shake128_BLOCKBYTES)
|
||||
|
||||
static void
|
||||
gen_matrix(polyvec *a, const unsigned char seed[32], int transposed)
|
||||
{
|
||||
crypto_xof_shake128_state state;
|
||||
unsigned char buf[GEN_MATRIX_NBLOCKS * crypto_xof_shake128_BLOCKBYTES + 2];
|
||||
unsigned char extseed[34];
|
||||
unsigned int ctr, i, j;
|
||||
unsigned int buflen;
|
||||
|
||||
memcpy(extseed, seed, 32);
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
for (j = 0; j < MLKEM768_K; j++) {
|
||||
if (transposed) {
|
||||
extseed[32] = (unsigned char) i;
|
||||
extseed[33] = (unsigned char) j;
|
||||
} else {
|
||||
extseed[32] = (unsigned char) j;
|
||||
extseed[33] = (unsigned char) i;
|
||||
}
|
||||
|
||||
crypto_xof_shake128_init(&state);
|
||||
crypto_xof_shake128_update(&state, extseed, 34);
|
||||
|
||||
buflen = GEN_MATRIX_NBLOCKS * crypto_xof_shake128_BLOCKBYTES;
|
||||
crypto_xof_shake128_squeeze(&state, buf, buflen);
|
||||
|
||||
ctr = rej_uniform(a[i].vec[j].coeffs, MLKEM768_N, buf, buflen);
|
||||
|
||||
while (ctr < MLKEM768_N) {
|
||||
crypto_xof_shake128_squeeze(&state, buf, crypto_xof_shake128_BLOCKBYTES);
|
||||
ctr += rej_uniform(a[i].vec[j].coeffs + ctr, MLKEM768_N - ctr, buf,
|
||||
crypto_xof_shake128_BLOCKBYTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
indcpa_keypair(unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
unsigned char sk[MLKEM768_POLYVECBYTES],
|
||||
const unsigned char seed[32])
|
||||
{
|
||||
polyvec a[MLKEM768_K], e, pkpv, skpv;
|
||||
unsigned char buf[64];
|
||||
unsigned char *publicseed = buf;
|
||||
unsigned char *noiseseed = buf + 32;
|
||||
unsigned int i;
|
||||
uint8_t nonce = 0;
|
||||
|
||||
sha3_512(buf, seed, 33);
|
||||
|
||||
gen_matrix(a, publicseed, 0);
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_getnoise_eta2(&skpv.vec[i], noiseseed, nonce++);
|
||||
}
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_getnoise_eta2(&e.vec[i], noiseseed, nonce++);
|
||||
}
|
||||
|
||||
polyvec_ntt(&skpv);
|
||||
polyvec_ntt(&e);
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
polyvec_basemul_acc(&pkpv.vec[i], &a[i], &skpv);
|
||||
poly_tomont(&pkpv.vec[i]);
|
||||
}
|
||||
|
||||
polyvec_add(&pkpv, &pkpv, &e);
|
||||
polyvec_reduce(&pkpv);
|
||||
polyvec_csubq(&pkpv);
|
||||
polyvec_reduce(&skpv);
|
||||
polyvec_csubq(&skpv);
|
||||
|
||||
polyvec_tobytes(sk, &skpv);
|
||||
polyvec_tobytes(pk, &pkpv);
|
||||
memcpy(pk + MLKEM768_POLYVECBYTES, publicseed, 32);
|
||||
}
|
||||
|
||||
static void
|
||||
indcpa_enc(unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
const unsigned char m[32],
|
||||
const unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
const unsigned char coins[32])
|
||||
{
|
||||
polyvec sp, pkpv, ep, at[MLKEM768_K], b;
|
||||
poly v, k, epp;
|
||||
unsigned char seed[32];
|
||||
unsigned int i;
|
||||
uint8_t nonce = 0;
|
||||
|
||||
memcpy(seed, pk + MLKEM768_POLYVECBYTES, 32);
|
||||
|
||||
polyvec_frombytes(&pkpv, pk);
|
||||
|
||||
poly_frommsg(&k, m);
|
||||
|
||||
gen_matrix(at, seed, 1);
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_getnoise_eta2(&sp.vec[i], coins, nonce++);
|
||||
}
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
poly_getnoise_eta2(&ep.vec[i], coins, nonce++);
|
||||
}
|
||||
poly_getnoise_eta2(&epp, coins, nonce++);
|
||||
|
||||
polyvec_ntt(&sp);
|
||||
polyvec_reduce(&sp);
|
||||
|
||||
for (i = 0; i < MLKEM768_K; i++) {
|
||||
polyvec_basemul_acc(&b.vec[i], &at[i], &sp);
|
||||
}
|
||||
|
||||
polyvec_basemul_acc(&v, &pkpv, &sp);
|
||||
|
||||
polyvec_invntt(&b);
|
||||
poly_invntt(&v);
|
||||
|
||||
polyvec_add(&b, &b, &ep);
|
||||
poly_add(&v, &v, &epp);
|
||||
poly_add(&v, &v, &k);
|
||||
|
||||
polyvec_reduce(&b);
|
||||
poly_reduce(&v);
|
||||
polyvec_csubq(&b);
|
||||
poly_csubq(&v);
|
||||
|
||||
polyvec_compress(ct, &b);
|
||||
poly_compress_dv(ct + MLKEM768_POLYVECCOMPRESSEDBYTES_DU, &v);
|
||||
}
|
||||
|
||||
static void
|
||||
indcpa_dec(unsigned char m[32],
|
||||
const unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
const unsigned char sk[MLKEM768_POLYVECBYTES])
|
||||
{
|
||||
polyvec b, skpv;
|
||||
poly v, mp;
|
||||
|
||||
polyvec_decompress(&b, ct);
|
||||
poly_decompress_dv(&v, ct + MLKEM768_POLYVECCOMPRESSEDBYTES_DU);
|
||||
|
||||
polyvec_frombytes(&skpv, sk);
|
||||
|
||||
polyvec_ntt(&b);
|
||||
polyvec_reduce(&b);
|
||||
polyvec_basemul_acc(&mp, &skpv, &b);
|
||||
poly_invntt(&mp);
|
||||
|
||||
poly_sub(&mp, &v, &mp);
|
||||
poly_reduce(&mp);
|
||||
poly_csubq(&mp);
|
||||
|
||||
poly_tomsg(m, &mp);
|
||||
}
|
||||
|
||||
static void
|
||||
cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b)
|
||||
{
|
||||
size_t i;
|
||||
unsigned char mask;
|
||||
|
||||
mask = (unsigned char) (-(int) b);
|
||||
|
||||
#ifdef HAVE_INLINE_ASM
|
||||
__asm__ __volatile__("" : "+r"(mask));
|
||||
#endif
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
r[i] ^= mask & (r[i] ^ x[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mlkem768_ref_seed_keypair(unsigned char *pk, unsigned char *sk, const unsigned char *seed)
|
||||
{
|
||||
unsigned char indseed[33];
|
||||
|
||||
memcpy(indseed, seed, 32);
|
||||
indseed[32] = MLKEM768_K;
|
||||
|
||||
indcpa_keypair(pk, sk, indseed);
|
||||
memcpy(sk + MLKEM768_POLYVECBYTES, pk, crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
sha3_256(sk + MLKEM768_POLYVECBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES, pk,
|
||||
crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
memcpy(sk + MLKEM768_POLYVECBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES + 32, seed + 32, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mlkem768_ref_keypair(unsigned char *pk, unsigned char *sk)
|
||||
{
|
||||
unsigned char seed[crypto_kem_mlkem768_SEEDBYTES];
|
||||
|
||||
randombytes_buf(seed, crypto_kem_mlkem768_SEEDBYTES);
|
||||
return mlkem768_ref_seed_keypair(pk, sk, seed);
|
||||
}
|
||||
|
||||
int
|
||||
mlkem768_ref_enc_deterministic(unsigned char *ct, unsigned char *ss, const unsigned char *pk,
|
||||
const unsigned char *seed)
|
||||
{
|
||||
polyvec pkpv;
|
||||
unsigned char buf[64];
|
||||
unsigned char kr[64];
|
||||
|
||||
polyvec_frombytes(&pkpv, pk);
|
||||
if (polyvec_is_canonical(&pkpv) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf, seed, 32);
|
||||
sha3_256(buf + 32, pk, crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
|
||||
sha3_512(kr, buf, 64);
|
||||
|
||||
indcpa_enc(ct, buf, pk, kr + 32);
|
||||
|
||||
memcpy(ss, kr, crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mlkem768_ref_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk)
|
||||
{
|
||||
unsigned char seed[32];
|
||||
|
||||
randombytes_buf(seed, 32);
|
||||
return mlkem768_ref_enc_deterministic(ct, ss, pk, seed);
|
||||
}
|
||||
|
||||
int
|
||||
mlkem768_ref_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk)
|
||||
{
|
||||
unsigned char buf[64];
|
||||
unsigned char kr[64];
|
||||
unsigned char k_bar[crypto_kem_mlkem768_SHAREDSECRETBYTES];
|
||||
unsigned char cmp[crypto_kem_mlkem768_CIPHERTEXTBYTES];
|
||||
const unsigned char *pk = sk + MLKEM768_POLYVECBYTES;
|
||||
const unsigned char *hpk = sk + MLKEM768_POLYVECBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES;
|
||||
const unsigned char *z = sk + MLKEM768_POLYVECBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES + 32;
|
||||
int fail;
|
||||
crypto_xof_shake256_state state;
|
||||
|
||||
indcpa_dec(buf, ct, sk);
|
||||
|
||||
memcpy(buf + 32, hpk, 32);
|
||||
|
||||
sha3_512(kr, buf, 64);
|
||||
|
||||
indcpa_enc(cmp, buf, pk, kr + 32);
|
||||
|
||||
fail = crypto_verify_32(ct, cmp);
|
||||
fail |= crypto_verify_32(ct + 32, cmp + 32);
|
||||
fail |= crypto_verify_32(ct + 64, cmp + 64);
|
||||
fail |= crypto_verify_32(ct + 96, cmp + 96);
|
||||
fail |= crypto_verify_32(ct + 128, cmp + 128);
|
||||
fail |= crypto_verify_32(ct + 160, cmp + 160);
|
||||
fail |= crypto_verify_32(ct + 192, cmp + 192);
|
||||
fail |= crypto_verify_32(ct + 224, cmp + 224);
|
||||
fail |= crypto_verify_32(ct + 256, cmp + 256);
|
||||
fail |= crypto_verify_32(ct + 288, cmp + 288);
|
||||
fail |= crypto_verify_32(ct + 320, cmp + 320);
|
||||
fail |= crypto_verify_32(ct + 352, cmp + 352);
|
||||
fail |= crypto_verify_32(ct + 384, cmp + 384);
|
||||
fail |= crypto_verify_32(ct + 416, cmp + 416);
|
||||
fail |= crypto_verify_32(ct + 448, cmp + 448);
|
||||
fail |= crypto_verify_32(ct + 480, cmp + 480);
|
||||
fail |= crypto_verify_32(ct + 512, cmp + 512);
|
||||
fail |= crypto_verify_32(ct + 544, cmp + 544);
|
||||
fail |= crypto_verify_32(ct + 576, cmp + 576);
|
||||
fail |= crypto_verify_32(ct + 608, cmp + 608);
|
||||
fail |= crypto_verify_32(ct + 640, cmp + 640);
|
||||
fail |= crypto_verify_32(ct + 672, cmp + 672);
|
||||
fail |= crypto_verify_32(ct + 704, cmp + 704);
|
||||
fail |= crypto_verify_32(ct + 736, cmp + 736);
|
||||
fail |= crypto_verify_32(ct + 768, cmp + 768);
|
||||
fail |= crypto_verify_32(ct + 800, cmp + 800);
|
||||
fail |= crypto_verify_32(ct + 832, cmp + 832);
|
||||
fail |= crypto_verify_32(ct + 864, cmp + 864);
|
||||
fail |= crypto_verify_32(ct + 896, cmp + 896);
|
||||
fail |= crypto_verify_32(ct + 928, cmp + 928);
|
||||
fail |= crypto_verify_32(ct + 960, cmp + 960);
|
||||
fail |= crypto_verify_32(ct + 992, cmp + 992);
|
||||
fail |= crypto_verify_32(ct + 1024, cmp + 1024);
|
||||
fail |= crypto_verify_32(ct + 1056, cmp + 1056);
|
||||
|
||||
crypto_xof_shake256_init(&state);
|
||||
crypto_xof_shake256_update(&state, z, 32);
|
||||
crypto_xof_shake256_update(&state, ct, crypto_kem_mlkem768_CIPHERTEXTBYTES);
|
||||
crypto_xof_shake256_squeeze(&state, k_bar, crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
|
||||
cmov(kr, k_bar, crypto_kem_mlkem768_SHAREDSECRETBYTES, (unsigned char) (fail != 0));
|
||||
|
||||
memcpy(ss, kr, crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef kem_mlkem768_ref_H
|
||||
#define kem_mlkem768_ref_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "private/quirks.h"
|
||||
|
||||
int mlkem768_ref_keypair(unsigned char *pk, unsigned char *sk);
|
||||
|
||||
int mlkem768_ref_seed_keypair(unsigned char *pk, unsigned char *sk, const unsigned char *seed);
|
||||
|
||||
int mlkem768_ref_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk);
|
||||
|
||||
int mlkem768_ref_enc_deterministic(unsigned char *ct, unsigned char *ss, const unsigned char *pk,
|
||||
const unsigned char *seed);
|
||||
|
||||
int mlkem768_ref_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@ SODIUM_EXPORT = \
|
||||
sodium/crypto_kdf_blake2b.h \
|
||||
sodium/crypto_kdf_hkdf_sha256.h \
|
||||
sodium/crypto_kdf_hkdf_sha512.h \
|
||||
sodium/crypto_kem_mlkem768.h \
|
||||
sodium/crypto_kx.h \
|
||||
sodium/crypto_onetimeauth.h \
|
||||
sodium/crypto_onetimeauth_poly1305.h \
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "sodium/crypto_kdf_hkdf_sha256.h"
|
||||
#include "sodium/crypto_kdf_hkdf_sha512.h"
|
||||
#include "sodium/crypto_kdf_blake2b.h"
|
||||
#include "sodium/crypto_kem_mlkem768.h"
|
||||
#include "sodium/crypto_kx.h"
|
||||
#include "sodium/crypto_onetimeauth.h"
|
||||
#include "sodium/crypto_onetimeauth_poly1305.h"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef crypto_kem_mlkem768_H
|
||||
#define crypto_kem_mlkem768_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# ifdef __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wlong-long"
|
||||
# endif
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define crypto_kem_mlkem768_PUBLICKEYBYTES 1184U
|
||||
SODIUM_EXPORT
|
||||
size_t crypto_kem_mlkem768_publickeybytes(void);
|
||||
|
||||
#define crypto_kem_mlkem768_SECRETKEYBYTES 2400U
|
||||
SODIUM_EXPORT
|
||||
size_t crypto_kem_mlkem768_secretkeybytes(void);
|
||||
|
||||
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088U
|
||||
SODIUM_EXPORT
|
||||
size_t crypto_kem_mlkem768_ciphertextbytes(void);
|
||||
|
||||
#define crypto_kem_mlkem768_SHAREDSECRETBYTES 32U
|
||||
SODIUM_EXPORT
|
||||
size_t crypto_kem_mlkem768_sharedsecretbytes(void);
|
||||
|
||||
#define crypto_kem_mlkem768_SEEDBYTES 64U
|
||||
SODIUM_EXPORT
|
||||
size_t crypto_kem_mlkem768_seedbytes(void);
|
||||
|
||||
SODIUM_EXPORT
|
||||
int crypto_kem_mlkem768_seed_keypair(unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
unsigned char sk[crypto_kem_mlkem768_SECRETKEYBYTES],
|
||||
const unsigned char seed[crypto_kem_mlkem768_SEEDBYTES])
|
||||
__attribute__((nonnull));
|
||||
|
||||
SODIUM_EXPORT
|
||||
int crypto_kem_mlkem768_keypair(unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
unsigned char sk[crypto_kem_mlkem768_SECRETKEYBYTES])
|
||||
__attribute__((nonnull));
|
||||
|
||||
SODIUM_EXPORT
|
||||
int crypto_kem_mlkem768_enc(unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
unsigned char ss[crypto_kem_mlkem768_SHAREDSECRETBYTES],
|
||||
const unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES])
|
||||
__attribute__((warn_unused_result)) __attribute__((nonnull));
|
||||
|
||||
SODIUM_EXPORT
|
||||
int
|
||||
crypto_kem_mlkem768_enc_deterministic(unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
unsigned char ss[crypto_kem_mlkem768_SHAREDSECRETBYTES],
|
||||
const unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
const unsigned char seed[32])
|
||||
__attribute__((warn_unused_result)) __attribute__((nonnull));
|
||||
|
||||
SODIUM_EXPORT
|
||||
int crypto_kem_mlkem768_dec(unsigned char ss[crypto_kem_mlkem768_SHAREDSECRETBYTES],
|
||||
const unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
const unsigned char sk[crypto_kem_mlkem768_SECRETKEYBYTES])
|
||||
__attribute__((warn_unused_result)) __attribute__((nonnull));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -75,6 +75,11 @@
|
||||
#define keccak1600_ref_permute_12 _sodium_keccak1600_ref_permute_12
|
||||
#define keccak1600_ref_permute_24 _sodium_keccak1600_ref_permute_24
|
||||
#define keccak1600_ref_xor_bytes _sodium_keccak1600_ref_xor_bytes
|
||||
#define mlkem768_ref_dec _sodium_mlkem768_ref_dec
|
||||
#define mlkem768_ref_enc _sodium_mlkem768_ref_enc
|
||||
#define mlkem768_ref_enc_deterministic _sodium_mlkem768_ref_enc_deterministic
|
||||
#define mlkem768_ref_keypair _sodium_mlkem768_ref_keypair
|
||||
#define mlkem768_ref_seed_keypair _sodium_mlkem768_ref_seed_keypair
|
||||
#define ristretto255_from_hash _sodium_ristretto255_from_hash
|
||||
#define ristretto255_frombytes _sodium_ristretto255_frombytes
|
||||
#define ristretto255_p3_tobytes _sodium_ristretto255_p3_tobytes
|
||||
|
||||
@@ -47,6 +47,7 @@ EXTRA_DIST = \
|
||||
ipcrypt.exp \
|
||||
kdf.exp \
|
||||
kdf_hkdf.exp \
|
||||
kem_mlkem768.exp \
|
||||
keygen.exp \
|
||||
kx.exp \
|
||||
metamorphic.exp \
|
||||
@@ -138,6 +139,7 @@ DISTCLEANFILES = \
|
||||
ipcrypt.res \
|
||||
kdf.res \
|
||||
kdf_hkdf.res \
|
||||
kem_mlkem768.res \
|
||||
keygen.res \
|
||||
kx.res \
|
||||
metamorphic.res \
|
||||
@@ -234,6 +236,7 @@ TESTS_TARGETS = \
|
||||
hash3 \
|
||||
ipcrypt \
|
||||
kdf \
|
||||
kem_mlkem768 \
|
||||
keygen \
|
||||
kx \
|
||||
metamorphic \
|
||||
@@ -412,6 +415,9 @@ kdf_LDADD = $(TESTS_LDADD)
|
||||
kdf_hkdf_SOURCE = cmptest.h kdf_hkdf.c
|
||||
kdf_hkdf_LDADD = $(TESTS_LDADD)
|
||||
|
||||
kem_mlkem768_SOURCE = cmptest.h kem_mlkem768.c
|
||||
kem_mlkem768_LDADD = $(TESTS_LDADD)
|
||||
|
||||
keygen_SOURCE = cmptest.h keygen.c
|
||||
keygen_LDADD = $(TESTS_LDADD)
|
||||
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
#define TEST_NAME "kem_mlkem768"
|
||||
#include "cmptest.h"
|
||||
|
||||
static const char tv1_d[] =
|
||||
"7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d";
|
||||
static const char tv1_z[] =
|
||||
"b505d7cfad1b497499323c8686325e4792f267aafa3f87ca60d01cb54f29202a";
|
||||
static const char tv1_pk_prefix[] =
|
||||
"a8e651a1e685f22478a8954f007bc7711b930772c78f092e82878e3e937f3679";
|
||||
static const char tv1_m[] =
|
||||
"eb4a7c66ef4eba2ddb38c88d8bc706b1d639002198172a7b1942eca8f6c001ba";
|
||||
static const char tv1_ct_prefix[] =
|
||||
"3b835a5fa145387a0819c4daa1e65fbe2ba5400afcd640bbddbbe3585f24bedd";
|
||||
static const char tv1_ss[] =
|
||||
"ac865f839fef1bf3d528dd7504bed2f64b5502b0fa81d1c32763658e4aac5037";
|
||||
|
||||
static const char tv2_d[] =
|
||||
"d60b93492a1d8c1c7ba6fc0b733137f3406cee8110a93f170e7a78658af326d9";
|
||||
static const char tv2_z[] =
|
||||
"588522d326e7f105f11c4e8d97e119e193af42dc28409f4f7572ada538b52c1f";
|
||||
static const char tv2_pk_prefix[] =
|
||||
"93c140f6c47b7e53b96f72bb18447d277cc021c144a0f7a35e30b57386a78ac9";
|
||||
static const char tv2_m[] =
|
||||
"46401015603c5e2174cb94ac742e836b516e2d570f15de0b9d0204d79ac64ba3";
|
||||
static const char tv2_ct_prefix[] =
|
||||
"4ddf4e2919943817ab3b4cb085daefe5fec774d83d96c2cee74149e25addd15f";
|
||||
static const char tv2_ss[] =
|
||||
"950492e940bfa86ac28183de02efc04017d1755106a854fbb2ac68174f705b41";
|
||||
|
||||
static const char wycheproof_seed[] =
|
||||
"cbfc4405d1b2a3a386c94c25e0f2d5f5ee92cb0388ff4d6aa04223086d51c3fd"
|
||||
"24752da14c9fc3b8ae0d9e4a8b1016b8d8fc69e229c03ea2ef08a4ae0cffc37f";
|
||||
|
||||
static const char wycheproof_ek[] =
|
||||
"8b9a7354e8c1c17a9898f96caf99bba1c625ae0983c4d26e60c12f59bc25d756"
|
||||
"182b17979e713fff129e6c336a2317770380858129cb91a902ea5455ca076c20"
|
||||
"e4158f393aa2783053d8acf92c3286a10436d72af3e8903ce22f8cbab18b1309"
|
||||
"aca205aa8a3da45807e5b60e6967a640a713a5b81b0d82bc406934e3c88910cc"
|
||||
"06f09a68bce991009bb56ae233296218ff5baf1f475390b8b7494a4e08364598"
|
||||
"5437f2495a073222fea058e1672034151a2e14c2cbcb4ad9068c8e7bcb02886c"
|
||||
"4087c14bd8823ec66393fb2e12326b38e655dcab1c13cb2931358715c8be5153"
|
||||
"293229cc994a5702b7295a6a4a73249ac5a30a32a6b91dca768c177d8fa59054"
|
||||
"e548bee40257db6962a83b64ec6de0d3be7b1812c5d99e7a38cf54eaa8413aa0"
|
||||
"bd02bf374461f29c2b6aea56f7d33e10dbbc75bb1bf6b555213aac33e652443a"
|
||||
"186a618b6e9a293919693f1673ea159d0ee52c92d53e9a5cbd5f4c44419b5493"
|
||||
"f0286efcb9b5a2ad56a713c4f61ed431aa987805a90475391ccd4c7a28d884bf"
|
||||
"1a926b7675b9208897a89a6384c154acca3521c32a994949b2e80b2ff2518c0b"
|
||||
"bd34184cda443de498a05dfba9ce701bb914179a0188be0bcda6fbc2cd93afb0"
|
||||
"42481b4a84d821160a1b0ade8bcb97dbaf681547cce0bca674151a261a21aa04"
|
||||
"fda318c921c38425b775b995cd7b4eb6369f0b211da186594de815fa3b53a680"
|
||||
"43e1946468a5899b90578ad49397306f71acab67c258fa17a31893b72d22151c"
|
||||
"e1afee150fe550bffde8a24fe4740dd43f387c7ad20b5cca5997879536c30715"
|
||||
"74901147727341c1a6fc8043e6454903e88217f0c92b9a4ac54a86e790939087"
|
||||
"8a35ba18c1e26b13049f41c522bc049d74db6f76caba39f23d8f07abc26a07a1"
|
||||
"caba71cb5c12fbc20a715450b2228d17b2ba985cfe2496b033b55f300baf9929"
|
||||
"64388a5422298f627ba567730ddb03a195806a5c23a1d315481c52814b0681ea"
|
||||
"3c804b02c90688c2fbbce9b17d230a42f8b865417c9e93347f5ff624879962747c7eed886acd48a994b22064ca499c69aa4825be60260d12f63102799828b3cc"
|
||||
"ebaa4340b749605017eb4598ff13a634500a48470de9078a34106267d25a0b14"
|
||||
"4d8cdb1a4d527692153da013c0ab27ae2ca955e7c66238746022d50630892933"
|
||||
"a05c283a9f4c00c9f984646f75471081722ed3404ce60fed4a06702b8728b62d"
|
||||
"920b3224206ab0a4694f9c18e3162b529c16af04b87b4a61cd53a757906d67a1"
|
||||
"39629585d423a3b0986f29c95984e2095ea4608784b0b31c20cbc36cfd60b773"
|
||||
"389fe5a8c0958211a35b2c45f21f51a08693c19bfc388c2d677a214ccc5a7cb0"
|
||||
"b67030fd6987503c91b0f2646985a9048489dd882bd4141f5419554eb738fa73"
|
||||
"33c5c57a4ce5541fd6b8ad454c2dea67ad1c6f848c0a44a1872e40a17d987075"
|
||||
"d762ba5aaa21d435dd079b989a8ee21a2ce898934a98a02d5b381c302a2d7694"
|
||||
"8fa65de15666f57750d580ae1a541c700692dd0b0a6799b5cdb893413bb89bb6"
|
||||
"5182f219a4bc3c80b43bb591bcd05ac6e9a366552558ed5c5fbe7b4acfc7b233"
|
||||
"72094f6a87f26c0c46646c55a888871324fea7536e586a5149b9b707c86ea076"
|
||||
"486a6822b9bbf59fbfcaedc13284b9813cabdd526e326a6832d3b36efc3102ef";
|
||||
|
||||
static const char wycheproof_ct[] =
|
||||
"00e96c44eb5f5380d80b4cb05d608971a28fbe838b912b558bf9676c6c67c969"
|
||||
"2ed6ea063fc47b70d5d6004825c269b9cb8e68b5728d67f44844d97686a11154"
|
||||
"e3cbd4a6f9e47524d93d851bdce480d7762ded09be53d17cce3ee28dc5a06911"
|
||||
"c062b99b218355dd5822108c55e2c67f7ea0b74b6189b33b4589d8ff9d0c867d"
|
||||
"13edaaf1c5f724675320beda3e6acc673c048fa04a1b30f899227d5a555e08a4"
|
||||
"12a47b7db75eb118a8844a71b186880426f6bb6e77186d18e753cbcce93c9fb7"
|
||||
"6c729875b4d473eeca8dab9941969faef04df9ce3178e7341eab416d28bb7aee"
|
||||
"12bfb8f4c3df814eff45a0187329079acfc41a8e8dba7b08d111f02e37a1d2b6"
|
||||
"9e57eef808fcc6ae23385534a420a93dfb6a95c73e2f1016177c4fe9d3ced769"
|
||||
"e277293aaceded6d07efc7890eaa885bd1e73f9e5da2b9cfda0d44cbc9090705"
|
||||
"ac2ce3e7bcd7775b593c706e45ddc97d3a65d9b7e73673f9473df5bfec689671"
|
||||
"f73e7f6ee77c730a8ebda1f0b2c112a71904d6f28a51d8d479f8323fb485e722"
|
||||
"f5c4d5857e95989a9cbaf273efd04fc3cbde98baa9969e095e88accecfbd12ef"
|
||||
"2155497a5b15fe91a70d1106b91a568363bfc2d6736cc8138cbc41ddf1e54fd4"
|
||||
"0511e27d89e2c26c027b63c156e2bbb998faaed3d72186f0cc626dad39a782dc"
|
||||
"da2087fd13a9498e10b41b685646a227ea1166a5632c195983f4aba2b718b407"
|
||||
"4030d57126d34c38349892007acbea9e393b967dc3146b270f080a1f0bee90d6"
|
||||
"5a095a6352a7718ed1717a8025a2bd38c66d120baf4247676a1fe044a57ce268"
|
||||
"c078f8ff46cc26fe4f9c7a03d6467adacb8418d26273662bce1f7bc00b906e00"
|
||||
"88e95dc0fb419e2232e85aac77b9e7cd563e604de5e1d3e9693dcd3b19865cf4"
|
||||
"250773691e7e0af9c2755cd31f49d1f96708f3ba2f0b98184525abb869e12d5c"
|
||||
"ea0ac1740181a875035300b05a54b8d42ee3b4ae92b06e43cd807e2b096c977f"
|
||||
"23c61ef8c989c52ed83754076dea1323f639250bef61bdf903bf7961cec89ba6"
|
||||
"942d9d0647ac9c8195d93ed8abcedf25754644035719a5a0a1c8c0798912726a"
|
||||
"9a8258e4c10ae7485322ed9ba331f6f090c4b492a4fcf53d753cd34f28ec0443"
|
||||
"fce0724f43c89a3942169d5099a8f7430f38691ebce2e3fe4f600d82c7ccedf6"
|
||||
"0673f8e5d4c1a9f84b8b5b23f5ed63defb6a7205e0a8b105bdc6ff4568fc78cb"
|
||||
"4456b02fedb7f4c48f6fd6b69216a3319821039db40e19a31a0b0c9d471d5a7a"
|
||||
"73865d26f8be3595525b1ec4a8579efd07ea98e2602e2bffdfd8febc5c7b4c73"
|
||||
"6d326b5030ccc6faf9420a9c156a8f4516b9fff675459dfdcd4ef85d7adbdffd"
|
||||
"60f4a39628e399cc752c2e99ab2f431dc765e7c9d206bfe32649fbef4ef48b1d"
|
||||
"7936fc74766d724350ee2245a8d5f23fbc6c7ed8b57168ce1e864e49d7f8c475"
|
||||
"dcda140bd42473825b3c72eb8c54780188813011e1962a503f9b516e13226de6"
|
||||
"375b204733b0192183c8f55e870e61cf8947c3191790bc0b657bc43aebc9d86c";
|
||||
|
||||
static const char wycheproof_ss[] =
|
||||
"76c10bb1d86d96d7eb18e298363e51f7728e113f455df7d15017940ed3541451";
|
||||
|
||||
static const char *wycheproof_invalid_ek[] = {
|
||||
"016d4cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d212d0e896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"ff6f4cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2ffe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bb019da8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4f18d07da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbff9fa8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8ff7da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff86501ad1d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d1513d0ade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff8657da81d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15f3ffade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
"8b654cd2120d97581838192bb56463ee2b9a96e620bc7976bf3634f441ab0641ba3a904cfd4b55ba40cc6fdb906ac1a95637112c7a48dbf311d67004bb583e068b9936b32463245da31685a878a90d205162b587a322cebe475fc70186b8b52cab2c9e838873406a98574c65e83b0063d316a8654b5ef7537b487a25114c521bbfdb4780d58804c1c172cfac59b9bb8ba804a10fc6607633cc89d18bc5579d7a0825a2a72bc32869ba8518fafa522a9950ef7ab4625c417e6b6fa3f0165dd47d78ab8587f22b99abad73a90880b2b08c4876b61ca536f675b4b8a37460ad4400910d265e5146ae4be61a09c30a1de01f4636a802413fc8f051cde78e4fc51ba8f3a2cad1474775a7d7ec513a57541861702d6480c70cce8912bd2c01bf11378604005695d64c1969cf25714ae287c019da020d66af70c1528e12365fe25106e8179b04ccf0cba724fb6a89b83ca9f55012b66267337f6618484a7a70e8a1163cb17426ea132e1a56e53ab86e491e39dbbe4e4465a785106b5255dc2190d2f2bbe896a8cc64a228c2b9a8943214f97a3b40a8e41053fd4346a7fcab06fa72de8c33cca788e2e09e09dacd7034038ec13d1a065cf7d14c5de44a6d56873232ba84585b9d0811320659be5a19cdc759969a0bdeca5410977e40163dc2da6665c45954b900e3a97512897d1c7a6cc2a60acfb2175151043ae29a0d240e760c1d100a6877b5a10fb69054d0cf80dca4c15350fc685001899a7a6515e9c7ac3325938997b4387c16022a91ee5c2fa2ab8bd6fa898667831af38349fb1badb52b472210e36bca63342a0cd869a44a04a7e68859f66094d8c104c33b38444b8fb168025b479a5a7772c1bacd73b3ef88ae6fe66aab1846723c65d3d106c1722e2235ac7865a04584324062826e463edc13803c9796e54bce6b80933057abb9328ab501688e69148fbaa811ea9792316068a9abfa583e45ab6549ccb7e9e70d9139c0ebc7b995ec1463fc321f38bff2b2b995192eaa305daf1c44352371089637ed673dd82b8ad8bb3bb2299b9e5369f104a454357e5a2238e6315fa720343064be4ff865ffaf1d7c5c4b51c5b896669f029483f7c273f763ce26150ce3550c4ddcb26f37b2dea19b2dd197d449c9b693c10a3cb04de8c9f425a523d173240a69c4f0479bc177d836cda34b2cb64440acd464d904b66662295f0950460b710f089090b49fc7a6276e219eb4f19ee4dc12d2045141497064c4beae80a8ded00438d90a012963de54380f137666b2ab09844198ba807a4972f1b35f7b9789d847cc3e5a4a08cb17d4d9065ce09a74609ae935218c5bcd51f9bd774aa720e6423f75a974a3c21835a6018886dd119766909413aa576144c10b49c17ba1c793f78f7c580eba195bd8407f00f568c4d546c7d677dd02c78930c3e969b5584129e1815c6ce32162f09d6f7303f69c5f0216625c782666f9c8cf8678f1b63635067392aa3fba56204a1831491c2eee759ce674abd34251732949f9874059b35b66089a1905896c94111511a449f695e2c036d87b585a74834d676b9a2b2aa74a386cfa446f86934b844068147013396fa2d3bb8fa11d97b992a04a6ba65c189b238106cb8d15837bade3c278034d6d86bd50a3049feb37499b08aba61b38ffeaf94bc84cdb360fbc",
|
||||
};
|
||||
|
||||
static int
|
||||
test_kat_vector(const char *d_hex, const char *z_hex, const char *pk_prefix_hex,
|
||||
const char *m_hex, const char *ct_prefix_hex, const char *ss_hex)
|
||||
{
|
||||
unsigned char seed[crypto_kem_mlkem768_SEEDBYTES];
|
||||
unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_kem_mlkem768_SECRETKEYBYTES];
|
||||
unsigned char enc_seed[32];
|
||||
unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES];
|
||||
unsigned char ss_enc[crypto_kem_mlkem768_SHAREDSECRETBYTES];
|
||||
unsigned char ss_dec[crypto_kem_mlkem768_SHAREDSECRETBYTES];
|
||||
unsigned char expected_pk_prefix[32];
|
||||
unsigned char expected_ct_prefix[32];
|
||||
unsigned char expected_ss[32];
|
||||
char hex[65];
|
||||
|
||||
sodium_hex2bin(seed, 32, d_hex, 64, NULL, NULL, NULL);
|
||||
sodium_hex2bin(seed + 32, 32, z_hex, 64, NULL, NULL, NULL);
|
||||
sodium_hex2bin(enc_seed, 32, m_hex, 64, NULL, NULL, NULL);
|
||||
sodium_hex2bin(expected_pk_prefix, 32, pk_prefix_hex, 64, NULL, NULL, NULL);
|
||||
sodium_hex2bin(expected_ct_prefix, 32, ct_prefix_hex, 64, NULL, NULL, NULL);
|
||||
sodium_hex2bin(expected_ss, 32, ss_hex, 64, NULL, NULL, NULL);
|
||||
|
||||
crypto_kem_mlkem768_seed_keypair(pk, sk, seed);
|
||||
|
||||
if (memcmp(pk, expected_pk_prefix, 32) != 0) {
|
||||
sodium_bin2hex(hex, sizeof hex, pk, 32);
|
||||
printf("pk mismatch: got %s\n", hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
crypto_kem_mlkem768_enc_deterministic(ct, ss_enc, pk, enc_seed);
|
||||
|
||||
if (memcmp(ct, expected_ct_prefix, 32) != 0) {
|
||||
sodium_bin2hex(hex, sizeof hex, ct, 32);
|
||||
printf("ct mismatch: got %s\n", hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (memcmp(ss_enc, expected_ss, 32) != 0) {
|
||||
sodium_bin2hex(hex, sizeof hex, ss_enc, 32);
|
||||
printf("ss_enc mismatch: got %s\n", hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
crypto_kem_mlkem768_dec(ss_dec, ct, sk);
|
||||
|
||||
if (memcmp(ss_dec, expected_ss, 32) != 0) {
|
||||
sodium_bin2hex(hex, sizeof hex, ss_dec, 32);
|
||||
printf("ss_dec mismatch: got %s\n", hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tv_kem_mlkem768(void)
|
||||
{
|
||||
unsigned char *pk;
|
||||
unsigned char *sk;
|
||||
unsigned char *seed;
|
||||
unsigned char *ct;
|
||||
unsigned char *ss_enc;
|
||||
unsigned char *ss_dec;
|
||||
unsigned char *enc_seed;
|
||||
char hex[65];
|
||||
int i;
|
||||
|
||||
pk = (unsigned char *) sodium_malloc(crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
sk = (unsigned char *) sodium_malloc(crypto_kem_mlkem768_SECRETKEYBYTES);
|
||||
seed = (unsigned char *) sodium_malloc(crypto_kem_mlkem768_SEEDBYTES);
|
||||
ct = (unsigned char *) sodium_malloc(crypto_kem_mlkem768_CIPHERTEXTBYTES);
|
||||
ss_enc = (unsigned char *) sodium_malloc(crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
ss_dec = (unsigned char *) sodium_malloc(crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
enc_seed = (unsigned char *) sodium_malloc(32);
|
||||
|
||||
for (i = 0; i < (int) crypto_kem_mlkem768_SEEDBYTES; i++) {
|
||||
seed[i] = (unsigned char) i;
|
||||
}
|
||||
crypto_kem_mlkem768_seed_keypair(pk, sk, seed);
|
||||
|
||||
sodium_bin2hex(hex, sizeof hex, pk, 32);
|
||||
printf("pk (first 32 bytes): [%s]\n", hex);
|
||||
sodium_bin2hex(hex, sizeof hex, sk, 32);
|
||||
printf("sk (first 32 bytes): [%s]\n", hex);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
enc_seed[i] = (unsigned char)(i + 64);
|
||||
}
|
||||
crypto_kem_mlkem768_enc_deterministic(ct, ss_enc, pk, enc_seed);
|
||||
|
||||
sodium_bin2hex(hex, sizeof hex, ct, 32);
|
||||
printf("ct (first 32 bytes): [%s]\n", hex);
|
||||
sodium_bin2hex(hex, sizeof hex, ss_enc, crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
printf("ss_enc: [%s]\n", hex);
|
||||
|
||||
crypto_kem_mlkem768_dec(ss_dec, ct, sk);
|
||||
|
||||
sodium_bin2hex(hex, sizeof hex, ss_dec, crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
printf("ss_dec: [%s]\n", hex);
|
||||
|
||||
if (memcmp(ss_enc, ss_dec, crypto_kem_mlkem768_SHAREDSECRETBYTES) != 0) {
|
||||
printf("ERROR: shared secrets don't match!\n");
|
||||
} else {
|
||||
printf("shared secrets match: ok\n");
|
||||
}
|
||||
|
||||
crypto_kem_mlkem768_keypair(pk, sk);
|
||||
crypto_kem_mlkem768_enc(ct, ss_enc, pk);
|
||||
crypto_kem_mlkem768_dec(ss_dec, ct, sk);
|
||||
if (memcmp(ss_enc, ss_dec, crypto_kem_mlkem768_SHAREDSECRETBYTES) != 0) {
|
||||
printf("ERROR: shared secrets don't match (random keys)!\n");
|
||||
} else {
|
||||
printf("random keypair test: ok\n");
|
||||
}
|
||||
|
||||
ct[0] ^= 0x01;
|
||||
crypto_kem_mlkem768_dec(ss_dec, ct, sk);
|
||||
if (memcmp(ss_enc, ss_dec, crypto_kem_mlkem768_SHAREDSECRETBYTES) == 0) {
|
||||
printf("ERROR: decapsulation succeeded with modified ciphertext!\n");
|
||||
} else {
|
||||
printf("modified ciphertext rejected: ok\n");
|
||||
}
|
||||
|
||||
assert(crypto_kem_mlkem768_publickeybytes() == crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
assert(crypto_kem_mlkem768_secretkeybytes() == crypto_kem_mlkem768_SECRETKEYBYTES);
|
||||
assert(crypto_kem_mlkem768_ciphertextbytes() == crypto_kem_mlkem768_CIPHERTEXTBYTES);
|
||||
assert(crypto_kem_mlkem768_sharedsecretbytes() == crypto_kem_mlkem768_SHAREDSECRETBYTES);
|
||||
assert(crypto_kem_mlkem768_seedbytes() == crypto_kem_mlkem768_SEEDBYTES);
|
||||
|
||||
assert(crypto_kem_mlkem768_PUBLICKEYBYTES == 1184U);
|
||||
assert(crypto_kem_mlkem768_SECRETKEYBYTES == 2400U);
|
||||
assert(crypto_kem_mlkem768_CIPHERTEXTBYTES == 1088U);
|
||||
assert(crypto_kem_mlkem768_SHAREDSECRETBYTES == 32U);
|
||||
assert(crypto_kem_mlkem768_SEEDBYTES == 64U);
|
||||
|
||||
printf("constants: ok\n");
|
||||
|
||||
sodium_free(enc_seed);
|
||||
sodium_free(ss_dec);
|
||||
sodium_free(ss_enc);
|
||||
sodium_free(ct);
|
||||
sodium_free(seed);
|
||||
sodium_free(sk);
|
||||
sodium_free(pk);
|
||||
|
||||
printf("tv_kem_mlkem768: ok\n");
|
||||
}
|
||||
|
||||
static void
|
||||
tv_kem_mlkem768_kat(void)
|
||||
{
|
||||
printf("NIST KAT vector 1: ");
|
||||
if (test_kat_vector(tv1_d, tv1_z, tv1_pk_prefix, tv1_m, tv1_ct_prefix, tv1_ss) == 0) {
|
||||
printf("ok\n");
|
||||
} else {
|
||||
printf("FAILED\n");
|
||||
}
|
||||
|
||||
printf("NIST KAT vector 2: ");
|
||||
if (test_kat_vector(tv2_d, tv2_z, tv2_pk_prefix, tv2_m, tv2_ct_prefix, tv2_ss) == 0) {
|
||||
printf("ok\n");
|
||||
} else {
|
||||
printf("FAILED\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tv_wycheproof_decapsulation(void)
|
||||
{
|
||||
unsigned char seed[64];
|
||||
unsigned char pk[crypto_kem_mlkem768_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_kem_mlkem768_SECRETKEYBYTES];
|
||||
unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES];
|
||||
unsigned char ss[crypto_kem_mlkem768_SHAREDSECRETBYTES];
|
||||
unsigned char expected_ek[crypto_kem_mlkem768_PUBLICKEYBYTES];
|
||||
unsigned char expected_ss[32];
|
||||
|
||||
sodium_hex2bin(seed, sizeof seed, wycheproof_seed, strlen(wycheproof_seed),
|
||||
NULL, NULL, NULL);
|
||||
sodium_hex2bin(ct, sizeof ct, wycheproof_ct, strlen(wycheproof_ct),
|
||||
NULL, NULL, NULL);
|
||||
sodium_hex2bin(expected_ek, sizeof expected_ek, wycheproof_ek, strlen(wycheproof_ek),
|
||||
NULL, NULL, NULL);
|
||||
sodium_hex2bin(expected_ss, sizeof expected_ss, wycheproof_ss, strlen(wycheproof_ss),
|
||||
NULL, NULL, NULL);
|
||||
|
||||
crypto_kem_mlkem768_seed_keypair(pk, sk, seed);
|
||||
|
||||
printf("Wycheproof decapsulation (tcId 1): ");
|
||||
if (memcmp(pk, expected_ek, crypto_kem_mlkem768_PUBLICKEYBYTES) != 0) {
|
||||
printf("FAILED (pk mismatch)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
crypto_kem_mlkem768_dec(ss, ct, sk);
|
||||
|
||||
if (memcmp(ss, expected_ss, 32) == 0) {
|
||||
printf("ok\n");
|
||||
} else {
|
||||
printf("FAILED (ss mismatch)\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tv_wycheproof_modulus_overflow(void)
|
||||
{
|
||||
unsigned char ek[crypto_kem_mlkem768_PUBLICKEYBYTES];
|
||||
unsigned char ct[crypto_kem_mlkem768_CIPHERTEXTBYTES];
|
||||
unsigned char ss[crypto_kem_mlkem768_SHAREDSECRETBYTES];
|
||||
unsigned char m[32];
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
memset(m, 0x42, 32);
|
||||
|
||||
printf("Wycheproof modulus overflow: ");
|
||||
for (i = 0; i < sizeof wycheproof_invalid_ek / sizeof wycheproof_invalid_ek[0]; i++) {
|
||||
sodium_hex2bin(ek, sizeof ek, wycheproof_invalid_ek[i],
|
||||
strlen(wycheproof_invalid_ek[i]), NULL, NULL, NULL);
|
||||
ret = crypto_kem_mlkem768_enc_deterministic(ct, ss, ek, m);
|
||||
if (ret != 0) {
|
||||
printf("ok (rejected invalid ek)\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("accepted (no validation)\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
tv_kem_mlkem768();
|
||||
tv_kem_mlkem768_kat();
|
||||
tv_wycheproof_decapsulation();
|
||||
tv_wycheproof_modulus_overflow();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
pk (first 32 bytes): [298aa10d423c8dda069d02bc59e6cdf03a096b8b3da4cab9b80ca4a14907672c]
|
||||
sk (first 32 bytes): [27d2a77f33756f61208ef113abe82595873d4abc730e5b5d679529bf6a4ceb63]
|
||||
ct (first 32 bytes): [695a60d9c79f08343ed9ff5802582063c2ca3a648e543d924affbb39ef4de656]
|
||||
ss_enc: [9cddd089ffe70e3996e76f7c8d06746df34d07e8657bc0fcf2bb0e1c3084aea1]
|
||||
ss_dec: [9cddd089ffe70e3996e76f7c8d06746df34d07e8657bc0fcf2bb0e1c3084aea1]
|
||||
shared secrets match: ok
|
||||
random keypair test: ok
|
||||
modified ciphertext rejected: ok
|
||||
constants: ok
|
||||
tv_kem_mlkem768: ok
|
||||
NIST KAT vector 1: ok
|
||||
NIST KAT vector 2: ok
|
||||
Wycheproof decapsulation (tcId 1): ok
|
||||
Wycheproof modulus overflow: ok (rejected invalid ek)
|
||||
Reference in New Issue
Block a user