1 /* $OpenBSD: kexsntrup761x25519.c,v 1.1 2020/12/29 00:59:15 djm Exp $ */ 2 /* 3 * Copyright (c) 2019 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 #ifdef USE_SNTRUP761X25519 29 30 #include <sys/types.h> 31 32 #include <stdio.h> 33 #include <string.h> 34 #include <signal.h> 35 36 #include "sshkey.h" 37 #include "kex.h" 38 #include "sshbuf.h" 39 #include "digest.h" 40 #include "ssherr.h" 41 42 int 43 kex_kem_sntrup761x25519_keypair(struct kex *kex) 44 { 45 struct sshbuf *buf = NULL; 46 u_char *cp = NULL; 47 size_t need; 48 int r; 49 50 if ((buf = sshbuf_new()) == NULL) 51 return SSH_ERR_ALLOC_FAIL; 52 need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; 53 if ((r = sshbuf_reserve(buf, need, &cp)) != 0) 54 goto out; 55 crypto_kem_sntrup761_keypair(cp, kex->sntrup761_client_key); 56 #ifdef DEBUG_KEXECDH 57 dump_digest("client public key sntrup761:", cp, 58 crypto_kem_sntrup761_PUBLICKEYBYTES); 59 #endif 60 cp += crypto_kem_sntrup761_PUBLICKEYBYTES; 61 kexc25519_keygen(kex->c25519_client_key, cp); 62 #ifdef DEBUG_KEXECDH 63 dump_digest("client public key c25519:", cp, CURVE25519_SIZE); 64 #endif 65 kex->client_pub = buf; 66 buf = NULL; 67 out: 68 sshbuf_free(buf); 69 return r; 70 } 71 72 int 73 kex_kem_sntrup761x25519_enc(struct kex *kex, 74 const struct sshbuf *client_blob, struct sshbuf **server_blobp, 75 struct sshbuf **shared_secretp) 76 { 77 struct sshbuf *server_blob = NULL; 78 struct sshbuf *buf = NULL; 79 const u_char *client_pub; 80 u_char *kem_key, *ciphertext, *server_pub; 81 u_char server_key[CURVE25519_SIZE]; 82 u_char hash[SSH_DIGEST_MAX_LENGTH]; 83 size_t need; 84 int r; 85 86 *server_blobp = NULL; 87 *shared_secretp = NULL; 88 89 /* client_blob contains both KEM and ECDH client pubkeys */ 90 need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; 91 if (sshbuf_len(client_blob) != need) { 92 r = SSH_ERR_SIGNATURE_INVALID; 93 goto out; 94 } 95 client_pub = sshbuf_ptr(client_blob); 96 #ifdef DEBUG_KEXECDH 97 dump_digest("client public key sntrup761:", client_pub, 98 crypto_kem_sntrup761_PUBLICKEYBYTES); 99 dump_digest("client public key 25519:", 100 client_pub + crypto_kem_sntrup761_PUBLICKEYBYTES, 101 CURVE25519_SIZE); 102 #endif 103 /* allocate buffer for concatenation of KEM key and ECDH shared key */ 104 /* the buffer will be hashed and the result is the shared secret */ 105 if ((buf = sshbuf_new()) == NULL) { 106 r = SSH_ERR_ALLOC_FAIL; 107 goto out; 108 } 109 if ((r = sshbuf_reserve(buf, crypto_kem_sntrup761_BYTES, 110 &kem_key)) != 0) 111 goto out; 112 /* allocate space for encrypted KEM key and ECDH pub key */ 113 if ((server_blob = sshbuf_new()) == NULL) { 114 r = SSH_ERR_ALLOC_FAIL; 115 goto out; 116 } 117 need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE; 118 if ((r = sshbuf_reserve(server_blob, need, &ciphertext)) != 0) 119 goto out; 120 /* generate and encrypt KEM key with client key */ 121 crypto_kem_sntrup761_enc(ciphertext, kem_key, client_pub); 122 /* generate ECDH key pair, store server pubkey after ciphertext */ 123 server_pub = ciphertext + crypto_kem_sntrup761_CIPHERTEXTBYTES; 124 kexc25519_keygen(server_key, server_pub); 125 /* append ECDH shared key */ 126 client_pub += crypto_kem_sntrup761_PUBLICKEYBYTES; 127 if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0) 128 goto out; 129 if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0) 130 goto out; 131 #ifdef DEBUG_KEXECDH 132 dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE); 133 dump_digest("server cipher text:", ciphertext, 134 crypto_kem_sntrup761_CIPHERTEXTBYTES); 135 dump_digest("server kem key:", kem_key, sizeof(kem_key)); 136 dump_digest("concatenation of KEM key and ECDH shared key:", 137 sshbuf_ptr(buf), sshbuf_len(buf)); 138 #endif 139 /* string-encoded hash is resulting shared secret */ 140 sshbuf_reset(buf); 141 if ((r = sshbuf_put_string(buf, hash, 142 ssh_digest_bytes(kex->hash_alg))) != 0) 143 goto out; 144 #ifdef DEBUG_KEXECDH 145 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); 146 #endif 147 *server_blobp = server_blob; 148 *shared_secretp = buf; 149 server_blob = NULL; 150 buf = NULL; 151 out: 152 explicit_bzero(hash, sizeof(hash)); 153 explicit_bzero(server_key, sizeof(server_key)); 154 sshbuf_free(server_blob); 155 sshbuf_free(buf); 156 return r; 157 } 158 159 int 160 kex_kem_sntrup761x25519_dec(struct kex *kex, 161 const struct sshbuf *server_blob, struct sshbuf **shared_secretp) 162 { 163 struct sshbuf *buf = NULL; 164 u_char *kem_key = NULL; 165 const u_char *ciphertext, *server_pub; 166 u_char hash[SSH_DIGEST_MAX_LENGTH]; 167 size_t need; 168 int r, decoded; 169 170 *shared_secretp = NULL; 171 172 need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE; 173 if (sshbuf_len(server_blob) != need) { 174 r = SSH_ERR_SIGNATURE_INVALID; 175 goto out; 176 } 177 ciphertext = sshbuf_ptr(server_blob); 178 server_pub = ciphertext + crypto_kem_sntrup761_CIPHERTEXTBYTES; 179 #ifdef DEBUG_KEXECDH 180 dump_digest("server cipher text:", ciphertext, 181 crypto_kem_sntrup761_CIPHERTEXTBYTES); 182 dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE); 183 #endif 184 /* hash concatenation of KEM key and ECDH shared key */ 185 if ((buf = sshbuf_new()) == NULL) { 186 r = SSH_ERR_ALLOC_FAIL; 187 goto out; 188 } 189 if ((r = sshbuf_reserve(buf, crypto_kem_sntrup761_BYTES, 190 &kem_key)) != 0) 191 goto out; 192 decoded = crypto_kem_sntrup761_dec(kem_key, ciphertext, 193 kex->sntrup761_client_key); 194 if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub, 195 buf, 1)) < 0) 196 goto out; 197 if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0) 198 goto out; 199 #ifdef DEBUG_KEXECDH 200 dump_digest("client kem key:", kem_key, crypto_kem_sntrup761_BYTES); 201 dump_digest("concatenation of KEM key and ECDH shared key:", 202 sshbuf_ptr(buf), sshbuf_len(buf)); 203 #endif 204 sshbuf_reset(buf); 205 if ((r = sshbuf_put_string(buf, hash, 206 ssh_digest_bytes(kex->hash_alg))) != 0) 207 goto out; 208 #ifdef DEBUG_KEXECDH 209 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); 210 #endif 211 if (decoded != 0) { 212 r = SSH_ERR_SIGNATURE_INVALID; 213 goto out; 214 } 215 *shared_secretp = buf; 216 buf = NULL; 217 out: 218 explicit_bzero(hash, sizeof(hash)); 219 sshbuf_free(buf); 220 return r; 221 } 222 223 #else 224 225 #include "ssherr.h" 226 227 struct kex; 228 struct sshbuf; 229 struct sshkey; 230 231 int 232 kex_kem_sntrup761x25519_keypair(struct kex *kex) 233 { 234 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 235 } 236 237 int 238 kex_kem_sntrup761x25519_enc(struct kex *kex, 239 const struct sshbuf *client_blob, struct sshbuf **server_blobp, 240 struct sshbuf **shared_secretp) 241 { 242 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 243 } 244 245 int 246 kex_kem_sntrup761x25519_dec(struct kex *kex, 247 const struct sshbuf *server_blob, struct sshbuf **shared_secretp) 248 { 249 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 250 } 251 #endif /* USE_SNTRUP761X25519 */ 252