xref: /openbsd/usr.bin/ssh/ssh-dss.c (revision 52113de9)
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