1323124Sdes/* $OpenBSD: ssh-rsa.c,v 1.59 2016/04/21 06:08:02 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
20295367Sdes#ifdef WITH_OPENSSL
21295367Sdes
22162852Sdes#include <sys/types.h>
23162852Sdes
2476259Sgreen#include <openssl/evp.h>
2576259Sgreen#include <openssl/err.h>
2676259Sgreen
27162852Sdes#include <stdarg.h>
28162852Sdes#include <string.h>
29162852Sdes
30295367Sdes#include "sshbuf.h"
3176259Sgreen#include "compat.h"
32295367Sdes#include "ssherr.h"
33295367Sdes#define SSHKEY_INTERNAL
34295367Sdes#include "sshkey.h"
35262566Sdes#include "digest.h"
3676259Sgreen
37295367Sdesstatic int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
38106121Sdes
39296781Sdesstatic const char *
40296781Sdesrsa_hash_alg_ident(int hash_alg)
41296781Sdes{
42296781Sdes	switch (hash_alg) {
43296781Sdes	case SSH_DIGEST_SHA1:
44296781Sdes		return "ssh-rsa";
45296781Sdes	case SSH_DIGEST_SHA256:
46296781Sdes		return "rsa-sha2-256";
47296781Sdes	case SSH_DIGEST_SHA512:
48296781Sdes		return "rsa-sha2-512";
49296781Sdes	}
50296781Sdes	return NULL;
51296781Sdes}
52296781Sdes
53296781Sdesstatic int
54296781Sdesrsa_hash_alg_from_ident(const char *ident)
55296781Sdes{
56296781Sdes	if (strcmp(ident, "ssh-rsa") == 0)
57296781Sdes		return SSH_DIGEST_SHA1;
58296781Sdes	if (strcmp(ident, "rsa-sha2-256") == 0)
59296781Sdes		return SSH_DIGEST_SHA256;
60296781Sdes	if (strcmp(ident, "rsa-sha2-512") == 0)
61296781Sdes		return SSH_DIGEST_SHA512;
62296781Sdes	return -1;
63296781Sdes}
64296781Sdes
65296781Sdesstatic int
66296781Sdesrsa_hash_alg_nid(int type)
67296781Sdes{
68296781Sdes	switch (type) {
69296781Sdes	case SSH_DIGEST_SHA1:
70296781Sdes		return NID_sha1;
71296781Sdes	case SSH_DIGEST_SHA256:
72296781Sdes		return NID_sha256;
73296781Sdes	case SSH_DIGEST_SHA512:
74296781Sdes		return NID_sha512;
75296781Sdes	default:
76296781Sdes		return -1;
77296781Sdes	}
78296781Sdes}
79296781Sdes
8076259Sgreen/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
8176259Sgreenint
82295367Sdesssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
83296781Sdes    const u_char *data, size_t datalen, const char *alg_ident)
8476259Sgreen{
85295367Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
86295367Sdes	size_t slen;
87295367Sdes	u_int dlen, len;
88296781Sdes	int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
89295367Sdes	struct sshbuf *b = NULL;
9076259Sgreen
91295367Sdes	if (lenp != NULL)
92295367Sdes		*lenp = 0;
93295367Sdes	if (sigp != NULL)
94295367Sdes		*sigp = NULL;
95262566Sdes
96296781Sdes	if (alg_ident == NULL || strlen(alg_ident) == 0 ||
97296781Sdes	    strncmp(alg_ident, "ssh-rsa-cert", strlen("ssh-rsa-cert")) == 0)
98296781Sdes		hash_alg = SSH_DIGEST_SHA1;
99296781Sdes	else
100296781Sdes		hash_alg = rsa_hash_alg_from_ident(alg_ident);
101296781Sdes	if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
102296781Sdes	    sshkey_type_plain(key->type) != KEY_RSA ||
103296781Sdes	    BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
104295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
105295367Sdes	slen = RSA_size(key->rsa);
106295367Sdes	if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
107295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
108295367Sdes
109262566Sdes	/* hash the data */
110296781Sdes	nid = rsa_hash_alg_nid(hash_alg);
111295367Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0)
112295367Sdes		return SSH_ERR_INTERNAL_ERROR;
113295367Sdes	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
114295367Sdes	    digest, sizeof(digest))) != 0)
115295367Sdes		goto out;
116295367Sdes
117295367Sdes	if ((sig = malloc(slen)) == NULL) {
118295367Sdes		ret = SSH_ERR_ALLOC_FAIL;
119295367Sdes		goto out;
12076259Sgreen	}
12176259Sgreen
122295367Sdes	if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) {
123295367Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
124295367Sdes		goto out;
12576259Sgreen	}
12676259Sgreen	if (len < slen) {
127295367Sdes		size_t diff = slen - len;
12876259Sgreen		memmove(sig + diff, sig, len);
129264377Sdes		explicit_bzero(sig, diff);
13076259Sgreen	} else if (len > slen) {
131295367Sdes		ret = SSH_ERR_INTERNAL_ERROR;
132295367Sdes		goto out;
13376259Sgreen	}
13476259Sgreen	/* encode signature */
135295367Sdes	if ((b = sshbuf_new()) == NULL) {
136295367Sdes		ret = SSH_ERR_ALLOC_FAIL;
137295367Sdes		goto out;
138295367Sdes	}
139296781Sdes	if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 ||
140295367Sdes	    (ret = sshbuf_put_string(b, sig, slen)) != 0)
141295367Sdes		goto out;
142295367Sdes	len = sshbuf_len(b);
143295367Sdes	if (sigp != NULL) {
144295367Sdes		if ((*sigp = malloc(len)) == NULL) {
145295367Sdes			ret = SSH_ERR_ALLOC_FAIL;
146295367Sdes			goto out;
147295367Sdes		}
148295367Sdes		memcpy(*sigp, sshbuf_ptr(b), len);
149295367Sdes	}
150106121Sdes	if (lenp != NULL)
151106121Sdes		*lenp = len;
152295367Sdes	ret = 0;
153295367Sdes out:
154295367Sdes	explicit_bzero(digest, sizeof(digest));
155295367Sdes	if (sig != NULL) {
156295367Sdes		explicit_bzero(sig, slen);
157295367Sdes		free(sig);
158106121Sdes	}
159296781Sdes	sshbuf_free(b);
160295367Sdes	return ret;
16176259Sgreen}
16276259Sgreen
16376259Sgreenint
164295367Sdesssh_rsa_verify(const struct sshkey *key,
165296781Sdes    const u_char *sig, size_t siglen, const u_char *data, size_t datalen)
16676259Sgreen{
167295367Sdes	char *ktype = NULL;
168295367Sdes	int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
169295367Sdes	size_t len, diff, modlen, dlen;
170295367Sdes	struct sshbuf *b = NULL;
171295367Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
17276259Sgreen
173295367Sdes	if (key == NULL || key->rsa == NULL ||
174295367Sdes	    sshkey_type_plain(key->type) != KEY_RSA ||
175323124Sdes	    BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE ||
176323124Sdes	    sig == NULL || siglen == 0)
177295367Sdes		return SSH_ERR_INVALID_ARGUMENT;
178262566Sdes
179296781Sdes	if ((b = sshbuf_from(sig, siglen)) == NULL)
180295367Sdes		return SSH_ERR_ALLOC_FAIL;
181295367Sdes	if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
182295367Sdes		ret = SSH_ERR_INVALID_FORMAT;
183295367Sdes		goto out;
18492555Sdes	}
185296781Sdes	if ((hash_alg = rsa_hash_alg_from_ident(ktype)) == -1) {
186295367Sdes		ret = SSH_ERR_KEY_TYPE_MISMATCH;
187295367Sdes		goto out;
18876259Sgreen	}
189295367Sdes	if (sshbuf_get_string(b, &sigblob, &len) != 0) {
190295367Sdes		ret = SSH_ERR_INVALID_FORMAT;
191295367Sdes		goto out;
19276259Sgreen	}
193295367Sdes	if (sshbuf_len(b) != 0) {
194295367Sdes		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
195295367Sdes		goto out;
196295367Sdes	}
19798675Sdes	/* RSA_verify expects a signature of RSA_size */
19898675Sdes	modlen = RSA_size(key->rsa);
19998675Sdes	if (len > modlen) {
200295367Sdes		ret = SSH_ERR_KEY_BITS_MISMATCH;
201295367Sdes		goto out;
20298675Sdes	} else if (len < modlen) {
203295367Sdes		diff = modlen - len;
204295367Sdes		osigblob = sigblob;
205295367Sdes		if ((sigblob = realloc(sigblob, modlen)) == NULL) {
206295367Sdes			sigblob = osigblob; /* put it back for clear/free */
207295367Sdes			ret = SSH_ERR_ALLOC_FAIL;
208295367Sdes			goto out;
209295367Sdes		}
21098675Sdes		memmove(sigblob + diff, sigblob, len);
211264377Sdes		explicit_bzero(sigblob, diff);
21298675Sdes		len = modlen;
21398675Sdes	}
214262566Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
215295367Sdes		ret = SSH_ERR_INTERNAL_ERROR;
216295367Sdes		goto out;
21776259Sgreen	}
218295367Sdes	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
219295367Sdes	    digest, sizeof(digest))) != 0)
220295367Sdes		goto out;
22176259Sgreen
222262566Sdes	ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
223262566Sdes	    key->rsa);
224295367Sdes out:
225295367Sdes	if (sigblob != NULL) {
226295367Sdes		explicit_bzero(sigblob, len);
227295367Sdes		free(sigblob);
228295367Sdes	}
229296781Sdes	free(ktype);
230296781Sdes	sshbuf_free(b);
231264377Sdes	explicit_bzero(digest, sizeof(digest));
23276259Sgreen	return ret;
23376259Sgreen}
234106121Sdes
235106121Sdes/*
236106121Sdes * See:
237106121Sdes * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
238106121Sdes * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
239106121Sdes */
240296781Sdes
241106121Sdes/*
242106121Sdes * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
243106121Sdes *	oiw(14) secsig(3) algorithms(2) 26 }
244106121Sdes */
245106121Sdesstatic const u_char id_sha1[] = {
246106121Sdes	0x30, 0x21, /* type Sequence, length 0x21 (33) */
247106121Sdes	0x30, 0x09, /* type Sequence, length 0x09 */
248106121Sdes	0x06, 0x05, /* type OID, length 0x05 */
249106121Sdes	0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
250106121Sdes	0x05, 0x00, /* NULL */
251106121Sdes	0x04, 0x14  /* Octet string, length 0x14 (20), followed by sha1 hash */
252106121Sdes};
253106121Sdes
254296781Sdes/*
255296781Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
256296781Sdes * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
257296781Sdes *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
258296781Sdes *      id-sha256(1) }
259296781Sdes */
260296781Sdesstatic const u_char id_sha256[] = {
261296781Sdes	0x30, 0x31, /* type Sequence, length 0x31 (49) */
262296781Sdes	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
263296781Sdes	0x06, 0x09, /* type OID, length 0x09 */
264296781Sdes	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */
265296781Sdes	0x05, 0x00, /* NULL */
266296781Sdes	0x04, 0x20  /* Octet string, length 0x20 (32), followed by sha256 hash */
267296781Sdes};
268296781Sdes
269296781Sdes/*
270296781Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
271296781Sdes * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
272296781Sdes *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
273296781Sdes *      id-sha256(3) }
274296781Sdes */
275296781Sdesstatic const u_char id_sha512[] = {
276296781Sdes	0x30, 0x51, /* type Sequence, length 0x51 (81) */
277296781Sdes	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
278296781Sdes	0x06, 0x09, /* type OID, length 0x09 */
279296781Sdes	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */
280296781Sdes	0x05, 0x00, /* NULL */
281296781Sdes	0x04, 0x40  /* Octet string, length 0x40 (64), followed by sha512 hash */
282296781Sdes};
283296781Sdes
284106121Sdesstatic int
285296781Sdesrsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp)
286296781Sdes{
287296781Sdes	switch (hash_alg) {
288296781Sdes	case SSH_DIGEST_SHA1:
289296781Sdes		*oidp = id_sha1;
290296781Sdes		*oidlenp = sizeof(id_sha1);
291296781Sdes		break;
292296781Sdes	case SSH_DIGEST_SHA256:
293296781Sdes		*oidp = id_sha256;
294296781Sdes		*oidlenp = sizeof(id_sha256);
295296781Sdes		break;
296296781Sdes	case SSH_DIGEST_SHA512:
297296781Sdes		*oidp = id_sha512;
298296781Sdes		*oidlenp = sizeof(id_sha512);
299296781Sdes		break;
300296781Sdes	default:
301296781Sdes		return SSH_ERR_INVALID_ARGUMENT;
302296781Sdes	}
303296781Sdes	return 0;
304296781Sdes}
305296781Sdes
306296781Sdesstatic int
307295367Sdesopenssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
308295367Sdes    u_char *sigbuf, size_t siglen, RSA *rsa)
309106121Sdes{
310296781Sdes	size_t rsasize = 0, oidlen = 0, hlen = 0;
311296781Sdes	int ret, len, oidmatch, hashmatch;
312106121Sdes	const u_char *oid = NULL;
313106121Sdes	u_char *decrypted = NULL;
314106121Sdes
315296781Sdes	if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0)
316296781Sdes		return ret;
317295367Sdes	ret = SSH_ERR_INTERNAL_ERROR;
318296781Sdes	hlen = ssh_digest_bytes(hash_alg);
319106121Sdes	if (hashlen != hlen) {
320295367Sdes		ret = SSH_ERR_INVALID_ARGUMENT;
321106121Sdes		goto done;
322106121Sdes	}
323106121Sdes	rsasize = RSA_size(rsa);
324295367Sdes	if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
325295367Sdes	    siglen == 0 || siglen > rsasize) {
326295367Sdes		ret = SSH_ERR_INVALID_ARGUMENT;
327106121Sdes		goto done;
328106121Sdes	}
329295367Sdes	if ((decrypted = malloc(rsasize)) == NULL) {
330295367Sdes		ret = SSH_ERR_ALLOC_FAIL;
331295367Sdes		goto done;
332295367Sdes	}
333106121Sdes	if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
334106121Sdes	    RSA_PKCS1_PADDING)) < 0) {
335295367Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
336106121Sdes		goto done;
337106121Sdes	}
338295367Sdes	if (len < 0 || (size_t)len != hlen + oidlen) {
339295367Sdes		ret = SSH_ERR_INVALID_FORMAT;
340106121Sdes		goto done;
341106121Sdes	}
342215116Sdes	oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
343215116Sdes	hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
344295367Sdes	if (!oidmatch || !hashmatch) {
345295367Sdes		ret = SSH_ERR_SIGNATURE_INVALID;
346106121Sdes		goto done;
347106121Sdes	}
348295367Sdes	ret = 0;
349295367Sdesdone:
350295367Sdes	if (decrypted) {
351295367Sdes		explicit_bzero(decrypted, rsasize);
352295367Sdes		free(decrypted);
353106121Sdes	}
354106121Sdes	return ret;
355106121Sdes}
356295367Sdes#endif /* WITH_OPENSSL */
357