1*8d6f708dStb /* $OpenBSD: mlkem_unittest.c,v 1.6 2024/12/26 12:35:25 tb Exp $ */
2d4ed7533Stb /*
38889493eStb * Copyright (c) 2024 Google Inc.
48889493eStb * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
575c083a0Sbeck *
675c083a0Sbeck * Permission to use, copy, modify, and/or distribute this software for any
775c083a0Sbeck * purpose with or without fee is hereby granted, provided that the above
875c083a0Sbeck * copyright notice and this permission notice appear in all copies.
975c083a0Sbeck *
1075c083a0Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1175c083a0Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1275c083a0Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1375c083a0Sbeck * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1475c083a0Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1575c083a0Sbeck * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16d4ed7533Stb * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d4ed7533Stb */
1875c083a0Sbeck
198889493eStb #include <err.h>
20d4ed7533Stb #include <stdint.h>
2175c083a0Sbeck #include <stdio.h>
22d4ed7533Stb #include <stdlib.h>
2375c083a0Sbeck #include <string.h>
2475c083a0Sbeck
258889493eStb #include "bytestring.h"
268889493eStb #include "mlkem.h"
27d4ed7533Stb
2875c083a0Sbeck #include "mlkem_tests_util.h"
2975c083a0Sbeck
300c814320Stb struct unittest_ctx {
310c814320Stb void *priv;
320c814320Stb void *pub;
330c814320Stb void *priv2;
340c814320Stb void *pub2;
350c814320Stb uint8_t *encoded_public_key;
360c814320Stb size_t encoded_public_key_len;
370c814320Stb uint8_t *ciphertext;
380c814320Stb size_t ciphertext_len;
390c814320Stb mlkem_decap_fn decap;
400c814320Stb mlkem_encap_fn encap;
410c814320Stb mlkem_generate_key_fn generate_key;
420c814320Stb mlkem_parse_private_key_fn parse_private_key;
430c814320Stb mlkem_parse_public_key_fn parse_public_key;
440c814320Stb mlkem_encode_private_key_fn encode_private_key;
450c814320Stb mlkem_encode_public_key_fn encode_public_key;
460c814320Stb mlkem_public_from_private_fn public_from_private;
470c814320Stb };
480c814320Stb
4975c083a0Sbeck static int
MlKemUnitTest(struct unittest_ctx * ctx)500c814320Stb MlKemUnitTest(struct unittest_ctx *ctx)
5175c083a0Sbeck {
5275c083a0Sbeck uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES];
5375c083a0Sbeck uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES];
5475c083a0Sbeck uint8_t first_two_bytes[2];
5575c083a0Sbeck uint8_t *encoded_private_key = NULL, *tmp_buf = NULL;
5675c083a0Sbeck size_t encoded_private_key_len, tmp_buf_len;
5775c083a0Sbeck CBS cbs;
588889493eStb int failed = 0;
5975c083a0Sbeck
600c814320Stb ctx->generate_key(ctx->encoded_public_key, NULL, ctx->priv);
6175c083a0Sbeck
620c814320Stb memcpy(first_two_bytes, ctx->encoded_public_key, sizeof(first_two_bytes));
630c814320Stb memset(ctx->encoded_public_key, 0xff, sizeof(first_two_bytes));
648889493eStb
650c814320Stb CBS_init(&cbs, ctx->encoded_public_key, ctx->encoded_public_key_len);
668889493eStb
678889493eStb /* Parsing should fail because the first coefficient is >= kPrime. */
680c814320Stb if (ctx->parse_public_key(ctx->pub, &cbs)) {
690c814320Stb warnx("parse_public_key should have failed");
708889493eStb failed |= 1;
718889493eStb }
7275c083a0Sbeck
730c814320Stb memcpy(ctx->encoded_public_key, first_two_bytes, sizeof(first_two_bytes));
740c814320Stb CBS_init(&cbs, ctx->encoded_public_key, ctx->encoded_public_key_len);
750c814320Stb if (!ctx->parse_public_key(ctx->pub, &cbs)) {
768889493eStb warnx("MLKEM768_parse_public_key");
778889493eStb failed |= 1;
788889493eStb }
7975c083a0Sbeck
808889493eStb if (CBS_len(&cbs) != 0u) {
818889493eStb warnx("CBS_len must be 0");
828889493eStb failed |= 1;
838889493eStb }
848889493eStb
850c814320Stb if (!ctx->encode_public_key(ctx->pub, &tmp_buf, &tmp_buf_len)) {
868889493eStb warnx("encode_public_key");
878889493eStb failed |= 1;
888889493eStb }
890c814320Stb if (ctx->encoded_public_key_len != tmp_buf_len) {
900c814320Stb warnx("encoded public key lengths differ");
918889493eStb failed |= 1;
928889493eStb }
938889493eStb
940c814320Stb if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len,
958889493eStb "encoded public keys") != 0) {
968889493eStb warnx("compare_data");
978889493eStb failed |= 1;
988889493eStb }
9975c083a0Sbeck free(tmp_buf);
10075c083a0Sbeck tmp_buf = NULL;
10175c083a0Sbeck
1020c814320Stb ctx->public_from_private(ctx->pub2, ctx->priv);
1030c814320Stb if (!ctx->encode_public_key(ctx->pub2, &tmp_buf, &tmp_buf_len)) {
1040c814320Stb warnx("encode_public_key");
1058889493eStb failed |= 1;
1068889493eStb }
1070c814320Stb if (ctx->encoded_public_key_len != tmp_buf_len) {
1080c814320Stb warnx("encoded public key lengths differ");
1098889493eStb failed |= 1;
1108889493eStb }
1118889493eStb
1120c814320Stb if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len,
1138889493eStb "encoded public keys") != 0) {
1148889493eStb warnx("compare_data");
1158889493eStb failed |= 1;
1168889493eStb }
11775c083a0Sbeck free(tmp_buf);
11875c083a0Sbeck tmp_buf = NULL;
11975c083a0Sbeck
1200c814320Stb if (!ctx->encode_private_key(ctx->priv, &encoded_private_key,
1218889493eStb &encoded_private_key_len)) {
1228889493eStb warnx("mlkem768_encode_private_key");
1238889493eStb failed |= 1;
1248889493eStb }
12575c083a0Sbeck
12675c083a0Sbeck memcpy(first_two_bytes, encoded_private_key, sizeof(first_two_bytes));
12775c083a0Sbeck memset(encoded_private_key, 0xff, sizeof(first_two_bytes));
12875c083a0Sbeck CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
1298889493eStb
13075c083a0Sbeck /* Parsing should fail because the first coefficient is >= kPrime. */
1310c814320Stb if (ctx->parse_private_key(ctx->priv2, &cbs)) {
1328889493eStb warnx("MLKEM768_parse_private_key should have failed");
1338889493eStb failed |= 1;
1348889493eStb }
13575c083a0Sbeck
13675c083a0Sbeck memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes));
13775c083a0Sbeck CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
1388889493eStb
1390c814320Stb if (!ctx->parse_private_key(ctx->priv2, &cbs)) {
1408889493eStb warnx("MLKEM768_parse_private_key");
1418889493eStb failed |= 1;
1428889493eStb }
1438889493eStb
1440c814320Stb if (!ctx->encode_private_key(ctx->priv2, &tmp_buf, &tmp_buf_len)) {
1450c814320Stb warnx("encode_private_key");
1468889493eStb failed |= 1;
1478889493eStb }
1488889493eStb
1498889493eStb if (encoded_private_key_len != tmp_buf_len) {
1500c814320Stb warnx("encode private key lengths differ");
1518889493eStb failed |= 1;
1528889493eStb }
1538889493eStb
1540c814320Stb if (compare_data(encoded_private_key, tmp_buf, tmp_buf_len,
1558889493eStb "encoded private key") != 0) {
1568889493eStb warnx("compare_data");
1578889493eStb failed |= 1;
1588889493eStb }
1598889493eStb
16075c083a0Sbeck free(tmp_buf);
16175c083a0Sbeck tmp_buf = NULL;
16275c083a0Sbeck
1630c814320Stb ctx->encap(ctx->ciphertext, shared_secret1, ctx->pub);
1640c814320Stb ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len,
1650c814320Stb ctx->priv);
1668889493eStb if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
1670c814320Stb "shared secrets with priv") != 0) {
1688889493eStb warnx("compare_data");
1698889493eStb failed |= 1;
1708889493eStb }
1718889493eStb
1720c814320Stb ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len,
1730c814320Stb ctx->priv2);
1748889493eStb if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
1750c814320Stb "shared secrets with priv2") != 0) {
1768889493eStb warnx("compare_data");
1778889493eStb failed |= 1;
1788889493eStb }
17908c63c71Sbeck
18008c63c71Sbeck free(encoded_private_key);
1818889493eStb
1828889493eStb return failed;
18308c63c71Sbeck }
18408c63c71Sbeck
185*8d6f708dStb static int
mlkem768_unittest(void)186*8d6f708dStb mlkem768_unittest(void)
18708c63c71Sbeck {
1880c814320Stb struct MLKEM768_private_key mlkem768_priv, mlkem768_priv2;
1890c814320Stb struct MLKEM768_public_key mlkem768_pub, mlkem768_pub2;
1900c814320Stb uint8_t mlkem768_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
1910c814320Stb uint8_t mlkem768_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
1920c814320Stb struct unittest_ctx mlkem768_test = {
1930c814320Stb .priv = &mlkem768_priv,
1940c814320Stb .pub = &mlkem768_pub,
1950c814320Stb .priv2 = &mlkem768_priv2,
1960c814320Stb .pub2 = &mlkem768_pub2,
1970c814320Stb .encoded_public_key = mlkem768_encoded_public_key,
1980c814320Stb .encoded_public_key_len = sizeof(mlkem768_encoded_public_key),
1990c814320Stb .ciphertext = mlkem768_ciphertext,
2000c814320Stb .ciphertext_len = sizeof(mlkem768_ciphertext),
2010c814320Stb .decap = mlkem768_decap,
2020c814320Stb .encap = mlkem768_encap,
2030c814320Stb .generate_key = mlkem768_generate_key,
2040c814320Stb .parse_private_key = mlkem768_parse_private_key,
2050c814320Stb .parse_public_key = mlkem768_parse_public_key,
2060c814320Stb .encode_private_key = mlkem768_encode_private_key,
2070c814320Stb .encode_public_key = mlkem768_encode_public_key,
2080c814320Stb .public_from_private = mlkem768_public_from_private,
2090c814320Stb };
210*8d6f708dStb
211*8d6f708dStb return MlKemUnitTest(&mlkem768_test);
212*8d6f708dStb }
213*8d6f708dStb
214*8d6f708dStb static int
mlkem1024_unittest(void)215*8d6f708dStb mlkem1024_unittest(void)
216*8d6f708dStb {
2170c814320Stb struct MLKEM1024_private_key mlkem1024_priv, mlkem1024_priv2;
2180c814320Stb struct MLKEM1024_public_key mlkem1024_pub, mlkem1024_pub2;
2190c814320Stb uint8_t mlkem1024_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
2200c814320Stb uint8_t mlkem1024_ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
2210c814320Stb struct unittest_ctx mlkem1024_test = {
2220c814320Stb .priv = &mlkem1024_priv,
2230c814320Stb .pub = &mlkem1024_pub,
2240c814320Stb .priv2 = &mlkem1024_priv2,
2250c814320Stb .pub2 = &mlkem1024_pub2,
2260c814320Stb .encoded_public_key = mlkem1024_encoded_public_key,
2270c814320Stb .encoded_public_key_len = sizeof(mlkem1024_encoded_public_key),
2280c814320Stb .ciphertext = mlkem1024_ciphertext,
2290c814320Stb .ciphertext_len = sizeof(mlkem1024_ciphertext),
2300c814320Stb .decap = mlkem1024_decap,
2310c814320Stb .encap = mlkem1024_encap,
2320c814320Stb .generate_key = mlkem1024_generate_key,
2330c814320Stb .parse_private_key = mlkem1024_parse_private_key,
2340c814320Stb .parse_public_key = mlkem1024_parse_public_key,
2350c814320Stb .encode_private_key = mlkem1024_encode_private_key,
2360c814320Stb .encode_public_key = mlkem1024_encode_public_key,
2370c814320Stb .public_from_private = mlkem1024_public_from_private,
2380c814320Stb };
239*8d6f708dStb
240*8d6f708dStb return MlKemUnitTest(&mlkem1024_test);
241*8d6f708dStb }
242*8d6f708dStb
243*8d6f708dStb int
main(void)244*8d6f708dStb main(void)
245*8d6f708dStb {
2468889493eStb int failed = 0;
24708c63c71Sbeck
248*8d6f708dStb /*
249*8d6f708dStb * XXX - this is split into two helper functions since having a few
250*8d6f708dStb * ML-KEM key blobs on the stack makes Emscripten's stack explode,
251*8d6f708dStb * leading to inscrutable silent failures unles ASAN is enabled.
252*8d6f708dStb * Go figure.
253*8d6f708dStb */
254*8d6f708dStb
255*8d6f708dStb failed |= mlkem768_unittest();
256*8d6f708dStb failed |= mlkem1024_unittest();
2578889493eStb
2588889493eStb return failed;
25975c083a0Sbeck }
260