xref: /netbsd-src/crypto/external/bsd/openssh/dist/kexmlkem768x25519.c (revision 617d6335c8147aba892a1ac09a7a4d72c0afb42a)
1 /*	$NetBSD: kexmlkem768x25519.c,v 1.3 2024/10/29 03:20:32 rin Exp $	*/
2 /* $OpenBSD: kexmlkem768x25519.c,v 1.2 2024/10/27 02:06:59 djm Exp $ */
3 /*
4  * Copyright (c) 2023 Markus Friedl.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 #include "includes.h"
27 __RCSID("$NetBSD: kexmlkem768x25519.c,v 1.3 2024/10/29 03:20:32 rin Exp $");
28 
29 #include <sys/types.h>
30 
31 #include <stdio.h>
32 #include <stdint.h>
33 #include <stdbool.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <endian.h>
37 
38 #include "sshkey.h"
39 #include "kex.h"
40 #include "sshbuf.h"
41 #include "digest.h"
42 #include "ssherr.h"
43 #include "log.h"
44 
45 #include "libcrux_mlkem768_sha3.h"
46 
47 int
48 kex_kem_mlkem768x25519_keypair(struct kex *kex)
49 {
50 	struct sshbuf *buf = NULL;
51 	u_char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN], *cp = NULL;
52 	size_t need;
53 	int r = SSH_ERR_INTERNAL_ERROR;
54 	struct libcrux_mlkem768_keypair keypair;
55 
56 	if ((buf = sshbuf_new()) == NULL)
57 		return SSH_ERR_ALLOC_FAIL;
58 	need = crypto_kem_mlkem768_PUBLICKEYBYTES + CURVE25519_SIZE;
59 	if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
60 		goto out;
61 	arc4random_buf(rnd, sizeof(rnd));
62 	keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
63 	memcpy(cp, keypair.pk.value, crypto_kem_mlkem768_PUBLICKEYBYTES);
64 	memcpy(kex->mlkem768_client_key, keypair.sk.value,
65 	    sizeof(kex->mlkem768_client_key));
66 #ifdef DEBUG_KEXECDH
67 	dump_digest("client public key mlkem768:", cp,
68 	    crypto_kem_mlkem768_PUBLICKEYBYTES);
69 #endif
70 	cp += crypto_kem_mlkem768_PUBLICKEYBYTES;
71 	kexc25519_keygen(kex->c25519_client_key, cp);
72 #ifdef DEBUG_KEXECDH
73 	dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
74 #endif
75 	/* success */
76 	r = 0;
77 	kex->client_pub = buf;
78 	buf = NULL;
79  out:
80 	explicit_bzero(&keypair, sizeof(keypair));
81 	explicit_bzero(rnd, sizeof(rnd));
82 	sshbuf_free(buf);
83 	return r;
84 }
85 
86 int
87 kex_kem_mlkem768x25519_enc(struct kex *kex,
88    const struct sshbuf *client_blob, struct sshbuf **server_blobp,
89    struct sshbuf **shared_secretp)
90 {
91 	struct sshbuf *server_blob = NULL;
92 	struct sshbuf *buf = NULL;
93 	const u_char *client_pub;
94 	u_char rnd[LIBCRUX_ML_KEM_ENC_PRNG_LEN];
95 	u_char server_pub[CURVE25519_SIZE], server_key[CURVE25519_SIZE];
96 	u_char hash[SSH_DIGEST_MAX_LENGTH];
97 	size_t need;
98 	int r = SSH_ERR_INTERNAL_ERROR;
99 	struct libcrux_mlkem768_enc_result enc;
100 	struct libcrux_mlkem768_pk mlkem_pub;
101 
102 	*server_blobp = NULL;
103 	*shared_secretp = NULL;
104 	memset(&mlkem_pub, 0, sizeof(mlkem_pub));
105 
106 	/* client_blob contains both KEM and ECDH client pubkeys */
107 	need = crypto_kem_mlkem768_PUBLICKEYBYTES + CURVE25519_SIZE;
108 	if (sshbuf_len(client_blob) != need) {
109 		r = SSH_ERR_SIGNATURE_INVALID;
110 		goto out;
111 	}
112 	client_pub = sshbuf_ptr(client_blob);
113 #ifdef DEBUG_KEXECDH
114 	dump_digest("client public key mlkem768:", client_pub,
115 	    crypto_kem_mlkem768_PUBLICKEYBYTES);
116 	dump_digest("client public key 25519:",
117 	    client_pub + crypto_kem_mlkem768_PUBLICKEYBYTES,
118 	    CURVE25519_SIZE);
119 #endif
120 	/* check public key validity */
121 	memcpy(mlkem_pub.value, client_pub, crypto_kem_mlkem768_PUBLICKEYBYTES);
122 	if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub)) {
123 		r = SSH_ERR_SIGNATURE_INVALID;
124 		goto out;
125 	}
126 
127 	/* allocate buffer for concatenation of KEM key and ECDH shared key */
128 	/* the buffer will be hashed and the result is the shared secret */
129 	if ((buf = sshbuf_new()) == NULL) {
130 		r = SSH_ERR_ALLOC_FAIL;
131 		goto out;
132 	}
133 	/* allocate space for encrypted KEM key and ECDH pub key */
134 	if ((server_blob = sshbuf_new()) == NULL) {
135 		r = SSH_ERR_ALLOC_FAIL;
136 		goto out;
137 	}
138 	/* generate and encrypt KEM key with client key */
139 	arc4random_buf(rnd, sizeof(rnd));
140 	enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub, rnd);
141 	/* generate ECDH key pair, store server pubkey after ciphertext */
142 	kexc25519_keygen(server_key, server_pub);
143 	if ((r = sshbuf_put(buf, enc.snd, sizeof(enc.snd))) != 0 ||
144 	    (r = sshbuf_put(server_blob, enc.fst.value, sizeof(enc.fst.value))) != 0 ||
145 	    (r = sshbuf_put(server_blob, server_pub, sizeof(server_pub))) != 0)
146 		goto out;
147 	/* append ECDH shared key */
148 	client_pub += crypto_kem_mlkem768_PUBLICKEYBYTES;
149 	if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0)
150 		goto out;
151 	if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
152 		goto out;
153 #ifdef DEBUG_KEXECDH
154 	dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE);
155 	dump_digest("server cipher text:",
156 	    enc.fst.value, sizeof(enc.fst.value));
157 	dump_digest("server kem key:", enc.snd, sizeof(enc.snd));
158 	dump_digest("concatenation of KEM key and ECDH shared key:",
159 	    sshbuf_ptr(buf), sshbuf_len(buf));
160 #endif
161 	/* string-encoded hash is resulting shared secret */
162 	sshbuf_reset(buf);
163 	if ((r = sshbuf_put_string(buf, hash,
164 	    ssh_digest_bytes(kex->hash_alg))) != 0)
165 		goto out;
166 #ifdef DEBUG_KEXECDH
167 	dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
168 #endif
169 	/* success */
170 	r = 0;
171 	*server_blobp = server_blob;
172 	*shared_secretp = buf;
173 	server_blob = NULL;
174 	buf = NULL;
175  out:
176 	explicit_bzero(hash, sizeof(hash));
177 	explicit_bzero(server_key, sizeof(server_key));
178 	explicit_bzero(rnd, sizeof(rnd));
179 	explicit_bzero(&enc, sizeof(enc));
180 	sshbuf_free(server_blob);
181 	sshbuf_free(buf);
182 	return r;
183 }
184 
185 int
186 kex_kem_mlkem768x25519_dec(struct kex *kex,
187     const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
188 {
189 	struct sshbuf *buf = NULL;
190 	u_char mlkem_key[crypto_kem_mlkem768_BYTES];
191 	const u_char *ciphertext, *server_pub;
192 	u_char hash[SSH_DIGEST_MAX_LENGTH];
193 	size_t need;
194 	int r;
195 	struct libcrux_mlkem768_sk mlkem_priv;
196 	struct libcrux_mlkem768_ciphertext mlkem_ciphertext;
197 
198 	*shared_secretp = NULL;
199 	memset(&mlkem_priv, 0, sizeof(mlkem_priv));
200 	memset(&mlkem_ciphertext, 0, sizeof(mlkem_ciphertext));
201 
202 	need = crypto_kem_mlkem768_CIPHERTEXTBYTES + CURVE25519_SIZE;
203 	if (sshbuf_len(server_blob) != need) {
204 		r = SSH_ERR_SIGNATURE_INVALID;
205 		goto out;
206 	}
207 	ciphertext = sshbuf_ptr(server_blob);
208 	server_pub = ciphertext + crypto_kem_mlkem768_CIPHERTEXTBYTES;
209 	/* hash concatenation of KEM key and ECDH shared key */
210 	if ((buf = sshbuf_new()) == NULL) {
211 		r = SSH_ERR_ALLOC_FAIL;
212 		goto out;
213 	}
214 	memcpy(mlkem_priv.value, kex->mlkem768_client_key,
215 	    sizeof(kex->mlkem768_client_key));
216 	memcpy(mlkem_ciphertext.value, ciphertext,
217 	    sizeof(mlkem_ciphertext.value));
218 #ifdef DEBUG_KEXECDH
219 	dump_digest("server cipher text:", mlkem_ciphertext.value,
220 	    sizeof(mlkem_ciphertext.value));
221 	dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
222 #endif
223 	libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv,
224 	    &mlkem_ciphertext, mlkem_key);
225 	if ((r = sshbuf_put(buf, mlkem_key, sizeof(mlkem_key))) != 0)
226 		goto out;
227 	if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub,
228 	    buf, 1)) < 0)
229 		goto out;
230 	if ((r = ssh_digest_buffer(kex->hash_alg, buf,
231 	    hash, sizeof(hash))) != 0)
232 		goto out;
233 #ifdef DEBUG_KEXECDH
234 	dump_digest("client kem key:", mlkem_key, sizeof(mlkem_key));
235 	dump_digest("concatenation of KEM key and ECDH shared key:",
236 	    sshbuf_ptr(buf), sshbuf_len(buf));
237 #endif
238 	sshbuf_reset(buf);
239 	if ((r = sshbuf_put_string(buf, hash,
240 	    ssh_digest_bytes(kex->hash_alg))) != 0)
241 		goto out;
242 #ifdef DEBUG_KEXECDH
243 	dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
244 #endif
245 	/* success */
246 	r = 0;
247 	*shared_secretp = buf;
248 	buf = NULL;
249  out:
250 	explicit_bzero(hash, sizeof(hash));
251 	explicit_bzero(&mlkem_priv, sizeof(mlkem_priv));
252 	explicit_bzero(&mlkem_ciphertext, sizeof(mlkem_ciphertext));
253 	explicit_bzero(mlkem_key, sizeof(mlkem_key));
254 	sshbuf_free(buf);
255 	return r;
256 }
257