1 /* $OpenBSD: mlkem_iteration_tests.c,v 1.2 2024/12/26 07:26:45 tb Exp $ */ 2 /* 3 * Copyright (c) 2024 Google Inc. 4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com> 5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 14 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 16 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <err.h> 21 #include <stdint.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 #include "mlkem.h" 26 27 #include "mlkem_internal.h" 28 #include "mlkem_tests_util.h" 29 #include "sha3_internal.h" 30 31 /* 32 * Based on https://c2sp.org/CCTV/ML-KEM 33 * 34 * The final value has been updated to reflect the change from Kyber to ML-KEM. 35 * 36 * The deterministic RNG is a single SHAKE-128 instance with an empty input. 37 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.) 38 */ 39 const uint8_t kExpectedSeedStart[16] = { 40 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45, 41 0x50, 0x76, 0x05, 0x85, 0x3e 42 }; 43 44 /* 45 * Filippo says: 46 * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3 47 * but Boring believes this: 48 */ 49 const uint8_t kExpectedAdam768[32] = { 50 0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf, 51 0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1, 52 0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1 53 }; 54 55 /* 56 * Filippo says: 57 * ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543 58 * but Boring believes this: 59 */ 60 const uint8_t kExpectedAdam1024[32] = { 61 0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd, 62 0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24, 63 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5 64 }; 65 66 struct iteration_ctx { 67 uint8_t *encoded_public_key; 68 size_t encoded_public_key_len; 69 uint8_t *ciphertext; 70 size_t ciphertext_len; 71 uint8_t *invalid_ciphertext; 72 size_t invalid_ciphertext_len; 73 void *priv; 74 void *pub; 75 76 mlkem_encode_private_key_fn encode_private_key; 77 mlkem_encap_external_entropy_fn encap_external_entropy; 78 mlkem_generate_key_external_entropy_fn generate_key_external_entropy; 79 mlkem_public_from_private_fn public_from_private; 80 mlkem_decap_fn decap; 81 82 const uint8_t *start; 83 size_t start_len; 84 85 const uint8_t *expected; 86 size_t expected_len; 87 }; 88 89 static int 90 MlkemIterativeTest(struct iteration_ctx *ctx) 91 { 92 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; 93 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; 94 uint8_t seed[MLKEM_SEED_BYTES] = {0}; 95 sha3_ctx drng, results; 96 uint8_t out[32]; 97 int i; 98 99 shake128_init(&drng); 100 shake128_init(&results); 101 102 shake_xof(&drng); 103 for (i = 0; i < 10000; i++) { 104 uint8_t *encoded_private_key = NULL; 105 size_t encoded_private_key_len; 106 107 /* 108 * This should draw both d and z from DRNG concatenating in 109 * seed. 110 */ 111 shake_out(&drng, seed, sizeof(seed)); 112 if (i == 0) { 113 if (compare_data(seed, ctx->start, ctx->start_len, 114 "seed start") != 0) 115 errx(1, "compare_data"); 116 } 117 118 /* generate ek as encoded_public_key */ 119 ctx->generate_key_external_entropy(ctx->encoded_public_key, 120 ctx->priv, seed); 121 ctx->public_from_private(ctx->pub, ctx->priv); 122 123 /* hash in ek */ 124 shake_update(&results, ctx->encoded_public_key, 125 ctx->encoded_public_key_len); 126 127 /* marshal priv to dk as encoded_private_key */ 128 if (!ctx->encode_private_key(ctx->priv, &encoded_private_key, 129 &encoded_private_key_len)) 130 errx(1, "encode private key"); 131 132 /* hash in dk */ 133 shake_update(&results, encoded_private_key, 134 encoded_private_key_len); 135 136 free(encoded_private_key); 137 138 /* draw m as encap entropy from DRNG */ 139 shake_out(&drng, encap_entropy, sizeof(encap_entropy)); 140 141 /* generate ct as ciphertext, k as shared_secret */ 142 ctx->encap_external_entropy(ctx->ciphertext, shared_secret, 143 ctx->pub, encap_entropy); 144 145 /* hash in ct */ 146 shake_update(&results, ctx->ciphertext, ctx->ciphertext_len); 147 /* hash in k */ 148 shake_update(&results, shared_secret, sizeof(shared_secret)); 149 150 /* draw ct as invalid_ciphertxt from DRNG */ 151 shake_out(&drng, ctx->invalid_ciphertext, 152 ctx->invalid_ciphertext_len); 153 154 /* generate k as shared secret from invalid ciphertext */ 155 if (!ctx->decap(shared_secret, ctx->invalid_ciphertext, 156 ctx->invalid_ciphertext_len, ctx->priv)) 157 errx(1, "decap failed"); 158 159 /* hash in k */ 160 shake_update(&results, shared_secret, sizeof(shared_secret)); 161 } 162 shake_xof(&results); 163 shake_out(&results, out, sizeof(out)); 164 165 return compare_data(ctx->expected, out, sizeof(out), "final result hash"); 166 } 167 168 int 169 main(void) 170 { 171 uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; 172 uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; 173 uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; 174 struct MLKEM768_private_key priv768; 175 struct MLKEM768_public_key pub768; 176 struct iteration_ctx iteration768 = { 177 .encoded_public_key = encoded_public_key768, 178 .encoded_public_key_len = sizeof(encoded_public_key768), 179 .ciphertext = ciphertext768, 180 .ciphertext_len = sizeof(ciphertext768), 181 .invalid_ciphertext = invalid_ciphertext768, 182 .invalid_ciphertext_len = sizeof(invalid_ciphertext768), 183 .priv = &priv768, 184 .pub = &pub768, 185 .encap_external_entropy = mlkem768_encap_external_entropy, 186 .encode_private_key = mlkem768_encode_private_key, 187 .generate_key_external_entropy = 188 mlkem768_generate_key_external_entropy, 189 .public_from_private = mlkem768_public_from_private, 190 .decap = mlkem768_decap, 191 .start = kExpectedSeedStart, 192 .start_len = sizeof(kExpectedSeedStart), 193 .expected = kExpectedAdam768, 194 .expected_len = sizeof(kExpectedAdam768), 195 }; 196 uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; 197 uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; 198 uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; 199 struct MLKEM1024_private_key priv1024; 200 struct MLKEM1024_public_key pub1024; 201 struct iteration_ctx iteration1024 = { 202 .encoded_public_key = encoded_public_key1024, 203 .encoded_public_key_len = sizeof(encoded_public_key1024), 204 .ciphertext = ciphertext1024, 205 .ciphertext_len = sizeof(ciphertext1024), 206 .invalid_ciphertext = invalid_ciphertext1024, 207 .invalid_ciphertext_len = sizeof(invalid_ciphertext1024), 208 .priv = &priv1024, 209 .pub = &pub1024, 210 .encap_external_entropy = mlkem1024_encap_external_entropy, 211 .encode_private_key = mlkem1024_encode_private_key, 212 .generate_key_external_entropy = 213 mlkem1024_generate_key_external_entropy, 214 .public_from_private = mlkem1024_public_from_private, 215 .decap = mlkem1024_decap, 216 .start = kExpectedSeedStart, 217 .start_len = sizeof(kExpectedSeedStart), 218 .expected = kExpectedAdam1024, 219 .expected_len = sizeof(kExpectedAdam1024), 220 }; 221 int failed = 0; 222 223 failed |= MlkemIterativeTest(&iteration768); 224 failed |= MlkemIterativeTest(&iteration1024); 225 226 return failed; 227 } 228