xref: /freebsd/crypto/openssh/regress/unittests/crypto/test_mldsa.c (revision bb5c77e9d281d6def6835d48249898764bc6a5fe)
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