1/* $OpenBSD: kexsntrup761x25519.c,v 1.2 2021/12/05 12:28:27 jsg 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 42int 43kex_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 72int 73kex_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, crypto_kem_sntrup761_BYTES); 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 159int 160kex_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 227struct kex; 228struct sshbuf; 229struct sshkey; 230 231int 232kex_kem_sntrup761x25519_keypair(struct kex *kex) 233{ 234 return SSH_ERR_SIGN_ALG_UNSUPPORTED; 235} 236 237int 238kex_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 245int 246kex_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