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 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 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 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 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