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