1 /* $OpenBSD: kexc25519.c,v 1.18 2024/09/02 12:13:56 djm Exp $ */
2 /*
3 * Copyright (c) 2019 Markus Friedl. All rights reserved.
4 * Copyright (c) 2010 Damien Miller. All rights reserved.
5 * Copyright (c) 2013 Aris Adamantiadis. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/types.h>
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <signal.h>
33
34 #include "sshkey.h"
35 #include "kex.h"
36 #include "sshbuf.h"
37 #include "digest.h"
38 #include "ssherr.h"
39 #include "ssh2.h"
40
41 extern int crypto_scalarmult_curve25519(u_char a[CURVE25519_SIZE],
42 const u_char b[CURVE25519_SIZE], const u_char c[CURVE25519_SIZE])
43 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
44 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)))
45 __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE)));
46
47 void
kexc25519_keygen(u_char key[CURVE25519_SIZE],u_char pub[CURVE25519_SIZE])48 kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
49 {
50 static const u_char basepoint[CURVE25519_SIZE] = {9};
51
52 arc4random_buf(key, CURVE25519_SIZE);
53 crypto_scalarmult_curve25519(pub, key, basepoint);
54 }
55
56 int
kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE],const u_char pub[CURVE25519_SIZE],struct sshbuf * out,int raw)57 kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE],
58 const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int raw)
59 {
60 u_char shared_key[CURVE25519_SIZE];
61 u_char zero[CURVE25519_SIZE];
62 int r;
63
64 crypto_scalarmult_curve25519(shared_key, key, pub);
65
66 /* Check for all-zero shared secret */
67 explicit_bzero(zero, CURVE25519_SIZE);
68 if (timingsafe_bcmp(zero, shared_key, CURVE25519_SIZE) == 0)
69 return SSH_ERR_KEY_INVALID_EC_VALUE;
70
71 #ifdef DEBUG_KEXECDH
72 dump_digest("shared secret 25519", shared_key, CURVE25519_SIZE);
73 #endif
74 if (raw)
75 r = sshbuf_put(out, shared_key, CURVE25519_SIZE);
76 else
77 r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE);
78 explicit_bzero(shared_key, CURVE25519_SIZE);
79 return r;
80 }
81
82 int
kexc25519_shared_key(const u_char key[CURVE25519_SIZE],const u_char pub[CURVE25519_SIZE],struct sshbuf * out)83 kexc25519_shared_key(const u_char key[CURVE25519_SIZE],
84 const u_char pub[CURVE25519_SIZE], struct sshbuf *out)
85 {
86 return kexc25519_shared_key_ext(key, pub, out, 0);
87 }
88
89 int
kex_c25519_keypair(struct kex * kex)90 kex_c25519_keypair(struct kex *kex)
91 {
92 struct sshbuf *buf = NULL;
93 u_char *cp = NULL;
94 int r;
95
96 if ((buf = sshbuf_new()) == NULL)
97 return SSH_ERR_ALLOC_FAIL;
98 if ((r = sshbuf_reserve(buf, CURVE25519_SIZE, &cp)) != 0)
99 goto out;
100 kexc25519_keygen(kex->c25519_client_key, cp);
101 #ifdef DEBUG_KEXECDH
102 dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
103 #endif
104 kex->client_pub = buf;
105 buf = NULL;
106 out:
107 sshbuf_free(buf);
108 return r;
109 }
110
111 int
kex_c25519_enc(struct kex * kex,const struct sshbuf * client_blob,struct sshbuf ** server_blobp,struct sshbuf ** shared_secretp)112 kex_c25519_enc(struct kex *kex, const struct sshbuf *client_blob,
113 struct sshbuf **server_blobp, struct sshbuf **shared_secretp)
114 {
115 struct sshbuf *server_blob = NULL;
116 struct sshbuf *buf = NULL;
117 const u_char *client_pub;
118 u_char *server_pub;
119 u_char server_key[CURVE25519_SIZE];
120 int r;
121
122 *server_blobp = NULL;
123 *shared_secretp = NULL;
124
125 if (sshbuf_len(client_blob) != CURVE25519_SIZE) {
126 r = SSH_ERR_SIGNATURE_INVALID;
127 goto out;
128 }
129 client_pub = sshbuf_ptr(client_blob);
130 #ifdef DEBUG_KEXECDH
131 dump_digest("client public key 25519:", client_pub, CURVE25519_SIZE);
132 #endif
133 /* allocate space for encrypted KEM key and ECDH pub key */
134 if ((server_blob = sshbuf_new()) == NULL) {
135 r = SSH_ERR_ALLOC_FAIL;
136 goto out;
137 }
138 if ((r = sshbuf_reserve(server_blob, CURVE25519_SIZE, &server_pub)) != 0)
139 goto out;
140 kexc25519_keygen(server_key, server_pub);
141 /* allocate shared secret */
142 if ((buf = sshbuf_new()) == NULL) {
143 r = SSH_ERR_ALLOC_FAIL;
144 goto out;
145 }
146 if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 0)) < 0)
147 goto out;
148 #ifdef DEBUG_KEXECDH
149 dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE);
150 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
151 #endif
152 *server_blobp = server_blob;
153 *shared_secretp = buf;
154 server_blob = NULL;
155 buf = NULL;
156 out:
157 explicit_bzero(server_key, sizeof(server_key));
158 sshbuf_free(server_blob);
159 sshbuf_free(buf);
160 return r;
161 }
162
163 int
kex_c25519_dec(struct kex * kex,const struct sshbuf * server_blob,struct sshbuf ** shared_secretp)164 kex_c25519_dec(struct kex *kex, const struct sshbuf *server_blob,
165 struct sshbuf **shared_secretp)
166 {
167 struct sshbuf *buf = NULL;
168 const u_char *server_pub;
169 int r;
170
171 *shared_secretp = NULL;
172
173 if (sshbuf_len(server_blob) != CURVE25519_SIZE) {
174 r = SSH_ERR_SIGNATURE_INVALID;
175 goto out;
176 }
177 server_pub = sshbuf_ptr(server_blob);
178 #ifdef DEBUG_KEXECDH
179 dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
180 #endif
181 /* shared secret */
182 if ((buf = sshbuf_new()) == NULL) {
183 r = SSH_ERR_ALLOC_FAIL;
184 goto out;
185 }
186 if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub,
187 buf, 0)) < 0)
188 goto out;
189 #ifdef DEBUG_KEXECDH
190 dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
191 #endif
192 *shared_secretp = buf;
193 buf = NULL;
194 out:
195 sshbuf_free(buf);
196 return r;
197 }
198