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