xref: /freebsd/crypto/openssh/ssh-rsa.c (revision f7167e0ea0bf5aaabff9490453b3b71b3f1b4d51)
1*f7167e0eSDag-Erling Smørgrav /* $OpenBSD: ssh-rsa.c,v 1.50 2014/01/09 23:20:00 djm Exp $ */
21e8db6e2SBrian Feldman /*
3d95e11bfSDag-Erling Smørgrav  * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
41e8db6e2SBrian Feldman  *
5d95e11bfSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
6d95e11bfSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
7d95e11bfSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
81e8db6e2SBrian Feldman  *
9d95e11bfSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d95e11bfSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d95e11bfSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d95e11bfSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d95e11bfSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d95e11bfSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d95e11bfSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
161e8db6e2SBrian Feldman  */
17761efaa7SDag-Erling Smørgrav 
181e8db6e2SBrian Feldman #include "includes.h"
19761efaa7SDag-Erling Smørgrav 
20761efaa7SDag-Erling Smørgrav #include <sys/types.h>
211e8db6e2SBrian Feldman 
221e8db6e2SBrian Feldman #include <openssl/evp.h>
231e8db6e2SBrian Feldman #include <openssl/err.h>
241e8db6e2SBrian Feldman 
25761efaa7SDag-Erling Smørgrav #include <stdarg.h>
26761efaa7SDag-Erling Smørgrav #include <string.h>
27761efaa7SDag-Erling Smørgrav 
281e8db6e2SBrian Feldman #include "xmalloc.h"
291e8db6e2SBrian Feldman #include "log.h"
301e8db6e2SBrian Feldman #include "buffer.h"
311e8db6e2SBrian Feldman #include "key.h"
321e8db6e2SBrian Feldman #include "compat.h"
33e2f6069cSDag-Erling Smørgrav #include "misc.h"
34545d5ecaSDag-Erling Smørgrav #include "ssh.h"
35*f7167e0eSDag-Erling Smørgrav #include "digest.h"
361e8db6e2SBrian Feldman 
374b17dab0SDag-Erling Smørgrav static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *);
384b17dab0SDag-Erling Smørgrav 
391e8db6e2SBrian Feldman /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
401e8db6e2SBrian Feldman int
41efcad6b7SDag-Erling Smørgrav ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
42efcad6b7SDag-Erling Smørgrav     const u_char *data, u_int datalen)
431e8db6e2SBrian Feldman {
44*f7167e0eSDag-Erling Smørgrav 	int hash_alg;
45*f7167e0eSDag-Erling Smørgrav 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig;
461e8db6e2SBrian Feldman 	u_int slen, dlen, len;
471e8db6e2SBrian Feldman 	int ok, nid;
481e8db6e2SBrian Feldman 	Buffer b;
491e8db6e2SBrian Feldman 
50*f7167e0eSDag-Erling Smørgrav 	if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
51*f7167e0eSDag-Erling Smørgrav 	    key->rsa == NULL) {
52*f7167e0eSDag-Erling Smørgrav 		error("%s: no RSA key", __func__);
531e8db6e2SBrian Feldman 		return -1;
541e8db6e2SBrian Feldman 	}
55*f7167e0eSDag-Erling Smørgrav 
56*f7167e0eSDag-Erling Smørgrav 	/* hash the data */
57*f7167e0eSDag-Erling Smørgrav 	hash_alg = SSH_DIGEST_SHA1;
58*f7167e0eSDag-Erling Smørgrav 	nid = NID_sha1;
59*f7167e0eSDag-Erling Smørgrav 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
60*f7167e0eSDag-Erling Smørgrav 		error("%s: bad hash algorithm %d", __func__, hash_alg);
611e8db6e2SBrian Feldman 		return -1;
621e8db6e2SBrian Feldman 	}
63*f7167e0eSDag-Erling Smørgrav 	if (ssh_digest_memory(hash_alg, data, datalen,
64*f7167e0eSDag-Erling Smørgrav 	    digest, sizeof(digest)) != 0) {
65*f7167e0eSDag-Erling Smørgrav 		error("%s: ssh_digest_memory failed", __func__);
66*f7167e0eSDag-Erling Smørgrav 		return -1;
67*f7167e0eSDag-Erling Smørgrav 	}
681e8db6e2SBrian Feldman 
691e8db6e2SBrian Feldman 	slen = RSA_size(key->rsa);
701e8db6e2SBrian Feldman 	sig = xmalloc(slen);
711e8db6e2SBrian Feldman 
721e8db6e2SBrian Feldman 	ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa);
73ae1f160dSDag-Erling Smørgrav 	memset(digest, 'd', sizeof(digest));
741e8db6e2SBrian Feldman 
751e8db6e2SBrian Feldman 	if (ok != 1) {
761e8db6e2SBrian Feldman 		int ecode = ERR_get_error();
77761efaa7SDag-Erling Smørgrav 
78*f7167e0eSDag-Erling Smørgrav 		error("%s: RSA_sign failed: %s", __func__,
79ee21a45fSDag-Erling Smørgrav 		    ERR_error_string(ecode, NULL));
80e4a9863fSDag-Erling Smørgrav 		free(sig);
811e8db6e2SBrian Feldman 		return -1;
821e8db6e2SBrian Feldman 	}
831e8db6e2SBrian Feldman 	if (len < slen) {
844b17dab0SDag-Erling Smørgrav 		u_int diff = slen - len;
85ee21a45fSDag-Erling Smørgrav 		debug("slen %u > len %u", slen, len);
861e8db6e2SBrian Feldman 		memmove(sig + diff, sig, len);
871e8db6e2SBrian Feldman 		memset(sig, 0, diff);
881e8db6e2SBrian Feldman 	} else if (len > slen) {
89*f7167e0eSDag-Erling Smørgrav 		error("%s: slen %u slen2 %u", __func__, slen, len);
90e4a9863fSDag-Erling Smørgrav 		free(sig);
911e8db6e2SBrian Feldman 		return -1;
921e8db6e2SBrian Feldman 	}
931e8db6e2SBrian Feldman 	/* encode signature */
941e8db6e2SBrian Feldman 	buffer_init(&b);
951e8db6e2SBrian Feldman 	buffer_put_cstring(&b, "ssh-rsa");
961e8db6e2SBrian Feldman 	buffer_put_string(&b, sig, slen);
971e8db6e2SBrian Feldman 	len = buffer_len(&b);
984b17dab0SDag-Erling Smørgrav 	if (lenp != NULL)
994b17dab0SDag-Erling Smørgrav 		*lenp = len;
1004b17dab0SDag-Erling Smørgrav 	if (sigp != NULL) {
1014b17dab0SDag-Erling Smørgrav 		*sigp = xmalloc(len);
1024b17dab0SDag-Erling Smørgrav 		memcpy(*sigp, buffer_ptr(&b), len);
1034b17dab0SDag-Erling Smørgrav 	}
1041e8db6e2SBrian Feldman 	buffer_free(&b);
1051e8db6e2SBrian Feldman 	memset(sig, 's', slen);
106e4a9863fSDag-Erling Smørgrav 	free(sig);
1071e8db6e2SBrian Feldman 
1081e8db6e2SBrian Feldman 	return 0;
1091e8db6e2SBrian Feldman }
1101e8db6e2SBrian Feldman 
1111e8db6e2SBrian Feldman int
112efcad6b7SDag-Erling Smørgrav ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
113efcad6b7SDag-Erling Smørgrav     const u_char *data, u_int datalen)
1141e8db6e2SBrian Feldman {
1151e8db6e2SBrian Feldman 	Buffer b;
116*f7167e0eSDag-Erling Smørgrav 	int hash_alg;
1171e8db6e2SBrian Feldman 	char *ktype;
118*f7167e0eSDag-Erling Smørgrav 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
119545d5ecaSDag-Erling Smørgrav 	u_int len, dlen, modlen;
120*f7167e0eSDag-Erling Smørgrav 	int rlen, ret;
1211e8db6e2SBrian Feldman 
122*f7167e0eSDag-Erling Smørgrav 	if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
123*f7167e0eSDag-Erling Smørgrav 	    key->rsa == NULL) {
124*f7167e0eSDag-Erling Smørgrav 		error("%s: no RSA key", __func__);
1251e8db6e2SBrian Feldman 		return -1;
1261e8db6e2SBrian Feldman 	}
127*f7167e0eSDag-Erling Smørgrav 
128545d5ecaSDag-Erling Smørgrav 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
129*f7167e0eSDag-Erling Smørgrav 		error("%s: RSA modulus too small: %d < minimum %d bits",
130*f7167e0eSDag-Erling Smørgrav 		    __func__, BN_num_bits(key->rsa->n),
131*f7167e0eSDag-Erling Smørgrav 		    SSH_RSA_MINIMUM_MODULUS_SIZE);
1321e8db6e2SBrian Feldman 		return -1;
1331e8db6e2SBrian Feldman 	}
1341e8db6e2SBrian Feldman 	buffer_init(&b);
135ae1f160dSDag-Erling Smørgrav 	buffer_append(&b, signature, signaturelen);
1364a421b63SDag-Erling Smørgrav 	ktype = buffer_get_cstring(&b, NULL);
1371e8db6e2SBrian Feldman 	if (strcmp("ssh-rsa", ktype) != 0) {
138*f7167e0eSDag-Erling Smørgrav 		error("%s: cannot handle type %s", __func__, ktype);
1391e8db6e2SBrian Feldman 		buffer_free(&b);
140e4a9863fSDag-Erling Smørgrav 		free(ktype);
1411e8db6e2SBrian Feldman 		return -1;
1421e8db6e2SBrian Feldman 	}
143e4a9863fSDag-Erling Smørgrav 	free(ktype);
144ae1f160dSDag-Erling Smørgrav 	sigblob = buffer_get_string(&b, &len);
1451e8db6e2SBrian Feldman 	rlen = buffer_len(&b);
1461e8db6e2SBrian Feldman 	buffer_free(&b);
1471e8db6e2SBrian Feldman 	if (rlen != 0) {
148*f7167e0eSDag-Erling Smørgrav 		error("%s: remaining bytes in signature %d", __func__, rlen);
149e4a9863fSDag-Erling Smørgrav 		free(sigblob);
1501e8db6e2SBrian Feldman 		return -1;
1511e8db6e2SBrian Feldman 	}
152545d5ecaSDag-Erling Smørgrav 	/* RSA_verify expects a signature of RSA_size */
153545d5ecaSDag-Erling Smørgrav 	modlen = RSA_size(key->rsa);
154545d5ecaSDag-Erling Smørgrav 	if (len > modlen) {
155*f7167e0eSDag-Erling Smørgrav 		error("%s: len %u > modlen %u", __func__, len, modlen);
156e4a9863fSDag-Erling Smørgrav 		free(sigblob);
157545d5ecaSDag-Erling Smørgrav 		return -1;
158545d5ecaSDag-Erling Smørgrav 	} else if (len < modlen) {
1594b17dab0SDag-Erling Smørgrav 		u_int diff = modlen - len;
160*f7167e0eSDag-Erling Smørgrav 		debug("%s: add padding: modlen %u > len %u", __func__,
161545d5ecaSDag-Erling Smørgrav 		    modlen, len);
162761efaa7SDag-Erling Smørgrav 		sigblob = xrealloc(sigblob, 1, modlen);
163545d5ecaSDag-Erling Smørgrav 		memmove(sigblob + diff, sigblob, len);
164545d5ecaSDag-Erling Smørgrav 		memset(sigblob, 0, diff);
165545d5ecaSDag-Erling Smørgrav 		len = modlen;
166545d5ecaSDag-Erling Smørgrav 	}
167*f7167e0eSDag-Erling Smørgrav 	/* hash the data */
168*f7167e0eSDag-Erling Smørgrav 	hash_alg = SSH_DIGEST_SHA1;
169*f7167e0eSDag-Erling Smørgrav 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
170*f7167e0eSDag-Erling Smørgrav 		error("%s: bad hash algorithm %d", __func__, hash_alg);
1711e8db6e2SBrian Feldman 		return -1;
1721e8db6e2SBrian Feldman 	}
173*f7167e0eSDag-Erling Smørgrav 	if (ssh_digest_memory(hash_alg, data, datalen,
174*f7167e0eSDag-Erling Smørgrav 	    digest, sizeof(digest)) != 0) {
175*f7167e0eSDag-Erling Smørgrav 		error("%s: ssh_digest_memory failed", __func__);
176*f7167e0eSDag-Erling Smørgrav 		return -1;
177*f7167e0eSDag-Erling Smørgrav 	}
1781e8db6e2SBrian Feldman 
179*f7167e0eSDag-Erling Smørgrav 	ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
180*f7167e0eSDag-Erling Smørgrav 	    key->rsa);
181ae1f160dSDag-Erling Smørgrav 	memset(digest, 'd', sizeof(digest));
1821e8db6e2SBrian Feldman 	memset(sigblob, 's', len);
183e4a9863fSDag-Erling Smørgrav 	free(sigblob);
184*f7167e0eSDag-Erling Smørgrav 	debug("%s: signature %scorrect", __func__, (ret == 0) ? "in" : "");
1851e8db6e2SBrian Feldman 	return ret;
1861e8db6e2SBrian Feldman }
1874b17dab0SDag-Erling Smørgrav 
1884b17dab0SDag-Erling Smørgrav /*
1894b17dab0SDag-Erling Smørgrav  * See:
1904b17dab0SDag-Erling Smørgrav  * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
1914b17dab0SDag-Erling Smørgrav  * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
1924b17dab0SDag-Erling Smørgrav  */
1934b17dab0SDag-Erling Smørgrav /*
1944b17dab0SDag-Erling Smørgrav  * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
1954b17dab0SDag-Erling Smørgrav  *	oiw(14) secsig(3) algorithms(2) 26 }
1964b17dab0SDag-Erling Smørgrav  */
1974b17dab0SDag-Erling Smørgrav static const u_char id_sha1[] = {
1984b17dab0SDag-Erling Smørgrav 	0x30, 0x21, /* type Sequence, length 0x21 (33) */
1994b17dab0SDag-Erling Smørgrav 	0x30, 0x09, /* type Sequence, length 0x09 */
2004b17dab0SDag-Erling Smørgrav 	0x06, 0x05, /* type OID, length 0x05 */
2014b17dab0SDag-Erling Smørgrav 	0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
2024b17dab0SDag-Erling Smørgrav 	0x05, 0x00, /* NULL */
2034b17dab0SDag-Erling Smørgrav 	0x04, 0x14  /* Octet string, length 0x14 (20), followed by sha1 hash */
2044b17dab0SDag-Erling Smørgrav };
2054b17dab0SDag-Erling Smørgrav 
2064b17dab0SDag-Erling Smørgrav static int
207*f7167e0eSDag-Erling Smørgrav openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen,
2084b17dab0SDag-Erling Smørgrav     u_char *sigbuf, u_int siglen, RSA *rsa)
2094b17dab0SDag-Erling Smørgrav {
2104b17dab0SDag-Erling Smørgrav 	u_int ret, rsasize, oidlen = 0, hlen = 0;
211e2f6069cSDag-Erling Smørgrav 	int len, oidmatch, hashmatch;
2124b17dab0SDag-Erling Smørgrav 	const u_char *oid = NULL;
2134b17dab0SDag-Erling Smørgrav 	u_char *decrypted = NULL;
2144b17dab0SDag-Erling Smørgrav 
2154b17dab0SDag-Erling Smørgrav 	ret = 0;
216*f7167e0eSDag-Erling Smørgrav 	switch (hash_alg) {
217*f7167e0eSDag-Erling Smørgrav 	case SSH_DIGEST_SHA1:
2184b17dab0SDag-Erling Smørgrav 		oid = id_sha1;
2194b17dab0SDag-Erling Smørgrav 		oidlen = sizeof(id_sha1);
2204b17dab0SDag-Erling Smørgrav 		hlen = 20;
2214b17dab0SDag-Erling Smørgrav 		break;
2224b17dab0SDag-Erling Smørgrav 	default:
2234b17dab0SDag-Erling Smørgrav 		goto done;
2244b17dab0SDag-Erling Smørgrav 	}
2254b17dab0SDag-Erling Smørgrav 	if (hashlen != hlen) {
2264b17dab0SDag-Erling Smørgrav 		error("bad hashlen");
2274b17dab0SDag-Erling Smørgrav 		goto done;
2284b17dab0SDag-Erling Smørgrav 	}
2294b17dab0SDag-Erling Smørgrav 	rsasize = RSA_size(rsa);
2304b17dab0SDag-Erling Smørgrav 	if (siglen == 0 || siglen > rsasize) {
2314b17dab0SDag-Erling Smørgrav 		error("bad siglen");
2324b17dab0SDag-Erling Smørgrav 		goto done;
2334b17dab0SDag-Erling Smørgrav 	}
2344b17dab0SDag-Erling Smørgrav 	decrypted = xmalloc(rsasize);
2354b17dab0SDag-Erling Smørgrav 	if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
2364b17dab0SDag-Erling Smørgrav 	    RSA_PKCS1_PADDING)) < 0) {
2374b17dab0SDag-Erling Smørgrav 		error("RSA_public_decrypt failed: %s",
2384b17dab0SDag-Erling Smørgrav 		    ERR_error_string(ERR_get_error(), NULL));
2394b17dab0SDag-Erling Smørgrav 		goto done;
2404b17dab0SDag-Erling Smørgrav 	}
241043840dfSDag-Erling Smørgrav 	if (len < 0 || (u_int)len != hlen + oidlen) {
2424b17dab0SDag-Erling Smørgrav 		error("bad decrypted len: %d != %d + %d", len, hlen, oidlen);
2434b17dab0SDag-Erling Smørgrav 		goto done;
2444b17dab0SDag-Erling Smørgrav 	}
245e2f6069cSDag-Erling Smørgrav 	oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
246e2f6069cSDag-Erling Smørgrav 	hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
247e2f6069cSDag-Erling Smørgrav 	if (!oidmatch) {
2484b17dab0SDag-Erling Smørgrav 		error("oid mismatch");
2494b17dab0SDag-Erling Smørgrav 		goto done;
2504b17dab0SDag-Erling Smørgrav 	}
251e2f6069cSDag-Erling Smørgrav 	if (!hashmatch) {
2524b17dab0SDag-Erling Smørgrav 		error("hash mismatch");
2534b17dab0SDag-Erling Smørgrav 		goto done;
2544b17dab0SDag-Erling Smørgrav 	}
2554b17dab0SDag-Erling Smørgrav 	ret = 1;
2564b17dab0SDag-Erling Smørgrav done:
257e4a9863fSDag-Erling Smørgrav 	free(decrypted);
2584b17dab0SDag-Erling Smørgrav 	return ret;
2594b17dab0SDag-Erling Smørgrav }
260