/src/openssh/ssh-mldsa-eddsa.c
Line | Count | Source |
1 | | /* $OpenBSD: ssh-mldsa-eddsa.c,v 1.7 2026/10/01 04:17:39 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" |
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_seed_keypair(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_seed_keypair(ed25519_pk, ed25519_sk, |
172 | 0 | sk + MLDSA44_SEEDBYTES) != 0) |
173 | 0 | goto out; |
174 | | |
175 | | /* Sign with Ed25519 */ |
176 | 0 | if (crypto_sign_ed25519_detached(sig + MLDSA44_SIGBYTES, &smlen, |
177 | 0 | m_prime, m_prime_len, ed25519_sk) != 0 || |
178 | 0 | smlen != crypto_sign_ed25519_BYTES) |
179 | 0 | goto out; |
180 | | |
181 | 0 | r = 0; |
182 | 0 | out: |
183 | 0 | free(m_prime); |
184 | 0 | explicit_bzero(mldsa_rnd, sizeof(mldsa_rnd)); |
185 | 0 | explicit_bzero(mldsa_sk, sizeof(mldsa_sk)); |
186 | 0 | explicit_bzero(ed25519_sk, sizeof(ed25519_sk)); |
187 | 0 | return r; |
188 | 0 | } |
189 | | |
190 | | int |
191 | | crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ], |
192 | | const uint8_t *msg, size_t msglen, |
193 | | const uint8_t *ctx, size_t ctxlen, |
194 | | const uint8_t pk[MLDSA44_ED25519_PK_SZ]) |
195 | 0 | { |
196 | 0 | uint8_t *m_prime = NULL; |
197 | 0 | size_t m_prime_len = 0; |
198 | 0 | int r = -1; |
199 | |
|
200 | 0 | if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen, |
201 | 0 | ctx, ctxlen) != 0) |
202 | 0 | return -1; |
203 | | |
204 | | /* Verify ML-DSA */ |
205 | 0 | if (crypto_sign_mldsa44_verify(sig, m_prime, m_prime_len, |
206 | 0 | (const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1, |
207 | 0 | pk) != 0) |
208 | 0 | goto out; |
209 | | |
210 | | /* Verify Ed25519 */ |
211 | 0 | if (crypto_sign_ed25519_verify_detached(sig + MLDSA44_SIGBYTES, |
212 | 0 | m_prime, m_prime_len, pk + MLDSA44_PUBLICKEYBYTES) != 0) |
213 | 0 | goto out; |
214 | | |
215 | 0 | r = 0; |
216 | 0 | out: |
217 | 0 | free(m_prime); |
218 | 0 | return r; |
219 | 0 | } |
220 | | |
221 | | /* sshkey integration */ |
222 | | |
223 | | static void |
224 | | ssh_mldsa44_ed25519_cleanup(struct sshkey *k) |
225 | 0 | { |
226 | 0 | freezero(k->mldsa_ed25519_pk, MLDSA44_ED25519_PK_SZ); |
227 | 0 | freezero(k->mldsa_ed25519_sk, MLDSA44_ED25519_SK_SZ); |
228 | 0 | k->mldsa_ed25519_pk = NULL; |
229 | 0 | k->mldsa_ed25519_sk = NULL; |
230 | 0 | } |
231 | | |
232 | | static int |
233 | | ssh_mldsa44_ed25519_equal(const struct sshkey *a, const struct sshkey *b) |
234 | 0 | { |
235 | 0 | if (a->mldsa_ed25519_pk == NULL || b->mldsa_ed25519_pk == NULL) |
236 | 0 | return 0; |
237 | 0 | if (memcmp(a->mldsa_ed25519_pk, b->mldsa_ed25519_pk, |
238 | 0 | MLDSA44_ED25519_PK_SZ) != 0) |
239 | 0 | return 0; |
240 | 0 | return 1; |
241 | 0 | } |
242 | | |
243 | | static int |
244 | | ssh_mldsa44_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b, |
245 | | enum sshkey_serialize_rep opts) |
246 | 0 | { |
247 | 0 | int r; |
248 | |
|
249 | 0 | if (key->mldsa_ed25519_pk == NULL) |
250 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
251 | 0 | if ((r = sshbuf_put_string(b, key->mldsa_ed25519_pk, |
252 | 0 | MLDSA44_ED25519_PK_SZ)) != 0) |
253 | 0 | return r; |
254 | | |
255 | 0 | return 0; |
256 | 0 | } |
257 | | |
258 | | static int |
259 | | ssh_mldsa44_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b, |
260 | | enum sshkey_serialize_rep opts) |
261 | 0 | { |
262 | 0 | int r; |
263 | |
|
264 | 0 | if (key->mldsa_ed25519_sk == NULL) |
265 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
266 | 0 | if (!sshkey_is_cert(key)) { |
267 | 0 | if ((r = ssh_mldsa44_ed25519_serialize_public(key, |
268 | 0 | b, opts)) != 0) |
269 | 0 | return r; |
270 | 0 | } |
271 | 0 | if ((r = sshbuf_put_string(b, key->mldsa_ed25519_sk, |
272 | 0 | MLDSA44_ED25519_SK_SZ)) != 0) |
273 | 0 | return r; |
274 | | |
275 | 0 | return 0; |
276 | 0 | } |
277 | | |
278 | | static int |
279 | | ssh_mldsa44_ed25519_deserialize_public(const char *ktype, struct sshbuf *b, |
280 | | struct sshkey *key) |
281 | 0 | { |
282 | 0 | u_char *pk = NULL; |
283 | 0 | size_t len = 0; |
284 | 0 | int r; |
285 | |
|
286 | 0 | if ((r = sshbuf_get_string(b, &pk, &len)) != 0) |
287 | 0 | return r; |
288 | 0 | if (len != MLDSA44_ED25519_PK_SZ) { |
289 | 0 | freezero(pk, len); |
290 | 0 | return SSH_ERR_INVALID_FORMAT; |
291 | 0 | } |
292 | 0 | key->mldsa_ed25519_pk = pk; |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | | static int |
297 | | ssh_mldsa44_ed25519_deserialize_private(const char *ktype, struct sshbuf *b, |
298 | | struct sshkey *key) |
299 | 0 | { |
300 | 0 | int r; |
301 | 0 | size_t sklen = 0; |
302 | 0 | u_char *sk = NULL; |
303 | |
|
304 | 0 | if (!sshkey_is_cert(key)) { |
305 | 0 | if ((r = ssh_mldsa44_ed25519_deserialize_public(ktype, |
306 | 0 | b, key)) != 0) |
307 | 0 | return r; |
308 | 0 | } |
309 | 0 | if ((r = sshbuf_get_string(b, &sk, &sklen)) != 0) |
310 | 0 | goto out; |
311 | 0 | if (sklen != MLDSA44_ED25519_SK_SZ) { |
312 | 0 | r = SSH_ERR_INVALID_FORMAT; |
313 | 0 | goto out; |
314 | 0 | } |
315 | 0 | key->mldsa_ed25519_sk = sk; |
316 | 0 | sk = NULL; /* transferred */ |
317 | 0 | r = 0; |
318 | 0 | out: |
319 | 0 | freezero(sk, sklen); |
320 | 0 | return r; |
321 | 0 | } |
322 | | |
323 | | static int |
324 | | ssh_mldsa44_ed25519_generate(struct sshkey *k, int bits) |
325 | 0 | { |
326 | 0 | ssh_mldsa44_ed25519_cleanup(k); |
327 | 0 | if ((k->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL || |
328 | 0 | (k->mldsa_ed25519_sk = malloc(MLDSA44_ED25519_SK_SZ)) == NULL) { |
329 | 0 | ssh_mldsa44_ed25519_cleanup(k); |
330 | 0 | return SSH_ERR_ALLOC_FAIL; |
331 | 0 | } |
332 | 0 | if (crypto_sign_mldsa44_ed25519_keygen(k->mldsa_ed25519_pk, |
333 | 0 | k->mldsa_ed25519_sk) != 0) { |
334 | 0 | ssh_mldsa44_ed25519_cleanup(k); |
335 | 0 | return SSH_ERR_CRYPTO_ERROR; |
336 | 0 | } |
337 | 0 | return 0; |
338 | 0 | } |
339 | | |
340 | | static int |
341 | | ssh_mldsa44_ed25519_copy_public(const struct sshkey *from, struct sshkey *to) |
342 | 0 | { |
343 | 0 | if (from->mldsa_ed25519_pk == NULL) |
344 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
345 | 0 | if ((to->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL) |
346 | 0 | return SSH_ERR_ALLOC_FAIL; |
347 | 0 | memcpy(to->mldsa_ed25519_pk, from->mldsa_ed25519_pk, |
348 | 0 | MLDSA44_ED25519_PK_SZ); |
349 | 0 | return 0; |
350 | 0 | } |
351 | | |
352 | | static int |
353 | | ssh_mldsa44_ed25519_sign(struct sshkey *key, |
354 | | u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, |
355 | | const char *alg, const char *sk_provider, const char *sk_pin, |
356 | | u_int compat) |
357 | 0 | { |
358 | 0 | u_char sig[MLDSA44_ED25519_SIG_SZ]; |
359 | 0 | struct sshbuf *b = NULL; |
360 | 0 | int r = SSH_ERR_INTERNAL_ERROR; |
361 | |
|
362 | 0 | if (lenp != NULL) |
363 | 0 | *lenp = 0; |
364 | 0 | if (sigp != NULL) |
365 | 0 | *sigp = NULL; |
366 | |
|
367 | 0 | if (key == NULL || |
368 | 0 | sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 || |
369 | 0 | key->mldsa_ed25519_sk == NULL) |
370 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
371 | | |
372 | 0 | if (crypto_sign_mldsa44_ed25519_sign(sig, data, datalen, NULL, 0, |
373 | 0 | key->mldsa_ed25519_sk) != 0) { |
374 | 0 | r = SSH_ERR_CRYPTO_ERROR; |
375 | 0 | goto out; |
376 | 0 | } |
377 | | |
378 | 0 | if ((b = sshbuf_new()) == NULL) { |
379 | 0 | r = SSH_ERR_ALLOC_FAIL; |
380 | 0 | goto out; |
381 | 0 | } |
382 | 0 | if ((r = sshbuf_put_cstring(b, SSH_MLDSA44_ED25519_ALG_NAME)) != 0 || |
383 | 0 | (r = sshbuf_put_string(b, sig, sizeof(sig))) != 0) |
384 | 0 | goto out; |
385 | | |
386 | 0 | if (sigp != NULL) { |
387 | 0 | if ((*sigp = malloc(sshbuf_len(b))) == NULL) { |
388 | 0 | r = SSH_ERR_ALLOC_FAIL; |
389 | 0 | goto out; |
390 | 0 | } |
391 | 0 | memcpy(*sigp, sshbuf_ptr(b), sshbuf_len(b)); |
392 | 0 | } |
393 | 0 | if (lenp != NULL) |
394 | 0 | *lenp = sshbuf_len(b); |
395 | 0 | r = 0; |
396 | 0 | out: |
397 | 0 | sshbuf_free(b); |
398 | 0 | explicit_bzero(sig, sizeof(sig)); |
399 | 0 | return r; |
400 | 0 | } |
401 | | |
402 | | static int |
403 | | ssh_mldsa44_ed25519_verify(const struct sshkey *key, |
404 | | const u_char *sig, size_t siglen, const u_char *data, size_t dlen, |
405 | | const char *alg, u_int compat, struct sshkey_sig_details **detailsp) |
406 | 0 | { |
407 | 0 | struct sshbuf *b = NULL; |
408 | 0 | char *ktype = NULL; |
409 | 0 | const u_char *sigblob; |
410 | 0 | size_t len; |
411 | 0 | int r; |
412 | |
|
413 | 0 | if (key == NULL || |
414 | 0 | sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 || |
415 | 0 | key->mldsa_ed25519_pk == NULL || |
416 | 0 | sig == NULL || siglen == 0) |
417 | 0 | return SSH_ERR_INVALID_ARGUMENT; |
418 | | |
419 | 0 | if ((b = sshbuf_from(sig, siglen)) == NULL) |
420 | 0 | return SSH_ERR_ALLOC_FAIL; |
421 | 0 | if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || |
422 | 0 | (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) |
423 | 0 | goto out; |
424 | 0 | if (strcmp(SSH_MLDSA44_ED25519_ALG_NAME, ktype) != 0) { |
425 | 0 | r = SSH_ERR_KEY_TYPE_MISMATCH; |
426 | 0 | goto out; |
427 | 0 | } |
428 | 0 | if (sshbuf_len(b) != 0) { |
429 | 0 | r = SSH_ERR_UNEXPECTED_TRAILING_DATA; |
430 | 0 | goto out; |
431 | 0 | } |
432 | 0 | if (len != MLDSA44_ED25519_SIG_SZ) { |
433 | 0 | r = SSH_ERR_INVALID_FORMAT; |
434 | 0 | goto out; |
435 | 0 | } |
436 | | |
437 | 0 | if (crypto_sign_mldsa44_ed25519_verify(sigblob, data, dlen, NULL, 0, |
438 | 0 | key->mldsa_ed25519_pk) != 0) { |
439 | 0 | r = SSH_ERR_SIGNATURE_INVALID; |
440 | 0 | goto out; |
441 | 0 | } |
442 | | |
443 | 0 | r = 0; |
444 | 0 | out: |
445 | 0 | sshbuf_free(b); |
446 | 0 | free(ktype); |
447 | 0 | return r; |
448 | 0 | } |
449 | | |
450 | | const struct sshkey_impl_funcs sshkey_mldsa44_ed25519_funcs = { |
451 | | /* .size = */ NULL, |
452 | | /* .alloc = */ NULL, |
453 | | /* .cleanup = */ ssh_mldsa44_ed25519_cleanup, |
454 | | /* .equal = */ ssh_mldsa44_ed25519_equal, |
455 | | /* .ssh_serialize_public = */ ssh_mldsa44_ed25519_serialize_public, |
456 | | /* .ssh_deserialize_public = */ ssh_mldsa44_ed25519_deserialize_public, |
457 | | /* .ssh_serialize_private = */ ssh_mldsa44_ed25519_serialize_private, |
458 | | /* .ssh_deserialize_private = */ ssh_mldsa44_ed25519_deserialize_private, |
459 | | /* .generate = */ ssh_mldsa44_ed25519_generate, |
460 | | /* .copy_public = */ ssh_mldsa44_ed25519_copy_public, |
461 | | /* .sign = */ ssh_mldsa44_ed25519_sign, |
462 | | /* .verify = */ ssh_mldsa44_ed25519_verify, |
463 | | }; |
464 | | |
465 | | const struct sshkey_impl sshkey_mldsa44_ed25519_impl = { |
466 | | /* .name = */ "ssh-mldsa44-ed25519", |
467 | | /* .shortname = */ "MLDSA44-ED25519", |
468 | | /* .sigalg = */ NULL, |
469 | | /* .type = */ KEY_MLDSA44_ED25519, |
470 | | /* .nid = */ 0, |
471 | | /* .cert = */ 0, |
472 | | /* .sigonly = */ 0, |
473 | | /* .keybits = */ 512, /* size of the private key */ |
474 | | /* .funcs = */ &sshkey_mldsa44_ed25519_funcs, |
475 | | }; |
476 | | |
477 | | const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl = { |
478 | | /* .name = */ "ssh-mldsa44-ed25519-cert", |
479 | | /* .shortname = */ "MLDSA44-ED25519-CERT", |
480 | | /* .sigalg = */ NULL, |
481 | | /* .type = */ KEY_MLDSA44_ED25519_CERT, |
482 | | /* .nid = */ 0, |
483 | | /* .cert = */ 1, |
484 | | /* .sigonly = */ 0, |
485 | | /* .keybits = */ 512, /* size of the private key */ |
486 | | /* .funcs = */ &sshkey_mldsa44_ed25519_funcs, |
487 | | }; |
488 | | #endif /* USE_MLDSA */ |