1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 16:42:56 -08:00

Add ML-KEM 1024 from BoringSSL

Changes include conversion from C++, basic KNF, then adaptation to
use our sha3 functions for sha3 and shake instead of the BorinSSL
version. This Adds units tests to run against BoringSSL and NIST test
vectors.

The future public API is the same as Boring's - but is not yet exposed
pending making bytestring.h public (which will happen separately) and
a minor bump

Currently this will just ensure we build and run regress.

ok tb@ to get it into the tree and massage from there.
This commit is contained in:
beck 2024-12-13 00:17:17 +00:00
parent 267e7b7d19
commit 08c63c712f
18 changed files with 6552 additions and 5 deletions

View File

@ -1,4 +1,4 @@
# $OpenBSD: Makefile,v 1.229 2024/12/13 00:03:57 beck Exp $
# $OpenBSD: Makefile,v 1.230 2024/12/13 00:17:17 beck Exp $
LIB= crypto
LIBREBUILD=y
@ -374,6 +374,7 @@ SRCS+= md5.c
# mlkem/
SRCS+= mlkem768.c
SRCS+= mlkem1024.c
# modes/
SRCS+= cbc128.c

View File

@ -1,4 +1,4 @@
/* $OpenBSD: mlkem.h,v 1.1 2024/12/13 00:03:57 beck Exp $ */
/* $OpenBSD: mlkem.h,v 1.2 2024/12/13 00:17:17 beck Exp $ */
/*
* Copyright (c) 2024 Bob Beck <beck@obtuse.com>
*
@ -35,6 +35,14 @@ LCRYPTO_USED(MLKEM768_marshal_public_key);
LCRYPTO_USED(MLKEM768_parse_public_key);
LCRYPTO_USED(MLKEM768_private_key_from_seed);
LCRYPTO_USED(MLKEM768_parse_private_key);
LCRYPTO_USED(MLKEM1024_generate_key);
LCRYPTO_USED(MLKEM1024_public_from_private);
LCRYPTO_USED(MLKEM1024_encap);
LCRYPTO_USED(MLKEM1024_decap);
LCRYPTO_USED(MLKEM1024_marshal_public_key);
LCRYPTO_USED(MLKEM1024_parse_public_key);
LCRYPTO_USED(MLKEM1024_private_key_from_seed);
LCRYPTO_USED(MLKEM1024_parse_private_key);
#endif
#endif /* _LIBCRYPTO_MLKEM_H */

View File

@ -161,6 +161,125 @@ int MLKEM768_parse_public_key(struct MLKEM768_public_key *out_public_key,
int MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key,
struct cbs_st *in);
/*
* ML-KEM-1024
*
* ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible.
*/
/*
* MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this
* object should never leave the address space since the format is unstable.
*/
struct MLKEM1024_public_key {
union {
uint8_t bytes[512 * (4 + 16) + 32 + 32];
uint16_t alignment;
} opaque;
};
/*
* MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of
* this object should never leave the address space since the format is
* unstable.
*/
struct MLKEM1024_private_key {
union {
uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32];
uint16_t alignment;
} opaque;
};
/*
* MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024
* public key.
*/
#define MLKEM1024_PUBLIC_KEY_BYTES 1568
/*
* MLKEM1024_generate_key generates a random public/private key pair, writes the
* encoded public key to |out_encoded_public_key| and sets |out_private_key| to
* the private key. If |optional_out_seed| is not NULL then the seed used to
* generate the private key is written to it.
*/
void MLKEM1024_generate_key(
uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
uint8_t optional_out_seed[MLKEM_SEED_BYTES],
struct MLKEM1024_private_key *out_private_key);
/*
* MLKEM1024_private_key_from_seed derives a private key from a seed that was
* generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len|
* is incorrect, otherwise it writes |*out_private_key| and returns 1.
*/
int MLKEM1024_private_key_from_seed(
struct MLKEM1024_private_key *out_private_key, const uint8_t *seed,
size_t seed_len);
/*
* MLKEM1024_public_from_private sets |*out_public_key| to the public key that
* corresponds to |private_key|. (This is faster than parsing the output of
* |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a
* key that was just generated.)
*/
void MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key,
const struct MLKEM1024_private_key *private_key);
/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */
#define MLKEM1024_CIPHERTEXT_BYTES 1568
/*
* MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the
* ciphertext to |out_ciphertext|, and writes the random shared secret to
* |out_shared_secret|.
*/
void MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
const struct MLKEM1024_public_key *public_key);
/*
* MLKEM1024_decap decrypts a shared secret from |ciphertext| using
* |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is
* incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid
* (but of the correct length), |out_shared_secret| is filled with a key that
* will always be the same for the same |ciphertext| and |private_key|, but
* which appears to be random unless one has access to |private_key|. These
* alternatives occur in constant time. Any subsequent symmetric encryption
* using |out_shared_secret| must use an authenticated encryption scheme in
* order to discover the decapsulation failure.
*/
int MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
const uint8_t *ciphertext, size_t ciphertext_len,
const struct MLKEM1024_private_key *private_key);
/*
* Serialisation of ML-KEM-1024 keys.
* MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard
* format for ML-KEM-1024 public keys. It returns one on success or zero on
* allocation error.
*/
int MLKEM1024_marshal_public_key(struct cbb_st *out,
const struct MLKEM1024_public_key *public_key);
/*
* MLKEM1024_parse_public_key parses a public key, in the format generated by
* |MLKEM1024_marshal_public_key|, from |in| and writes the result to
* |out_public_key|. It returns one on success or zero on parse error or if
* there are trailing bytes in |in|.
*/
int MLKEM1024_parse_public_key(struct MLKEM1024_public_key *out_public_key,
struct cbs_st *in);
/*
* MLKEM1024_parse_private_key parses a private key, in NIST's format for
* private keys, from |in| and writes the result to |out_private_key|. It
* returns one on success or zero on parse error or if there are trailing bytes
* in |in|. This format is verbose and should be avoided. Private keys should be
* stored as seeds and parsed using |MLKEM1024_private_key_from_seed|.
*/
int MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key,
struct cbs_st *in);
#if defined(__cplusplus)
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,45 @@ void MLKEM768_encap_external_entropy(
const struct MLKEM768_public_key *public_key,
const uint8_t entropy[MLKEM_ENCAP_ENTROPY]);
/*
* MLKEM1024_generate_key_external_entropy is a deterministic function to create a
* pair of ML-KEM 1024 keys, using the supplied entropy. The entropy needs to be
* uniformly random generated. This function is should only be used for tests,
* regular callers should use the non-deterministic |MLKEM_generate_key|
* directly.
*/
void MLKEM1024_generate_key_external_entropy(
uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
struct MLKEM1024_private_key *out_private_key,
const uint8_t entropy[MLKEM_SEED_BYTES]);
/*
* MLKEM1024_PRIVATE_KEY_BYTES is the length of the data produced by
* |MLKEM1024_marshal_private_key|.
*/
#define MLKEM1024_PRIVATE_KEY_BYTES 3168
/*
* MLKEM1024_marshal_private_key serializes |private_key| to |out| in the
* standard format for ML-KEM private keys. It returns one on success or zero on
* allocation error.
*/
int MLKEM1024_marshal_private_key(CBB *out,
const struct MLKEM1024_private_key *private_key);
/*
* MLKEM_encap_external_entropy behaves like |MLKEM_encap|, but uses
* |MLKEM_ENCAP_ENTROPY| bytes of |entropy| for randomization. The decapsulating
* side will be able to recover |entropy| in full. This function should only be
* used for tests, regular callers should use the non-deterministic
* |MLKEM_encap| directly.
*/
void MLKEM1024_encap_external_entropy(
uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
const struct MLKEM1024_public_key *public_key,
const uint8_t entropy[MLKEM_ENCAP_ENTROPY]);
__END_HIDDEN_DECLS
#if defined(__cplusplus)

View File

@ -1,4 +1,4 @@
# $OpenBSD: Makefile,v 1.1 2024/12/13 00:03:57 beck Exp $
# $OpenBSD: Makefile,v 1.2 2024/12/13 00:17:18 beck Exp $
PROGS += mlkem_unittest
PROGS += mlkem768_nist_keygen_tests
@ -7,6 +7,12 @@ PROGS += mlkem768_nist_decap_tests
PROGS += mlkem768_decap_tests
PROGS += mlkem768_encap_tests
PROGS += mlkem768_iteration_test
PROGS += mlkem1024_nist_keygen_tests
PROGS += mlkem1024_keygen_tests
PROGS += mlkem1024_nist_decap_tests
PROGS += mlkem1024_decap_tests
PROGS += mlkem1024_encap_tests
PROGS += mlkem1024_iteration_test
# Link test programs with mlkem_tests_util.c and use custom target
.for p in ${PROGS}

View File

@ -0,0 +1,128 @@
/* Copyright (c) 2024, Google Inc.
* Copyright (c) 2024, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include "bytestring.h"
#include "mlkem.h"
#include "mlkem_internal.h"
#include "sha3_internal.h"
#include "mlkem_tests_util.h"
static void
MlkemDecapFileTest(CBS *c, CBS *k, CBS *dk, int should_fail)
{
uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
struct MLKEM1024_private_key priv;
int parse_ok, decap_ok;
parse_ok = MLKEM1024_parse_private_key(&priv, dk);
if (!parse_ok) {
TEST(!should_fail, "parse_private_key");
return;
}
decap_ok = MLKEM1024_decap(shared_secret, CBS_data(c), CBS_len(c),
&priv);
if (!decap_ok) {
TEST(!should_fail, "decap");
return;
}
TEST_DATAEQ(shared_secret, CBS_data(k),
MLKEM_SHARED_SECRET_BYTES, "shared_secret");
}
#define S_START 0
#define S_COMMENT 1
#define S_PRIVATE_KEY 2
#define S_CIPHERTEXT 3
#define S_RESULT 4
#define S_SHARED_SECRET 5
int
main(int argc, char **argv)
{
CBS ciphertext, shared_secret, private_key;
const uint8_t *p = NULL;
int should_fail = 0;
char *buf;
FILE *fp;
int state;
fprintf(stderr, "Testing decap test vectors in %s\n", argv[1]);
TEST((fp = fopen(argv[1], "r")) == NULL, "can't open test file");
MALLOC(buf, 16*1024);
state = S_COMMENT;
test_number = 1;
while (fgets(buf, 16*1024, fp) != NULL) {
switch (state) {
case S_START:
if (strcmp(buf, "\n") != 0)
break;
state = S_COMMENT;
break;
case S_COMMENT:
if (strncmp(buf, "#", 1) != 0)
break;
state = S_PRIVATE_KEY;
break;
case S_PRIVATE_KEY:
if (strncmp(buf, "private_key: ",
strlen("private_key: ")) != 0)
break;
grab_data(&private_key, buf, strlen("private_key: "));
p = CBS_data(&private_key);
state = S_CIPHERTEXT;
break;
case S_CIPHERTEXT:
if (strncmp(buf, "ciphertext: ",
strlen("ciphertext: ")) != 0)
break;
grab_data(&ciphertext, buf, strlen("ciphertext: "));
state = S_RESULT;
break;
case S_RESULT:
if (strncmp(buf, "result: pass",
strlen("result: pass")) != 0)
should_fail = 1;
else
should_fail = 0;
state = S_SHARED_SECRET;
break;
case S_SHARED_SECRET:
if (strncmp(buf, "shared_secret: ",
strlen("shared_secret: ")) != 0)
break;
grab_data(&shared_secret, buf,
strlen("shared_secret: "));
MlkemDecapFileTest(&ciphertext, &shared_secret,
&private_key, should_fail);
free((void *)CBS_data(&ciphertext));
free((void *)CBS_data(&shared_secret));
free((void *)p);
test_number++;
state = S_START;
break;
}
}
free(buf);
exit(failure);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,135 @@
/* Copyright (c) 2024, Google Inc.
* Copyright (c) 2024, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include "bytestring.h"
#include "mlkem.h"
#include "mlkem_internal.h"
#include "sha3_internal.h"
#include "mlkem_tests_util.h"
static void
MlkemEncapFileTest(CBS *entropy, CBS *public_key, CBS *expected_ciphertext,
CBS *expected_shared_secret, int should_fail)
{
uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
struct MLKEM1024_public_key pub;
int parse_ok;
parse_ok = MLKEM1024_parse_public_key(&pub, public_key);
if (!parse_ok) {
TEST(!should_fail, "parse_public_key");
return;
}
MLKEM1024_encap(ciphertext, shared_secret, &pub);
TEST_DATAEQ(shared_secret, CBS_data(expected_shared_secret),
MLKEM_SHARED_SECRET_BYTES, "shared_secret");
TEST_DATAEQ(ciphertext, CBS_data(expected_ciphertext),
MLKEM1024_CIPHERTEXT_BYTES, "shared_secret");
}
#define S_START 0
#define S_COMMENT 1
#define S_ENTROPY 2
#define S_PUBLIC_KEY 3
#define S_RESULT 4
#define S_CIPHERTEXT 5
#define S_SHARED_SECRET 6
int
main(int argc, char **argv)
{
CBS entropy, public_key, ciphertext, shared_secret;
const uint8_t *p = NULL;
int should_fail = 0;
char *buf;
FILE *fp;
int state;
fprintf(stderr, "Testing encap test vectors in %s\n", argv[1]);
TEST((fp = fopen(argv[1], "r")) == NULL, "can't open test file");
MALLOC(buf, 16*1024);
state = S_COMMENT;
test_number = 1;
while (fgets(buf, 16*1024, fp) != NULL) {
switch (state) {
case S_START:
if (strcmp(buf, "\n") != 0)
break;
state = S_COMMENT;
break;
case S_COMMENT:
if (strncmp(buf, "#", 1) != 0)
break;
state = S_ENTROPY;
break;
case S_ENTROPY:
if (strncmp(buf, "entropy: ", strlen("entropy: ")) != 0)
break;
grab_data(&entropy, buf, strlen("entropy: "));
p = CBS_data(&entropy);
state = S_PUBLIC_KEY;
break;
case S_PUBLIC_KEY:
if (strncmp(buf, "public_key: ",
strlen("public_key: ")) != 0)
break;
grab_data(&public_key, buf, strlen("public_key: "));
p = CBS_data(&public_key);
state = S_RESULT;
break;
case S_RESULT:
if (strncmp(buf, "result: pass",
strlen("result: pass")) != 0)
should_fail = 1;
else
should_fail = 0;
state = S_CIPHERTEXT;
break;
case S_CIPHERTEXT:
if (strncmp(buf, "ciphertext: ",
strlen("ciphertext: ")) != 0)
break;
grab_data(&ciphertext, buf, strlen("ciphertext: "));
state = S_RESULT;
break;
case S_SHARED_SECRET:
if (strncmp(buf, "shared_secret: ",
strlen("shared_secret: ")) != 0)
break;
grab_data(&shared_secret, buf,
strlen("shared_secret: "));
MlkemEncapFileTest(&entropy, &public_key, &ciphertext,
&shared_secret, should_fail);
free((void *)CBS_data(&ciphertext));
free((void *)CBS_data(&shared_secret));
free((void *)p);
test_number++;
state = S_START;
break;
}
}
free(buf);
exit(failure);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
/* Copyright (c) 2024, Google Inc.
* Copyright (c) 2024, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bytestring.h"
#include "sha3_internal.h"
#include "mlkem.h"
#include "mlkem_internal.h"
#include "mlkem_tests_util.h"
static int
encode_private_key(const struct MLKEM1024_private_key *priv, uint8_t **out_buf,
size_t *out_len)
{
CBB cbb;
if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
return 0;
if (!MLKEM1024_marshal_private_key(&cbb, priv))
return 0;
if (!CBB_finish(&cbb, out_buf, out_len))
return 0;
CBB_cleanup(&cbb);
return 1;
}
/*
* The structure of this test is taken from
* https://github.com/C2SP/CCTV/blob/main/ML-KEM/README.md?ref=words.filippo.io#accumulated-pq-crystals-vectors
* but the final value has been updated to reflect the change from Kyber to
* ML-KEM.
*
* The deterministic RNG is a single SHAKE-128 instance with an empty input.
* (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
*/
static void
MlkemIterativeTest()
{
/* https://github.com/C2SP/CCTV/tree/main/ML-KEM */
/*
* The deterministic RNG is a single SHAKE-128 instance with an empty input.
* (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
*/
const uint8_t kExpectedSeedStart[16] = {
0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
0x50, 0x76, 0x05, 0x85, 0x3e
};
/*
* Filippo says:
* ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543
* but Boring believes this:
*/
const uint8_t kExpectedAdam[32] = {
0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd,
0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24,
0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5
};
uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
uint8_t invalid_ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
uint8_t seed[MLKEM_SEED_BYTES] = {0};
struct MLKEM1024_private_key priv;
struct MLKEM1024_public_key pub;
sha3_ctx drng, results;
uint8_t out[32];
int i;
shake128_init(&drng);
shake128_init(&results);
shake_xof(&drng);
for (i = 0; i < 10000; i++) {
uint8_t *encoded_private_key = NULL;
size_t encoded_private_key_len;
/*
* This should draw both d and z from DRNG concatenating in
* seed.
*/
shake_out(&drng, seed, sizeof(seed));
if (i == 0) {
TEST_DATAEQ(seed, kExpectedSeedStart,
sizeof(kExpectedSeedStart), "seed start");
}
/* generate ek as encoded_public_key */
MLKEM1024_generate_key_external_entropy(encoded_public_key,
&priv, seed);
MLKEM1024_public_from_private(&pub, &priv);
/* hash in ek */
shake_update(&results, encoded_public_key,
sizeof(encoded_public_key));
/* marshal priv to dk as encoded_private_key */
TEST(!encode_private_key(&priv, &encoded_private_key,
&encoded_private_key_len), "encode_private_key");
/* hash in dk */
shake_update(&results, encoded_private_key,
encoded_private_key_len);
free(encoded_private_key);
/* draw m as encap entropy from DRNG */
shake_out(&drng, encap_entropy, sizeof(encap_entropy));
/* generate ct as ciphertext, k as shared_secret */
MLKEM1024_encap_external_entropy(ciphertext, shared_secret,
&pub, encap_entropy);
/* hash in ct */
shake_update(&results, ciphertext, sizeof(ciphertext));
/* hash in k */
shake_update(&results, shared_secret, sizeof(shared_secret));
/* draw ct as invalid_ciphertxt from DRNG */
shake_out(&drng, invalid_ciphertext,
sizeof(invalid_ciphertext));
/* generte k as shared secret from invalid ciphertext */
TEST(!MLKEM1024_decap(shared_secret, invalid_ciphertext,
sizeof(invalid_ciphertext), &priv), "decap failed!");
/* hash in k */
shake_update(&results, shared_secret, sizeof(shared_secret));
}
shake_xof(&results);
shake_out(&results, out, 32);
TEST_DATAEQ(out, kExpectedAdam, 32, "final result hash");
}
int
main(int argc, char **argv)
{
MlkemIterativeTest();
exit(failure);
}

View File

@ -0,0 +1,129 @@
/* Copyright (c) 2024, Google Inc.
* Copyright (c) 2024, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include "bytestring.h"
#include "mlkem.h"
#include "mlkem_internal.h"
#include "sha3_internal.h"
#include "mlkem_tests_util.h"
static int
encode_private_key(const struct MLKEM1024_private_key *priv, uint8_t **out_buf,
size_t *out_len)
{
CBB cbb;
if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
return 0;
if (!MLKEM1024_marshal_private_key(&cbb, priv))
return 0;
if (!CBB_finish(&cbb, out_buf, out_len))
return 0;
CBB_cleanup(&cbb);
return 1;
}
static void
MlkemKeygenFileTest(CBS *seed, CBS *public_key, CBS *private_key)
{
struct MLKEM1024_private_key priv;
uint8_t *encoded_private_key = NULL;
uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
size_t len;
TEST(CBS_len(seed) != MLKEM_SEED_BYTES, "seed len bogus");
TEST(CBS_len(private_key) != MLKEM1024_PRIVATE_KEY_BYTES,
"expected private key len bogus");
TEST(CBS_len(public_key) != MLKEM1024_PUBLIC_KEY_BYTES,
"expected public key len bogus");
MLKEM1024_generate_key_external_entropy(encoded_public_key, &priv,
CBS_data(seed));
TEST(!encode_private_key(&priv, &encoded_private_key,
&len), "encode_private_key");
TEST(len != MLKEM1024_PRIVATE_KEY_BYTES, "private key len bogus");
TEST_DATAEQ(encoded_public_key, CBS_data(public_key),
MLKEM1024_PUBLIC_KEY_BYTES, "public key");
TEST_DATAEQ(encoded_private_key, CBS_data(private_key),
MLKEM1024_PRIVATE_KEY_BYTES, "private key");
free(encoded_private_key);
}
#define S_START 0
#define S_SEED 1
#define S_PUBLIC_KEY 2
#define S_PRIVATE_KEY 3
int
main(int argc, char **argv)
{
CBS seed, public_key, private_key;
char *buf;
FILE *fp;
int state;
fprintf(stderr, "Testing keygen test vectors in %s\n", argv[1]);
TEST((fp = fopen(argv[1], "r")) == NULL, "can't open test file");
MALLOC(buf, 16*1024);
state = S_SEED;
test_number = 1;
while (fgets(buf, 16*1024, fp) != NULL) {
switch (state) {
case S_START:
if (strcmp(buf, "\n") != 0)
break;
state = S_SEED;
break;
case S_SEED:
if (strncmp(buf, "seed: ", strlen("seed: ")) != 0) {
break;
}
grab_data(&seed, buf, strlen("seed: "));
state = S_PUBLIC_KEY;
break;
case S_PUBLIC_KEY:
if (strncmp(buf, "public_key: ",
strlen("public_key: ")) != 0)
break;
grab_data(&public_key, buf, strlen("public_key: "));
state = S_PRIVATE_KEY;
break;
case S_PRIVATE_KEY:
if (strncmp(buf, "private_key: ",
strlen("private_key: ")) != 0)
break;
grab_data(&private_key, buf, strlen("private_key: "));
state = S_START;
break;
MlkemKeygenFileTest(&seed, &public_key, &private_key);
free((void *)CBS_data(&seed));
free((void *)CBS_data(&public_key));
free((void *)CBS_data(&private_key));
test_number++;
state = S_START;
break;
}
}
free(buf);
exit(failure);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,110 @@
/* Copyright (c) 2024, Google Inc.
* Copyright (c) 2024, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include "bytestring.h"
#include "mlkem.h"
#include "mlkem_internal.h"
#include "sha3_internal.h"
#include "mlkem_tests_util.h"
static void
MlkemNistDecapFileTest(CBS *c, CBS *k, CBS *dk)
{
uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
struct MLKEM1024_private_key priv;
TEST(CBS_len(dk) != MLKEM1024_PRIVATE_KEY_BYTES,
"private key len bogus");
TEST(CBS_len(k) != MLKEM_SHARED_SECRET_BYTES,
"shared secret len bogus");
TEST(!MLKEM1024_parse_private_key(&priv, dk), "parse_private_key");
TEST(!MLKEM1024_decap(shared_secret, CBS_data(c), CBS_len(c), &priv),
"decap");
TEST_DATAEQ(shared_secret, CBS_data(k),
MLKEM_SHARED_SECRET_BYTES, "shared_secret");
}
#define S_START 0
#define S_CIPHERTEXT 1
#define S_SHARED_SECRET 2
#define S_PRIVATE_KEY 3
int
main(int argc, char **argv)
{
CBS ciphertext, shared_secret, private_key;
const uint8_t *p;
char *buf;
FILE *fp;
int state;
fprintf(stderr, "Testing NIST decap test vectors in %s\n", argv[1]);
TEST((fp = fopen(argv[1], "r")) == NULL, "can't open test file");
MALLOC(buf, 16*1024);
state = S_CIPHERTEXT;
test_number = 1;
while (fgets(buf, 16*1024, fp) != NULL) {
switch (state) {
case S_START:
if (strcmp(buf, "\n") != 0)
break;
state = S_CIPHERTEXT;
break;
case S_CIPHERTEXT:
if (strncmp(buf, "ciphertext: ",
strlen("ciphertext: ")) != 0) {
break;
}
grab_data(&ciphertext, buf, strlen("ciphertext: "));
state = S_SHARED_SECRET;
break;
case S_SHARED_SECRET:
if (strncmp(buf, "shared_secret: ",
strlen("shared_secret: ")) != 0)
break;
grab_data(&shared_secret, buf,
strlen("shared_secret: "));
state = S_PRIVATE_KEY;
break;
case S_PRIVATE_KEY:
if (strncmp(buf, "private_key: ",
strlen("private_key: ")) != 0)
break;
grab_data(&private_key, buf, strlen("private_key: "));
p = CBS_data(&private_key);
MlkemNistDecapFileTest(&ciphertext, &shared_secret,
&private_key);
free((void *)CBS_data(&ciphertext));
free((void *)CBS_data(&shared_secret));
free((void *)p);
state = S_START;
test_number++;
break;
}
}
free(buf);
exit(failure);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,136 @@
/* Copyright (c) 2024, Google Inc.
* Copyright (c) 2024, Bob Beck <beck@obtuse.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include "bytestring.h"
#include "mlkem.h"
#include "mlkem_internal.h"
#include "sha3_internal.h"
#include "mlkem_tests_util.h"
static int
encode_private_key(const struct MLKEM1024_private_key *priv, uint8_t **out_buf,
size_t *out_len)
{
CBB cbb;
if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
return 0;
if (!MLKEM1024_marshal_private_key(&cbb, priv))
return 0;
if (!CBB_finish(&cbb, out_buf, out_len))
return 0;
CBB_cleanup(&cbb);
return 1;
}
static void
MlkemNistKeygenFileTest(CBS *z, CBS *d, CBS *ek, CBS *dk)
{
uint8_t seed[MLKEM_SEED_BYTES];
struct MLKEM1024_private_key priv;
uint8_t *encoded_private_key = NULL;
uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
size_t len;
TEST(CBS_len(d) != (MLKEM_SEED_BYTES / 2), "d len bogus");
TEST(CBS_len(z) != (MLKEM_SEED_BYTES / 2), "z len bogus");
TEST(CBS_len(dk) != MLKEM1024_PRIVATE_KEY_BYTES,
"expected private key len bogus");
TEST(CBS_len(ek) != MLKEM1024_PUBLIC_KEY_BYTES,
"expected public key len bogus");
memcpy(&seed[0], CBS_data(d), CBS_len(d));
memcpy(&seed[MLKEM_SEED_BYTES / 2], CBS_data(z), CBS_len(z));
MLKEM1024_generate_key_external_entropy(encoded_public_key, &priv, seed);
TEST(!encode_private_key(&priv, &encoded_private_key,
&len), "encode_private_key");
TEST(len != MLKEM1024_PRIVATE_KEY_BYTES, "private key len bogus");
TEST_DATAEQ(encoded_public_key, CBS_data(ek),
MLKEM1024_PUBLIC_KEY_BYTES, "public key");
TEST_DATAEQ(encoded_private_key, CBS_data(dk),
MLKEM1024_PRIVATE_KEY_BYTES, "private key");
free(encoded_private_key);
}
#define S_START 0
#define S_Z 1
#define S_D 2
#define S_EK 3
#define S_DK 4
int
main(int argc, char **argv)
{
CBS z, d, ek, dk;
char *buf;
FILE *fp;
int state;
fprintf(stderr, "Testing NIST keygen test vectors in %s\n", argv[1]);
TEST((fp = fopen(argv[1], "r")) == NULL, "can't open test file");
MALLOC(buf, 16*1024);
state = S_Z;
test_number = 1;
while (fgets(buf, 16*1024, fp) != NULL) {
switch (state) {
case S_START:
if (strcmp(buf, "\n") != 0)
break;
state = S_Z;
break;
case S_Z:
if (strncmp(buf, "z: ", strlen("z: ")) != 0) {
break;
}
grab_data(&z, buf, strlen("z: "));
state = S_D;
break;
case S_D:
if (strncmp(buf, "d: ", strlen("d: ")) != 0)
break;
grab_data(&d, buf, strlen("d: "));
state = S_EK;
break;
case S_EK:
if (strncmp(buf, "ek: ", strlen("ek: ")) != 0)
break;
grab_data(&ek, buf, strlen("ek: "));
state = S_DK;
break;
case S_DK:
if (strncmp(buf, "dk: ", strlen("dk: ")) != 0)
break;
grab_data(&dk, buf, strlen("dk: "));
MlkemNistKeygenFileTest(&z, &d, &ek, &dk);
free((void *)CBS_data(&z));
free((void *)CBS_data(&d));
free((void *)CBS_data(&ek));
free((void *)CBS_data(&dk));
test_number++;
state = S_START;
break;
}
}
free(buf);
exit(failure);
}

File diff suppressed because one or more lines are too long

View File

@ -52,8 +52,8 @@ encode_private_key(const struct MLKEM768_private_key *priv, uint8_t **out_buf,
return 1;
}
int
main(int argc, char **argv)
static void
MlKem768UnitTest()
{
struct MLKEM768_private_key *priv, *priv2;
struct MLKEM768_public_key *pub, *pub2;
@ -145,5 +145,137 @@ main(int argc, char **argv)
free(priv);
free(priv2);
}
static int
encode_1024public_key(const struct MLKEM1024_public_key *pub, uint8_t **out_buf,
size_t *out_len)
{
CBB cbb;
if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
return 0;
if (!MLKEM1024_marshal_public_key(&cbb, pub))
return 0;
if (!CBB_finish(&cbb, out_buf, out_len))
return 0;
CBB_cleanup(&cbb);
return 1;
}
static int
encode_1024private_key(const struct MLKEM1024_private_key *priv, uint8_t **out_buf,
size_t *out_len)
{
CBB cbb;
if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
return 0;
if (!MLKEM1024_marshal_private_key(&cbb, priv))
return 0;
if (!CBB_finish(&cbb, out_buf, out_len))
return 0;
CBB_cleanup(&cbb);
return 1;
}
static void
MlKem1024UnitTest()
{
struct MLKEM1024_private_key *priv, *priv2;
struct MLKEM1024_public_key *pub, *pub2;
uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES];
uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES];
uint8_t first_two_bytes[2];
uint8_t *encoded_private_key = NULL, *tmp_buf = NULL;
size_t encoded_private_key_len, tmp_buf_len;
CBS cbs;
fprintf(stderr, "ML-KEM 1024...\n");
MALLOC(priv, sizeof(struct MLKEM1024_private_key));
MLKEM1024_generate_key(encoded_public_key, NULL, priv);
memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes));
memset(encoded_public_key, 0xff, sizeof(first_two_bytes));
CBS_init(&cbs, encoded_public_key,
sizeof(encoded_public_key));
MALLOC(pub, sizeof(struct MLKEM1024_public_key));
/* Parsing should fail because the first coefficient is >= kPrime; */
TEST(MLKEM1024_parse_public_key(pub, &cbs),
"Kyber_parse_public_key should have failed");
memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes));
CBS_init(&cbs, encoded_public_key, sizeof(encoded_public_key));
TEST(!MLKEM1024_parse_public_key(pub, &cbs),
"MLKEM1024_parse_public_key");
TEST(CBS_len(&cbs) != 0u, "CBS_len must be 0");
TEST(!encode_1024public_key(pub, &tmp_buf, &tmp_buf_len),
"encode_1024public_key");
TEST(sizeof(encoded_public_key) != tmp_buf_len,
"encoded public key lengths differ");
TEST_DATAEQ(tmp_buf, encoded_public_key, tmp_buf_len,
"encoded public keys");
free(tmp_buf);
tmp_buf = NULL;
MALLOC(pub2, sizeof(struct MLKEM1024_public_key));
MLKEM1024_public_from_private(pub2, priv);
TEST(!encode_1024public_key(pub2, &tmp_buf, &tmp_buf_len),
"encode_public_key");
TEST(sizeof(encoded_public_key) != tmp_buf_len,
"encoded public key lengths differ");
TEST_DATAEQ(tmp_buf, encoded_public_key, tmp_buf_len,
"encoded pubic keys");
free(tmp_buf);
tmp_buf = NULL;
TEST(!encode_1024private_key(priv, &encoded_private_key,
&encoded_private_key_len), "encode_1024private_key");
memcpy(first_two_bytes, encoded_private_key, sizeof(first_two_bytes));
memset(encoded_private_key, 0xff, sizeof(first_two_bytes));
CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
MALLOC(priv2, sizeof(struct MLKEM1024_private_key));
/* Parsing should fail because the first coefficient is >= kPrime. */
TEST(MLKEM1024_parse_private_key(priv2, &cbs), "Should not have parsed");
memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes));
CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
TEST(!MLKEM1024_parse_private_key(priv2, &cbs),
"MLKEM1024_parse_private_key");
TEST(!encode_1024private_key(priv2, &tmp_buf, &tmp_buf_len),
"encode_private_key");
TEST(encoded_private_key_len != tmp_buf_len,
"encoded private key lengths differ");
TEST_DATAEQ(tmp_buf, encoded_private_key, encoded_private_key_len,
"encoded private keys");
free(tmp_buf);
tmp_buf = NULL;
MLKEM1024_encap(ciphertext, shared_secret1, pub);
MLKEM1024_decap(shared_secret2, ciphertext, MLKEM1024_CIPHERTEXT_BYTES,
priv);
TEST_DATAEQ(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
"shared secrets with priv");
MLKEM1024_decap(shared_secret2, ciphertext, MLKEM1024_CIPHERTEXT_BYTES,
priv2);
TEST_DATAEQ(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
"shared secrets with priv2");
free(encoded_private_key);
free(pub);
free(pub2);
free(priv);
free(priv2);
}
int
main(int argc, char **argv)
{
MlKem768UnitTest();
MlKem1024UnitTest();
exit(failure);
}