1264377Sdes/* $OpenBSD: ssh-rsa.c,v 1.51 2014/02/02 03:44:31 djm Exp $ */
276259Sgreen/*
3124208Sdes * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
476259Sgreen *
5124208Sdes * Permission to use, copy, modify, and distribute this software for any
6124208Sdes * purpose with or without fee is hereby granted, provided that the above
7124208Sdes * copyright notice and this permission notice appear in all copies.
876259Sgreen *
9124208Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10124208Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11124208Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12124208Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13124208Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14124208Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15124208Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1676259Sgreen */
17162852Sdes
1876259Sgreen#include "includes.h"
1976259Sgreen
20162852Sdes#include <sys/types.h>
21162852Sdes
2276259Sgreen#include <openssl/evp.h>
2376259Sgreen#include <openssl/err.h>
2476259Sgreen
25162852Sdes#include <stdarg.h>
26162852Sdes#include <string.h>
27162852Sdes
2876259Sgreen#include "xmalloc.h"
2976259Sgreen#include "log.h"
3076259Sgreen#include "buffer.h"
3176259Sgreen#include "key.h"
3276259Sgreen#include "compat.h"
33215116Sdes#include "misc.h"
3498675Sdes#include "ssh.h"
35262566Sdes#include "digest.h"
3676259Sgreen
37113908Sdesstatic int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *);
38106121Sdes
3976259Sgreen/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
4076259Sgreenint
41126274Sdesssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
42126274Sdes    const u_char *data, u_int datalen)
4376259Sgreen{
44262566Sdes	int hash_alg;
45262566Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig;
4676259Sgreen	u_int slen, dlen, len;
4776259Sgreen	int ok, nid;
4876259Sgreen	Buffer b;
4976259Sgreen
50262566Sdes	if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
51262566Sdes	    key->rsa == NULL) {
52262566Sdes		error("%s: no RSA key", __func__);
5376259Sgreen		return -1;
5476259Sgreen	}
55262566Sdes
56262566Sdes	/* hash the data */
57262566Sdes	hash_alg = SSH_DIGEST_SHA1;
58262566Sdes	nid = NID_sha1;
59262566Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
60262566Sdes		error("%s: bad hash algorithm %d", __func__, hash_alg);
6176259Sgreen		return -1;
6276259Sgreen	}
63262566Sdes	if (ssh_digest_memory(hash_alg, data, datalen,
64262566Sdes	    digest, sizeof(digest)) != 0) {
65262566Sdes		error("%s: ssh_digest_memory failed", __func__);
66262566Sdes		return -1;
67262566Sdes	}
6876259Sgreen
6976259Sgreen	slen = RSA_size(key->rsa);
7076259Sgreen	sig = xmalloc(slen);
7176259Sgreen
7276259Sgreen	ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa);
73264377Sdes	explicit_bzero(digest, sizeof(digest));
7476259Sgreen
7576259Sgreen	if (ok != 1) {
7676259Sgreen		int ecode = ERR_get_error();
77162852Sdes
78262566Sdes		error("%s: RSA_sign failed: %s", __func__,
7999060Sdes		    ERR_error_string(ecode, NULL));
80255767Sdes		free(sig);
8176259Sgreen		return -1;
8276259Sgreen	}
8376259Sgreen	if (len < slen) {
84106121Sdes		u_int diff = slen - len;
8599060Sdes		debug("slen %u > len %u", slen, len);
8676259Sgreen		memmove(sig + diff, sig, len);
87264377Sdes		explicit_bzero(sig, diff);
8876259Sgreen	} else if (len > slen) {
89262566Sdes		error("%s: slen %u slen2 %u", __func__, slen, len);
90255767Sdes		free(sig);
9176259Sgreen		return -1;
9276259Sgreen	}
9376259Sgreen	/* encode signature */
9476259Sgreen	buffer_init(&b);
9576259Sgreen	buffer_put_cstring(&b, "ssh-rsa");
9676259Sgreen	buffer_put_string(&b, sig, slen);
9776259Sgreen	len = buffer_len(&b);
98106121Sdes	if (lenp != NULL)
99106121Sdes		*lenp = len;
100106121Sdes	if (sigp != NULL) {
101106121Sdes		*sigp = xmalloc(len);
102106121Sdes		memcpy(*sigp, buffer_ptr(&b), len);
103106121Sdes	}
10476259Sgreen	buffer_free(&b);
105264377Sdes	explicit_bzero(sig, slen);
106255767Sdes	free(sig);
10776259Sgreen
10876259Sgreen	return 0;
10976259Sgreen}
11076259Sgreen
11176259Sgreenint
112126274Sdesssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
113126274Sdes    const u_char *data, u_int datalen)
11476259Sgreen{
11576259Sgreen	Buffer b;
116262566Sdes	int hash_alg;
11776259Sgreen	char *ktype;
118262566Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
11998675Sdes	u_int len, dlen, modlen;
120262566Sdes	int rlen, ret;
12176259Sgreen
122262566Sdes	if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
123262566Sdes	    key->rsa == NULL) {
124262566Sdes		error("%s: no RSA key", __func__);
12576259Sgreen		return -1;
12676259Sgreen	}
127262566Sdes
12898675Sdes	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
129262566Sdes		error("%s: RSA modulus too small: %d < minimum %d bits",
130262566Sdes		    __func__, BN_num_bits(key->rsa->n),
131262566Sdes		    SSH_RSA_MINIMUM_MODULUS_SIZE);
13292555Sdes		return -1;
13392555Sdes	}
13476259Sgreen	buffer_init(&b);
13592555Sdes	buffer_append(&b, signature, signaturelen);
136221420Sdes	ktype = buffer_get_cstring(&b, NULL);
13776259Sgreen	if (strcmp("ssh-rsa", ktype) != 0) {
138262566Sdes		error("%s: cannot handle type %s", __func__, ktype);
13976259Sgreen		buffer_free(&b);
140255767Sdes		free(ktype);
14176259Sgreen		return -1;
14276259Sgreen	}
143255767Sdes	free(ktype);
14492555Sdes	sigblob = buffer_get_string(&b, &len);
14576259Sgreen	rlen = buffer_len(&b);
14676259Sgreen	buffer_free(&b);
14792555Sdes	if (rlen != 0) {
148262566Sdes		error("%s: remaining bytes in signature %d", __func__, rlen);
149255767Sdes		free(sigblob);
15076259Sgreen		return -1;
15176259Sgreen	}
15298675Sdes	/* RSA_verify expects a signature of RSA_size */
15398675Sdes	modlen = RSA_size(key->rsa);
15498675Sdes	if (len > modlen) {
155262566Sdes		error("%s: len %u > modlen %u", __func__, len, modlen);
156255767Sdes		free(sigblob);
15798675Sdes		return -1;
15898675Sdes	} else if (len < modlen) {
159106121Sdes		u_int diff = modlen - len;
160262566Sdes		debug("%s: add padding: modlen %u > len %u", __func__,
16198675Sdes		    modlen, len);
162162852Sdes		sigblob = xrealloc(sigblob, 1, modlen);
16398675Sdes		memmove(sigblob + diff, sigblob, len);
164264377Sdes		explicit_bzero(sigblob, diff);
16598675Sdes		len = modlen;
16698675Sdes	}
167262566Sdes	/* hash the data */
168262566Sdes	hash_alg = SSH_DIGEST_SHA1;
169262566Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
170262566Sdes		error("%s: bad hash algorithm %d", __func__, hash_alg);
17176259Sgreen		return -1;
17276259Sgreen	}
173262566Sdes	if (ssh_digest_memory(hash_alg, data, datalen,
174262566Sdes	    digest, sizeof(digest)) != 0) {
175262566Sdes		error("%s: ssh_digest_memory failed", __func__);
176262566Sdes		return -1;
177262566Sdes	}
17876259Sgreen
179262566Sdes	ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
180262566Sdes	    key->rsa);
181264377Sdes	explicit_bzero(digest, sizeof(digest));
182264377Sdes	explicit_bzero(sigblob, len);
183255767Sdes	free(sigblob);
184262566Sdes	debug("%s: signature %scorrect", __func__, (ret == 0) ? "in" : "");
18576259Sgreen	return ret;
18676259Sgreen}
187106121Sdes
188106121Sdes/*
189106121Sdes * See:
190106121Sdes * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
191106121Sdes * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
192106121Sdes */
193106121Sdes/*
194106121Sdes * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
195106121Sdes *	oiw(14) secsig(3) algorithms(2) 26 }
196106121Sdes */
197106121Sdesstatic const u_char id_sha1[] = {
198106121Sdes	0x30, 0x21, /* type Sequence, length 0x21 (33) */
199106121Sdes	0x30, 0x09, /* type Sequence, length 0x09 */
200106121Sdes	0x06, 0x05, /* type OID, length 0x05 */
201106121Sdes	0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
202106121Sdes	0x05, 0x00, /* NULL */
203106121Sdes	0x04, 0x14  /* Octet string, length 0x14 (20), followed by sha1 hash */
204106121Sdes};
205106121Sdes
206106121Sdesstatic int
207262566Sdesopenssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen,
208106121Sdes    u_char *sigbuf, u_int siglen, RSA *rsa)
209106121Sdes{
210106121Sdes	u_int ret, rsasize, oidlen = 0, hlen = 0;
211215116Sdes	int len, oidmatch, hashmatch;
212106121Sdes	const u_char *oid = NULL;
213106121Sdes	u_char *decrypted = NULL;
214106121Sdes
215106121Sdes	ret = 0;
216262566Sdes	switch (hash_alg) {
217262566Sdes	case SSH_DIGEST_SHA1:
218106121Sdes		oid = id_sha1;
219106121Sdes		oidlen = sizeof(id_sha1);
220106121Sdes		hlen = 20;
221106121Sdes		break;
222106121Sdes	default:
223106121Sdes		goto done;
224106121Sdes	}
225106121Sdes	if (hashlen != hlen) {
226106121Sdes		error("bad hashlen");
227106121Sdes		goto done;
228106121Sdes	}
229106121Sdes	rsasize = RSA_size(rsa);
230106121Sdes	if (siglen == 0 || siglen > rsasize) {
231106121Sdes		error("bad siglen");
232106121Sdes		goto done;
233106121Sdes	}
234106121Sdes	decrypted = xmalloc(rsasize);
235106121Sdes	if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
236106121Sdes	    RSA_PKCS1_PADDING)) < 0) {
237106121Sdes		error("RSA_public_decrypt failed: %s",
238106121Sdes		    ERR_error_string(ERR_get_error(), NULL));
239106121Sdes		goto done;
240106121Sdes	}
241149749Sdes	if (len < 0 || (u_int)len != hlen + oidlen) {
242106121Sdes		error("bad decrypted len: %d != %d + %d", len, hlen, oidlen);
243106121Sdes		goto done;
244106121Sdes	}
245215116Sdes	oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
246215116Sdes	hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
247215116Sdes	if (!oidmatch) {
248106121Sdes		error("oid mismatch");
249106121Sdes		goto done;
250106121Sdes	}
251215116Sdes	if (!hashmatch) {
252106121Sdes		error("hash mismatch");
253106121Sdes		goto done;
254106121Sdes	}
255106121Sdes	ret = 1;
256106121Sdesdone:
257255767Sdes	free(decrypted);
258106121Sdes	return ret;
259106121Sdes}
260