1 /* $OpenBSD: kexmlkem768x25519.c,v 1.2 2024/10/27 02:06:59 djm Exp $ */
3 * Copyright (c) 2023 Markus Friedl. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/types.h>
48 #ifdef USE_MLKEM768X25519
50 #include "libcrux_mlkem768_sha3.h"
53 kex_kem_mlkem768x25519_keypair(struct kex
*kex
)
55 struct sshbuf
*buf
= NULL
;
56 u_char rnd
[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN
], *cp
= NULL
;
58 int r
= SSH_ERR_INTERNAL_ERROR
;
59 struct libcrux_mlkem768_keypair keypair
;
61 if ((buf
= sshbuf_new()) == NULL
)
62 return SSH_ERR_ALLOC_FAIL
;
63 need
= crypto_kem_mlkem768_PUBLICKEYBYTES
+ CURVE25519_SIZE
;
64 if ((r
= sshbuf_reserve(buf
, need
, &cp
)) != 0)
66 arc4random_buf(rnd
, sizeof(rnd
));
67 keypair
= libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd
);
68 memcpy(cp
, keypair
.pk
.value
, crypto_kem_mlkem768_PUBLICKEYBYTES
);
69 memcpy(kex
->mlkem768_client_key
, keypair
.sk
.value
,
70 sizeof(kex
->mlkem768_client_key
));
72 dump_digest("client public key mlkem768:", cp
,
73 crypto_kem_mlkem768_PUBLICKEYBYTES
);
75 cp
+= crypto_kem_mlkem768_PUBLICKEYBYTES
;
76 kexc25519_keygen(kex
->c25519_client_key
, cp
);
78 dump_digest("client public key c25519:", cp
, CURVE25519_SIZE
);
82 kex
->client_pub
= buf
;
85 explicit_bzero(&keypair
, sizeof(keypair
));
86 explicit_bzero(rnd
, sizeof(rnd
));
92 kex_kem_mlkem768x25519_enc(struct kex
*kex
,
93 const struct sshbuf
*client_blob
, struct sshbuf
**server_blobp
,
94 struct sshbuf
**shared_secretp
)
96 struct sshbuf
*server_blob
= NULL
;
97 struct sshbuf
*buf
= NULL
;
98 const u_char
*client_pub
;
99 u_char rnd
[LIBCRUX_ML_KEM_ENC_PRNG_LEN
];
100 u_char server_pub
[CURVE25519_SIZE
], server_key
[CURVE25519_SIZE
];
101 u_char hash
[SSH_DIGEST_MAX_LENGTH
];
103 int r
= SSH_ERR_INTERNAL_ERROR
;
104 struct libcrux_mlkem768_enc_result enc
;
105 struct libcrux_mlkem768_pk mlkem_pub
;
107 *server_blobp
= NULL
;
108 *shared_secretp
= NULL
;
109 memset(&mlkem_pub
, 0, sizeof(mlkem_pub
));
111 /* client_blob contains both KEM and ECDH client pubkeys */
112 need
= crypto_kem_mlkem768_PUBLICKEYBYTES
+ CURVE25519_SIZE
;
113 if (sshbuf_len(client_blob
) != need
) {
114 r
= SSH_ERR_SIGNATURE_INVALID
;
117 client_pub
= sshbuf_ptr(client_blob
);
119 dump_digest("client public key mlkem768:", client_pub
,
120 crypto_kem_mlkem768_PUBLICKEYBYTES
);
121 dump_digest("client public key 25519:",
122 client_pub
+ crypto_kem_mlkem768_PUBLICKEYBYTES
,
125 /* check public key validity */
126 memcpy(mlkem_pub
.value
, client_pub
, crypto_kem_mlkem768_PUBLICKEYBYTES
);
127 if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub
)) {
128 r
= SSH_ERR_SIGNATURE_INVALID
;
132 /* allocate buffer for concatenation of KEM key and ECDH shared key */
133 /* the buffer will be hashed and the result is the shared secret */
134 if ((buf
= sshbuf_new()) == NULL
) {
135 r
= SSH_ERR_ALLOC_FAIL
;
138 /* allocate space for encrypted KEM key and ECDH pub key */
139 if ((server_blob
= sshbuf_new()) == NULL
) {
140 r
= SSH_ERR_ALLOC_FAIL
;
143 /* generate and encrypt KEM key with client key */
144 arc4random_buf(rnd
, sizeof(rnd
));
145 enc
= libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub
, rnd
);
146 /* generate ECDH key pair, store server pubkey after ciphertext */
147 kexc25519_keygen(server_key
, server_pub
);
148 if ((r
= sshbuf_put(buf
, enc
.snd
, sizeof(enc
.snd
))) != 0 ||
149 (r
= sshbuf_put(server_blob
, enc
.fst
.value
, sizeof(enc
.fst
.value
))) != 0 ||
150 (r
= sshbuf_put(server_blob
, server_pub
, sizeof(server_pub
))) != 0)
152 /* append ECDH shared key */
153 client_pub
+= crypto_kem_mlkem768_PUBLICKEYBYTES
;
154 if ((r
= kexc25519_shared_key_ext(server_key
, client_pub
, buf
, 1)) < 0)
156 if ((r
= ssh_digest_buffer(kex
->hash_alg
, buf
, hash
, sizeof(hash
))) != 0)
159 dump_digest("server public key 25519:", server_pub
, CURVE25519_SIZE
);
160 dump_digest("server cipher text:",
161 enc
.fst
.value
, sizeof(enc
.fst
.value
));
162 dump_digest("server kem key:", enc
.snd
, sizeof(enc
.snd
));
163 dump_digest("concatenation of KEM key and ECDH shared key:",
164 sshbuf_ptr(buf
), sshbuf_len(buf
));
166 /* string-encoded hash is resulting shared secret */
168 if ((r
= sshbuf_put_string(buf
, hash
,
169 ssh_digest_bytes(kex
->hash_alg
))) != 0)
172 dump_digest("encoded shared secret:", sshbuf_ptr(buf
), sshbuf_len(buf
));
176 *server_blobp
= server_blob
;
177 *shared_secretp
= buf
;
181 explicit_bzero(hash
, sizeof(hash
));
182 explicit_bzero(server_key
, sizeof(server_key
));
183 explicit_bzero(rnd
, sizeof(rnd
));
184 explicit_bzero(&enc
, sizeof(enc
));
185 sshbuf_free(server_blob
);
191 kex_kem_mlkem768x25519_dec(struct kex
*kex
,
192 const struct sshbuf
*server_blob
, struct sshbuf
**shared_secretp
)
194 struct sshbuf
*buf
= NULL
;
195 u_char mlkem_key
[crypto_kem_mlkem768_BYTES
];
196 const u_char
*ciphertext
, *server_pub
;
197 u_char hash
[SSH_DIGEST_MAX_LENGTH
];
200 struct libcrux_mlkem768_sk mlkem_priv
;
201 struct libcrux_mlkem768_ciphertext mlkem_ciphertext
;
203 *shared_secretp
= NULL
;
204 memset(&mlkem_priv
, 0, sizeof(mlkem_priv
));
205 memset(&mlkem_ciphertext
, 0, sizeof(mlkem_ciphertext
));
207 need
= crypto_kem_mlkem768_CIPHERTEXTBYTES
+ CURVE25519_SIZE
;
208 if (sshbuf_len(server_blob
) != need
) {
209 r
= SSH_ERR_SIGNATURE_INVALID
;
212 ciphertext
= sshbuf_ptr(server_blob
);
213 server_pub
= ciphertext
+ crypto_kem_mlkem768_CIPHERTEXTBYTES
;
214 /* hash concatenation of KEM key and ECDH shared key */
215 if ((buf
= sshbuf_new()) == NULL
) {
216 r
= SSH_ERR_ALLOC_FAIL
;
219 memcpy(mlkem_priv
.value
, kex
->mlkem768_client_key
,
220 sizeof(kex
->mlkem768_client_key
));
221 memcpy(mlkem_ciphertext
.value
, ciphertext
,
222 sizeof(mlkem_ciphertext
.value
));
224 dump_digest("server cipher text:", mlkem_ciphertext
.value
,
225 sizeof(mlkem_ciphertext
.value
));
226 dump_digest("server public key c25519:", server_pub
, CURVE25519_SIZE
);
228 libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv
,
229 &mlkem_ciphertext
, mlkem_key
);
230 if ((r
= sshbuf_put(buf
, mlkem_key
, sizeof(mlkem_key
))) != 0)
232 if ((r
= kexc25519_shared_key_ext(kex
->c25519_client_key
, server_pub
,
235 if ((r
= ssh_digest_buffer(kex
->hash_alg
, buf
,
236 hash
, sizeof(hash
))) != 0)
239 dump_digest("client kem key:", mlkem_key
, sizeof(mlkem_key
));
240 dump_digest("concatenation of KEM key and ECDH shared key:",
241 sshbuf_ptr(buf
), sshbuf_len(buf
));
244 if ((r
= sshbuf_put_string(buf
, hash
,
245 ssh_digest_bytes(kex
->hash_alg
))) != 0)
248 dump_digest("encoded shared secret:", sshbuf_ptr(buf
), sshbuf_len(buf
));
252 *shared_secretp
= buf
;
255 explicit_bzero(hash
, sizeof(hash
));
256 explicit_bzero(&mlkem_priv
, sizeof(mlkem_priv
));
257 explicit_bzero(&mlkem_ciphertext
, sizeof(mlkem_ciphertext
));
258 explicit_bzero(mlkem_key
, sizeof(mlkem_key
));
262 #else /* USE_MLKEM768X25519 */
264 kex_kem_mlkem768x25519_keypair(struct kex
*kex
)
266 return SSH_ERR_SIGN_ALG_UNSUPPORTED
;
270 kex_kem_mlkem768x25519_enc(struct kex
*kex
,
271 const struct sshbuf
*client_blob
, struct sshbuf
**server_blobp
,
272 struct sshbuf
**shared_secretp
)
274 return SSH_ERR_SIGN_ALG_UNSUPPORTED
;
278 kex_kem_mlkem768x25519_dec(struct kex
*kex
,
279 const struct sshbuf
*server_blob
, struct sshbuf
**shared_secretp
)
281 return SSH_ERR_SIGN_ALG_UNSUPPORTED
;
283 #endif /* USE_MLKEM768X25519 */