xref: /openbsd-src/usr.bin/ssh/ssh-rsa.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /* $OpenBSD: ssh-rsa.c,v 1.51 2014/02/02 03:44:31 djm Exp $ */
2 /*
3  * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 
20 #include <openssl/evp.h>
21 #include <openssl/err.h>
22 
23 #include <string.h>
24 
25 #include "xmalloc.h"
26 #include "log.h"
27 #include "buffer.h"
28 #include "key.h"
29 #include "compat.h"
30 #include "misc.h"
31 #include "ssh.h"
32 #include "digest.h"
33 
34 static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *);
35 
36 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
37 int
38 ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
39     const u_char *data, u_int datalen)
40 {
41 	int hash_alg;
42 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig;
43 	u_int slen, dlen, len;
44 	int ok, nid;
45 	Buffer b;
46 
47 	if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
48 	    key->rsa == NULL) {
49 		error("%s: no RSA key", __func__);
50 		return -1;
51 	}
52 
53 	/* hash the data */
54 	hash_alg = SSH_DIGEST_SHA1;
55 	nid = NID_sha1;
56 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
57 		error("%s: bad hash algorithm %d", __func__, hash_alg);
58 		return -1;
59 	}
60 	if (ssh_digest_memory(hash_alg, data, datalen,
61 	    digest, sizeof(digest)) != 0) {
62 		error("%s: ssh_digest_memory failed", __func__);
63 		return -1;
64 	}
65 
66 	slen = RSA_size(key->rsa);
67 	sig = xmalloc(slen);
68 
69 	ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa);
70 	explicit_bzero(digest, sizeof(digest));
71 
72 	if (ok != 1) {
73 		int ecode = ERR_get_error();
74 
75 		error("%s: RSA_sign failed: %s", __func__,
76 		    ERR_error_string(ecode, NULL));
77 		free(sig);
78 		return -1;
79 	}
80 	if (len < slen) {
81 		u_int diff = slen - len;
82 		debug("slen %u > len %u", slen, len);
83 		memmove(sig + diff, sig, len);
84 		explicit_bzero(sig, diff);
85 	} else if (len > slen) {
86 		error("%s: slen %u slen2 %u", __func__, slen, len);
87 		free(sig);
88 		return -1;
89 	}
90 	/* encode signature */
91 	buffer_init(&b);
92 	buffer_put_cstring(&b, "ssh-rsa");
93 	buffer_put_string(&b, sig, slen);
94 	len = buffer_len(&b);
95 	if (lenp != NULL)
96 		*lenp = len;
97 	if (sigp != NULL) {
98 		*sigp = xmalloc(len);
99 		memcpy(*sigp, buffer_ptr(&b), len);
100 	}
101 	buffer_free(&b);
102 	explicit_bzero(sig, slen);
103 	free(sig);
104 
105 	return 0;
106 }
107 
108 int
109 ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
110     const u_char *data, u_int datalen)
111 {
112 	Buffer b;
113 	int hash_alg;
114 	char *ktype;
115 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
116 	u_int len, dlen, modlen;
117 	int rlen, ret;
118 
119 	if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
120 	    key->rsa == NULL) {
121 		error("%s: no RSA key", __func__);
122 		return -1;
123 	}
124 
125 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
126 		error("%s: RSA modulus too small: %d < minimum %d bits",
127 		    __func__, BN_num_bits(key->rsa->n),
128 		    SSH_RSA_MINIMUM_MODULUS_SIZE);
129 		return -1;
130 	}
131 	buffer_init(&b);
132 	buffer_append(&b, signature, signaturelen);
133 	ktype = buffer_get_cstring(&b, NULL);
134 	if (strcmp("ssh-rsa", ktype) != 0) {
135 		error("%s: cannot handle type %s", __func__, ktype);
136 		buffer_free(&b);
137 		free(ktype);
138 		return -1;
139 	}
140 	free(ktype);
141 	sigblob = buffer_get_string(&b, &len);
142 	rlen = buffer_len(&b);
143 	buffer_free(&b);
144 	if (rlen != 0) {
145 		error("%s: remaining bytes in signature %d", __func__, rlen);
146 		free(sigblob);
147 		return -1;
148 	}
149 	/* RSA_verify expects a signature of RSA_size */
150 	modlen = RSA_size(key->rsa);
151 	if (len > modlen) {
152 		error("%s: len %u > modlen %u", __func__, len, modlen);
153 		free(sigblob);
154 		return -1;
155 	} else if (len < modlen) {
156 		u_int diff = modlen - len;
157 		debug("%s: add padding: modlen %u > len %u", __func__,
158 		    modlen, len);
159 		sigblob = xrealloc(sigblob, 1, modlen);
160 		memmove(sigblob + diff, sigblob, len);
161 		explicit_bzero(sigblob, diff);
162 		len = modlen;
163 	}
164 	/* hash the data */
165 	hash_alg = SSH_DIGEST_SHA1;
166 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
167 		error("%s: bad hash algorithm %d", __func__, hash_alg);
168 		return -1;
169 	}
170 	if (ssh_digest_memory(hash_alg, data, datalen,
171 	    digest, sizeof(digest)) != 0) {
172 		error("%s: ssh_digest_memory failed", __func__);
173 		return -1;
174 	}
175 
176 	ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
177 	    key->rsa);
178 	explicit_bzero(digest, sizeof(digest));
179 	explicit_bzero(sigblob, len);
180 	free(sigblob);
181 	debug("%s: signature %scorrect", __func__, (ret == 0) ? "in" : "");
182 	return ret;
183 }
184 
185 /*
186  * See:
187  * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
188  * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
189  */
190 /*
191  * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
192  *	oiw(14) secsig(3) algorithms(2) 26 }
193  */
194 static const u_char id_sha1[] = {
195 	0x30, 0x21, /* type Sequence, length 0x21 (33) */
196 	0x30, 0x09, /* type Sequence, length 0x09 */
197 	0x06, 0x05, /* type OID, length 0x05 */
198 	0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
199 	0x05, 0x00, /* NULL */
200 	0x04, 0x14  /* Octet string, length 0x14 (20), followed by sha1 hash */
201 };
202 
203 static int
204 openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen,
205     u_char *sigbuf, u_int siglen, RSA *rsa)
206 {
207 	u_int ret, rsasize, oidlen = 0, hlen = 0;
208 	int len, oidmatch, hashmatch;
209 	const u_char *oid = NULL;
210 	u_char *decrypted = NULL;
211 
212 	ret = 0;
213 	switch (hash_alg) {
214 	case SSH_DIGEST_SHA1:
215 		oid = id_sha1;
216 		oidlen = sizeof(id_sha1);
217 		hlen = 20;
218 		break;
219 	default:
220 		goto done;
221 	}
222 	if (hashlen != hlen) {
223 		error("bad hashlen");
224 		goto done;
225 	}
226 	rsasize = RSA_size(rsa);
227 	if (siglen == 0 || siglen > rsasize) {
228 		error("bad siglen");
229 		goto done;
230 	}
231 	decrypted = xmalloc(rsasize);
232 	if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
233 	    RSA_PKCS1_PADDING)) < 0) {
234 		error("RSA_public_decrypt failed: %s",
235 		    ERR_error_string(ERR_get_error(), NULL));
236 		goto done;
237 	}
238 	if (len < 0 || (u_int)len != hlen + oidlen) {
239 		error("bad decrypted len: %d != %d + %d", len, hlen, oidlen);
240 		goto done;
241 	}
242 	oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
243 	hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
244 	if (!oidmatch) {
245 		error("oid mismatch");
246 		goto done;
247 	}
248 	if (!hashmatch) {
249 		error("hash mismatch");
250 		goto done;
251 	}
252 	ret = 1;
253 done:
254 	free(decrypted);
255 	return ret;
256 }
257