summaryrefslogtreecommitdiff
path: root/kexsntrup4591761x25519.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-01-21 10:20:12 +0000
committerDamien Miller <djm@mindrot.org>2019-01-21 22:07:02 +1100
commitdfd591618cdf2c96727ac0eb65f89cf54af0d97e (patch)
tree59700563da0dc6f1de649394ffb4c787710eda5a /kexsntrup4591761x25519.c
parentb1b2ff4ed559051d1035419f8f236275fa66d5d6 (diff)
upstream: Add support for a PQC KEX/KEM:
sntrup4591761x25519-sha512@tinyssh.org using the Streamlined NTRU Prime 4591^761 implementation from SUPERCOP coupled with X25519 as a stop-loss. Not enabled by default. introduce KEM API; a simplified framework for DH-ish KEX methods. from markus@ feedback & ok djm@ OpenBSD-Commit-ID: d687f76cffd3561dd73eb302d17a1c3bf321d1a7
Diffstat (limited to 'kexsntrup4591761x25519.c')
-rw-r--r--kexsntrup4591761x25519.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/kexsntrup4591761x25519.c b/kexsntrup4591761x25519.c
new file mode 100644
index 000000000..ffe05f420
--- /dev/null
+++ b/kexsntrup4591761x25519.c
@@ -0,0 +1,213 @@
1/* $OpenBSD: kexsntrup4591761x25519.c,v 1.1 2019/01/21 10:20:12 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 <sys/types.h>
27
28#include <stdio.h>
29#include <string.h>
30#include <signal.h>
31
32#include "sshkey.h"
33#include "kex.h"
34#include "sshbuf.h"
35#include "digest.h"
36#include "ssherr.h"
37
38int
39kex_kem_sntrup4591761x25519_keypair(struct kex *kex)
40{
41 struct sshbuf *buf = NULL;
42 u_char *cp = NULL;
43 size_t need;
44 int r;
45
46 if ((buf = sshbuf_new()) == NULL)
47 return SSH_ERR_ALLOC_FAIL;
48 need = crypto_kem_sntrup4591761_PUBLICKEYBYTES + CURVE25519_SIZE;
49 if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
50 goto out;
51 crypto_kem_sntrup4591761_keypair(cp, kex->sntrup4591761_client_key);
52#ifdef DEBUG_KEXECDH
53 dump_digest("client public key sntrup4591761:", cp,
54 crypto_kem_sntrup4591761_PUBLICKEYBYTES);
55#endif
56 cp += crypto_kem_sntrup4591761_PUBLICKEYBYTES;
57 kexc25519_keygen(kex->c25519_client_key, cp);
58#ifdef DEBUG_KEXECDH
59 dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
60#endif
61 kex->kem_client_pub = buf;
62 buf = NULL;
63 out:
64 sshbuf_free(buf);
65 return r;
66}
67
68int
69kex_kem_sntrup4591761x25519_enc(struct kex *kex, const u_char *pkblob,
70 size_t pklen, struct sshbuf **server_blobp, struct sshbuf **shared_secretp)
71{
72 struct sshbuf *server_blob = NULL;
73 struct sshbuf *buf = NULL;
74 u_char *kem_key, *ciphertext, *server_pub;
75 u_char server_key[CURVE25519_SIZE];
76 u_char hash[SSH_DIGEST_MAX_LENGTH];
77 size_t need;
78 int r;
79
80 *server_blobp = NULL;
81 *shared_secretp = NULL;
82
83 /* pkblob contains both KEM and ECDH client pubkeys */
84 need = crypto_kem_sntrup4591761_PUBLICKEYBYTES + CURVE25519_SIZE;
85 if (pklen != need) {
86 r = SSH_ERR_SIGNATURE_INVALID;
87 goto out;
88 }
89#ifdef DEBUG_KEXECDH
90 dump_digest("client public key sntrup4591761:", pkblob,
91 crypto_kem_sntrup4591761_PUBLICKEYBYTES);
92 dump_digest("client public key 25519:",
93 pkblob + crypto_kem_sntrup4591761_PUBLICKEYBYTES, CURVE25519_SIZE);
94#endif
95 /* allocate buffer for concatenation of KEM key and ECDH shared key */
96 /* the buffer will be hashed and the result is the shared secret */
97 if ((buf = sshbuf_new()) == NULL) {
98 r = SSH_ERR_ALLOC_FAIL;
99 goto out;
100 }
101 if ((r = sshbuf_reserve(buf, crypto_kem_sntrup4591761_BYTES,
102 &kem_key)) != 0)
103 goto out;
104 /* allocate space for encrypted KEM key and ECDH pub key */
105 if ((server_blob = sshbuf_new()) == NULL) {
106 r = SSH_ERR_ALLOC_FAIL;
107 goto out;
108 }
109 need = crypto_kem_sntrup4591761_CIPHERTEXTBYTES + CURVE25519_SIZE;
110 if ((r = sshbuf_reserve(server_blob, need, &ciphertext)) != 0)
111 goto out;
112 /* generate and encrypt KEM key with client key */
113 crypto_kem_sntrup4591761_enc(ciphertext, kem_key, pkblob);
114 /* generate ECDH key pair, store server pubkey after ciphertext */
115 server_pub = ciphertext + crypto_kem_sntrup4591761_CIPHERTEXTBYTES;
116 kexc25519_keygen(server_key, server_pub);
117 /* append ECDH shared key */
118 if ((r = kexc25519_shared_key_ext(server_key,
119 pkblob + crypto_kem_sntrup4591761_PUBLICKEYBYTES, buf, 1)) < 0)
120 goto out;
121 if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
122 goto out;
123#ifdef DEBUG_KEXECDH
124 dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE);
125 dump_digest("server cipher text:", ciphertext,
126 crypto_kem_sntrup4591761_CIPHERTEXTBYTES);
127 dump_digest("server kem key:", kem_key, sizeof(kem_key));
128 dump_digest("concatenation of KEM key and ECDH shared key:",
129 sshbuf_ptr(buf), sshbuf_len(buf));
130#endif
131 /* string-encoded hash is resulting shared secret */
132 sshbuf_reset(buf);
133 if ((r = sshbuf_put_string(buf, hash,
134 ssh_digest_bytes(kex->hash_alg))) != 0)
135 goto out;
136#ifdef DEBUG_KEXECDH
137 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
138#endif
139 *server_blobp = server_blob;
140 *shared_secretp = buf;
141 server_blob = NULL;
142 buf = NULL;
143 out:
144 explicit_bzero(hash, sizeof(hash));
145 explicit_bzero(server_key, sizeof(server_key));
146 sshbuf_free(server_blob);
147 sshbuf_free(buf);
148 return r;
149}
150
151int
152kex_kem_sntrup4591761x25519_dec(struct kex *kex, const u_char *pkblob,
153 size_t pklen, struct sshbuf **shared_secretp)
154{
155 struct sshbuf *buf = NULL;
156 u_char *kem_key = NULL;
157 const u_char *ciphertext, *server_pub;
158 u_char hash[SSH_DIGEST_MAX_LENGTH];
159 size_t need;
160 int r, decoded;
161
162 *shared_secretp = NULL;
163
164 need = crypto_kem_sntrup4591761_CIPHERTEXTBYTES + CURVE25519_SIZE;
165 if (pklen != need) {
166 r = SSH_ERR_SIGNATURE_INVALID;
167 goto out;
168 }
169 ciphertext = pkblob;
170 server_pub = pkblob + crypto_kem_sntrup4591761_CIPHERTEXTBYTES;
171#ifdef DEBUG_KEXECDH
172 dump_digest("server cipher text:", ciphertext,
173 crypto_kem_sntrup4591761_CIPHERTEXTBYTES);
174 dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
175#endif
176 /* hash concatenation of KEM key and ECDH shared key */
177 if ((buf = sshbuf_new()) == NULL) {
178 r = SSH_ERR_ALLOC_FAIL;
179 goto out;
180 }
181 if ((r = sshbuf_reserve(buf, crypto_kem_sntrup4591761_BYTES,
182 &kem_key)) != 0)
183 goto out;
184 decoded = crypto_kem_sntrup4591761_dec(kem_key, ciphertext,
185 kex->sntrup4591761_client_key);
186 if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub,
187 buf, 1)) < 0)
188 goto out;
189 if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
190 goto out;
191#ifdef DEBUG_KEXECDH
192 dump_digest("client kem key:", kem_key, sizeof(kem_key));
193 dump_digest("concatenation of KEM key and ECDH shared key:",
194 sshbuf_ptr(buf), sshbuf_len(buf));
195#endif
196 sshbuf_reset(buf);
197 if ((r = sshbuf_put_string(buf, hash,
198 ssh_digest_bytes(kex->hash_alg))) != 0)
199 goto out;
200#ifdef DEBUG_KEXECDH
201 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
202#endif
203 if (decoded != 0) {
204 r = SSH_ERR_SIGNATURE_INVALID;
205 goto out;
206 }
207 *shared_secretp = buf;
208 buf = NULL;
209 out:
210 explicit_bzero(hash, sizeof(hash));
211 sshbuf_free(buf);
212 return r;
213}