xref: /openbsd-src/usr.bin/ssh/ssh-rsa.c (revision bbc49888b8451e9ac00361c5ad428a454a626afc)
1 /* $OpenBSD: ssh-rsa.c,v 1.67 2018/07/03 11:39:54 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 "sshbuf.h"
26 #include "compat.h"
27 #include "ssherr.h"
28 #define SSHKEY_INTERNAL
29 #include "sshkey.h"
30 #include "digest.h"
31 #include "log.h"
32 
33 static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
34 
35 static const char *
36 rsa_hash_alg_ident(int hash_alg)
37 {
38 	switch (hash_alg) {
39 	case SSH_DIGEST_SHA1:
40 		return "ssh-rsa";
41 	case SSH_DIGEST_SHA256:
42 		return "rsa-sha2-256";
43 	case SSH_DIGEST_SHA512:
44 		return "rsa-sha2-512";
45 	}
46 	return NULL;
47 }
48 
49 /*
50  * Returns the hash algorithm ID for a given algorithm identifier as used
51  * inside the signature blob,
52  */
53 static int
54 rsa_hash_id_from_ident(const char *ident)
55 {
56 	if (strcmp(ident, "ssh-rsa") == 0)
57 		return SSH_DIGEST_SHA1;
58 	if (strcmp(ident, "rsa-sha2-256") == 0)
59 		return SSH_DIGEST_SHA256;
60 	if (strcmp(ident, "rsa-sha2-512") == 0)
61 		return SSH_DIGEST_SHA512;
62 	return -1;
63 }
64 
65 /*
66  * Return the hash algorithm ID for the specified key name. This includes
67  * all the cases of rsa_hash_id_from_ident() but also the certificate key
68  * types.
69  */
70 static int
71 rsa_hash_id_from_keyname(const char *alg)
72 {
73 	int r;
74 
75 	if ((r = rsa_hash_id_from_ident(alg)) != -1)
76 		return r;
77 	if (strcmp(alg, "ssh-rsa-cert-v01@openssh.com") == 0)
78 		return SSH_DIGEST_SHA1;
79 	if (strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0)
80 		return SSH_DIGEST_SHA256;
81 	if (strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0)
82 		return SSH_DIGEST_SHA512;
83 	return -1;
84 }
85 
86 static int
87 rsa_hash_alg_nid(int type)
88 {
89 	switch (type) {
90 	case SSH_DIGEST_SHA1:
91 		return NID_sha1;
92 	case SSH_DIGEST_SHA256:
93 		return NID_sha256;
94 	case SSH_DIGEST_SHA512:
95 		return NID_sha512;
96 	default:
97 		return -1;
98 	}
99 }
100 
101 int
102 ssh_rsa_generate_additional_parameters(struct sshkey *key)
103 {
104 	BIGNUM *aux = NULL;
105 	BN_CTX *ctx = NULL;
106 	BIGNUM d;
107 	int r;
108 
109 	if (key == NULL || key->rsa == NULL ||
110 	    sshkey_type_plain(key->type) != KEY_RSA)
111 		return SSH_ERR_INVALID_ARGUMENT;
112 
113 	if ((ctx = BN_CTX_new()) == NULL)
114 		return SSH_ERR_ALLOC_FAIL;
115 	if ((aux = BN_new()) == NULL) {
116 		r = SSH_ERR_ALLOC_FAIL;
117 		goto out;
118 	}
119 	BN_set_flags(aux, BN_FLG_CONSTTIME);
120 
121 	BN_init(&d);
122 	BN_with_flags(&d, key->rsa->d, BN_FLG_CONSTTIME);
123 
124 	if ((BN_sub(aux, key->rsa->q, BN_value_one()) == 0) ||
125 	    (BN_mod(key->rsa->dmq1, &d, aux, ctx) == 0) ||
126 	    (BN_sub(aux, key->rsa->p, BN_value_one()) == 0) ||
127 	    (BN_mod(key->rsa->dmp1, &d, aux, ctx) == 0)) {
128 		r = SSH_ERR_LIBCRYPTO_ERROR;
129 		goto out;
130 	}
131 	r = 0;
132  out:
133 	BN_clear_free(aux);
134 	BN_CTX_free(ctx);
135 	return r;
136 }
137 
138 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
139 int
140 ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
141     const u_char *data, size_t datalen, const char *alg_ident)
142 {
143 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
144 	size_t slen = 0;
145 	u_int dlen, len;
146 	int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
147 	struct sshbuf *b = NULL;
148 
149 	if (lenp != NULL)
150 		*lenp = 0;
151 	if (sigp != NULL)
152 		*sigp = NULL;
153 
154 	if (alg_ident == NULL || strlen(alg_ident) == 0)
155 		hash_alg = SSH_DIGEST_SHA1;
156 	else
157 		hash_alg = rsa_hash_id_from_keyname(alg_ident);
158 	if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
159 	    sshkey_type_plain(key->type) != KEY_RSA)
160 		return SSH_ERR_INVALID_ARGUMENT;
161 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
162 		return SSH_ERR_KEY_LENGTH;
163 	slen = RSA_size(key->rsa);
164 	if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
165 		return SSH_ERR_INVALID_ARGUMENT;
166 
167 	/* hash the data */
168 	nid = rsa_hash_alg_nid(hash_alg);
169 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0)
170 		return SSH_ERR_INTERNAL_ERROR;
171 	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
172 	    digest, sizeof(digest))) != 0)
173 		goto out;
174 
175 	if ((sig = malloc(slen)) == NULL) {
176 		ret = SSH_ERR_ALLOC_FAIL;
177 		goto out;
178 	}
179 
180 	if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) {
181 		ret = SSH_ERR_LIBCRYPTO_ERROR;
182 		goto out;
183 	}
184 	if (len < slen) {
185 		size_t diff = slen - len;
186 		memmove(sig + diff, sig, len);
187 		explicit_bzero(sig, diff);
188 	} else if (len > slen) {
189 		ret = SSH_ERR_INTERNAL_ERROR;
190 		goto out;
191 	}
192 	/* encode signature */
193 	if ((b = sshbuf_new()) == NULL) {
194 		ret = SSH_ERR_ALLOC_FAIL;
195 		goto out;
196 	}
197 	if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 ||
198 	    (ret = sshbuf_put_string(b, sig, slen)) != 0)
199 		goto out;
200 	len = sshbuf_len(b);
201 	if (sigp != NULL) {
202 		if ((*sigp = malloc(len)) == NULL) {
203 			ret = SSH_ERR_ALLOC_FAIL;
204 			goto out;
205 		}
206 		memcpy(*sigp, sshbuf_ptr(b), len);
207 	}
208 	if (lenp != NULL)
209 		*lenp = len;
210 	ret = 0;
211  out:
212 	explicit_bzero(digest, sizeof(digest));
213 	freezero(sig, slen);
214 	sshbuf_free(b);
215 	return ret;
216 }
217 
218 int
219 ssh_rsa_verify(const struct sshkey *key,
220     const u_char *sig, size_t siglen, const u_char *data, size_t datalen,
221     const char *alg)
222 {
223 	char *sigtype = NULL;
224 	int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR;
225 	size_t len = 0, diff, modlen, dlen;
226 	struct sshbuf *b = NULL;
227 	u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
228 
229 	if (key == NULL || key->rsa == NULL ||
230 	    sshkey_type_plain(key->type) != KEY_RSA ||
231 	    sig == NULL || siglen == 0)
232 		return SSH_ERR_INVALID_ARGUMENT;
233 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
234 		return SSH_ERR_KEY_LENGTH;
235 
236 	if ((b = sshbuf_from(sig, siglen)) == NULL)
237 		return SSH_ERR_ALLOC_FAIL;
238 	if (sshbuf_get_cstring(b, &sigtype, NULL) != 0) {
239 		ret = SSH_ERR_INVALID_FORMAT;
240 		goto out;
241 	}
242 	if ((hash_alg = rsa_hash_id_from_ident(sigtype)) == -1) {
243 		ret = SSH_ERR_KEY_TYPE_MISMATCH;
244 		goto out;
245 	}
246 	/*
247 	 * Allow ssh-rsa-cert-v01 certs to generate SHA2 signatures for
248 	 * legacy reasons, but otherwise the signature type should match.
249 	 */
250 	if (alg != NULL && strcmp(alg, "ssh-rsa-cert-v01@openssh.com") != 0) {
251 		if ((want_alg = rsa_hash_id_from_keyname(alg)) == -1) {
252 			ret = SSH_ERR_INVALID_ARGUMENT;
253 			goto out;
254 		}
255 		if (hash_alg != want_alg) {
256 			ret = SSH_ERR_SIGNATURE_INVALID;
257 			goto out;
258 		}
259 	}
260 	if (sshbuf_get_string(b, &sigblob, &len) != 0) {
261 		ret = SSH_ERR_INVALID_FORMAT;
262 		goto out;
263 	}
264 	if (sshbuf_len(b) != 0) {
265 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
266 		goto out;
267 	}
268 	/* RSA_verify expects a signature of RSA_size */
269 	modlen = RSA_size(key->rsa);
270 	if (len > modlen) {
271 		ret = SSH_ERR_KEY_BITS_MISMATCH;
272 		goto out;
273 	} else if (len < modlen) {
274 		diff = modlen - len;
275 		osigblob = sigblob;
276 		if ((sigblob = realloc(sigblob, modlen)) == NULL) {
277 			sigblob = osigblob; /* put it back for clear/free */
278 			ret = SSH_ERR_ALLOC_FAIL;
279 			goto out;
280 		}
281 		memmove(sigblob + diff, sigblob, len);
282 		explicit_bzero(sigblob, diff);
283 		len = modlen;
284 	}
285 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
286 		ret = SSH_ERR_INTERNAL_ERROR;
287 		goto out;
288 	}
289 	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
290 	    digest, sizeof(digest))) != 0)
291 		goto out;
292 
293 	ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
294 	    key->rsa);
295  out:
296 	freezero(sigblob, len);
297 	free(sigtype);
298 	sshbuf_free(b);
299 	explicit_bzero(digest, sizeof(digest));
300 	return ret;
301 }
302 
303 /*
304  * See:
305  * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
306  * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
307  */
308 
309 /*
310  * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
311  *	oiw(14) secsig(3) algorithms(2) 26 }
312  */
313 static const u_char id_sha1[] = {
314 	0x30, 0x21, /* type Sequence, length 0x21 (33) */
315 	0x30, 0x09, /* type Sequence, length 0x09 */
316 	0x06, 0x05, /* type OID, length 0x05 */
317 	0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
318 	0x05, 0x00, /* NULL */
319 	0x04, 0x14  /* Octet string, length 0x14 (20), followed by sha1 hash */
320 };
321 
322 /*
323  * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
324  * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
325  *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
326  *      id-sha256(1) }
327  */
328 static const u_char id_sha256[] = {
329 	0x30, 0x31, /* type Sequence, length 0x31 (49) */
330 	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
331 	0x06, 0x09, /* type OID, length 0x09 */
332 	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */
333 	0x05, 0x00, /* NULL */
334 	0x04, 0x20  /* Octet string, length 0x20 (32), followed by sha256 hash */
335 };
336 
337 /*
338  * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
339  * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
340  *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
341  *      id-sha256(3) }
342  */
343 static const u_char id_sha512[] = {
344 	0x30, 0x51, /* type Sequence, length 0x51 (81) */
345 	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
346 	0x06, 0x09, /* type OID, length 0x09 */
347 	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */
348 	0x05, 0x00, /* NULL */
349 	0x04, 0x40  /* Octet string, length 0x40 (64), followed by sha512 hash */
350 };
351 
352 static int
353 rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp)
354 {
355 	switch (hash_alg) {
356 	case SSH_DIGEST_SHA1:
357 		*oidp = id_sha1;
358 		*oidlenp = sizeof(id_sha1);
359 		break;
360 	case SSH_DIGEST_SHA256:
361 		*oidp = id_sha256;
362 		*oidlenp = sizeof(id_sha256);
363 		break;
364 	case SSH_DIGEST_SHA512:
365 		*oidp = id_sha512;
366 		*oidlenp = sizeof(id_sha512);
367 		break;
368 	default:
369 		return SSH_ERR_INVALID_ARGUMENT;
370 	}
371 	return 0;
372 }
373 
374 static int
375 openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
376     u_char *sigbuf, size_t siglen, RSA *rsa)
377 {
378 	size_t rsasize = 0, oidlen = 0, hlen = 0;
379 	int ret, len, oidmatch, hashmatch;
380 	const u_char *oid = NULL;
381 	u_char *decrypted = NULL;
382 
383 	if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0)
384 		return ret;
385 	ret = SSH_ERR_INTERNAL_ERROR;
386 	hlen = ssh_digest_bytes(hash_alg);
387 	if (hashlen != hlen) {
388 		ret = SSH_ERR_INVALID_ARGUMENT;
389 		goto done;
390 	}
391 	rsasize = RSA_size(rsa);
392 	if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
393 	    siglen == 0 || siglen > rsasize) {
394 		ret = SSH_ERR_INVALID_ARGUMENT;
395 		goto done;
396 	}
397 	if ((decrypted = malloc(rsasize)) == NULL) {
398 		ret = SSH_ERR_ALLOC_FAIL;
399 		goto done;
400 	}
401 	if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
402 	    RSA_PKCS1_PADDING)) < 0) {
403 		ret = SSH_ERR_LIBCRYPTO_ERROR;
404 		goto done;
405 	}
406 	if (len < 0 || (size_t)len != hlen + oidlen) {
407 		ret = SSH_ERR_INVALID_FORMAT;
408 		goto done;
409 	}
410 	oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
411 	hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
412 	if (!oidmatch || !hashmatch) {
413 		ret = SSH_ERR_SIGNATURE_INVALID;
414 		goto done;
415 	}
416 	ret = 0;
417 done:
418 	freezero(decrypted, rsasize);
419 	return ret;
420 }
421