ssh-dss.c revision 99060
1204793Srdivacky/*
2204793Srdivacky * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3204793Srdivacky *
4204793Srdivacky * Redistribution and use in source and binary forms, with or without
5204793Srdivacky * modification, are permitted provided that the following conditions
6204793Srdivacky * are met:
7204793Srdivacky * 1. Redistributions of source code must retain the above copyright
8204793Srdivacky *    notice, this list of conditions and the following disclaimer.
9204793Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
10204793Srdivacky *    notice, this list of conditions and the following disclaimer in the
11204793Srdivacky *    documentation and/or other materials provided with the distribution.
12204793Srdivacky *
13204793Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14204793Srdivacky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15204793Srdivacky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16204793Srdivacky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17204793Srdivacky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18204793Srdivacky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19204793Srdivacky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20204793Srdivacky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21204793Srdivacky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22204793Srdivacky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23204793Srdivacky */
24204793Srdivacky
25204793Srdivacky#include "includes.h"
26204793SrdivackyRCSID("$OpenBSD: ssh-dss.c,v 1.15 2002/06/23 03:30:17 deraadt Exp $");
27204793Srdivacky
28204793Srdivacky#include <openssl/bn.h>
29204793Srdivacky#include <openssl/evp.h>
30204793Srdivacky
31204793Srdivacky#include "xmalloc.h"
32204793Srdivacky#include "buffer.h"
33204793Srdivacky#include "bufaux.h"
34204793Srdivacky#include "compat.h"
35204962Srdivacky#include "log.h"
36204793Srdivacky#include "key.h"
37204793Srdivacky#include "ssh-dss.h"
38204793Srdivacky
39204793Srdivacky#define INTBLOB_LEN	20
40204793Srdivacky#define SIGBLOB_LEN	(2*INTBLOB_LEN)
41204793Srdivacky
42204793Srdivackyint
43204793Srdivackyssh_dss_sign(Key *key, u_char **sigp, u_int *lenp,
44204793Srdivacky    u_char *data, u_int datalen)
45204793Srdivacky{
46204793Srdivacky	DSA_SIG *sig;
47204793Srdivacky	const EVP_MD *evp_md = EVP_sha1();
48204793Srdivacky	EVP_MD_CTX md;
49204793Srdivacky	u_char *ret, digest[EVP_MAX_MD_SIZE], sigblob[SIGBLOB_LEN];
50204793Srdivacky	u_int rlen, slen, len, dlen;
51204793Srdivacky	Buffer b;
52204962Srdivacky
53204793Srdivacky	if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) {
54204793Srdivacky		error("ssh_dss_sign: no DSA key");
55204793Srdivacky		return -1;
56204793Srdivacky	}
57204793Srdivacky	EVP_DigestInit(&md, evp_md);
58204793Srdivacky	EVP_DigestUpdate(&md, data, datalen);
59204793Srdivacky	EVP_DigestFinal(&md, digest, &dlen);
60204793Srdivacky
61204793Srdivacky	sig = DSA_do_sign(digest, dlen, key->dsa);
62204793Srdivacky	memset(digest, 'd', sizeof(digest));
63204793Srdivacky
64204962Srdivacky	if (sig == NULL) {
65204962Srdivacky		error("ssh_dss_sign: sign failed");
66204962Srdivacky		return -1;
67204962Srdivacky	}
68204793Srdivacky
69204793Srdivacky	rlen = BN_num_bytes(sig->r);
70204793Srdivacky	slen = BN_num_bytes(sig->s);
71204793Srdivacky	if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) {
72204793Srdivacky		error("bad sig size %u %u", rlen, slen);
73204793Srdivacky		DSA_SIG_free(sig);
74204793Srdivacky		return -1;
75204793Srdivacky	}
76204793Srdivacky	memset(sigblob, 0, SIGBLOB_LEN);
77204793Srdivacky	BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen);
78204793Srdivacky	BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen);
79204793Srdivacky	DSA_SIG_free(sig);
80204793Srdivacky
81204793Srdivacky	if (datafellows & SSH_BUG_SIGBLOB) {
82204793Srdivacky		ret = xmalloc(SIGBLOB_LEN);
83204793Srdivacky		memcpy(ret, sigblob, SIGBLOB_LEN);
84204793Srdivacky		if (lenp != NULL)
85204793Srdivacky			*lenp = SIGBLOB_LEN;
86204793Srdivacky		if (sigp != NULL)
87204793Srdivacky			*sigp = ret;
88204793Srdivacky	} else {
89204793Srdivacky		/* ietf-drafts */
90204793Srdivacky		buffer_init(&b);
91204793Srdivacky		buffer_put_cstring(&b, "ssh-dss");
92204793Srdivacky		buffer_put_string(&b, sigblob, SIGBLOB_LEN);
93204793Srdivacky		len = buffer_len(&b);
94204793Srdivacky		ret = xmalloc(len);
95204793Srdivacky		memcpy(ret, buffer_ptr(&b), len);
96204793Srdivacky		buffer_free(&b);
97204793Srdivacky		if (lenp != NULL)
98204793Srdivacky			*lenp = len;
99204793Srdivacky		if (sigp != NULL)
100204793Srdivacky			*sigp = ret;
101204793Srdivacky	}
102204793Srdivacky	return 0;
103204793Srdivacky}
104204793Srdivackyint
105204793Srdivackyssh_dss_verify(Key *key, u_char *signature, u_int signaturelen,
106204793Srdivacky    u_char *data, u_int datalen)
107204793Srdivacky{
108204793Srdivacky	DSA_SIG *sig;
109204962Srdivacky	const EVP_MD *evp_md = EVP_sha1();
110204962Srdivacky	EVP_MD_CTX md;
111204962Srdivacky	u_char digest[EVP_MAX_MD_SIZE], *sigblob;
112204962Srdivacky	u_int len, dlen;
113204962Srdivacky	int rlen, ret;
114204962Srdivacky	Buffer b;
115204962Srdivacky
116204962Srdivacky	if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) {
117204962Srdivacky		error("ssh_dss_verify: no DSA key");
118204962Srdivacky		return -1;
119204962Srdivacky	}
120204962Srdivacky
121204962Srdivacky	/* fetch signature */
122204962Srdivacky	if (datafellows & SSH_BUG_SIGBLOB) {
123204962Srdivacky		sigblob = signature;
124204962Srdivacky		len = signaturelen;
125204962Srdivacky	} else {
126204962Srdivacky		/* ietf-drafts */
127204962Srdivacky		char *ktype;
128204962Srdivacky		buffer_init(&b);
129204962Srdivacky		buffer_append(&b, signature, signaturelen);
130204962Srdivacky		ktype = buffer_get_string(&b, NULL);
131204962Srdivacky		if (strcmp("ssh-dss", ktype) != 0) {
132204962Srdivacky			error("ssh_dss_verify: cannot handle type %s", ktype);
133204962Srdivacky			buffer_free(&b);
134204962Srdivacky			xfree(ktype);
135204962Srdivacky			return -1;
136204962Srdivacky		}
137204962Srdivacky		xfree(ktype);
138204962Srdivacky		sigblob = buffer_get_string(&b, &len);
139204962Srdivacky		rlen = buffer_len(&b);
140204962Srdivacky		buffer_free(&b);
141204962Srdivacky		if (rlen != 0) {
142204962Srdivacky			error("ssh_dss_verify: "
143204962Srdivacky			    "remaining bytes in signature %d", rlen);
144204962Srdivacky			xfree(sigblob);
145204962Srdivacky			return -1;
146204962Srdivacky		}
147204962Srdivacky	}
148204962Srdivacky
149204962Srdivacky	if (len != SIGBLOB_LEN) {
150204962Srdivacky		fatal("bad sigbloblen %u != SIGBLOB_LEN", len);
151204962Srdivacky	}
152204962Srdivacky
153204962Srdivacky	/* parse signature */
154204962Srdivacky	if ((sig = DSA_SIG_new()) == NULL)
155204962Srdivacky		fatal("ssh_dss_verify: DSA_SIG_new failed");
156204962Srdivacky	if ((sig->r = BN_new()) == NULL)
157204962Srdivacky		fatal("ssh_dss_verify: BN_new failed");
158204962Srdivacky	if ((sig->s = BN_new()) == NULL)
159204962Srdivacky		fatal("ssh_dss_verify: BN_new failed");
160204962Srdivacky	BN_bin2bn(sigblob, INTBLOB_LEN, sig->r);
161204962Srdivacky	BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s);
162204962Srdivacky
163204962Srdivacky	if (!(datafellows & SSH_BUG_SIGBLOB)) {
164204962Srdivacky		memset(sigblob, 0, len);
165204962Srdivacky		xfree(sigblob);
166204962Srdivacky	}
167204962Srdivacky
168204962Srdivacky	/* sha1 the data */
169204962Srdivacky	EVP_DigestInit(&md, evp_md);
170204962Srdivacky	EVP_DigestUpdate(&md, data, datalen);
171204962Srdivacky	EVP_DigestFinal(&md, digest, &dlen);
172204962Srdivacky
173204962Srdivacky	ret = DSA_do_verify(digest, dlen, sig, key->dsa);
174204962Srdivacky	memset(digest, 'd', sizeof(digest));
175204962Srdivacky
176204962Srdivacky	DSA_SIG_free(sig);
177204962Srdivacky
178204962Srdivacky	debug("ssh_dss_verify: signature %s",
179204962Srdivacky	    ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error");
180204962Srdivacky	return ret;
181204962Srdivacky}
182204962Srdivacky