1 /* $OpenBSD: test_mldsa.c,v 1.4 2026/06/22 12:28:48 dtucker Exp $ */
2 /*
3 * Regress test for ML-DSA
4 *
5 * Placed in the public domain
6 */
7
8 #include "includes.h"
9
10 #ifdef USE_MLDSA
11
12 #include <sys/types.h>
13 #include <stdio.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "../test_helper/test_helper.h"
19 #include "crypto_api.h"
20 #include "xmalloc.h"
21 #include "sshbuf.h"
22
23 /* in tests.c */
24 struct sshbuf *load_text_file(const char *name);
25 char *get_json_string(struct sshbuf *content, const char *key, int consume);
26
27 void mldsa_tests(void);
28
29 struct kat {
30 char *seed;
31 char *pk_hash;
32 char *sk_hash;
33 char *msg;
34 char *rand;
35 char *sig_hash;
36 };
37
38 static void
load_kats(const char * file,struct kat *** katsp,size_t * nkatsp)39 load_kats(const char *file, struct kat ***katsp, size_t *nkatsp)
40 {
41 struct sshbuf *json_buf;
42 struct kat *kat = NULL, **kats = NULL;
43 size_t nkats = 0;
44
45 json_buf = load_text_file(file);
46 while (sshbuf_find(json_buf, 0, "key_generation_seed",
47 strlen("key_generation_seed"), NULL) == 0) {
48 kat = xcalloc(1, sizeof(*kat));
49 kat->seed = get_json_string(json_buf, "key_generation_seed", 1);
50 kat->pk_hash = get_json_string(json_buf,
51 "sha3_256_hash_of_verification_key", 1);
52 kat->sk_hash = get_json_string(json_buf,
53 "sha3_256_hash_of_signing_key", 1);
54 kat->msg = get_json_string(json_buf, "message", 1);
55 kat->rand = get_json_string(json_buf, "signing_randomness", 1);
56 kat->sig_hash = get_json_string(json_buf,
57 "sha3_256_hash_of_signature", 1);
58 kats = xrecallocarray(kats, nkats, nkats + 1, sizeof(*kats));
59 kats[nkats++] = kat;
60 }
61 *katsp = kats;
62 *nkatsp = nkats;
63 sshbuf_free(json_buf);
64 }
65
66 static void
free_kats(struct kat ** kats,size_t nkats)67 free_kats(struct kat **kats, size_t nkats)
68 {
69 size_t i;
70 struct kat *kat;
71
72 for (i = 0; i < nkats; i++) {
73 kat = kats[i];
74 free(kat->seed);
75 free(kat->pk_hash);
76 free(kat->sk_hash);
77 free(kat->msg);
78 free(kat->rand);
79 free(kat->sig_hash);
80 free(kat);
81 }
82 free(kats);
83 }
84
85 void
mldsa_tests(void)86 mldsa_tests(void)
87 {
88 uint8_t pk[MLDSA44_PUBLICKEYBYTES];
89 uint8_t sk[MLDSA44_SECRETKEYBYTES];
90 uint8_t sig[MLDSA44_SIGBYTES];
91 uint8_t pk_hash[32], sk_hash[32], sig_hash[32];
92 uint8_t expected_pk_hash[32], expected_sk_hash[32];
93 uint8_t expected_sig_hash[32];
94 uint8_t seed[32], rand[32];
95 uint8_t *msg;
96 size_t nkats, i, msglen;
97 struct kat **kats, *kat;
98
99 TEST_START("ML-DSA 44 KATs");
100 load_kats("nistkats-44.json", &kats, &nkats);
101 for (i = 0; i < nkats; i++) {
102 kat = kats[i];
103 test_subtest_info("vector %zu", i);
104
105 hex2bin(seed, kat->seed, 32);
106 hex2bin(expected_pk_hash, kat->pk_hash, 32);
107 hex2bin(expected_sk_hash, kat->sk_hash, 32);
108
109 /* Keypair generation */
110 ASSERT_INT_EQ(crypto_sign_mldsa44_keypair_seeded(pk,
111 sk, seed), 0);
112
113 sha3_256(pk_hash, pk, sizeof(pk));
114 sha3_256(sk_hash, sk, sizeof(sk));
115
116 ASSERT_MEM_EQ(pk_hash, expected_pk_hash, 32);
117 ASSERT_MEM_EQ(sk_hash, expected_sk_hash, 32);
118
119 msglen = strlen(kat->msg) / 2;
120 ASSERT_PTR_NE(msg = malloc(msglen), NULL);
121 hex2bin(msg, kat->msg, msglen);
122 hex2bin(rand, kat->rand, 32);
123 hex2bin(expected_sig_hash, kat->sig_hash, 32);
124
125 /* Signing */
126 ASSERT_INT_EQ(crypto_sign_mldsa44_seeded(sig, msg,
127 msglen, NULL, 0, sk, rand), 0);
128
129 sha3_256(sig_hash, sig, sizeof(sig));
130 ASSERT_MEM_EQ(sig_hash, expected_sig_hash, 32);
131
132 /* Verification */
133 ASSERT_INT_EQ(crypto_sign_mldsa44_verify(sig, msg,
134 msglen, NULL, 0, pk), 0);
135 free(msg);
136 }
137 free_kats(kats, nkats);
138 TEST_DONE();
139 }
140 #endif /* USE_MLDSA */
141