1296633Sdes/* $OpenBSD: ssh-dss.c,v 1.34 2015/12/11 04:21:12 mmcc Exp $ */
276259Sgreen/*
376259Sgreen * Copyright (c) 2000 Markus Friedl.  All rights reserved.
476259Sgreen *
576259Sgreen * Redistribution and use in source and binary forms, with or without
676259Sgreen * modification, are permitted provided that the following conditions
776259Sgreen * are met:
876259Sgreen * 1. Redistributions of source code must retain the above copyright
976259Sgreen *    notice, this list of conditions and the following disclaimer.
1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1176259Sgreen *    notice, this list of conditions and the following disclaimer in the
1276259Sgreen *    documentation and/or other materials provided with the distribution.
1376259Sgreen *
1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2476259Sgreen */
2576259Sgreen
2676259Sgreen#include "includes.h"
2776259Sgreen
28294332Sdes#ifdef WITH_OPENSSL
29294332Sdes
30162852Sdes#include <sys/types.h>
31162852Sdes
3276259Sgreen#include <openssl/bn.h>
33294328Sdes#include <openssl/dsa.h>
3476259Sgreen#include <openssl/evp.h>
3576259Sgreen
36162852Sdes#include <stdarg.h>
37162852Sdes#include <string.h>
38162852Sdes
39294328Sdes#include "sshbuf.h"
4076259Sgreen#include "compat.h"
41294328Sdes#include "ssherr.h"
42261320Sdes#include "digest.h"
43294328Sdes#define SSHKEY_INTERNAL
44294328Sdes#include "sshkey.h"
4576259Sgreen
4676259Sgreen#define INTBLOB_LEN	20
4776259Sgreen#define SIGBLOB_LEN	(2*INTBLOB_LEN)
4876259Sgreen
4976259Sgreenint
50294328Sdesssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
51294328Sdes    const u_char *data, size_t datalen, u_int compat)
5276259Sgreen{
53294328Sdes	DSA_SIG *sig = NULL;
54261320Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN];
55294328Sdes	size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
56294328Sdes	struct sshbuf *b = NULL;
57294328Sdes	int ret = SSH_ERR_INVALID_ARGUMENT;
5876259Sgreen
59294328Sdes	if (lenp != NULL)
60294328Sdes		*lenp = 0;
61294328Sdes	if (sigp != NULL)
62294328Sdes		*sigp = NULL;
6376259Sgreen
64294328Sdes	if (key == NULL || key->dsa == NULL ||
65294328Sdes	    sshkey_type_plain(key->type) != KEY_DSA)
66294328Sdes		return SSH_ERR_INVALID_ARGUMENT;
67294328Sdes	if (dlen == 0)
68294328Sdes		return SSH_ERR_INTERNAL_ERROR;
69261320Sdes
70294328Sdes	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
71294328Sdes	    digest, sizeof(digest))) != 0)
72294328Sdes		goto out;
7392555Sdes
74294328Sdes	if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) {
75294328Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
76294328Sdes		goto out;
7776259Sgreen	}
7876259Sgreen
7976259Sgreen	rlen = BN_num_bytes(sig->r);
8076259Sgreen	slen = BN_num_bytes(sig->s);
8176259Sgreen	if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) {
82294328Sdes		ret = SSH_ERR_INTERNAL_ERROR;
83294328Sdes		goto out;
8476259Sgreen	}
85263712Sdes	explicit_bzero(sigblob, SIGBLOB_LEN);
86294328Sdes	BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen);
87294328Sdes	BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen);
8876259Sgreen
89294328Sdes	if (compat & SSH_BUG_SIGBLOB) {
90106121Sdes		if (sigp != NULL) {
91294328Sdes			if ((*sigp = malloc(SIGBLOB_LEN)) == NULL) {
92294328Sdes				ret = SSH_ERR_ALLOC_FAIL;
93294328Sdes				goto out;
94294328Sdes			}
95106121Sdes			memcpy(*sigp, sigblob, SIGBLOB_LEN);
96106121Sdes		}
97294328Sdes		if (lenp != NULL)
98294328Sdes			*lenp = SIGBLOB_LEN;
99294328Sdes		ret = 0;
10076259Sgreen	} else {
10176259Sgreen		/* ietf-drafts */
102294328Sdes		if ((b = sshbuf_new()) == NULL) {
103294328Sdes			ret = SSH_ERR_ALLOC_FAIL;
104294328Sdes			goto out;
105294328Sdes		}
106294328Sdes		if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 ||
107294328Sdes		    (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0)
108294328Sdes			goto out;
109294328Sdes		len = sshbuf_len(b);
110294328Sdes		if (sigp != NULL) {
111294328Sdes			if ((*sigp = malloc(len)) == NULL) {
112294328Sdes				ret = SSH_ERR_ALLOC_FAIL;
113294328Sdes				goto out;
114294328Sdes			}
115294328Sdes			memcpy(*sigp, sshbuf_ptr(b), len);
116294328Sdes		}
11776259Sgreen		if (lenp != NULL)
11876259Sgreen			*lenp = len;
119294328Sdes		ret = 0;
12076259Sgreen	}
121294328Sdes out:
122294328Sdes	explicit_bzero(digest, sizeof(digest));
123294328Sdes	if (sig != NULL)
124294328Sdes		DSA_SIG_free(sig);
125296633Sdes	sshbuf_free(b);
126294328Sdes	return ret;
12776259Sgreen}
128294328Sdes
12976259Sgreenint
130294328Sdesssh_dss_verify(const struct sshkey *key,
131294328Sdes    const u_char *signature, size_t signaturelen,
132294328Sdes    const u_char *data, size_t datalen, u_int compat)
13376259Sgreen{
134294328Sdes	DSA_SIG *sig = NULL;
135294328Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL;
136294328Sdes	size_t len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
137294328Sdes	int ret = SSH_ERR_INTERNAL_ERROR;
138294328Sdes	struct sshbuf *b = NULL;
139294328Sdes	char *ktype = NULL;
14076259Sgreen
141294328Sdes	if (key == NULL || key->dsa == NULL ||
142294328Sdes	    sshkey_type_plain(key->type) != KEY_DSA)
143294328Sdes		return SSH_ERR_INVALID_ARGUMENT;
144294328Sdes	if (dlen == 0)
145294328Sdes		return SSH_ERR_INTERNAL_ERROR;
14676259Sgreen
14776259Sgreen	/* fetch signature */
148294328Sdes	if (compat & SSH_BUG_SIGBLOB) {
149294328Sdes		if ((sigblob = malloc(signaturelen)) == NULL)
150294328Sdes			return SSH_ERR_ALLOC_FAIL;
151126274Sdes		memcpy(sigblob, signature, signaturelen);
15276259Sgreen		len = signaturelen;
15376259Sgreen	} else {
15476259Sgreen		/* ietf-drafts */
155294328Sdes		if ((b = sshbuf_from(signature, signaturelen)) == NULL)
156294328Sdes			return SSH_ERR_ALLOC_FAIL;
157294328Sdes		if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
158294328Sdes		    sshbuf_get_string(b, &sigblob, &len) != 0) {
159294328Sdes			ret = SSH_ERR_INVALID_FORMAT;
160294328Sdes			goto out;
161294328Sdes		}
16276259Sgreen		if (strcmp("ssh-dss", ktype) != 0) {
163294328Sdes			ret = SSH_ERR_KEY_TYPE_MISMATCH;
164294328Sdes			goto out;
16576259Sgreen		}
166294328Sdes		if (sshbuf_len(b) != 0) {
167294328Sdes			ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
168294328Sdes			goto out;
16976259Sgreen		}
17076259Sgreen	}
17176259Sgreen
17276259Sgreen	if (len != SIGBLOB_LEN) {
173294328Sdes		ret = SSH_ERR_INVALID_FORMAT;
174294328Sdes		goto out;
17576259Sgreen	}
17676259Sgreen
17776259Sgreen	/* parse signature */
178294328Sdes	if ((sig = DSA_SIG_new()) == NULL ||
179294328Sdes	    (sig->r = BN_new()) == NULL ||
180294328Sdes	    (sig->s = BN_new()) == NULL) {
181294328Sdes		ret = SSH_ERR_ALLOC_FAIL;
182294328Sdes		goto out;
183294328Sdes	}
184164146Sdes	if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) ||
185294328Sdes	    (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) {
186294328Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
187294328Sdes		goto out;
188294328Sdes	}
18976259Sgreen
190294328Sdes	/* sha1 the data */
191294328Sdes	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
192294328Sdes	    digest, sizeof(digest))) != 0)
193294328Sdes		goto out;
19476259Sgreen
195294328Sdes	switch (DSA_do_verify(digest, dlen, sig, key->dsa)) {
196294328Sdes	case 1:
197294328Sdes		ret = 0;
198294328Sdes		break;
199294328Sdes	case 0:
200294328Sdes		ret = SSH_ERR_SIGNATURE_INVALID;
201294328Sdes		goto out;
202294328Sdes	default:
203294328Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
204294328Sdes		goto out;
205261320Sdes	}
20676259Sgreen
207294328Sdes out:
208263712Sdes	explicit_bzero(digest, sizeof(digest));
209294328Sdes	if (sig != NULL)
210294328Sdes		DSA_SIG_free(sig);
211296633Sdes	sshbuf_free(b);
212296633Sdes	free(ktype);
213294328Sdes	if (sigblob != NULL) {
214294328Sdes		explicit_bzero(sigblob, len);
215294328Sdes		free(sigblob);
216294328Sdes	}
21776259Sgreen	return ret;
21876259Sgreen}
219294332Sdes#endif /* WITH_OPENSSL */
220