ssh-dss.c revision 76259
176259Sgreen/*
276259Sgreen * Copyright (c) 2000 Markus Friedl.  All rights reserved.
376259Sgreen *
476259Sgreen * Redistribution and use in source and binary forms, with or without
576259Sgreen * modification, are permitted provided that the following conditions
676259Sgreen * are met:
776259Sgreen * 1. Redistributions of source code must retain the above copyright
876259Sgreen *    notice, this list of conditions and the following disclaimer.
976259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1076259Sgreen *    notice, this list of conditions and the following disclaimer in the
1176259Sgreen *    documentation and/or other materials provided with the distribution.
1276259Sgreen *
1376259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1476259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1576259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1676259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1776259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1876259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1976259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2076259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2176259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2276259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2376259Sgreen */
2476259Sgreen
2576259Sgreen#include "includes.h"
2676259SgreenRCSID("$OpenBSD: ssh-dss.c,v 1.6 2001/02/08 19:30:52 itojun Exp $");
2776259Sgreen
2876259Sgreen#include <openssl/bn.h>
2976259Sgreen#include <openssl/evp.h>
3076259Sgreen
3176259Sgreen#include "xmalloc.h"
3276259Sgreen#include "buffer.h"
3376259Sgreen#include "bufaux.h"
3476259Sgreen#include "compat.h"
3576259Sgreen#include "log.h"
3676259Sgreen#include "key.h"
3776259Sgreen#include "ssh-dss.h"
3876259Sgreen
3976259Sgreen#define INTBLOB_LEN	20
4076259Sgreen#define SIGBLOB_LEN	(2*INTBLOB_LEN)
4176259Sgreen
4276259Sgreenint
4376259Sgreenssh_dss_sign(
4476259Sgreen    Key *key,
4576259Sgreen    u_char **sigp, int *lenp,
4676259Sgreen    u_char *data, int datalen)
4776259Sgreen{
4876259Sgreen	u_char *digest;
4976259Sgreen	u_char *ret;
5076259Sgreen	DSA_SIG *sig;
5176259Sgreen	EVP_MD *evp_md = EVP_sha1();
5276259Sgreen	EVP_MD_CTX md;
5376259Sgreen	u_int rlen;
5476259Sgreen	u_int slen;
5576259Sgreen	u_int len, dlen;
5676259Sgreen	u_char sigblob[SIGBLOB_LEN];
5776259Sgreen	Buffer b;
5876259Sgreen
5976259Sgreen	if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) {
6076259Sgreen		error("ssh_dss_sign: no DSA key");
6176259Sgreen		return -1;
6276259Sgreen	}
6376259Sgreen	dlen = evp_md->md_size;
6476259Sgreen	digest = xmalloc(dlen);
6576259Sgreen	EVP_DigestInit(&md, evp_md);
6676259Sgreen	EVP_DigestUpdate(&md, data, datalen);
6776259Sgreen	EVP_DigestFinal(&md, digest, NULL);
6876259Sgreen
6976259Sgreen	sig = DSA_do_sign(digest, dlen, key->dsa);
7076259Sgreen	if (sig == NULL) {
7176259Sgreen		fatal("ssh_dss_sign: cannot sign");
7276259Sgreen	}
7376259Sgreen	memset(digest, 0, dlen);
7476259Sgreen	xfree(digest);
7576259Sgreen
7676259Sgreen	rlen = BN_num_bytes(sig->r);
7776259Sgreen	slen = BN_num_bytes(sig->s);
7876259Sgreen	if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) {
7976259Sgreen		error("bad sig size %d %d", rlen, slen);
8076259Sgreen		DSA_SIG_free(sig);
8176259Sgreen		return -1;
8276259Sgreen	}
8376259Sgreen	debug("sig size %d %d", rlen, slen);
8476259Sgreen
8576259Sgreen	memset(sigblob, 0, SIGBLOB_LEN);
8676259Sgreen	BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen);
8776259Sgreen	BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen);
8876259Sgreen	DSA_SIG_free(sig);
8976259Sgreen
9076259Sgreen	if (datafellows & SSH_BUG_SIGBLOB) {
9176259Sgreen		debug("datafellows");
9276259Sgreen		ret = xmalloc(SIGBLOB_LEN);
9376259Sgreen		memcpy(ret, sigblob, SIGBLOB_LEN);
9476259Sgreen		if (lenp != NULL)
9576259Sgreen			*lenp = SIGBLOB_LEN;
9676259Sgreen		if (sigp != NULL)
9776259Sgreen			*sigp = ret;
9876259Sgreen	} else {
9976259Sgreen		/* ietf-drafts */
10076259Sgreen		buffer_init(&b);
10176259Sgreen		buffer_put_cstring(&b, "ssh-dss");
10276259Sgreen		buffer_put_string(&b, sigblob, SIGBLOB_LEN);
10376259Sgreen		len = buffer_len(&b);
10476259Sgreen		ret = xmalloc(len);
10576259Sgreen		memcpy(ret, buffer_ptr(&b), len);
10676259Sgreen		buffer_free(&b);
10776259Sgreen		if (lenp != NULL)
10876259Sgreen			*lenp = len;
10976259Sgreen		if (sigp != NULL)
11076259Sgreen			*sigp = ret;
11176259Sgreen	}
11276259Sgreen	return 0;
11376259Sgreen}
11476259Sgreenint
11576259Sgreenssh_dss_verify(
11676259Sgreen    Key *key,
11776259Sgreen    u_char *signature, int signaturelen,
11876259Sgreen    u_char *data, int datalen)
11976259Sgreen{
12076259Sgreen	Buffer b;
12176259Sgreen	u_char *digest;
12276259Sgreen	DSA_SIG *sig;
12376259Sgreen	EVP_MD *evp_md = EVP_sha1();
12476259Sgreen	EVP_MD_CTX md;
12576259Sgreen	u_char *sigblob;
12676259Sgreen	char *txt;
12776259Sgreen	u_int len, dlen;
12876259Sgreen	int rlen;
12976259Sgreen	int ret;
13076259Sgreen
13176259Sgreen	if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) {
13276259Sgreen		error("ssh_dss_verify: no DSA key");
13376259Sgreen		return -1;
13476259Sgreen	}
13576259Sgreen
13676259Sgreen	if (!(datafellows & SSH_BUG_SIGBLOB) &&
13776259Sgreen	    signaturelen == SIGBLOB_LEN) {
13876259Sgreen		datafellows |= ~SSH_BUG_SIGBLOB;
13976259Sgreen		log("autodetect SSH_BUG_SIGBLOB");
14076259Sgreen	} else if ((datafellows & SSH_BUG_SIGBLOB) &&
14176259Sgreen	    signaturelen != SIGBLOB_LEN) {
14276259Sgreen		log("autoremove SSH_BUG_SIGBLOB");
14376259Sgreen		datafellows &= ~SSH_BUG_SIGBLOB;
14476259Sgreen	}
14576259Sgreen
14676259Sgreen	debug("len %d datafellows %d", signaturelen, datafellows);
14776259Sgreen
14876259Sgreen	/* fetch signature */
14976259Sgreen	if (datafellows & SSH_BUG_SIGBLOB) {
15076259Sgreen		sigblob = signature;
15176259Sgreen		len = signaturelen;
15276259Sgreen	} else {
15376259Sgreen		/* ietf-drafts */
15476259Sgreen		char *ktype;
15576259Sgreen		buffer_init(&b);
15676259Sgreen		buffer_append(&b, (char *) signature, signaturelen);
15776259Sgreen		ktype = buffer_get_string(&b, NULL);
15876259Sgreen		if (strcmp("ssh-dss", ktype) != 0) {
15976259Sgreen			error("ssh_dss_verify: cannot handle type %s", ktype);
16076259Sgreen			buffer_free(&b);
16176259Sgreen			return -1;
16276259Sgreen		}
16376259Sgreen		sigblob = (u_char *)buffer_get_string(&b, &len);
16476259Sgreen		rlen = buffer_len(&b);
16576259Sgreen		if(rlen != 0) {
16676259Sgreen			error("remaining bytes in signature %d", rlen);
16776259Sgreen			buffer_free(&b);
16876259Sgreen			return -1;
16976259Sgreen		}
17076259Sgreen		buffer_free(&b);
17176259Sgreen		xfree(ktype);
17276259Sgreen	}
17376259Sgreen
17476259Sgreen	if (len != SIGBLOB_LEN) {
17576259Sgreen		fatal("bad sigbloblen %d != SIGBLOB_LEN", len);
17676259Sgreen	}
17776259Sgreen
17876259Sgreen	/* parse signature */
17976259Sgreen	sig = DSA_SIG_new();
18076259Sgreen	sig->r = BN_new();
18176259Sgreen	sig->s = BN_new();
18276259Sgreen	BN_bin2bn(sigblob, INTBLOB_LEN, sig->r);
18376259Sgreen	BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s);
18476259Sgreen
18576259Sgreen	if (!(datafellows & SSH_BUG_SIGBLOB)) {
18676259Sgreen		memset(sigblob, 0, len);
18776259Sgreen		xfree(sigblob);
18876259Sgreen	}
18976259Sgreen
19076259Sgreen	/* sha1 the data */
19176259Sgreen	dlen = evp_md->md_size;
19276259Sgreen	digest = xmalloc(dlen);
19376259Sgreen	EVP_DigestInit(&md, evp_md);
19476259Sgreen	EVP_DigestUpdate(&md, data, datalen);
19576259Sgreen	EVP_DigestFinal(&md, digest, NULL);
19676259Sgreen
19776259Sgreen	ret = DSA_do_verify(digest, dlen, sig, key->dsa);
19876259Sgreen
19976259Sgreen	memset(digest, 0, dlen);
20076259Sgreen	xfree(digest);
20176259Sgreen	DSA_SIG_free(sig);
20276259Sgreen
20376259Sgreen	switch (ret) {
20476259Sgreen	case 1:
20576259Sgreen		txt = "correct";
20676259Sgreen		break;
20776259Sgreen	case 0:
20876259Sgreen		txt = "incorrect";
20976259Sgreen		break;
21076259Sgreen	case -1:
21176259Sgreen	default:
21276259Sgreen		txt = "error";
21376259Sgreen		break;
21476259Sgreen	}
21576259Sgreen	debug("ssh_dss_verify: signature %s", txt);
21676259Sgreen	return ret;
21776259Sgreen}
218