xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-dss.c (revision c5555919c1ef14dca119ee5d4a4e93656523a56e)
1*c5555919Schristos /*	$NetBSD: ssh-dss.c,v 1.19 2024/06/25 16:36:54 christos Exp $	*/
2*c5555919Schristos /* $OpenBSD: ssh-dss.c,v 1.50 2024/01/11 01:45:36 djm Exp $ */
3*c5555919Schristos 
4ca32bd8dSchristos /*
5ca32bd8dSchristos  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
6ca32bd8dSchristos  *
7ca32bd8dSchristos  * Redistribution and use in source and binary forms, with or without
8ca32bd8dSchristos  * modification, are permitted provided that the following conditions
9ca32bd8dSchristos  * are met:
10ca32bd8dSchristos  * 1. Redistributions of source code must retain the above copyright
11ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer.
12ca32bd8dSchristos  * 2. Redistributions in binary form must reproduce the above copyright
13ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer in the
14ca32bd8dSchristos  *    documentation and/or other materials provided with the distribution.
15ca32bd8dSchristos  *
16ca32bd8dSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17ca32bd8dSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18ca32bd8dSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19ca32bd8dSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20ca32bd8dSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21ca32bd8dSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22ca32bd8dSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23ca32bd8dSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24ca32bd8dSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25ca32bd8dSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26ca32bd8dSchristos  */
27ca32bd8dSchristos 
28313c6c94Schristos #include "includes.h"
29*c5555919Schristos __RCSID("$NetBSD: ssh-dss.c,v 1.19 2024/06/25 16:36:54 christos Exp $");
30ca32bd8dSchristos #include <sys/types.h>
31ca32bd8dSchristos 
32ca32bd8dSchristos #include <openssl/bn.h>
33ca32bd8dSchristos #include <openssl/evp.h>
34ca32bd8dSchristos 
35ca32bd8dSchristos #include <string.h>
36ca32bd8dSchristos 
378a4530f9Schristos #include "sshbuf.h"
388a4530f9Schristos #include "ssherr.h"
398a4530f9Schristos #include "digest.h"
408a4530f9Schristos #define SSHKEY_INTERNAL
418a4530f9Schristos #include "sshkey.h"
42ca32bd8dSchristos 
43*c5555919Schristos #ifdef WITH_DSA
44*c5555919Schristos 
45ca32bd8dSchristos #define INTBLOB_LEN	20
46ca32bd8dSchristos #define SIGBLOB_LEN	(2*INTBLOB_LEN)
47ca32bd8dSchristos 
48b1066cf3Schristos static u_int
ssh_dss_size(const struct sshkey * key)49b1066cf3Schristos ssh_dss_size(const struct sshkey *key)
50b1066cf3Schristos {
51b1066cf3Schristos 	const BIGNUM *dsa_p;
52b1066cf3Schristos 
53b1066cf3Schristos 	if (key->dsa == NULL)
54b1066cf3Schristos 		return 0;
55b1066cf3Schristos 	DSA_get0_pqg(key->dsa, &dsa_p, NULL, NULL);
56b1066cf3Schristos 	return BN_num_bits(dsa_p);
57b1066cf3Schristos }
58b1066cf3Schristos 
59b1066cf3Schristos static int
ssh_dss_alloc(struct sshkey * k)60b1066cf3Schristos ssh_dss_alloc(struct sshkey *k)
61b1066cf3Schristos {
62b1066cf3Schristos 	if ((k->dsa = DSA_new()) == NULL)
63b1066cf3Schristos 		return SSH_ERR_ALLOC_FAIL;
64b1066cf3Schristos 	return 0;
65b1066cf3Schristos }
66b1066cf3Schristos 
67b1066cf3Schristos static void
ssh_dss_cleanup(struct sshkey * k)68b1066cf3Schristos ssh_dss_cleanup(struct sshkey *k)
69b1066cf3Schristos {
70b1066cf3Schristos 	DSA_free(k->dsa);
71b1066cf3Schristos 	k->dsa = NULL;
72b1066cf3Schristos }
73b1066cf3Schristos 
74b1066cf3Schristos static int
ssh_dss_equal(const struct sshkey * a,const struct sshkey * b)75b1066cf3Schristos ssh_dss_equal(const struct sshkey *a, const struct sshkey *b)
76b1066cf3Schristos {
77b1066cf3Schristos 	const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a;
78b1066cf3Schristos 	const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b;
79b1066cf3Schristos 
80b1066cf3Schristos 	if (a->dsa == NULL || b->dsa == NULL)
81b1066cf3Schristos 		return 0;
82b1066cf3Schristos 	DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a);
83b1066cf3Schristos 	DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b);
84b1066cf3Schristos 	DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL);
85b1066cf3Schristos 	DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL);
86b1066cf3Schristos 	if (dsa_p_a == NULL || dsa_p_b == NULL ||
87b1066cf3Schristos 	    dsa_q_a == NULL || dsa_q_b == NULL ||
88b1066cf3Schristos 	    dsa_g_a == NULL || dsa_g_b == NULL ||
89b1066cf3Schristos 	    dsa_pub_key_a == NULL || dsa_pub_key_b == NULL)
90b1066cf3Schristos 		return 0;
91b1066cf3Schristos 	if (BN_cmp(dsa_p_a, dsa_p_b) != 0)
92b1066cf3Schristos 		return 0;
93b1066cf3Schristos 	if (BN_cmp(dsa_q_a, dsa_q_b) != 0)
94b1066cf3Schristos 		return 0;
95b1066cf3Schristos 	if (BN_cmp(dsa_g_a, dsa_g_b) != 0)
96b1066cf3Schristos 		return 0;
97b1066cf3Schristos 	if (BN_cmp(dsa_pub_key_a, dsa_pub_key_b) != 0)
98b1066cf3Schristos 		return 0;
99b1066cf3Schristos 	return 1;
100b1066cf3Schristos }
101b1066cf3Schristos 
102b1066cf3Schristos static int
ssh_dss_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)103b1066cf3Schristos ssh_dss_serialize_public(const struct sshkey *key, struct sshbuf *b,
104b1066cf3Schristos     enum sshkey_serialize_rep opts)
105b1066cf3Schristos {
106b1066cf3Schristos 	int r;
107b1066cf3Schristos 	const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
108b1066cf3Schristos 
109b1066cf3Schristos 	if (key->dsa == NULL)
110b1066cf3Schristos 		return SSH_ERR_INVALID_ARGUMENT;
111b1066cf3Schristos 	DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
112b1066cf3Schristos 	DSA_get0_key(key->dsa, &dsa_pub_key, NULL);
113b1066cf3Schristos 	if (dsa_p == NULL || dsa_q == NULL ||
114b1066cf3Schristos 	    dsa_g == NULL || dsa_pub_key == NULL)
115b1066cf3Schristos 		return SSH_ERR_INTERNAL_ERROR;
116b1066cf3Schristos 	if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
117b1066cf3Schristos 	    (r = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
118b1066cf3Schristos 	    (r = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
119b1066cf3Schristos 	    (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0)
120b1066cf3Schristos 		return r;
121b1066cf3Schristos 
122b1066cf3Schristos 	return 0;
123b1066cf3Schristos }
124b1066cf3Schristos 
125b1066cf3Schristos static int
ssh_dss_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)126b1066cf3Schristos ssh_dss_serialize_private(const struct sshkey *key, struct sshbuf *b,
127b1066cf3Schristos     enum sshkey_serialize_rep opts)
128b1066cf3Schristos {
129b1066cf3Schristos 	int r;
130b1066cf3Schristos 	const BIGNUM *dsa_priv_key;
131b1066cf3Schristos 
132b1066cf3Schristos 	DSA_get0_key(key->dsa, NULL, &dsa_priv_key);
133b1066cf3Schristos 	if (!sshkey_is_cert(key)) {
134b1066cf3Schristos 		if ((r = ssh_dss_serialize_public(key, b, opts)) != 0)
135b1066cf3Schristos 			return r;
136b1066cf3Schristos 	}
137b1066cf3Schristos 	if ((r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0)
138b1066cf3Schristos 		return r;
139b1066cf3Schristos 
140b1066cf3Schristos 	return 0;
141b1066cf3Schristos }
142b1066cf3Schristos 
143b1066cf3Schristos static int
ssh_dss_generate(struct sshkey * k,int bits)144b1066cf3Schristos ssh_dss_generate(struct sshkey *k, int bits)
145b1066cf3Schristos {
146b1066cf3Schristos 	DSA *private;
147b1066cf3Schristos 
148b1066cf3Schristos 	if (bits != 1024)
149b1066cf3Schristos 		return SSH_ERR_KEY_LENGTH;
150b1066cf3Schristos 	if ((private = DSA_new()) == NULL)
151b1066cf3Schristos 		return SSH_ERR_ALLOC_FAIL;
152b1066cf3Schristos 	if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
153b1066cf3Schristos 	    NULL, NULL) || !DSA_generate_key(private)) {
154b1066cf3Schristos 		DSA_free(private);
155b1066cf3Schristos 		return SSH_ERR_LIBCRYPTO_ERROR;
156b1066cf3Schristos 	}
157b1066cf3Schristos 	k->dsa = private;
158b1066cf3Schristos 	return 0;
159b1066cf3Schristos }
160b1066cf3Schristos 
161b1066cf3Schristos static int
ssh_dss_copy_public(const struct sshkey * from,struct sshkey * to)162b1066cf3Schristos ssh_dss_copy_public(const struct sshkey *from, struct sshkey *to)
163b1066cf3Schristos {
164b1066cf3Schristos 	const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
165b1066cf3Schristos 	BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL;
166b1066cf3Schristos 	BIGNUM *dsa_pub_key_dup = NULL;
167b1066cf3Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
168b1066cf3Schristos 
169b1066cf3Schristos 	DSA_get0_pqg(from->dsa, &dsa_p, &dsa_q, &dsa_g);
170b1066cf3Schristos 	DSA_get0_key(from->dsa, &dsa_pub_key, NULL);
171b1066cf3Schristos 	if ((dsa_p_dup = BN_dup(dsa_p)) == NULL ||
172b1066cf3Schristos 	    (dsa_q_dup = BN_dup(dsa_q)) == NULL ||
173b1066cf3Schristos 	    (dsa_g_dup = BN_dup(dsa_g)) == NULL ||
174b1066cf3Schristos 	    (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) {
175b1066cf3Schristos 		r = SSH_ERR_ALLOC_FAIL;
176b1066cf3Schristos 		goto out;
177b1066cf3Schristos 	}
178b1066cf3Schristos 	if (!DSA_set0_pqg(to->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) {
179b1066cf3Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
180b1066cf3Schristos 		goto out;
181b1066cf3Schristos 	}
182b1066cf3Schristos 	dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */
183b1066cf3Schristos 	if (!DSA_set0_key(to->dsa, dsa_pub_key_dup, NULL)) {
184b1066cf3Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
185b1066cf3Schristos 		goto out;
186b1066cf3Schristos 	}
187b1066cf3Schristos 	dsa_pub_key_dup = NULL; /* transferred */
188b1066cf3Schristos 	/* success */
189b1066cf3Schristos 	r = 0;
190b1066cf3Schristos  out:
191b1066cf3Schristos 	BN_clear_free(dsa_p_dup);
192b1066cf3Schristos 	BN_clear_free(dsa_q_dup);
193b1066cf3Schristos 	BN_clear_free(dsa_g_dup);
194b1066cf3Schristos 	BN_clear_free(dsa_pub_key_dup);
195b1066cf3Schristos 	return r;
196b1066cf3Schristos }
197b1066cf3Schristos 
198b1066cf3Schristos static int
ssh_dss_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)199b1066cf3Schristos ssh_dss_deserialize_public(const char *ktype, struct sshbuf *b,
200b1066cf3Schristos     struct sshkey *key)
201b1066cf3Schristos {
202b1066cf3Schristos 	int ret = SSH_ERR_INTERNAL_ERROR;
203b1066cf3Schristos 	BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL;
204b1066cf3Schristos 
205b1066cf3Schristos 	if (sshbuf_get_bignum2(b, &dsa_p) != 0 ||
206b1066cf3Schristos 	    sshbuf_get_bignum2(b, &dsa_q) != 0 ||
207b1066cf3Schristos 	    sshbuf_get_bignum2(b, &dsa_g) != 0 ||
208b1066cf3Schristos 	    sshbuf_get_bignum2(b, &dsa_pub_key) != 0) {
209b1066cf3Schristos 		ret = SSH_ERR_INVALID_FORMAT;
210b1066cf3Schristos 		goto out;
211b1066cf3Schristos 	}
212b1066cf3Schristos 	if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) {
213b1066cf3Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
214b1066cf3Schristos 		goto out;
215b1066cf3Schristos 	}
216b1066cf3Schristos 	dsa_p = dsa_q = dsa_g = NULL; /* transferred */
217b1066cf3Schristos 	if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) {
218b1066cf3Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
219b1066cf3Schristos 		goto out;
220b1066cf3Schristos 	}
221b1066cf3Schristos 	dsa_pub_key = NULL; /* transferred */
222b1066cf3Schristos #ifdef DEBUG_PK
223b1066cf3Schristos 	DSA_print_fp(stderr, key->dsa, 8);
224b1066cf3Schristos #endif
225b1066cf3Schristos 	/* success */
226b1066cf3Schristos 	ret = 0;
227b1066cf3Schristos  out:
228b1066cf3Schristos 	BN_clear_free(dsa_p);
229b1066cf3Schristos 	BN_clear_free(dsa_q);
230b1066cf3Schristos 	BN_clear_free(dsa_g);
231b1066cf3Schristos 	BN_clear_free(dsa_pub_key);
232b1066cf3Schristos 	return ret;
233b1066cf3Schristos }
234b1066cf3Schristos 
235b1066cf3Schristos static int
ssh_dss_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)236b1066cf3Schristos ssh_dss_deserialize_private(const char *ktype, struct sshbuf *b,
237b1066cf3Schristos     struct sshkey *key)
238b1066cf3Schristos {
239b1066cf3Schristos 	int r;
240b1066cf3Schristos 	BIGNUM *dsa_priv_key = NULL;
241b1066cf3Schristos 
242b1066cf3Schristos 	if (!sshkey_is_cert(key)) {
243b1066cf3Schristos 		if ((r = ssh_dss_deserialize_public(ktype, b, key)) != 0)
244b1066cf3Schristos 			return r;
245b1066cf3Schristos 	}
246b1066cf3Schristos 
247b1066cf3Schristos 	if ((r = sshbuf_get_bignum2(b, &dsa_priv_key)) != 0)
248b1066cf3Schristos 		return r;
249b1066cf3Schristos 	if (!DSA_set0_key(key->dsa, NULL, dsa_priv_key)) {
250b1066cf3Schristos 		BN_clear_free(dsa_priv_key);
251b1066cf3Schristos 		return SSH_ERR_LIBCRYPTO_ERROR;
252b1066cf3Schristos 	}
253b1066cf3Schristos 	return 0;
254b1066cf3Schristos }
255b1066cf3Schristos 
256b1066cf3Schristos 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)257b1066cf3Schristos ssh_dss_sign(struct sshkey *key,
258b1066cf3Schristos     u_char **sigp, size_t *lenp,
259b1066cf3Schristos     const u_char *data, size_t datalen,
260b1066cf3Schristos     const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
261ca32bd8dSchristos {
2628a4530f9Schristos 	DSA_SIG *sig = NULL;
263aa36fcacSchristos 	const BIGNUM *sig_r, *sig_s;
2648a4530f9Schristos 	u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN];
2658a4530f9Schristos 	size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
2668a4530f9Schristos 	struct sshbuf *b = NULL;
2678a4530f9Schristos 	int ret = SSH_ERR_INVALID_ARGUMENT;
268ca32bd8dSchristos 
2698a4530f9Schristos 	if (lenp != NULL)
2708a4530f9Schristos 		*lenp = 0;
2718a4530f9Schristos 	if (sigp != NULL)
2728a4530f9Schristos 		*sigp = NULL;
273ca32bd8dSchristos 
2748a4530f9Schristos 	if (key == NULL || key->dsa == NULL ||
2758a4530f9Schristos 	    sshkey_type_plain(key->type) != KEY_DSA)
2768a4530f9Schristos 		return SSH_ERR_INVALID_ARGUMENT;
2778a4530f9Schristos 	if (dlen == 0)
2788a4530f9Schristos 		return SSH_ERR_INTERNAL_ERROR;
279ca32bd8dSchristos 
2808a4530f9Schristos 	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
2818a4530f9Schristos 	    digest, sizeof(digest))) != 0)
2828a4530f9Schristos 		goto out;
2838a4530f9Schristos 
2848a4530f9Schristos 	if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) {
2858a4530f9Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
2868a4530f9Schristos 		goto out;
287ca32bd8dSchristos 	}
288ca32bd8dSchristos 
289aa36fcacSchristos 	DSA_SIG_get0(sig, &sig_r, &sig_s);
290aa36fcacSchristos 	rlen = BN_num_bytes(sig_r);
291aa36fcacSchristos 	slen = BN_num_bytes(sig_s);
292ca32bd8dSchristos 	if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) {
2938a4530f9Schristos 		ret = SSH_ERR_INTERNAL_ERROR;
2948a4530f9Schristos 		goto out;
295ca32bd8dSchristos 	}
2968a4530f9Schristos 	explicit_bzero(sigblob, SIGBLOB_LEN);
297aa36fcacSchristos 	BN_bn2bin(sig_r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen);
298aa36fcacSchristos 	BN_bn2bin(sig_s, sigblob + SIGBLOB_LEN - slen);
299ca32bd8dSchristos 
3008a4530f9Schristos 	if ((b = sshbuf_new()) == NULL) {
3018a4530f9Schristos 		ret = SSH_ERR_ALLOC_FAIL;
3028a4530f9Schristos 		goto out;
3038a4530f9Schristos 	}
3048a4530f9Schristos 	if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 ||
3058a4530f9Schristos 	    (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0)
3068a4530f9Schristos 		goto out;
307ffae97bbSchristos 
3088a4530f9Schristos 	len = sshbuf_len(b);
3098a4530f9Schristos 	if (sigp != NULL) {
3108a4530f9Schristos 		if ((*sigp = malloc(len)) == NULL) {
3118a4530f9Schristos 			ret = SSH_ERR_ALLOC_FAIL;
3128a4530f9Schristos 			goto out;
3138a4530f9Schristos 		}
3148a4530f9Schristos 		memcpy(*sigp, sshbuf_ptr(b), len);
3158a4530f9Schristos 	}
316ca32bd8dSchristos 	if (lenp != NULL)
317ca32bd8dSchristos 		*lenp = len;
3188a4530f9Schristos 	ret = 0;
3198a4530f9Schristos  out:
3208a4530f9Schristos 	explicit_bzero(digest, sizeof(digest));
3218a4530f9Schristos 	DSA_SIG_free(sig);
3228a4530f9Schristos 	sshbuf_free(b);
3238a4530f9Schristos 	return ret;
324ca32bd8dSchristos }
325ca32bd8dSchristos 
326b1066cf3Schristos 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)3278a4530f9Schristos ssh_dss_verify(const struct sshkey *key,
328b1066cf3Schristos     const u_char *sig, size_t siglen,
329b1066cf3Schristos     const u_char *data, size_t dlen, const char *alg, u_int compat,
330b1066cf3Schristos     struct sshkey_sig_details **detailsp)
3318a4530f9Schristos {
332b1066cf3Schristos 	DSA_SIG *dsig = NULL;
333aa36fcacSchristos 	BIGNUM *sig_r = NULL, *sig_s = NULL;
3348a4530f9Schristos 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL;
335b1066cf3Schristos 	size_t len, hlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
3368a4530f9Schristos 	int ret = SSH_ERR_INTERNAL_ERROR;
3378a4530f9Schristos 	struct sshbuf *b = NULL;
3388a4530f9Schristos 	char *ktype = NULL;
3398a4530f9Schristos 
3408a4530f9Schristos 	if (key == NULL || key->dsa == NULL ||
3415101d403Schristos 	    sshkey_type_plain(key->type) != KEY_DSA ||
342b1066cf3Schristos 	    sig == NULL || siglen == 0)
3438a4530f9Schristos 		return SSH_ERR_INVALID_ARGUMENT;
344b1066cf3Schristos 	if (hlen == 0)
3458a4530f9Schristos 		return SSH_ERR_INTERNAL_ERROR;
346ca32bd8dSchristos 
347ca32bd8dSchristos 	/* fetch signature */
348b1066cf3Schristos 	if ((b = sshbuf_from(sig, siglen)) == NULL)
3498a4530f9Schristos 		return SSH_ERR_ALLOC_FAIL;
3508a4530f9Schristos 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
3518a4530f9Schristos 	    sshbuf_get_string(b, &sigblob, &len) != 0) {
3528a4530f9Schristos 		ret = SSH_ERR_INVALID_FORMAT;
3538a4530f9Schristos 		goto out;
354ca32bd8dSchristos 	}
3558a4530f9Schristos 	if (strcmp("ssh-dss", ktype) != 0) {
3568a4530f9Schristos 		ret = SSH_ERR_KEY_TYPE_MISMATCH;
3578a4530f9Schristos 		goto out;
3588a4530f9Schristos 	}
3598a4530f9Schristos 	if (sshbuf_len(b) != 0) {
3608a4530f9Schristos 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
3618a4530f9Schristos 		goto out;
362ca32bd8dSchristos 	}
363ca32bd8dSchristos 
364ca32bd8dSchristos 	if (len != SIGBLOB_LEN) {
3658a4530f9Schristos 		ret = SSH_ERR_INVALID_FORMAT;
3668a4530f9Schristos 		goto out;
367ca32bd8dSchristos 	}
368ca32bd8dSchristos 
369ca32bd8dSchristos 	/* parse signature */
370b1066cf3Schristos 	if ((dsig = DSA_SIG_new()) == NULL ||
371aa36fcacSchristos 	    (sig_r = BN_new()) == NULL ||
372aa36fcacSchristos 	    (sig_s = BN_new()) == NULL) {
3738a4530f9Schristos 		ret = SSH_ERR_ALLOC_FAIL;
3748a4530f9Schristos 		goto out;
3758a4530f9Schristos 	}
376aa36fcacSchristos 	if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig_r) == NULL) ||
377aa36fcacSchristos 	    (BN_bin2bn(sigblob + INTBLOB_LEN, INTBLOB_LEN, sig_s) == NULL)) {
3788a4530f9Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
3798a4530f9Schristos 		goto out;
3808a4530f9Schristos 	}
381b1066cf3Schristos 	if (!DSA_SIG_set0(dsig, sig_r, sig_s)) {
382aa36fcacSchristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
383aa36fcacSchristos 		goto out;
384aa36fcacSchristos 	}
385aa36fcacSchristos 	sig_r = sig_s = NULL; /* transferred */
386ca32bd8dSchristos 
387ca32bd8dSchristos 	/* sha1 the data */
388b1066cf3Schristos 	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, dlen,
3898a4530f9Schristos 	    digest, sizeof(digest))) != 0)
3908a4530f9Schristos 		goto out;
391ca32bd8dSchristos 
392b1066cf3Schristos 	switch (DSA_do_verify(digest, hlen, dsig, key->dsa)) {
3938a4530f9Schristos 	case 1:
3948a4530f9Schristos 		ret = 0;
3958a4530f9Schristos 		break;
3968a4530f9Schristos 	case 0:
3978a4530f9Schristos 		ret = SSH_ERR_SIGNATURE_INVALID;
3988a4530f9Schristos 		goto out;
3998a4530f9Schristos 	default:
4008a4530f9Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
4018a4530f9Schristos 		goto out;
4028a4530f9Schristos 	}
403ca32bd8dSchristos 
4048a4530f9Schristos  out:
4058a4530f9Schristos 	explicit_bzero(digest, sizeof(digest));
406b1066cf3Schristos 	DSA_SIG_free(dsig);
407aa36fcacSchristos 	BN_clear_free(sig_r);
408aa36fcacSchristos 	BN_clear_free(sig_s);
4098a4530f9Schristos 	sshbuf_free(b);
4108a4530f9Schristos 	free(ktype);
4118db691beSchristos 	if (sigblob != NULL)
4128db691beSchristos 		freezero(sigblob, len);
413ca32bd8dSchristos 	return ret;
414ca32bd8dSchristos }
415b1066cf3Schristos 
416b1066cf3Schristos static const struct sshkey_impl_funcs sshkey_dss_funcs = {
417b1066cf3Schristos 	/* .size = */		ssh_dss_size,
418b1066cf3Schristos 	/* .alloc = */		ssh_dss_alloc,
419b1066cf3Schristos 	/* .cleanup = */	ssh_dss_cleanup,
420b1066cf3Schristos 	/* .equal = */		ssh_dss_equal,
421b1066cf3Schristos 	/* .ssh_serialize_public = */ ssh_dss_serialize_public,
422b1066cf3Schristos 	/* .ssh_deserialize_public = */ ssh_dss_deserialize_public,
423b1066cf3Schristos 	/* .ssh_serialize_private = */ ssh_dss_serialize_private,
424b1066cf3Schristos 	/* .ssh_deserialize_private = */ ssh_dss_deserialize_private,
425b1066cf3Schristos 	/* .generate = */	ssh_dss_generate,
426b1066cf3Schristos 	/* .copy_public = */	ssh_dss_copy_public,
427b1066cf3Schristos 	/* .sign = */		ssh_dss_sign,
428b1066cf3Schristos 	/* .verify = */		ssh_dss_verify,
429b1066cf3Schristos };
430b1066cf3Schristos 
431b1066cf3Schristos const struct sshkey_impl sshkey_dss_impl = {
432b1066cf3Schristos 	/* .name = */		"ssh-dss",
433b1066cf3Schristos 	/* .shortname = */	"DSA",
434b1066cf3Schristos 	/* .sigalg = */		NULL,
435b1066cf3Schristos 	/* .type = */		KEY_DSA,
436b1066cf3Schristos 	/* .nid = */		0,
437b1066cf3Schristos 	/* .cert = */		0,
438b1066cf3Schristos 	/* .sigonly = */	0,
439b1066cf3Schristos 	/* .keybits = */	0,
440b1066cf3Schristos 	/* .funcs = */		&sshkey_dss_funcs,
441b1066cf3Schristos };
442b1066cf3Schristos 
443b1066cf3Schristos const struct sshkey_impl sshkey_dsa_cert_impl = {
444b1066cf3Schristos 	/* .name = */		"ssh-dss-cert-v01@openssh.com",
445b1066cf3Schristos 	/* .shortname = */	"DSA-CERT",
446b1066cf3Schristos 	/* .sigalg = */		NULL,
447b1066cf3Schristos 	/* .type = */		KEY_DSA_CERT,
448b1066cf3Schristos 	/* .nid = */		0,
449b1066cf3Schristos 	/* .cert = */		1,
450b1066cf3Schristos 	/* .sigonly = */	0,
451b1066cf3Schristos 	/* .keybits = */	0,
452b1066cf3Schristos 	/* .funcs = */		&sshkey_dss_funcs,
453b1066cf3Schristos };
454*c5555919Schristos 
455*c5555919Schristos #endif /* WITH_DSA */
456