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