/src/openssh/ssh-mldsa-eddsa.c
Line | Count | Source |
1 | | /* $OpenBSD: ssh-mldsa-eddsa.c,v 1.2 2026/07/14 04:43:13 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[MLDSA44_SEEDBYTES]; |
52 | 0 | uint8_t ed25519_seed[crypto_sign_ed25519_SEEDBYTES]; |
53 | 0 | int r; |
54 | |
|
55 | 0 | arc4random_buf(mldsa_seed, sizeof(mldsa_seed)); |
56 | 0 | arc4random_buf(ed25519_seed, sizeof(ed25519_seed)); |
57 | |
|
58 | 0 | r = crypto_sign_mldsa44_ed25519_keygen_seeded(pk, sk, mldsa_seed, |
59 | 0 | ed25519_seed); |
60 | 0 | explicit_bzero(mldsa_seed, sizeof(mldsa_seed)); |
61 | 0 | explicit_bzero(ed25519_seed, sizeof(ed25519_seed)); |
62 | 0 | return r; |
63 | 0 | } |
64 | | |
65 | | int |
66 | | crypto_sign_mldsa44_ed25519_keygen_seeded(uint8_t pk[MLDSA44_ED25519_PK_SZ], |
67 | | uint8_t sk[MLDSA44_ED25519_SK_SZ], |
68 | | const uint8_t mldsa_seed[MLDSA44_SEEDBYTES], |
69 | | const uint8_t ed25519_seed[crypto_sign_ed25519_SEEDBYTES]) |
70 | 0 | { |
71 | 0 | uint8_t ed25519_pk[MLDSA44_SEEDBYTES]; |
72 | 0 | uint8_t ed25519_sk[crypto_sign_ed25519_SECRETKEYBYTES]; |
73 | 0 | uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES]; |
74 | 0 | int ret = -1; |
75 | |
|
76 | 0 | if (crypto_sign_mldsa44_keypair_seeded(pk, mldsa_sk, mldsa_seed) != 0) |
77 | 0 | goto out; |
78 | 0 | if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk, |
79 | 0 | ed25519_seed) != 0) |
80 | 0 | goto out; |
81 | | |
82 | | /* Serialize PK: mldsaPK || ed25519PK */ |
83 | 0 | memcpy(pk + MLDSA44_PUBLICKEYBYTES, ed25519_pk, |
84 | 0 | crypto_sign_ed25519_SEEDBYTES); |
85 | | |
86 | | /* Serialize SK: mldsaSeed || ed25519Seed */ |
87 | 0 | memcpy(sk, mldsa_seed, MLDSA44_SEEDBYTES); |
88 | 0 | memcpy(sk + MLDSA44_SEEDBYTES, ed25519_seed, |
89 | 0 | crypto_sign_ed25519_SEEDBYTES); |
90 | | |
91 | | /* success */ |
92 | 0 | ret = 0; |
93 | 0 | out: |
94 | 0 | explicit_bzero(mldsa_sk, sizeof(mldsa_sk)); |
95 | 0 | explicit_bzero(ed25519_sk, sizeof(ed25519_sk)); |
96 | 0 | return ret; |
97 | 0 | } |
98 | | |
99 | | static int |
100 | | construct_m_prime(uint8_t **m_primep, size_t *m_prime_lenp, |
101 | | const uint8_t *msg, size_t msglen, |
102 | | const uint8_t *ctx, size_t ctxlen) |
103 | 0 | { |
104 | 0 | int r; |
105 | 0 | uint8_t hash[64]; |
106 | 0 | struct sshbuf *m_prime; |
107 | |
|
108 | 0 | *m_primep = NULL; |
109 | 0 | *m_prime_lenp = 0; |
110 | |
|
111 | 0 | if (ctxlen > 255) |
112 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
113 | 0 | if ((r = ssh_digest_memory(SSH_DIGEST_SHA512, msg, msglen, |
114 | 0 | hash, sizeof(hash))) != 0) |
115 | 0 | return r; |
116 | 0 | if ((m_prime = sshbuf_new()) == NULL) |
117 | 0 | return SSH_ERR_ALLOC_FAIL; |
118 | 0 | if ((r = sshbuf_put(m_prime, COMPOSITE_PREFIX, |
119 | 0 | sizeof(COMPOSITE_PREFIX) - 1)) != 0 || |
120 | 0 | (r = sshbuf_put(m_prime, COMPOSITE_LABEL, |
121 | 0 | sizeof(COMPOSITE_LABEL) - 1)) != 0 || |
122 | 0 | (r = sshbuf_put_u8(m_prime, (uint8_t)ctxlen)) != 0 || |
123 | 0 | (r = sshbuf_put(m_prime, ctx, ctxlen)) != 0 || |
124 | 0 | (r = sshbuf_put(m_prime, hash, sizeof(hash))) != 0) { |
125 | 0 | sshbuf_free(m_prime); |
126 | 0 | return r; |
127 | 0 | } |
128 | 0 | if ((*m_primep = malloc(sshbuf_len(m_prime))) == NULL) { |
129 | 0 | sshbuf_free(m_prime); |
130 | 0 | return SSH_ERR_ALLOC_FAIL; |
131 | 0 | } |
132 | 0 | memcpy(*m_primep, sshbuf_ptr(m_prime), sshbuf_len(m_prime)); |
133 | 0 | *m_prime_lenp = sshbuf_len(m_prime); |
134 | | /* success */ |
135 | 0 | sshbuf_free(m_prime); |
136 | 0 | return 0; |
137 | 0 | } |
138 | | |
139 | | int |
140 | | crypto_sign_mldsa44_ed25519_sign(uint8_t sig[MLDSA44_ED25519_SIG_SZ], |
141 | | const uint8_t *msg, size_t msglen, |
142 | | const uint8_t *ctx, size_t ctxlen, |
143 | | const uint8_t sk[MLDSA44_ED25519_SK_SZ]) |
144 | 0 | { |
145 | 0 | uint8_t *m_prime = NULL; |
146 | 0 | size_t m_prime_len = 0; |
147 | 0 | uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES]; |
148 | 0 | uint8_t mldsa_pk_dummy[MLDSA44_PUBLICKEYBYTES]; |
149 | 0 | uint8_t ed25519_pk[crypto_sign_ed25519_PUBLICKEYBYTES]; |
150 | 0 | uint8_t ed25519_sk[crypto_sign_ed25519_SECRETKEYBYTES]; |
151 | 0 | uint8_t mldsa_rnd[MLDSA44_SEEDBYTES]; |
152 | 0 | unsigned long long smlen; |
153 | 0 | int r = -1; |
154 | |
|
155 | 0 | if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen, |
156 | 0 | ctx, ctxlen) != 0) |
157 | 0 | return -1; |
158 | | |
159 | | /* Expand ML-DSA key from seed */ |
160 | 0 | if (crypto_sign_mldsa44_keypair_seeded(mldsa_pk_dummy, mldsa_sk, sk) != 0) |
161 | 0 | goto out; |
162 | | |
163 | | /* Sign with ML-DSA */ |
164 | 0 | arc4random_buf(mldsa_rnd, sizeof(mldsa_rnd)); |
165 | 0 | if (crypto_sign_mldsa44_seeded(sig, m_prime, m_prime_len, |
166 | 0 | (const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1, |
167 | 0 | mldsa_sk, mldsa_rnd) != 0) |
168 | 0 | goto out; |
169 | | |
170 | | /* Expand Ed25519 key from seed */ |
171 | 0 | if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk, |
172 | 0 | sk + MLDSA44_SEEDBYTES) != 0) |
173 | 0 | goto out; |
174 | | |
175 | | /* Sign with Ed25519 */ |
176 | 0 | uint8_t *sm = malloc(m_prime_len + crypto_sign_ed25519_BYTES); |
177 | 0 | if (sm == NULL) |
178 | 0 | goto out; |
179 | | |
180 | 0 | if (crypto_sign_ed25519(sm, &smlen, m_prime, m_prime_len, |
181 | 0 | ed25519_sk) != 0) { |
182 | 0 | free(sm); |
183 | 0 | goto out; |
184 | 0 | } |
185 | 0 | memcpy(sig + MLDSA44_SIGBYTES, sm, crypto_sign_ed25519_BYTES); |
186 | 0 | free(sm); |
187 | |
|
188 | 0 | r = 0; |
189 | 0 | out: |
190 | 0 | free(m_prime); |
191 | 0 | explicit_bzero(mldsa_rnd, sizeof(mldsa_rnd)); |
192 | 0 | explicit_bzero(mldsa_sk, sizeof(mldsa_sk)); |
193 | 0 | explicit_bzero(ed25519_sk, sizeof(ed25519_sk)); |
194 | 0 | return r; |
195 | 0 | } |
196 | | |
197 | | int |
198 | | crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ], |
199 | | const uint8_t *msg, size_t msglen, |
200 | | const uint8_t *ctx, size_t ctxlen, |
201 | | const uint8_t pk[MLDSA44_ED25519_PK_SZ]) |
202 | 0 | { |
203 | 0 | uint8_t *m_prime = NULL; |
204 | 0 | size_t m_prime_len = 0; |
205 | 0 | uint8_t *sm = NULL, *m = NULL; |
206 | 0 | unsigned long long smlen, mlen; |
207 | 0 | int r = -1; |
208 | |
|
209 | 0 | if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen, |
210 | 0 | ctx, ctxlen) != 0) |
211 | 0 | return -1; |
212 | | |
213 | | /* Verify ML-DSA */ |
214 | 0 | if (crypto_sign_mldsa44_verify(sig, m_prime, m_prime_len, |
215 | 0 | (const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1, |
216 | 0 | pk) != 0) |
217 | 0 | goto out; |
218 | | |
219 | | /* Verify Ed25519 */ |
220 | 0 | smlen = m_prime_len + crypto_sign_ed25519_BYTES; |
221 | 0 | mlen = smlen; |
222 | 0 | if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) |
223 | 0 | goto out; |
224 | 0 | memcpy(sm, sig + MLDSA44_SIGBYTES, crypto_sign_ed25519_BYTES); |
225 | 0 | memcpy(sm + crypto_sign_ed25519_BYTES, m_prime, m_prime_len); |
226 | |
|
227 | 0 | if (crypto_sign_ed25519_open(m, &mlen, sm, smlen, |
228 | 0 | pk + MLDSA44_PUBLICKEYBYTES) != 0) |
229 | 0 | goto out; |
230 | 0 | if (mlen != m_prime_len) |
231 | 0 | goto out; |
232 | | |
233 | 0 | r = 0; |
234 | 0 | out: |
235 | 0 | free(m_prime); |
236 | 0 | free(sm); |
237 | 0 | free(m); |
238 | 0 | return r; |
239 | 0 | } |
240 | | |
241 | | /* sshkey integration */ |
242 | | |
243 | | static void |
244 | | ssh_mldsa44_ed25519_cleanup(struct sshkey *k) |
245 | 81 | { |
246 | 81 | freezero(k->mldsa_ed25519_pk, MLDSA44_ED25519_PK_SZ); |
247 | 81 | freezero(k->mldsa_ed25519_sk, MLDSA44_ED25519_SK_SZ); |
248 | 81 | k->mldsa_ed25519_pk = NULL; |
249 | 81 | k->mldsa_ed25519_sk = NULL; |
250 | 81 | } |
251 | | |
252 | | static int |
253 | | ssh_mldsa44_ed25519_equal(const struct sshkey *a, const struct sshkey *b) |
254 | 0 | { |
255 | 0 | if (a->mldsa_ed25519_pk == NULL || b->mldsa_ed25519_pk == NULL) |
256 | 0 | return 0; |
257 | 0 | if (memcmp(a->mldsa_ed25519_pk, b->mldsa_ed25519_pk, |
258 | 0 | MLDSA44_ED25519_PK_SZ) != 0) |
259 | 0 | return 0; |
260 | 0 | return 1; |
261 | 0 | } |
262 | | |
263 | | static int |
264 | | ssh_mldsa44_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b, |
265 | | enum sshkey_serialize_rep opts) |
266 | 0 | { |
267 | 0 | int r; |
268 | |
|
269 | 0 | if (key->mldsa_ed25519_pk == NULL) |
270 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
271 | 0 | if ((r = sshbuf_put_string(b, key->mldsa_ed25519_pk, |
272 | 0 | MLDSA44_ED25519_PK_SZ)) != 0) |
273 | 0 | return r; |
274 | | |
275 | 0 | return 0; |
276 | 0 | } |
277 | | |
278 | | static int |
279 | | ssh_mldsa44_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b, |
280 | | enum sshkey_serialize_rep opts) |
281 | 0 | { |
282 | 0 | int r; |
283 | |
|
284 | 0 | if (key->mldsa_ed25519_sk == NULL) |
285 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
286 | 0 | if (!sshkey_is_cert(key)) { |
287 | 0 | if ((r = ssh_mldsa44_ed25519_serialize_public(key, |
288 | 0 | b, opts)) != 0) |
289 | 0 | return r; |
290 | 0 | } |
291 | 0 | if ((r = sshbuf_put_string(b, key->mldsa_ed25519_sk, |
292 | 0 | MLDSA44_ED25519_SK_SZ)) != 0) |
293 | 0 | return r; |
294 | | |
295 | 0 | return 0; |
296 | 0 | } |
297 | | |
298 | | static int |
299 | | ssh_mldsa44_ed25519_deserialize_public(const char *ktype, struct sshbuf *b, |
300 | | struct sshkey *key) |
301 | 81 | { |
302 | 81 | u_char *pk = NULL; |
303 | 81 | size_t len = 0; |
304 | 81 | int r; |
305 | | |
306 | 81 | if ((r = sshbuf_get_string(b, &pk, &len)) != 0) |
307 | 10 | return r; |
308 | 71 | if (len != MLDSA44_ED25519_PK_SZ) { |
309 | 46 | freezero(pk, len); |
310 | 46 | return SSH_ERR_INVALID_FORMAT; |
311 | 46 | } |
312 | 25 | key->mldsa_ed25519_pk = pk; |
313 | 25 | return 0; |
314 | 71 | } |
315 | | |
316 | | static int |
317 | | ssh_mldsa44_ed25519_deserialize_private(const char *ktype, struct sshbuf *b, |
318 | | struct sshkey *key) |
319 | 55 | { |
320 | 55 | int r; |
321 | 55 | size_t sklen = 0; |
322 | 55 | u_char *sk = NULL; |
323 | | |
324 | 55 | if (!sshkey_is_cert(key)) { |
325 | 55 | if ((r = ssh_mldsa44_ed25519_deserialize_public(ktype, |
326 | 55 | b, key)) != 0) |
327 | 32 | return r; |
328 | 55 | } |
329 | 23 | if ((r = sshbuf_get_string(b, &sk, &sklen)) != 0) |
330 | 2 | goto out; |
331 | 21 | if (sklen != MLDSA44_ED25519_SK_SZ) { |
332 | 20 | r = SSH_ERR_INVALID_FORMAT; |
333 | 20 | goto out; |
334 | 20 | } |
335 | 1 | key->mldsa_ed25519_sk = sk; |
336 | 1 | sk = NULL; /* transferred */ |
337 | 1 | r = 0; |
338 | 23 | out: |
339 | 23 | freezero(sk, sklen); |
340 | 23 | return r; |
341 | 1 | } |
342 | | |
343 | | static int |
344 | | ssh_mldsa44_ed25519_generate(struct sshkey *k, int bits) |
345 | 0 | { |
346 | 0 | free(k->mldsa_ed25519_pk); |
347 | 0 | free(k->mldsa_ed25519_sk); |
348 | 0 | k->mldsa_ed25519_pk = NULL; |
349 | 0 | k->mldsa_ed25519_sk = NULL; |
350 | 0 | if ((k->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL || |
351 | 0 | (k->mldsa_ed25519_sk = malloc(MLDSA44_ED25519_SK_SZ)) == NULL) { |
352 | 0 | free(k->mldsa_ed25519_pk); |
353 | 0 | return SSH_ERR_ALLOC_FAIL; |
354 | 0 | } |
355 | 0 | if (crypto_sign_mldsa44_ed25519_keygen(k->mldsa_ed25519_pk, |
356 | 0 | k->mldsa_ed25519_sk) != 0) { |
357 | 0 | free(k->mldsa_ed25519_pk); |
358 | 0 | free(k->mldsa_ed25519_sk); |
359 | 0 | return SSH_ERR_CRYPTO_ERROR; |
360 | 0 | } |
361 | 0 | return 0; |
362 | 0 | } |
363 | | |
364 | | static int |
365 | | ssh_mldsa44_ed25519_copy_public(const struct sshkey *from, struct sshkey *to) |
366 | 0 | { |
367 | 0 | if (from->mldsa_ed25519_pk == NULL) |
368 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
369 | 0 | if ((to->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL) |
370 | 0 | return SSH_ERR_ALLOC_FAIL; |
371 | 0 | memcpy(to->mldsa_ed25519_pk, from->mldsa_ed25519_pk, |
372 | 0 | MLDSA44_ED25519_PK_SZ); |
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | | static int |
377 | | ssh_mldsa44_ed25519_sign(struct sshkey *key, |
378 | | u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, |
379 | | const char *alg, const char *sk_provider, const char *sk_pin, |
380 | | u_int compat) |
381 | 0 | { |
382 | 0 | u_char sig[MLDSA44_ED25519_SIG_SZ]; |
383 | 0 | struct sshbuf *b = NULL; |
384 | 0 | int r = SSH_ERR_INTERNAL_ERROR; |
385 | |
|
386 | 0 | if (lenp != NULL) |
387 | 0 | *lenp = 0; |
388 | 0 | if (sigp != NULL) |
389 | 0 | *sigp = NULL; |
390 | |
|
391 | 0 | if (key == NULL || |
392 | 0 | sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 || |
393 | 0 | key->mldsa_ed25519_sk == NULL) |
394 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
395 | | |
396 | 0 | if (crypto_sign_mldsa44_ed25519_sign(sig, data, datalen, NULL, 0, |
397 | 0 | key->mldsa_ed25519_sk) != 0) { |
398 | 0 | r = SSH_ERR_CRYPTO_ERROR; |
399 | 0 | goto out; |
400 | 0 | } |
401 | | |
402 | 0 | if ((b = sshbuf_new()) == NULL) { |
403 | 0 | r = SSH_ERR_ALLOC_FAIL; |
404 | 0 | goto out; |
405 | 0 | } |
406 | 0 | if ((r = sshbuf_put_cstring(b, SSH_MLDSA44_ED25519_ALG_NAME)) != 0 || |
407 | 0 | (r = sshbuf_put_string(b, sig, sizeof(sig))) != 0) |
408 | 0 | goto out; |
409 | | |
410 | 0 | if (sigp != NULL) { |
411 | 0 | if ((*sigp = malloc(sshbuf_len(b))) == NULL) { |
412 | 0 | r = SSH_ERR_ALLOC_FAIL; |
413 | 0 | goto out; |
414 | 0 | } |
415 | 0 | memcpy(*sigp, sshbuf_ptr(b), sshbuf_len(b)); |
416 | 0 | } |
417 | 0 | if (lenp != NULL) |
418 | 0 | *lenp = sshbuf_len(b); |
419 | 0 | r = 0; |
420 | 0 | out: |
421 | 0 | sshbuf_free(b); |
422 | 0 | explicit_bzero(sig, sizeof(sig)); |
423 | 0 | return r; |
424 | 0 | } |
425 | | |
426 | | static int |
427 | | ssh_mldsa44_ed25519_verify(const struct sshkey *key, |
428 | | const u_char *sig, size_t siglen, const u_char *data, size_t dlen, |
429 | | const char *alg, u_int compat, struct sshkey_sig_details **detailsp) |
430 | 0 | { |
431 | 0 | struct sshbuf *b = NULL; |
432 | 0 | char *ktype = NULL; |
433 | 0 | const u_char *sigblob; |
434 | 0 | size_t len; |
435 | 0 | int r; |
436 | |
|
437 | 0 | if (key == NULL || |
438 | 0 | sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 || |
439 | 0 | key->mldsa_ed25519_pk == NULL || |
440 | 0 | sig == NULL || siglen == 0) |
441 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
442 | | |
443 | 0 | if ((b = sshbuf_from(sig, siglen)) == NULL) |
444 | 0 | return SSH_ERR_ALLOC_FAIL; |
445 | 0 | if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || |
446 | 0 | (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) |
447 | 0 | goto out; |
448 | 0 | if (strcmp(SSH_MLDSA44_ED25519_ALG_NAME, ktype) != 0) { |
449 | 0 | r = SSH_ERR_KEY_TYPE_MISMATCH; |
450 | 0 | goto out; |
451 | 0 | } |
452 | 0 | if (sshbuf_len(b) != 0) { |
453 | 0 | r = SSH_ERR_UNEXPECTED_TRAILING_DATA; |
454 | 0 | goto out; |
455 | 0 | } |
456 | 0 | if (len != MLDSA44_ED25519_SIG_SZ) { |
457 | 0 | r = SSH_ERR_INVALID_FORMAT; |
458 | 0 | goto out; |
459 | 0 | } |
460 | | |
461 | 0 | if (crypto_sign_mldsa44_ed25519_verify(sigblob, data, dlen, NULL, 0, |
462 | 0 | key->mldsa_ed25519_pk) != 0) { |
463 | 0 | r = SSH_ERR_SIGNATURE_INVALID; |
464 | 0 | goto out; |
465 | 0 | } |
466 | | |
467 | 0 | r = 0; |
468 | 0 | out: |
469 | 0 | sshbuf_free(b); |
470 | 0 | free(ktype); |
471 | 0 | return r; |
472 | 0 | } |
473 | | |
474 | | const struct sshkey_impl_funcs sshkey_mldsa44_ed25519_funcs = { |
475 | | /* .size = */ NULL, |
476 | | /* .alloc = */ NULL, |
477 | | /* .cleanup = */ ssh_mldsa44_ed25519_cleanup, |
478 | | /* .equal = */ ssh_mldsa44_ed25519_equal, |
479 | | /* .ssh_serialize_public = */ ssh_mldsa44_ed25519_serialize_public, |
480 | | /* .ssh_deserialize_public = */ ssh_mldsa44_ed25519_deserialize_public, |
481 | | /* .ssh_serialize_private = */ ssh_mldsa44_ed25519_serialize_private, |
482 | | /* .ssh_deserialize_private = */ ssh_mldsa44_ed25519_deserialize_private, |
483 | | /* .generate = */ ssh_mldsa44_ed25519_generate, |
484 | | /* .copy_public = */ ssh_mldsa44_ed25519_copy_public, |
485 | | /* .sign = */ ssh_mldsa44_ed25519_sign, |
486 | | /* .verify = */ ssh_mldsa44_ed25519_verify, |
487 | | }; |
488 | | |
489 | | const struct sshkey_impl sshkey_mldsa44_ed25519_impl = { |
490 | | /* .name = */ "ssh-mldsa44-ed25519@openssh.com", |
491 | | /* .shortname = */ "MLDSA44-ED25519", |
492 | | /* .sigalg = */ NULL, |
493 | | /* .type = */ KEY_MLDSA44_ED25519, |
494 | | /* .nid = */ 0, |
495 | | /* .cert = */ 0, |
496 | | /* .sigonly = */ 0, |
497 | | /* .keybits = */ 256, |
498 | | /* .funcs = */ &sshkey_mldsa44_ed25519_funcs, |
499 | | }; |
500 | | |
501 | | const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl = { |
502 | | /* .name = */ "ssh-mldsa44-ed25519-cert-v01@openssh.com", |
503 | | /* .shortname = */ "MLDSA44-ED25519-CERT", |
504 | | /* .sigalg = */ NULL, |
505 | | /* .type = */ KEY_MLDSA44_ED25519_CERT, |
506 | | /* .nid = */ 0, |
507 | | /* .cert = */ 1, |
508 | | /* .sigonly = */ 0, |
509 | | /* .keybits = */ 256, |
510 | | /* .funcs = */ &sshkey_mldsa44_ed25519_funcs, |
511 | | }; |
512 | | #endif /* USE_MLDSA */ |