1*74549188Stb /* $OpenBSD: mlkem_iteration_tests.c,v 1.2 2024/12/26 07:26:45 tb Exp $ */ 20c814320Stb /* 30c814320Stb * Copyright (c) 2024 Google Inc. 40c814320Stb * Copyright (c) 2024 Bob Beck <beck@obtuse.com> 50c814320Stb * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> 60c814320Stb * 70c814320Stb * Permission to use, copy, modify, and/or distribute this software for any 80c814320Stb * purpose with or without fee is hereby granted, provided that the above 90c814320Stb * copyright notice and this permission notice appear in all copies. 100c814320Stb * 110c814320Stb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 120c814320Stb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 130c814320Stb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 140c814320Stb * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 150c814320Stb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 160c814320Stb * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 170c814320Stb * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 180c814320Stb */ 190c814320Stb 200c814320Stb #include <err.h> 210c814320Stb #include <stdint.h> 220c814320Stb #include <stdio.h> 230c814320Stb #include <stdlib.h> 240c814320Stb 250c814320Stb #include "mlkem.h" 260c814320Stb 270c814320Stb #include "mlkem_internal.h" 280c814320Stb #include "mlkem_tests_util.h" 290c814320Stb #include "sha3_internal.h" 300c814320Stb 310c814320Stb /* 320c814320Stb * Based on https://c2sp.org/CCTV/ML-KEM 330c814320Stb * 340c814320Stb * The final value has been updated to reflect the change from Kyber to ML-KEM. 350c814320Stb * 360c814320Stb * The deterministic RNG is a single SHAKE-128 instance with an empty input. 370c814320Stb * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.) 380c814320Stb */ 390c814320Stb const uint8_t kExpectedSeedStart[16] = { 400c814320Stb 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45, 410c814320Stb 0x50, 0x76, 0x05, 0x85, 0x3e 420c814320Stb }; 430c814320Stb 440c814320Stb /* 450c814320Stb * Filippo says: 460c814320Stb * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3 470c814320Stb * but Boring believes this: 480c814320Stb */ 490c814320Stb const uint8_t kExpectedAdam768[32] = { 500c814320Stb 0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf, 510c814320Stb 0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1, 520c814320Stb 0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1 530c814320Stb }; 540c814320Stb 550c814320Stb /* 560c814320Stb * Filippo says: 570c814320Stb * ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543 580c814320Stb * but Boring believes this: 590c814320Stb */ 600c814320Stb const uint8_t kExpectedAdam1024[32] = { 610c814320Stb 0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd, 620c814320Stb 0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24, 630c814320Stb 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5 640c814320Stb }; 650c814320Stb 660c814320Stb struct iteration_ctx { 670c814320Stb uint8_t *encoded_public_key; 680c814320Stb size_t encoded_public_key_len; 690c814320Stb uint8_t *ciphertext; 700c814320Stb size_t ciphertext_len; 710c814320Stb uint8_t *invalid_ciphertext; 720c814320Stb size_t invalid_ciphertext_len; 730c814320Stb void *priv; 740c814320Stb void *pub; 750c814320Stb 760c814320Stb mlkem_encode_private_key_fn encode_private_key; 770c814320Stb mlkem_encap_external_entropy_fn encap_external_entropy; 780c814320Stb mlkem_generate_key_external_entropy_fn generate_key_external_entropy; 790c814320Stb mlkem_public_from_private_fn public_from_private; 800c814320Stb mlkem_decap_fn decap; 810c814320Stb 820c814320Stb const uint8_t *start; 830c814320Stb size_t start_len; 840c814320Stb 850c814320Stb const uint8_t *expected; 860c814320Stb size_t expected_len; 870c814320Stb }; 880c814320Stb 890c814320Stb static int 900c814320Stb MlkemIterativeTest(struct iteration_ctx *ctx) 910c814320Stb { 920c814320Stb uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; 930c814320Stb uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; 940c814320Stb uint8_t seed[MLKEM_SEED_BYTES] = {0}; 950c814320Stb sha3_ctx drng, results; 960c814320Stb uint8_t out[32]; 970c814320Stb int i; 980c814320Stb 990c814320Stb shake128_init(&drng); 1000c814320Stb shake128_init(&results); 1010c814320Stb 1020c814320Stb shake_xof(&drng); 1030c814320Stb for (i = 0; i < 10000; i++) { 1040c814320Stb uint8_t *encoded_private_key = NULL; 1050c814320Stb size_t encoded_private_key_len; 1060c814320Stb 1070c814320Stb /* 1080c814320Stb * This should draw both d and z from DRNG concatenating in 1090c814320Stb * seed. 1100c814320Stb */ 1110c814320Stb shake_out(&drng, seed, sizeof(seed)); 1120c814320Stb if (i == 0) { 1130c814320Stb if (compare_data(seed, ctx->start, ctx->start_len, 1140c814320Stb "seed start") != 0) 1150c814320Stb errx(1, "compare_data"); 1160c814320Stb } 1170c814320Stb 1180c814320Stb /* generate ek as encoded_public_key */ 1190c814320Stb ctx->generate_key_external_entropy(ctx->encoded_public_key, 1200c814320Stb ctx->priv, seed); 1210c814320Stb ctx->public_from_private(ctx->pub, ctx->priv); 1220c814320Stb 1230c814320Stb /* hash in ek */ 1240c814320Stb shake_update(&results, ctx->encoded_public_key, 1250c814320Stb ctx->encoded_public_key_len); 1260c814320Stb 1270c814320Stb /* marshal priv to dk as encoded_private_key */ 1280c814320Stb if (!ctx->encode_private_key(ctx->priv, &encoded_private_key, 1290c814320Stb &encoded_private_key_len)) 1300c814320Stb errx(1, "encode private key"); 1310c814320Stb 1320c814320Stb /* hash in dk */ 1330c814320Stb shake_update(&results, encoded_private_key, 1340c814320Stb encoded_private_key_len); 1350c814320Stb 1360c814320Stb free(encoded_private_key); 1370c814320Stb 1380c814320Stb /* draw m as encap entropy from DRNG */ 1390c814320Stb shake_out(&drng, encap_entropy, sizeof(encap_entropy)); 1400c814320Stb 1410c814320Stb /* generate ct as ciphertext, k as shared_secret */ 1420c814320Stb ctx->encap_external_entropy(ctx->ciphertext, shared_secret, 1430c814320Stb ctx->pub, encap_entropy); 1440c814320Stb 1450c814320Stb /* hash in ct */ 1460c814320Stb shake_update(&results, ctx->ciphertext, ctx->ciphertext_len); 1470c814320Stb /* hash in k */ 1480c814320Stb shake_update(&results, shared_secret, sizeof(shared_secret)); 1490c814320Stb 1500c814320Stb /* draw ct as invalid_ciphertxt from DRNG */ 1510c814320Stb shake_out(&drng, ctx->invalid_ciphertext, 1520c814320Stb ctx->invalid_ciphertext_len); 1530c814320Stb 1540c814320Stb /* generate k as shared secret from invalid ciphertext */ 1550c814320Stb if (!ctx->decap(shared_secret, ctx->invalid_ciphertext, 1560c814320Stb ctx->invalid_ciphertext_len, ctx->priv)) 1570c814320Stb errx(1, "decap failed"); 1580c814320Stb 1590c814320Stb /* hash in k */ 1600c814320Stb shake_update(&results, shared_secret, sizeof(shared_secret)); 1610c814320Stb } 1620c814320Stb shake_xof(&results); 1630c814320Stb shake_out(&results, out, sizeof(out)); 1640c814320Stb 1650c814320Stb return compare_data(ctx->expected, out, sizeof(out), "final result hash"); 1660c814320Stb } 1670c814320Stb 1680c814320Stb int 1690c814320Stb main(void) 1700c814320Stb { 1710c814320Stb uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; 1720c814320Stb uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; 1730c814320Stb uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; 1740c814320Stb struct MLKEM768_private_key priv768; 1750c814320Stb struct MLKEM768_public_key pub768; 1760c814320Stb struct iteration_ctx iteration768 = { 1770c814320Stb .encoded_public_key = encoded_public_key768, 1780c814320Stb .encoded_public_key_len = sizeof(encoded_public_key768), 1790c814320Stb .ciphertext = ciphertext768, 1800c814320Stb .ciphertext_len = sizeof(ciphertext768), 1810c814320Stb .invalid_ciphertext = invalid_ciphertext768, 1820c814320Stb .invalid_ciphertext_len = sizeof(invalid_ciphertext768), 1830c814320Stb .priv = &priv768, 1840c814320Stb .pub = &pub768, 1850c814320Stb .encap_external_entropy = mlkem768_encap_external_entropy, 1860c814320Stb .encode_private_key = mlkem768_encode_private_key, 1870c814320Stb .generate_key_external_entropy = 1880c814320Stb mlkem768_generate_key_external_entropy, 1890c814320Stb .public_from_private = mlkem768_public_from_private, 1900c814320Stb .decap = mlkem768_decap, 1910c814320Stb .start = kExpectedSeedStart, 1920c814320Stb .start_len = sizeof(kExpectedSeedStart), 1930c814320Stb .expected = kExpectedAdam768, 1940c814320Stb .expected_len = sizeof(kExpectedAdam768), 1950c814320Stb }; 1960c814320Stb uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; 1970c814320Stb uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; 1980c814320Stb uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; 1990c814320Stb struct MLKEM1024_private_key priv1024; 2000c814320Stb struct MLKEM1024_public_key pub1024; 2010c814320Stb struct iteration_ctx iteration1024 = { 2020c814320Stb .encoded_public_key = encoded_public_key1024, 2030c814320Stb .encoded_public_key_len = sizeof(encoded_public_key1024), 2040c814320Stb .ciphertext = ciphertext1024, 2050c814320Stb .ciphertext_len = sizeof(ciphertext1024), 2060c814320Stb .invalid_ciphertext = invalid_ciphertext1024, 2070c814320Stb .invalid_ciphertext_len = sizeof(invalid_ciphertext1024), 2080c814320Stb .priv = &priv1024, 2090c814320Stb .pub = &pub1024, 2100c814320Stb .encap_external_entropy = mlkem1024_encap_external_entropy, 2110c814320Stb .encode_private_key = mlkem1024_encode_private_key, 2120c814320Stb .generate_key_external_entropy = 2130c814320Stb mlkem1024_generate_key_external_entropy, 2140c814320Stb .public_from_private = mlkem1024_public_from_private, 2150c814320Stb .decap = mlkem1024_decap, 2160c814320Stb .start = kExpectedSeedStart, 2170c814320Stb .start_len = sizeof(kExpectedSeedStart), 2180c814320Stb .expected = kExpectedAdam1024, 2190c814320Stb .expected_len = sizeof(kExpectedAdam1024), 2200c814320Stb }; 2210c814320Stb int failed = 0; 2220c814320Stb 2230c814320Stb failed |= MlkemIterativeTest(&iteration768); 2240c814320Stb failed |= MlkemIterativeTest(&iteration1024); 2250c814320Stb 2260c814320Stb return failed; 2270c814320Stb } 228