/src/openssh/ssh-mldsa-eddsa.c
Line | Count | Source |
1 | | /* $OpenBSD: ssh-mldsa-eddsa.c,v 1.1 2026/06/14 03:59:34 djm Exp $ */ |
2 | | /* |
3 | | * Copyright (c) 2026 Damien Miller <djm@mindrot.org> |
4 | | * |
5 | | * Permission to use, copy, modify, and distribute this software for any |
6 | | * purpose with or without fee is hereby granted, provided that the above |
7 | | * copyright notice and this permission notice appear in all copies. |
8 | | * |
9 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | | */ |
17 | | |
18 | | /* draft-miller-sshm-mldsa44-ed25519-composite-sigs-00 */ |
19 | | |
20 | | #include "includes.h" |
21 | | |
22 | | #ifdef USE_MLDSA |
23 | | |
24 | | #include <sys/types.h> |
25 | | #include <stdint.h> |
26 | | #include <string.h> |
27 | | #include <stdlib.h> |
28 | | |
29 | | #include "crypto_api.h" |
30 | | #include "sshbuf.h" |
31 | | #include "ssherr.h" |
32 | | #include "digest.h" |
33 | | #define SSHKEY_INTERNAL |
34 | | #include "sshkey.h" |
35 | | #include "log.h" |
36 | | |
37 | 0 | #define COMPOSITE_PREFIX "CompositeAlgorithmSignatures2025" |
38 | 0 | #define COMPOSITE_LABEL "COMPSIG-MLDSA44-Ed25519-SHA512" |
39 | 0 | #define SSH_MLDSA44_ED25519_ALG_NAME "ssh-mldsa44-ed25519@openssh.com" |
40 | | |
41 | | /* |
42 | | * raw_* functions implement the draft-ietf-lamps-pq-composite-sigs-18 |
43 | | * composite signature scheme. These are exposed (i.e. not static) so |
44 | | * we can test them separately in unittests/crypto. |
45 | | */ |
46 | | |
47 | | int |
48 | | crypto_sign_mldsa44_ed25519_keygen(uint8_t pk[MLDSA44_ED25519_PK_SZ], |
49 | | uint8_t sk[MLDSA44_ED25519_SK_SZ]) |
50 | 0 | { |
51 | 0 | uint8_t mldsa_seed[32], ed25519_seed[32]; |
52 | 0 | int r; |
53 | |
|
54 | 0 | arc4random_buf(mldsa_seed, sizeof(mldsa_seed)); |
55 | 0 | arc4random_buf(ed25519_seed, sizeof(ed25519_seed)); |
56 | |
|
57 | 0 | r = crypto_sign_mldsa44_ed25519_keygen_seeded(pk, sk, mldsa_seed, |
58 | 0 | ed25519_seed); |
59 | 0 | explicit_bzero(mldsa_seed, sizeof(mldsa_seed)); |
60 | 0 | explicit_bzero(ed25519_seed, sizeof(ed25519_seed)); |
61 | 0 | return r; |
62 | 0 | } |
63 | | |
64 | | int |
65 | | crypto_sign_mldsa44_ed25519_keygen_seeded(uint8_t pk[MLDSA44_ED25519_PK_SZ], |
66 | | uint8_t sk[MLDSA44_ED25519_SK_SZ], const uint8_t mldsa_seed[32], |
67 | | const uint8_t ed25519_seed[32]) |
68 | 0 | { |
69 | 0 | uint8_t ed25519_pk[32], ed25519_sk[64]; |
70 | 0 | uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES]; |
71 | 0 | int ret = -1; |
72 | |
|
73 | 0 | if (crypto_sign_mldsa44_keypair_seeded(pk, mldsa_sk, mldsa_seed) != 0) |
74 | 0 | goto out; |
75 | 0 | if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk, |
76 | 0 | ed25519_seed) != 0) |
77 | 0 | goto out; |
78 | | |
79 | | /* Serialize PK: mldsaPK || ed25519PK */ |
80 | 0 | memcpy(pk + MLDSA44_PUBLICKEYBYTES, ed25519_pk, 32); |
81 | | |
82 | | /* Serialize SK: mldsaSeed || ed25519Seed */ |
83 | 0 | memcpy(sk, mldsa_seed, 32); |
84 | 0 | memcpy(sk + 32, ed25519_seed, 32); |
85 | | |
86 | | /* success */ |
87 | 0 | ret = 0; |
88 | 0 | out: |
89 | 0 | explicit_bzero(mldsa_sk, sizeof(mldsa_sk)); |
90 | 0 | explicit_bzero(ed25519_sk, sizeof(ed25519_sk)); |
91 | 0 | return ret; |
92 | 0 | } |
93 | | |
94 | | static int |
95 | | construct_m_prime(uint8_t **m_primep, size_t *m_prime_lenp, |
96 | | const uint8_t *msg, size_t msglen, |
97 | | const uint8_t *ctx, size_t ctxlen) |
98 | 0 | { |
99 | 0 | int r; |
100 | 0 | uint8_t hash[64]; |
101 | 0 | struct sshbuf *m_prime; |
102 | |
|
103 | 0 | *m_primep = NULL; |
104 | 0 | *m_prime_lenp = 0; |
105 | |
|
106 | 0 | if (ctxlen > 255) |
107 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
108 | 0 | if ((r = ssh_digest_memory(SSH_DIGEST_SHA512, msg, msglen, |
109 | 0 | hash, sizeof(hash))) != 0) |
110 | 0 | return r; |
111 | 0 | if ((m_prime = sshbuf_new()) == NULL) |
112 | 0 | return SSH_ERR_ALLOC_FAIL; |
113 | 0 | if ((r = sshbuf_put(m_prime, COMPOSITE_PREFIX, |
114 | 0 | sizeof(COMPOSITE_PREFIX) - 1)) != 0 || |
115 | 0 | (r = sshbuf_put(m_prime, COMPOSITE_LABEL, |
116 | 0 | sizeof(COMPOSITE_LABEL) - 1)) != 0 || |
117 | 0 | (r = sshbuf_put_u8(m_prime, (uint8_t)ctxlen)) != 0 || |
118 | 0 | (r = sshbuf_put(m_prime, ctx, ctxlen)) != 0 || |
119 | 0 | (r = sshbuf_put(m_prime, hash, sizeof(hash))) != 0) { |
120 | 0 | sshbuf_free(m_prime); |
121 | 0 | return r; |
122 | 0 | } |
123 | 0 | if ((*m_primep = malloc(sshbuf_len(m_prime))) == NULL) { |
124 | 0 | sshbuf_free(m_prime); |
125 | 0 | return SSH_ERR_ALLOC_FAIL; |
126 | 0 | } |
127 | 0 | memcpy(*m_primep, sshbuf_ptr(m_prime), sshbuf_len(m_prime)); |
128 | 0 | *m_prime_lenp = sshbuf_len(m_prime); |
129 | | /* success */ |
130 | 0 | sshbuf_free(m_prime); |
131 | 0 | return 0; |
132 | 0 | } |
133 | | |
134 | | int |
135 | | crypto_sign_mldsa44_ed25519_sign(uint8_t sig[MLDSA44_ED25519_SIG_SZ], |
136 | | const uint8_t *msg, size_t msglen, |
137 | | const uint8_t *ctx, size_t ctxlen, |
138 | | const uint8_t sk[MLDSA44_ED25519_SK_SZ]) |
139 | 0 | { |
140 | 0 | uint8_t *m_prime = NULL; |
141 | 0 | size_t m_prime_len = 0; |
142 | 0 | uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES]; |
143 | 0 | uint8_t mldsa_pk_dummy[MLDSA44_PUBLICKEYBYTES]; |
144 | 0 | uint8_t ed25519_pk[32], ed25519_sk[64]; |
145 | 0 | uint8_t mldsa_rnd[32]; |
146 | 0 | unsigned long long smlen; |
147 | 0 | int r = -1; |
148 | |
|
149 | 0 | if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen, |
150 | 0 | ctx, ctxlen) != 0) |
151 | 0 | return -1; |
152 | | |
153 | | /* Expand ML-DSA key from seed */ |
154 | 0 | if (crypto_sign_mldsa44_keypair_seeded(mldsa_pk_dummy, mldsa_sk, sk) != 0) |
155 | 0 | goto out; |
156 | | |
157 | | /* Sign with ML-DSA */ |
158 | 0 | arc4random_buf(mldsa_rnd, sizeof(mldsa_rnd)); |
159 | 0 | if (crypto_sign_mldsa44_seeded(sig, m_prime, m_prime_len, |
160 | 0 | (const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1, |
161 | 0 | mldsa_sk, mldsa_rnd) != 0) |
162 | 0 | goto out; |
163 | | |
164 | | /* Expand Ed25519 key from seed */ |
165 | 0 | if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk, |
166 | 0 | sk + 32) != 0) |
167 | 0 | goto out; |
168 | | |
169 | | /* Sign with Ed25519 */ |
170 | 0 | uint8_t *sm = malloc(m_prime_len + 64); |
171 | 0 | if (sm == NULL) |
172 | 0 | goto out; |
173 | | |
174 | 0 | if (crypto_sign_ed25519(sm, &smlen, m_prime, m_prime_len, |
175 | 0 | ed25519_sk) != 0) { |
176 | 0 | free(sm); |
177 | 0 | goto out; |
178 | 0 | } |
179 | 0 | memcpy(sig + MLDSA44_SIGBYTES, sm, 64); |
180 | 0 | free(sm); |
181 | |
|
182 | 0 | r = 0; |
183 | 0 | out: |
184 | 0 | free(m_prime); |
185 | 0 | explicit_bzero(mldsa_rnd, sizeof(mldsa_rnd)); |
186 | 0 | explicit_bzero(mldsa_sk, sizeof(mldsa_sk)); |
187 | 0 | explicit_bzero(ed25519_sk, sizeof(ed25519_sk)); |
188 | 0 | return r; |
189 | 0 | } |
190 | | |
191 | | int |
192 | | crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ], |
193 | | const uint8_t *msg, size_t msglen, |
194 | | const uint8_t *ctx, size_t ctxlen, |
195 | | const uint8_t pk[MLDSA44_ED25519_PK_SZ]) |
196 | 0 | { |
197 | 0 | uint8_t *m_prime = NULL; |
198 | 0 | size_t m_prime_len = 0; |
199 | 0 | uint8_t *sm = NULL, *m = NULL; |
200 | 0 | unsigned long long smlen, mlen; |
201 | 0 | int r = -1; |
202 | |
|
203 | 0 | if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen, |
204 | 0 | ctx, ctxlen) != 0) |
205 | 0 | return -1; |
206 | | |
207 | | /* Verify ML-DSA */ |
208 | 0 | if (crypto_sign_mldsa44_verify(sig, m_prime, m_prime_len, |
209 | 0 | (const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1, |
210 | 0 | pk) != 0) |
211 | 0 | goto out; |
212 | | |
213 | | /* Verify Ed25519 */ |
214 | 0 | smlen = m_prime_len + 64; |
215 | 0 | mlen = smlen; |
216 | 0 | if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) |
217 | 0 | goto out; |
218 | 0 | memcpy(sm, sig + MLDSA44_SIGBYTES, 64); |
219 | 0 | memcpy(sm + 64, m_prime, m_prime_len); |
220 | |
|
221 | 0 | if (crypto_sign_ed25519_open(m, &mlen, sm, smlen, |
222 | 0 | pk + MLDSA44_PUBLICKEYBYTES) != 0) |
223 | 0 | goto out; |
224 | 0 | if (mlen != m_prime_len) |
225 | 0 | goto out; |
226 | | |
227 | 0 | r = 0; |
228 | 0 | out: |
229 | 0 | free(m_prime); |
230 | 0 | free(sm); |
231 | 0 | free(m); |
232 | 0 | return r; |
233 | 0 | } |
234 | | |
235 | | /* sshkey integration */ |
236 | | |
237 | | static void |
238 | | ssh_mldsa44_ed25519_cleanup(struct sshkey *k) |
239 | 0 | { |
240 | 0 | freezero(k->mldsa_ed25519_pk, MLDSA44_ED25519_PK_SZ); |
241 | 0 | freezero(k->mldsa_ed25519_sk, MLDSA44_ED25519_SK_SZ); |
242 | 0 | k->mldsa_ed25519_pk = NULL; |
243 | 0 | k->mldsa_ed25519_sk = NULL; |
244 | 0 | } |
245 | | |
246 | | static int |
247 | | ssh_mldsa44_ed25519_equal(const struct sshkey *a, const struct sshkey *b) |
248 | 0 | { |
249 | 0 | if (a->mldsa_ed25519_pk == NULL || b->mldsa_ed25519_pk == NULL) |
250 | 0 | return 0; |
251 | 0 | if (memcmp(a->mldsa_ed25519_pk, b->mldsa_ed25519_pk, |
252 | 0 | MLDSA44_ED25519_PK_SZ) != 0) |
253 | 0 | return 0; |
254 | 0 | return 1; |
255 | 0 | } |
256 | | |
257 | | static int |
258 | | ssh_mldsa44_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b, |
259 | | enum sshkey_serialize_rep opts) |
260 | 0 | { |
261 | 0 | int r; |
262 | |
|
263 | 0 | if (key->mldsa_ed25519_pk == NULL) |
264 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
265 | 0 | if ((r = sshbuf_put_string(b, key->mldsa_ed25519_pk, |
266 | 0 | MLDSA44_ED25519_PK_SZ)) != 0) |
267 | 0 | return r; |
268 | | |
269 | 0 | return 0; |
270 | 0 | } |
271 | | |
272 | | static int |
273 | | ssh_mldsa44_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b, |
274 | | enum sshkey_serialize_rep opts) |
275 | 0 | { |
276 | 0 | int r; |
277 | |
|
278 | 0 | if (key->mldsa_ed25519_sk == NULL) |
279 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
280 | 0 | if (!sshkey_is_cert(key)) { |
281 | 0 | if ((r = ssh_mldsa44_ed25519_serialize_public(key, |
282 | 0 | b, opts)) != 0) |
283 | 0 | return r; |
284 | 0 | } |
285 | 0 | if ((r = sshbuf_put_string(b, key->mldsa_ed25519_sk, |
286 | 0 | MLDSA44_ED25519_SK_SZ)) != 0) |
287 | 0 | return r; |
288 | | |
289 | 0 | return 0; |
290 | 0 | } |
291 | | |
292 | | static int |
293 | | ssh_mldsa44_ed25519_deserialize_public(const char *ktype, struct sshbuf *b, |
294 | | struct sshkey *key) |
295 | 0 | { |
296 | 0 | u_char *pk = NULL; |
297 | 0 | size_t len = 0; |
298 | 0 | int r; |
299 | |
|
300 | 0 | if ((r = sshbuf_get_string(b, &pk, &len)) != 0) |
301 | 0 | return r; |
302 | 0 | if (len != MLDSA44_ED25519_PK_SZ) { |
303 | 0 | freezero(pk, len); |
304 | 0 | return SSH_ERR_INVALID_FORMAT; |
305 | 0 | } |
306 | 0 | key->mldsa_ed25519_pk = pk; |
307 | 0 | return 0; |
308 | 0 | } |
309 | | |
310 | | static int |
311 | | ssh_mldsa44_ed25519_deserialize_private(const char *ktype, struct sshbuf *b, |
312 | | struct sshkey *key) |
313 | 0 | { |
314 | 0 | int r; |
315 | 0 | size_t sklen = 0; |
316 | 0 | u_char *sk = NULL; |
317 | |
|
318 | 0 | if (!sshkey_is_cert(key)) { |
319 | 0 | if ((r = ssh_mldsa44_ed25519_deserialize_public(ktype, |
320 | 0 | b, key)) != 0) |
321 | 0 | return r; |
322 | 0 | } |
323 | 0 | if ((r = sshbuf_get_string(b, &sk, &sklen)) != 0) |
324 | 0 | goto out; |
325 | 0 | if (sklen != MLDSA44_ED25519_SK_SZ) { |
326 | 0 | r = SSH_ERR_INVALID_FORMAT; |
327 | 0 | goto out; |
328 | 0 | } |
329 | 0 | key->mldsa_ed25519_sk = sk; |
330 | 0 | sk = NULL; /* transferred */ |
331 | 0 | r = 0; |
332 | 0 | out: |
333 | 0 | freezero(sk, sklen); |
334 | 0 | return r; |
335 | 0 | } |
336 | | |
337 | | static int |
338 | | ssh_mldsa44_ed25519_generate(struct sshkey *k, int bits) |
339 | 0 | { |
340 | 0 | free(k->mldsa_ed25519_pk); |
341 | 0 | free(k->mldsa_ed25519_sk); |
342 | 0 | k->mldsa_ed25519_pk = NULL; |
343 | 0 | k->mldsa_ed25519_sk = NULL; |
344 | 0 | if ((k->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL || |
345 | 0 | (k->mldsa_ed25519_sk = malloc(MLDSA44_ED25519_SK_SZ)) == NULL) { |
346 | 0 | free(k->mldsa_ed25519_pk); |
347 | 0 | return SSH_ERR_ALLOC_FAIL; |
348 | 0 | } |
349 | 0 | if (crypto_sign_mldsa44_ed25519_keygen(k->mldsa_ed25519_pk, |
350 | 0 | k->mldsa_ed25519_sk) != 0) { |
351 | 0 | free(k->mldsa_ed25519_pk); |
352 | 0 | free(k->mldsa_ed25519_sk); |
353 | 0 | return SSH_ERR_CRYPTO_ERROR; |
354 | 0 | } |
355 | 0 | return 0; |
356 | 0 | } |
357 | | |
358 | | static int |
359 | | ssh_mldsa44_ed25519_copy_public(const struct sshkey *from, struct sshkey *to) |
360 | 0 | { |
361 | 0 | if (from->mldsa_ed25519_pk == NULL) |
362 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
363 | 0 | if ((to->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL) |
364 | 0 | return SSH_ERR_ALLOC_FAIL; |
365 | 0 | memcpy(to->mldsa_ed25519_pk, from->mldsa_ed25519_pk, |
366 | 0 | MLDSA44_ED25519_PK_SZ); |
367 | 0 | return 0; |
368 | 0 | } |
369 | | |
370 | | static int |
371 | | ssh_mldsa44_ed25519_sign(struct sshkey *key, |
372 | | u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, |
373 | | const char *alg, const char *sk_provider, const char *sk_pin, |
374 | | u_int compat) |
375 | 0 | { |
376 | 0 | u_char sig[MLDSA44_ED25519_SIG_SZ]; |
377 | 0 | struct sshbuf *b = NULL; |
378 | 0 | int r = SSH_ERR_INTERNAL_ERROR; |
379 | |
|
380 | 0 | if (lenp != NULL) |
381 | 0 | *lenp = 0; |
382 | 0 | if (sigp != NULL) |
383 | 0 | *sigp = NULL; |
384 | |
|
385 | 0 | if (key == NULL || |
386 | 0 | sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 || |
387 | 0 | key->mldsa_ed25519_sk == NULL) |
388 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
389 | | |
390 | 0 | if (crypto_sign_mldsa44_ed25519_sign(sig, data, datalen, NULL, 0, |
391 | 0 | key->mldsa_ed25519_sk) != 0) { |
392 | 0 | r = SSH_ERR_CRYPTO_ERROR; |
393 | 0 | goto out; |
394 | 0 | } |
395 | | |
396 | 0 | if ((b = sshbuf_new()) == NULL) { |
397 | 0 | r = SSH_ERR_ALLOC_FAIL; |
398 | 0 | goto out; |
399 | 0 | } |
400 | 0 | if ((r = sshbuf_put_cstring(b, SSH_MLDSA44_ED25519_ALG_NAME)) != 0 || |
401 | 0 | (r = sshbuf_put_string(b, sig, sizeof(sig))) != 0) |
402 | 0 | goto out; |
403 | | |
404 | 0 | if (sigp != NULL) { |
405 | 0 | if ((*sigp = malloc(sshbuf_len(b))) == NULL) { |
406 | 0 | r = SSH_ERR_ALLOC_FAIL; |
407 | 0 | goto out; |
408 | 0 | } |
409 | 0 | memcpy(*sigp, sshbuf_ptr(b), sshbuf_len(b)); |
410 | 0 | } |
411 | 0 | if (lenp != NULL) |
412 | 0 | *lenp = sshbuf_len(b); |
413 | 0 | r = 0; |
414 | 0 | out: |
415 | 0 | sshbuf_free(b); |
416 | 0 | explicit_bzero(sig, sizeof(sig)); |
417 | 0 | return r; |
418 | 0 | } |
419 | | |
420 | | static int |
421 | | ssh_mldsa44_ed25519_verify(const struct sshkey *key, |
422 | | const u_char *sig, size_t siglen, const u_char *data, size_t dlen, |
423 | | const char *alg, u_int compat, struct sshkey_sig_details **detailsp) |
424 | 0 | { |
425 | 0 | struct sshbuf *b = NULL; |
426 | 0 | char *ktype = NULL; |
427 | 0 | const u_char *sigblob; |
428 | 0 | size_t len; |
429 | 0 | int r; |
430 | |
|
431 | 0 | if (key == NULL || |
432 | 0 | sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 || |
433 | 0 | key->mldsa_ed25519_pk == NULL || |
434 | 0 | sig == NULL || siglen == 0) |
435 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
436 | | |
437 | 0 | if ((b = sshbuf_from(sig, siglen)) == NULL) |
438 | 0 | return SSH_ERR_ALLOC_FAIL; |
439 | 0 | if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || |
440 | 0 | (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) |
441 | 0 | goto out; |
442 | 0 | if (strcmp(SSH_MLDSA44_ED25519_ALG_NAME, ktype) != 0) { |
443 | 0 | r = SSH_ERR_KEY_TYPE_MISMATCH; |
444 | 0 | goto out; |
445 | 0 | } |
446 | 0 | if (sshbuf_len(b) != 0) { |
447 | 0 | r = SSH_ERR_UNEXPECTED_TRAILING_DATA; |
448 | 0 | goto out; |
449 | 0 | } |
450 | 0 | if (len != MLDSA44_ED25519_SIG_SZ) { |
451 | 0 | r = SSH_ERR_INVALID_FORMAT; |
452 | 0 | goto out; |
453 | 0 | } |
454 | | |
455 | 0 | if (crypto_sign_mldsa44_ed25519_verify(sigblob, data, dlen, NULL, 0, |
456 | 0 | key->mldsa_ed25519_pk) != 0) { |
457 | 0 | r = SSH_ERR_SIGNATURE_INVALID; |
458 | 0 | goto out; |
459 | 0 | } |
460 | | |
461 | 0 | r = 0; |
462 | 0 | out: |
463 | 0 | sshbuf_free(b); |
464 | 0 | free(ktype); |
465 | 0 | return r; |
466 | 0 | } |
467 | | |
468 | | const struct sshkey_impl_funcs sshkey_mldsa44_ed25519_funcs = { |
469 | | /* .size = */ NULL, |
470 | | /* .alloc = */ NULL, |
471 | | /* .cleanup = */ ssh_mldsa44_ed25519_cleanup, |
472 | | /* .equal = */ ssh_mldsa44_ed25519_equal, |
473 | | /* .ssh_serialize_public = */ ssh_mldsa44_ed25519_serialize_public, |
474 | | /* .ssh_deserialize_public = */ ssh_mldsa44_ed25519_deserialize_public, |
475 | | /* .ssh_serialize_private = */ ssh_mldsa44_ed25519_serialize_private, |
476 | | /* .ssh_deserialize_private = */ ssh_mldsa44_ed25519_deserialize_private, |
477 | | /* .generate = */ ssh_mldsa44_ed25519_generate, |
478 | | /* .copy_public = */ ssh_mldsa44_ed25519_copy_public, |
479 | | /* .sign = */ ssh_mldsa44_ed25519_sign, |
480 | | /* .verify = */ ssh_mldsa44_ed25519_verify, |
481 | | }; |
482 | | |
483 | | const struct sshkey_impl sshkey_mldsa44_ed25519_impl = { |
484 | | /* .name = */ "ssh-mldsa44-ed25519@openssh.com", |
485 | | /* .shortname = */ "MLDSA44-ED25519", |
486 | | /* .sigalg = */ NULL, |
487 | | /* .type = */ KEY_MLDSA44_ED25519, |
488 | | /* .nid = */ 0, |
489 | | /* .cert = */ 0, |
490 | | /* .sigonly = */ 0, |
491 | | /* .keybits = */ 256, |
492 | | /* .funcs = */ &sshkey_mldsa44_ed25519_funcs, |
493 | | }; |
494 | | |
495 | | const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl = { |
496 | | /* .name = */ "ssh-mldsa44-ed25519-cert-v01@openssh.com", |
497 | | /* .shortname = */ "MLDSA44-ED25519-CERT", |
498 | | /* .sigalg = */ NULL, |
499 | | /* .type = */ KEY_MLDSA44_ED25519_CERT, |
500 | | /* .nid = */ 0, |
501 | | /* .cert = */ 1, |
502 | | /* .sigonly = */ 0, |
503 | | /* .keybits = */ 256, |
504 | | /* .funcs = */ &sshkey_mldsa44_ed25519_funcs, |
505 | | }; |
506 | | #endif /* USE_MLDSA */ |