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