1 /* $OpenBSD: kexmlkem768x25519.c,v 1.2 2024/10/27 02:06:59 djm Exp $ */ 2 /* 3 * Copyright (c) 2023 Markus Friedl. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 13 * 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. 24 */ 25 26 #include "includes.h" 27 28 #include <sys/types.h> 29 30 #include <stdio.h> 31 #ifdef HAVE_STDINT_H 32 #include <stdint.h> 33 #endif 34 #include <stdbool.h> 35 #include <string.h> 36 #include <signal.h> 37 #ifdef HAVE_ENDIAN_H 38 # include <endian.h> 39 #endif 40 41 #include "sshkey.h" 42 #include "kex.h" 43 #include "sshbuf.h" 44 #include "digest.h" 45 #include "ssherr.h" 46 #include "log.h" 47 48 #ifdef USE_MLKEM768X25519 49 50 #include "libcrux_mlkem768_sha3.h" 51 52 int 53 kex_kem_mlkem768x25519_keypair(struct kex *kex) 54 { 55 struct sshbuf *buf = NULL; 56 u_char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN], *cp = NULL; 57 size_t need; 58 int r = SSH_ERR_INTERNAL_ERROR; 59 struct libcrux_mlkem768_keypair keypair; 60 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) 65 goto out; 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)); 71 #ifdef DEBUG_KEXECDH 72 dump_digest("client public key mlkem768:", cp, 73 crypto_kem_mlkem768_PUBLICKEYBYTES); 74 #endif 75 cp += crypto_kem_mlkem768_PUBLICKEYBYTES; 76 kexc25519_keygen(kex->c25519_client_key, cp); 77 #ifdef DEBUG_KEXECDH 78 dump_digest("client public key c25519:", cp, CURVE25519_SIZE); 79 #endif 80 /* success */ 81 r = 0; 82 kex->client_pub = buf; 83 buf = NULL; 84 out: 85 explicit_bzero(&keypair, sizeof(keypair)); 86 explicit_bzero(rnd, sizeof(rnd)); 87 sshbuf_free(buf); 88 return r; 89 } 90 91 int 92 kex_kem_mlkem768x25519_enc(struct kex *kex, 93 const struct sshbuf *client_blob, struct sshbuf **server_blobp, 94 struct sshbuf **shared_secretp) 95 { 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]; 102 size_t need; 103 int r = SSH_ERR_INTERNAL_ERROR; 104 struct libcrux_mlkem768_enc_result enc; 105 struct libcrux_mlkem768_pk mlkem_pub; 106 107 *server_blobp = NULL; 108 *shared_secretp = NULL; 109 memset(&mlkem_pub, 0, sizeof(mlkem_pub)); 110 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; 115 goto out; 116 } 117 client_pub = sshbuf_ptr(client_blob); 118 #ifdef DEBUG_KEXECDH 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, 123 CURVE25519_SIZE); 124 #endif 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; 129 goto out; 130 } 131 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; 136 goto out; 137 } 138 /* allocate space for encrypted KEM key and ECDH pub key */ 139 if ((server_blob = sshbuf_new()) == NULL) { 140 r = SSH_ERR_ALLOC_FAIL; 141 goto out; 142 } 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) 151 goto out; 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) 155 goto out; 156 if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0) 157 goto out; 158 #ifdef DEBUG_KEXECDH 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)); 165 #endif 166 /* string-encoded hash is resulting shared secret */ 167 sshbuf_reset(buf); 168 if ((r = sshbuf_put_string(buf, hash, 169 ssh_digest_bytes(kex->hash_alg))) != 0) 170 goto out; 171 #ifdef DEBUG_KEXECDH 172 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); 173 #endif 174 /* success */ 175 r = 0; 176 *server_blobp = server_blob; 177 *shared_secretp = buf; 178 server_blob = NULL; 179 buf = NULL; 180 out: 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); 186 sshbuf_free(buf); 187 return r; 188 } 189 190 int 191 kex_kem_mlkem768x25519_dec(struct kex *kex, 192 const struct sshbuf *server_blob, struct sshbuf **shared_secretp) 193 { 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]; 198 size_t need; 199 int r; 200 struct libcrux_mlkem768_sk mlkem_priv; 201 struct libcrux_mlkem768_ciphertext mlkem_ciphertext; 202 203 *shared_secretp = NULL; 204 memset(&mlkem_priv, 0, sizeof(mlkem_priv)); 205 memset(&mlkem_ciphertext, 0, sizeof(mlkem_ciphertext)); 206 207 need = crypto_kem_mlkem768_CIPHERTEXTBYTES + CURVE25519_SIZE; 208 if (sshbuf_len(server_blob) != need) { 209 r = SSH_ERR_SIGNATURE_INVALID; 210 goto out; 211 } 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; 217 goto out; 218 } 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)); 223 #ifdef DEBUG_KEXECDH 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); 227 #endif 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) 231 goto out; 232 if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub, 233 buf, 1)) < 0) 234 goto out; 235 if ((r = ssh_digest_buffer(kex->hash_alg, buf, 236 hash, sizeof(hash))) != 0) 237 goto out; 238 #ifdef DEBUG_KEXECDH 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)); 242 #endif 243 sshbuf_reset(buf); 244 if ((r = sshbuf_put_string(buf, hash, 245 ssh_digest_bytes(kex->hash_alg))) != 0) 246 goto out; 247 #ifdef DEBUG_KEXECDH 248 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); 249 #endif 250 /* success */ 251 r = 0; 252 *shared_secretp = buf; 253 buf = NULL; 254 out: 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)); 259 sshbuf_free(buf); 260 return r; 261 } 262 #else /* USE_MLKEM768X25519 */ 263 int 264 kex_kem_mlkem768x25519_keypair(struct kex *kex) 265 { 266 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 267 } 268 269 int 270 kex_kem_mlkem768x25519_enc(struct kex *kex, 271 const struct sshbuf *client_blob, struct sshbuf **server_blobp, 272 struct sshbuf **shared_secretp) 273 { 274 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 275 } 276 277 int 278 kex_kem_mlkem768x25519_dec(struct kex *kex, 279 const struct sshbuf *server_blob, struct sshbuf **shared_secretp) 280 { 281 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 282 } 283 #endif /* USE_MLKEM768X25519 */ 284