1 /* $NetBSD: ssh-dss.c,v 1.19 2024/06/25 16:36:54 christos Exp $ */
2 /* $OpenBSD: ssh-dss.c,v 1.50 2024/01/11 01:45:36 djm Exp $ */
3
4 /*
5 * Copyright (c) 2000 Markus Friedl. 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 "includes.h"
29 __RCSID("$NetBSD: ssh-dss.c,v 1.19 2024/06/25 16:36:54 christos Exp $");
30 #include <sys/types.h>
31
32 #include <openssl/bn.h>
33 #include <openssl/evp.h>
34
35 #include <string.h>
36
37 #include "sshbuf.h"
38 #include "ssherr.h"
39 #include "digest.h"
40 #define SSHKEY_INTERNAL
41 #include "sshkey.h"
42
43 #ifdef WITH_DSA
44
45 #define INTBLOB_LEN 20
46 #define SIGBLOB_LEN (2*INTBLOB_LEN)
47
48 static u_int
ssh_dss_size(const struct sshkey * key)49 ssh_dss_size(const struct sshkey *key)
50 {
51 const BIGNUM *dsa_p;
52
53 if (key->dsa == NULL)
54 return 0;
55 DSA_get0_pqg(key->dsa, &dsa_p, NULL, NULL);
56 return BN_num_bits(dsa_p);
57 }
58
59 static int
ssh_dss_alloc(struct sshkey * k)60 ssh_dss_alloc(struct sshkey *k)
61 {
62 if ((k->dsa = DSA_new()) == NULL)
63 return SSH_ERR_ALLOC_FAIL;
64 return 0;
65 }
66
67 static void
ssh_dss_cleanup(struct sshkey * k)68 ssh_dss_cleanup(struct sshkey *k)
69 {
70 DSA_free(k->dsa);
71 k->dsa = NULL;
72 }
73
74 static int
ssh_dss_equal(const struct sshkey * a,const struct sshkey * b)75 ssh_dss_equal(const struct sshkey *a, const struct sshkey *b)
76 {
77 const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a;
78 const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b;
79
80 if (a->dsa == NULL || b->dsa == NULL)
81 return 0;
82 DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a);
83 DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b);
84 DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL);
85 DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL);
86 if (dsa_p_a == NULL || dsa_p_b == NULL ||
87 dsa_q_a == NULL || dsa_q_b == NULL ||
88 dsa_g_a == NULL || dsa_g_b == NULL ||
89 dsa_pub_key_a == NULL || dsa_pub_key_b == NULL)
90 return 0;
91 if (BN_cmp(dsa_p_a, dsa_p_b) != 0)
92 return 0;
93 if (BN_cmp(dsa_q_a, dsa_q_b) != 0)
94 return 0;
95 if (BN_cmp(dsa_g_a, dsa_g_b) != 0)
96 return 0;
97 if (BN_cmp(dsa_pub_key_a, dsa_pub_key_b) != 0)
98 return 0;
99 return 1;
100 }
101
102 static int
ssh_dss_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)103 ssh_dss_serialize_public(const struct sshkey *key, struct sshbuf *b,
104 enum sshkey_serialize_rep opts)
105 {
106 int r;
107 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
108
109 if (key->dsa == NULL)
110 return SSH_ERR_INVALID_ARGUMENT;
111 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
112 DSA_get0_key(key->dsa, &dsa_pub_key, NULL);
113 if (dsa_p == NULL || dsa_q == NULL ||
114 dsa_g == NULL || dsa_pub_key == NULL)
115 return SSH_ERR_INTERNAL_ERROR;
116 if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
117 (r = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
118 (r = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
119 (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0)
120 return r;
121
122 return 0;
123 }
124
125 static int
ssh_dss_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)126 ssh_dss_serialize_private(const struct sshkey *key, struct sshbuf *b,
127 enum sshkey_serialize_rep opts)
128 {
129 int r;
130 const BIGNUM *dsa_priv_key;
131
132 DSA_get0_key(key->dsa, NULL, &dsa_priv_key);
133 if (!sshkey_is_cert(key)) {
134 if ((r = ssh_dss_serialize_public(key, b, opts)) != 0)
135 return r;
136 }
137 if ((r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0)
138 return r;
139
140 return 0;
141 }
142
143 static int
ssh_dss_generate(struct sshkey * k,int bits)144 ssh_dss_generate(struct sshkey *k, int bits)
145 {
146 DSA *private;
147
148 if (bits != 1024)
149 return SSH_ERR_KEY_LENGTH;
150 if ((private = DSA_new()) == NULL)
151 return SSH_ERR_ALLOC_FAIL;
152 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
153 NULL, NULL) || !DSA_generate_key(private)) {
154 DSA_free(private);
155 return SSH_ERR_LIBCRYPTO_ERROR;
156 }
157 k->dsa = private;
158 return 0;
159 }
160
161 static int
ssh_dss_copy_public(const struct sshkey * from,struct sshkey * to)162 ssh_dss_copy_public(const struct sshkey *from, struct sshkey *to)
163 {
164 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
165 BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL;
166 BIGNUM *dsa_pub_key_dup = NULL;
167 int r = SSH_ERR_INTERNAL_ERROR;
168
169 DSA_get0_pqg(from->dsa, &dsa_p, &dsa_q, &dsa_g);
170 DSA_get0_key(from->dsa, &dsa_pub_key, NULL);
171 if ((dsa_p_dup = BN_dup(dsa_p)) == NULL ||
172 (dsa_q_dup = BN_dup(dsa_q)) == NULL ||
173 (dsa_g_dup = BN_dup(dsa_g)) == NULL ||
174 (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) {
175 r = SSH_ERR_ALLOC_FAIL;
176 goto out;
177 }
178 if (!DSA_set0_pqg(to->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) {
179 r = SSH_ERR_LIBCRYPTO_ERROR;
180 goto out;
181 }
182 dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */
183 if (!DSA_set0_key(to->dsa, dsa_pub_key_dup, NULL)) {
184 r = SSH_ERR_LIBCRYPTO_ERROR;
185 goto out;
186 }
187 dsa_pub_key_dup = NULL; /* transferred */
188 /* success */
189 r = 0;
190 out:
191 BN_clear_free(dsa_p_dup);
192 BN_clear_free(dsa_q_dup);
193 BN_clear_free(dsa_g_dup);
194 BN_clear_free(dsa_pub_key_dup);
195 return r;
196 }
197
198 static int
ssh_dss_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)199 ssh_dss_deserialize_public(const char *ktype, struct sshbuf *b,
200 struct sshkey *key)
201 {
202 int ret = SSH_ERR_INTERNAL_ERROR;
203 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL;
204
205 if (sshbuf_get_bignum2(b, &dsa_p) != 0 ||
206 sshbuf_get_bignum2(b, &dsa_q) != 0 ||
207 sshbuf_get_bignum2(b, &dsa_g) != 0 ||
208 sshbuf_get_bignum2(b, &dsa_pub_key) != 0) {
209 ret = SSH_ERR_INVALID_FORMAT;
210 goto out;
211 }
212 if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) {
213 ret = SSH_ERR_LIBCRYPTO_ERROR;
214 goto out;
215 }
216 dsa_p = dsa_q = dsa_g = NULL; /* transferred */
217 if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) {
218 ret = SSH_ERR_LIBCRYPTO_ERROR;
219 goto out;
220 }
221 dsa_pub_key = NULL; /* transferred */
222 #ifdef DEBUG_PK
223 DSA_print_fp(stderr, key->dsa, 8);
224 #endif
225 /* success */
226 ret = 0;
227 out:
228 BN_clear_free(dsa_p);
229 BN_clear_free(dsa_q);
230 BN_clear_free(dsa_g);
231 BN_clear_free(dsa_pub_key);
232 return ret;
233 }
234
235 static int
ssh_dss_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)236 ssh_dss_deserialize_private(const char *ktype, struct sshbuf *b,
237 struct sshkey *key)
238 {
239 int r;
240 BIGNUM *dsa_priv_key = NULL;
241
242 if (!sshkey_is_cert(key)) {
243 if ((r = ssh_dss_deserialize_public(ktype, b, key)) != 0)
244 return r;
245 }
246
247 if ((r = sshbuf_get_bignum2(b, &dsa_priv_key)) != 0)
248 return r;
249 if (!DSA_set0_key(key->dsa, NULL, dsa_priv_key)) {
250 BN_clear_free(dsa_priv_key);
251 return SSH_ERR_LIBCRYPTO_ERROR;
252 }
253 return 0;
254 }
255
256 static int
ssh_dss_sign(struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t datalen,const char * alg,const char * sk_provider,const char * sk_pin,u_int compat)257 ssh_dss_sign(struct sshkey *key,
258 u_char **sigp, size_t *lenp,
259 const u_char *data, size_t datalen,
260 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
261 {
262 DSA_SIG *sig = NULL;
263 const BIGNUM *sig_r, *sig_s;
264 u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN];
265 size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
266 struct sshbuf *b = NULL;
267 int ret = SSH_ERR_INVALID_ARGUMENT;
268
269 if (lenp != NULL)
270 *lenp = 0;
271 if (sigp != NULL)
272 *sigp = NULL;
273
274 if (key == NULL || key->dsa == NULL ||
275 sshkey_type_plain(key->type) != KEY_DSA)
276 return SSH_ERR_INVALID_ARGUMENT;
277 if (dlen == 0)
278 return SSH_ERR_INTERNAL_ERROR;
279
280 if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
281 digest, sizeof(digest))) != 0)
282 goto out;
283
284 if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) {
285 ret = SSH_ERR_LIBCRYPTO_ERROR;
286 goto out;
287 }
288
289 DSA_SIG_get0(sig, &sig_r, &sig_s);
290 rlen = BN_num_bytes(sig_r);
291 slen = BN_num_bytes(sig_s);
292 if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) {
293 ret = SSH_ERR_INTERNAL_ERROR;
294 goto out;
295 }
296 explicit_bzero(sigblob, SIGBLOB_LEN);
297 BN_bn2bin(sig_r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen);
298 BN_bn2bin(sig_s, sigblob + SIGBLOB_LEN - slen);
299
300 if ((b = sshbuf_new()) == NULL) {
301 ret = SSH_ERR_ALLOC_FAIL;
302 goto out;
303 }
304 if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 ||
305 (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0)
306 goto out;
307
308 len = sshbuf_len(b);
309 if (sigp != NULL) {
310 if ((*sigp = malloc(len)) == NULL) {
311 ret = SSH_ERR_ALLOC_FAIL;
312 goto out;
313 }
314 memcpy(*sigp, sshbuf_ptr(b), len);
315 }
316 if (lenp != NULL)
317 *lenp = len;
318 ret = 0;
319 out:
320 explicit_bzero(digest, sizeof(digest));
321 DSA_SIG_free(sig);
322 sshbuf_free(b);
323 return ret;
324 }
325
326 static int
ssh_dss_verify(const struct sshkey * key,const u_char * sig,size_t siglen,const u_char * data,size_t dlen,const char * alg,u_int compat,struct sshkey_sig_details ** detailsp)327 ssh_dss_verify(const struct sshkey *key,
328 const u_char *sig, size_t siglen,
329 const u_char *data, size_t dlen, const char *alg, u_int compat,
330 struct sshkey_sig_details **detailsp)
331 {
332 DSA_SIG *dsig = NULL;
333 BIGNUM *sig_r = NULL, *sig_s = NULL;
334 u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL;
335 size_t len, hlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
336 int ret = SSH_ERR_INTERNAL_ERROR;
337 struct sshbuf *b = NULL;
338 char *ktype = NULL;
339
340 if (key == NULL || key->dsa == NULL ||
341 sshkey_type_plain(key->type) != KEY_DSA ||
342 sig == NULL || siglen == 0)
343 return SSH_ERR_INVALID_ARGUMENT;
344 if (hlen == 0)
345 return SSH_ERR_INTERNAL_ERROR;
346
347 /* fetch signature */
348 if ((b = sshbuf_from(sig, siglen)) == NULL)
349 return SSH_ERR_ALLOC_FAIL;
350 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
351 sshbuf_get_string(b, &sigblob, &len) != 0) {
352 ret = SSH_ERR_INVALID_FORMAT;
353 goto out;
354 }
355 if (strcmp("ssh-dss", ktype) != 0) {
356 ret = SSH_ERR_KEY_TYPE_MISMATCH;
357 goto out;
358 }
359 if (sshbuf_len(b) != 0) {
360 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
361 goto out;
362 }
363
364 if (len != SIGBLOB_LEN) {
365 ret = SSH_ERR_INVALID_FORMAT;
366 goto out;
367 }
368
369 /* parse signature */
370 if ((dsig = DSA_SIG_new()) == NULL ||
371 (sig_r = BN_new()) == NULL ||
372 (sig_s = BN_new()) == NULL) {
373 ret = SSH_ERR_ALLOC_FAIL;
374 goto out;
375 }
376 if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig_r) == NULL) ||
377 (BN_bin2bn(sigblob + INTBLOB_LEN, INTBLOB_LEN, sig_s) == NULL)) {
378 ret = SSH_ERR_LIBCRYPTO_ERROR;
379 goto out;
380 }
381 if (!DSA_SIG_set0(dsig, sig_r, sig_s)) {
382 ret = SSH_ERR_LIBCRYPTO_ERROR;
383 goto out;
384 }
385 sig_r = sig_s = NULL; /* transferred */
386
387 /* sha1 the data */
388 if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, dlen,
389 digest, sizeof(digest))) != 0)
390 goto out;
391
392 switch (DSA_do_verify(digest, hlen, dsig, key->dsa)) {
393 case 1:
394 ret = 0;
395 break;
396 case 0:
397 ret = SSH_ERR_SIGNATURE_INVALID;
398 goto out;
399 default:
400 ret = SSH_ERR_LIBCRYPTO_ERROR;
401 goto out;
402 }
403
404 out:
405 explicit_bzero(digest, sizeof(digest));
406 DSA_SIG_free(dsig);
407 BN_clear_free(sig_r);
408 BN_clear_free(sig_s);
409 sshbuf_free(b);
410 free(ktype);
411 if (sigblob != NULL)
412 freezero(sigblob, len);
413 return ret;
414 }
415
416 static const struct sshkey_impl_funcs sshkey_dss_funcs = {
417 /* .size = */ ssh_dss_size,
418 /* .alloc = */ ssh_dss_alloc,
419 /* .cleanup = */ ssh_dss_cleanup,
420 /* .equal = */ ssh_dss_equal,
421 /* .ssh_serialize_public = */ ssh_dss_serialize_public,
422 /* .ssh_deserialize_public = */ ssh_dss_deserialize_public,
423 /* .ssh_serialize_private = */ ssh_dss_serialize_private,
424 /* .ssh_deserialize_private = */ ssh_dss_deserialize_private,
425 /* .generate = */ ssh_dss_generate,
426 /* .copy_public = */ ssh_dss_copy_public,
427 /* .sign = */ ssh_dss_sign,
428 /* .verify = */ ssh_dss_verify,
429 };
430
431 const struct sshkey_impl sshkey_dss_impl = {
432 /* .name = */ "ssh-dss",
433 /* .shortname = */ "DSA",
434 /* .sigalg = */ NULL,
435 /* .type = */ KEY_DSA,
436 /* .nid = */ 0,
437 /* .cert = */ 0,
438 /* .sigonly = */ 0,
439 /* .keybits = */ 0,
440 /* .funcs = */ &sshkey_dss_funcs,
441 };
442
443 const struct sshkey_impl sshkey_dsa_cert_impl = {
444 /* .name = */ "ssh-dss-cert-v01@openssh.com",
445 /* .shortname = */ "DSA-CERT",
446 /* .sigalg = */ NULL,
447 /* .type = */ KEY_DSA_CERT,
448 /* .nid = */ 0,
449 /* .cert = */ 1,
450 /* .sigonly = */ 0,
451 /* .keybits = */ 0,
452 /* .funcs = */ &sshkey_dss_funcs,
453 };
454
455 #endif /* WITH_DSA */
456