1 /*
2  * This file is part of the SSH Library
3  *
4  * Copyright (c) 2019 by Simo Sorce - Red Hat, Inc.
5  *
6  * The SSH Library is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or (at your
9  * option) any later version.
10  *
11  * The SSH Library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with the SSH Library; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
19  * MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 #include "libssh/session.h"
24 #include "libssh/dh.h"
25 #include "libssh/buffer.h"
26 #include "libssh/ssh2.h"
27 #include "libssh/pki.h"
28 #include "libssh/bignum.h"
29 
30 #include "openssl/crypto.h"
31 #include "openssl/dh.h"
32 #include "libcrypto-compat.h"
33 
34 extern bignum ssh_dh_generator;
35 extern bignum ssh_dh_group1;
36 extern bignum ssh_dh_group14;
37 extern bignum ssh_dh_group16;
38 extern bignum ssh_dh_group18;
39 
40 struct dh_ctx {
41     DH *keypair[2];
42 };
43 
ssh_dh_debug_crypto(struct ssh_crypto_struct * c)44 void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
45 {
46 #ifdef DEBUG_CRYPTO
47     const_bignum x = NULL, y = NULL, e = NULL, f = NULL;
48 
49     ssh_dh_keypair_get_keys(c->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e);
50     ssh_dh_keypair_get_keys(c->dh_ctx, DH_SERVER_KEYPAIR, &y, &f);
51     ssh_print_bignum("x", x);
52     ssh_print_bignum("y", y);
53     ssh_print_bignum("e", e);
54     ssh_print_bignum("f", f);
55 
56     ssh_log_hexdump("Session server cookie", c->server_kex.cookie, 16);
57     ssh_log_hexdump("Session client cookie", c->client_kex.cookie, 16);
58     ssh_print_bignum("k", c->shared_secret);
59 
60 #else
61     (void)c; /* UNUSED_PARAM */
62 #endif
63 }
64 
ssh_dh_keypair_get_keys(struct dh_ctx * ctx,int peer,const_bignum * priv,const_bignum * pub)65 int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
66                             const_bignum *priv, const_bignum *pub)
67 {
68     if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) ||
69         ((priv == NULL) && (pub == NULL)) || (ctx == NULL) ||
70         (ctx->keypair[peer] == NULL)) {
71         return SSH_ERROR;
72     }
73     DH_get0_key(ctx->keypair[peer], pub, priv);
74     if (priv && (*priv == NULL || bignum_num_bits(*priv) == 0)) {
75         return SSH_ERROR;
76     }
77     if (pub && (*pub == NULL || bignum_num_bits(*pub) == 0)) {
78         return SSH_ERROR;
79     }
80 
81     return SSH_OK;
82 }
83 
ssh_dh_keypair_set_keys(struct dh_ctx * ctx,int peer,const bignum priv,const bignum pub)84 int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
85                             const bignum priv, const bignum pub)
86 {
87     bignum priv_key = NULL;
88     bignum pub_key = NULL;
89 
90     if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) ||
91         ((priv == NULL) && (pub == NULL)) || (ctx == NULL) ||
92         (ctx->keypair[peer] == NULL)) {
93         return SSH_ERROR;
94     }
95 
96     if (priv) {
97         priv_key = priv;
98     }
99     if (pub) {
100         pub_key = pub;
101     }
102     (void)DH_set0_key(ctx->keypair[peer], pub_key, priv_key);
103 
104     return SSH_OK;
105 }
106 
ssh_dh_get_parameters(struct dh_ctx * ctx,const_bignum * modulus,const_bignum * generator)107 int ssh_dh_get_parameters(struct dh_ctx *ctx,
108                           const_bignum *modulus, const_bignum *generator)
109 {
110     if (ctx == NULL || ctx->keypair[0] == NULL) {
111         return SSH_ERROR;
112     }
113     DH_get0_pqg(ctx->keypair[0], modulus, NULL, generator);
114     return SSH_OK;
115 }
116 
ssh_dh_set_parameters(struct dh_ctx * ctx,const bignum modulus,const bignum generator)117 int ssh_dh_set_parameters(struct dh_ctx *ctx,
118                           const bignum modulus, const bignum generator)
119 {
120     size_t i;
121     int rc;
122 
123     if ((ctx == NULL) || (modulus == NULL) || (generator == NULL)) {
124         return SSH_ERROR;
125     }
126 
127     for (i = 0; i < 2; i++) {
128         bignum p = NULL;
129         bignum g = NULL;
130 
131         /* when setting modulus or generator,
132          * make sure to invalidate existing keys */
133         DH_free(ctx->keypair[i]);
134         ctx->keypair[i] = DH_new();
135         if (ctx->keypair[i] == NULL) {
136             rc = SSH_ERROR;
137             goto done;
138         }
139 
140         p = BN_dup(modulus);
141         g = BN_dup(generator);
142         rc = DH_set0_pqg(ctx->keypair[i], p, NULL, g);
143         if (rc != 1) {
144             BN_free(p);
145             BN_free(g);
146             rc = SSH_ERROR;
147             goto done;
148         }
149     }
150 
151     rc = SSH_OK;
152 done:
153     if (rc != SSH_OK) {
154         DH_free(ctx->keypair[0]);
155         DH_free(ctx->keypair[1]);
156         ctx->keypair[0] = NULL;
157         ctx->keypair[1] = NULL;
158     }
159     return rc;
160 }
161 
162 /**
163  * @internal
164  * @brief allocate and initialize ephemeral values used in dh kex
165  */
ssh_dh_init_common(struct ssh_crypto_struct * crypto)166 int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
167 {
168     struct dh_ctx *ctx;
169     int rc;
170 
171     ctx = calloc(1, sizeof(*ctx));
172     if (ctx == NULL) {
173         return SSH_ERROR;
174     }
175     crypto->dh_ctx = ctx;
176 
177     switch (crypto->kex_type) {
178     case SSH_KEX_DH_GROUP1_SHA1:
179         rc = ssh_dh_set_parameters(ctx, ssh_dh_group1, ssh_dh_generator);
180         break;
181     case SSH_KEX_DH_GROUP14_SHA1:
182     case SSH_KEX_DH_GROUP14_SHA256:
183         rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
184         break;
185     case SSH_KEX_DH_GROUP16_SHA512:
186         rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
187         break;
188     case SSH_KEX_DH_GROUP18_SHA512:
189         rc = ssh_dh_set_parameters(ctx, ssh_dh_group18, ssh_dh_generator);
190         break;
191     default:
192         rc = SSH_OK;
193         break;
194     }
195 
196     if (rc != SSH_OK) {
197         ssh_dh_cleanup(crypto);
198     }
199     return rc;
200 }
201 
ssh_dh_cleanup(struct ssh_crypto_struct * crypto)202 void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
203 {
204     if (crypto->dh_ctx != NULL) {
205         DH_free(crypto->dh_ctx->keypair[0]);
206         DH_free(crypto->dh_ctx->keypair[1]);
207         free(crypto->dh_ctx);
208         crypto->dh_ctx = NULL;
209     }
210 }
211 
212 /** @internal
213  * @brief generates a secret DH parameter of at least DH_SECURITY_BITS
214  *        security as well as the corresponding public key.
215  * @param[out] parms a dh_ctx that will hold the new keys.
216  * @param peer Select either client or server key storage. Valid values are:
217  *        DH_CLIENT_KEYPAIR or DH_SERVER_KEYPAIR
218  *
219  * @return SSH_OK on success, SSH_ERROR on error
220  */
ssh_dh_keypair_gen_keys(struct dh_ctx * dh_ctx,int peer)221 int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer)
222 {
223     int rc;
224 
225     if ((dh_ctx == NULL) || (dh_ctx->keypair[peer] == NULL)) {
226         return SSH_ERROR;
227     }
228     rc = DH_generate_key(dh_ctx->keypair[peer]);
229     if (rc != 1) {
230         return SSH_ERROR;
231     }
232     return SSH_OK;
233 }
234 
235 /** @internal
236  * @brief generates a shared secret between the local peer and the remote
237  *        peer. The local peer must have been initialized using either the
238  *        ssh_dh_keypair_gen_keys() function or by seetting manually both
239  *        the private and public keys. The remote peer only needs to have
240  *        the remote's peer public key set.
241  * @param[in] local peer identifier (DH_CLIENT_KEYPAIR or DH_SERVER_KEYPAIR)
242  * @param[in] remote peer identifier (DH_CLIENT_KEYPAIR or DH_SERVER_KEYPAIR)
243  * @param[out] dest a new bignum with the shared secret value is returned.
244  * @return SSH_OK on success, SSH_ERROR on error
245  */
ssh_dh_compute_shared_secret(struct dh_ctx * dh_ctx,int local,int remote,bignum * dest)246 int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
247                                  bignum *dest)
248 {
249     unsigned char *kstring = NULL;
250     const_bignum pub_key = NULL;
251     int klen, rc;
252 
253     if ((dh_ctx == NULL) ||
254         (dh_ctx->keypair[local] == NULL) ||
255         (dh_ctx->keypair[remote] == NULL)) {
256         return SSH_ERROR;
257     }
258 
259     kstring = malloc(DH_size(dh_ctx->keypair[local]));
260     if (kstring == NULL) {
261         rc = SSH_ERROR;
262         goto done;
263     }
264 
265     rc = ssh_dh_keypair_get_keys(dh_ctx, remote, NULL, &pub_key);
266     if (rc != SSH_OK) {
267         rc = SSH_ERROR;
268         goto done;
269     }
270 
271     klen = DH_compute_key(kstring, pub_key, dh_ctx->keypair[local]);
272     if (klen == -1) {
273         rc = SSH_ERROR;
274         goto done;
275     }
276 
277     *dest = BN_bin2bn(kstring, klen, NULL);
278     if (*dest == NULL) {
279         rc = SSH_ERROR;
280         goto done;
281     }
282 
283     rc = SSH_OK;
284 done:
285     free(kstring);
286     return rc;
287 }
288