xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-dss.c (revision c5555919c1ef14dca119ee5d4a4e93656523a56e)
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