diff options
Diffstat (limited to 'kexsntrup4591761x25519.c')
-rw-r--r-- | kexsntrup4591761x25519.c | 213 |
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 | |||
38 | int | ||
39 | kex_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 | |||
68 | int | ||
69 | kex_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 | |||
151 | int | ||
152 | kex_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 | } | ||