1323134Sdes/* $OpenBSD: ssh-rsa.c,v 1.60 2016/09/12 23:39:34 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
20294332Sdes#ifdef WITH_OPENSSL
21294332Sdes
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
30294328Sdes#include "sshbuf.h"
3176259Sgreen#include "compat.h"
32294328Sdes#include "ssherr.h"
33294328Sdes#define SSHKEY_INTERNAL
34294328Sdes#include "sshkey.h"
35261320Sdes#include "digest.h"
3676259Sgreen
37294328Sdesstatic int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
38106121Sdes
39296633Sdesstatic const char *
40296633Sdesrsa_hash_alg_ident(int hash_alg)
41296633Sdes{
42296633Sdes	switch (hash_alg) {
43296633Sdes	case SSH_DIGEST_SHA1:
44296633Sdes		return "ssh-rsa";
45296633Sdes	case SSH_DIGEST_SHA256:
46296633Sdes		return "rsa-sha2-256";
47296633Sdes	case SSH_DIGEST_SHA512:
48296633Sdes		return "rsa-sha2-512";
49296633Sdes	}
50296633Sdes	return NULL;
51296633Sdes}
52296633Sdes
53296633Sdesstatic int
54296633Sdesrsa_hash_alg_from_ident(const char *ident)
55296633Sdes{
56323134Sdes	if (strcmp(ident, "ssh-rsa") == 0 ||
57323134Sdes	    strcmp(ident, "ssh-rsa-cert-v01@openssh.com") == 0)
58296633Sdes		return SSH_DIGEST_SHA1;
59296633Sdes	if (strcmp(ident, "rsa-sha2-256") == 0)
60296633Sdes		return SSH_DIGEST_SHA256;
61296633Sdes	if (strcmp(ident, "rsa-sha2-512") == 0)
62296633Sdes		return SSH_DIGEST_SHA512;
63296633Sdes	return -1;
64296633Sdes}
65296633Sdes
66296633Sdesstatic int
67296633Sdesrsa_hash_alg_nid(int type)
68296633Sdes{
69296633Sdes	switch (type) {
70296633Sdes	case SSH_DIGEST_SHA1:
71296633Sdes		return NID_sha1;
72296633Sdes	case SSH_DIGEST_SHA256:
73296633Sdes		return NID_sha256;
74296633Sdes	case SSH_DIGEST_SHA512:
75296633Sdes		return NID_sha512;
76296633Sdes	default:
77296633Sdes		return -1;
78296633Sdes	}
79296633Sdes}
80296633Sdes
8176259Sgreen/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
8276259Sgreenint
83294328Sdesssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
84296633Sdes    const u_char *data, size_t datalen, const char *alg_ident)
8576259Sgreen{
86294328Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
87294328Sdes	size_t slen;
88294328Sdes	u_int dlen, len;
89296633Sdes	int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
90294328Sdes	struct sshbuf *b = NULL;
9176259Sgreen
92294328Sdes	if (lenp != NULL)
93294328Sdes		*lenp = 0;
94294328Sdes	if (sigp != NULL)
95294328Sdes		*sigp = NULL;
96261320Sdes
97323134Sdes	if (alg_ident == NULL || strlen(alg_ident) == 0)
98296633Sdes		hash_alg = SSH_DIGEST_SHA1;
99296633Sdes	else
100296633Sdes		hash_alg = rsa_hash_alg_from_ident(alg_ident);
101296633Sdes	if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
102296633Sdes	    sshkey_type_plain(key->type) != KEY_RSA ||
103296633Sdes	    BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
104294328Sdes		return SSH_ERR_INVALID_ARGUMENT;
105294328Sdes	slen = RSA_size(key->rsa);
106294328Sdes	if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
107294328Sdes		return SSH_ERR_INVALID_ARGUMENT;
108294328Sdes
109261320Sdes	/* hash the data */
110296633Sdes	nid = rsa_hash_alg_nid(hash_alg);
111294328Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0)
112294328Sdes		return SSH_ERR_INTERNAL_ERROR;
113294328Sdes	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
114294328Sdes	    digest, sizeof(digest))) != 0)
115294328Sdes		goto out;
116294328Sdes
117294328Sdes	if ((sig = malloc(slen)) == NULL) {
118294328Sdes		ret = SSH_ERR_ALLOC_FAIL;
119294328Sdes		goto out;
12076259Sgreen	}
12176259Sgreen
122294328Sdes	if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) {
123294328Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
124294328Sdes		goto out;
12576259Sgreen	}
12676259Sgreen	if (len < slen) {
127294328Sdes		size_t diff = slen - len;
12876259Sgreen		memmove(sig + diff, sig, len);
129263712Sdes		explicit_bzero(sig, diff);
13076259Sgreen	} else if (len > slen) {
131294328Sdes		ret = SSH_ERR_INTERNAL_ERROR;
132294328Sdes		goto out;
13376259Sgreen	}
13476259Sgreen	/* encode signature */
135294328Sdes	if ((b = sshbuf_new()) == NULL) {
136294328Sdes		ret = SSH_ERR_ALLOC_FAIL;
137294328Sdes		goto out;
138294328Sdes	}
139296633Sdes	if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 ||
140294328Sdes	    (ret = sshbuf_put_string(b, sig, slen)) != 0)
141294328Sdes		goto out;
142294328Sdes	len = sshbuf_len(b);
143294328Sdes	if (sigp != NULL) {
144294328Sdes		if ((*sigp = malloc(len)) == NULL) {
145294328Sdes			ret = SSH_ERR_ALLOC_FAIL;
146294328Sdes			goto out;
147294328Sdes		}
148294328Sdes		memcpy(*sigp, sshbuf_ptr(b), len);
149294328Sdes	}
150106121Sdes	if (lenp != NULL)
151106121Sdes		*lenp = len;
152294328Sdes	ret = 0;
153294328Sdes out:
154294328Sdes	explicit_bzero(digest, sizeof(digest));
155294328Sdes	if (sig != NULL) {
156294328Sdes		explicit_bzero(sig, slen);
157294328Sdes		free(sig);
158106121Sdes	}
159296633Sdes	sshbuf_free(b);
160294336Sdes	return ret;
16176259Sgreen}
16276259Sgreen
16376259Sgreenint
164294328Sdesssh_rsa_verify(const struct sshkey *key,
165296633Sdes    const u_char *sig, size_t siglen, const u_char *data, size_t datalen)
16676259Sgreen{
167294328Sdes	char *ktype = NULL;
168294328Sdes	int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
169294328Sdes	size_t len, diff, modlen, dlen;
170294328Sdes	struct sshbuf *b = NULL;
171294328Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
17276259Sgreen
173294328Sdes	if (key == NULL || key->rsa == NULL ||
174294328Sdes	    sshkey_type_plain(key->type) != KEY_RSA ||
175323129Sdes	    BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE ||
176323129Sdes	    sig == NULL || siglen == 0)
177294328Sdes		return SSH_ERR_INVALID_ARGUMENT;
178261320Sdes
179296633Sdes	if ((b = sshbuf_from(sig, siglen)) == NULL)
180294328Sdes		return SSH_ERR_ALLOC_FAIL;
181294328Sdes	if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
182294328Sdes		ret = SSH_ERR_INVALID_FORMAT;
183294328Sdes		goto out;
18492555Sdes	}
185296633Sdes	if ((hash_alg = rsa_hash_alg_from_ident(ktype)) == -1) {
186294328Sdes		ret = SSH_ERR_KEY_TYPE_MISMATCH;
187294328Sdes		goto out;
18876259Sgreen	}
189294328Sdes	if (sshbuf_get_string(b, &sigblob, &len) != 0) {
190294328Sdes		ret = SSH_ERR_INVALID_FORMAT;
191294328Sdes		goto out;
19276259Sgreen	}
193294328Sdes	if (sshbuf_len(b) != 0) {
194294328Sdes		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
195294328Sdes		goto out;
196294328Sdes	}
19798675Sdes	/* RSA_verify expects a signature of RSA_size */
19898675Sdes	modlen = RSA_size(key->rsa);
19998675Sdes	if (len > modlen) {
200294328Sdes		ret = SSH_ERR_KEY_BITS_MISMATCH;
201294328Sdes		goto out;
20298675Sdes	} else if (len < modlen) {
203294328Sdes		diff = modlen - len;
204294328Sdes		osigblob = sigblob;
205294328Sdes		if ((sigblob = realloc(sigblob, modlen)) == NULL) {
206294328Sdes			sigblob = osigblob; /* put it back for clear/free */
207294328Sdes			ret = SSH_ERR_ALLOC_FAIL;
208294328Sdes			goto out;
209294328Sdes		}
21098675Sdes		memmove(sigblob + diff, sigblob, len);
211263712Sdes		explicit_bzero(sigblob, diff);
21298675Sdes		len = modlen;
21398675Sdes	}
214261320Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
215294328Sdes		ret = SSH_ERR_INTERNAL_ERROR;
216294328Sdes		goto out;
21776259Sgreen	}
218294328Sdes	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
219294328Sdes	    digest, sizeof(digest))) != 0)
220294328Sdes		goto out;
22176259Sgreen
222261320Sdes	ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
223261320Sdes	    key->rsa);
224294328Sdes out:
225294328Sdes	if (sigblob != NULL) {
226294328Sdes		explicit_bzero(sigblob, len);
227294328Sdes		free(sigblob);
228294328Sdes	}
229296633Sdes	free(ktype);
230296633Sdes	sshbuf_free(b);
231263712Sdes	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 */
240296633Sdes
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
254296633Sdes/*
255296633Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
256296633Sdes * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
257296633Sdes *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
258296633Sdes *      id-sha256(1) }
259296633Sdes */
260296633Sdesstatic const u_char id_sha256[] = {
261296633Sdes	0x30, 0x31, /* type Sequence, length 0x31 (49) */
262296633Sdes	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
263296633Sdes	0x06, 0x09, /* type OID, length 0x09 */
264296633Sdes	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */
265296633Sdes	0x05, 0x00, /* NULL */
266296633Sdes	0x04, 0x20  /* Octet string, length 0x20 (32), followed by sha256 hash */
267296633Sdes};
268296633Sdes
269296633Sdes/*
270296633Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
271296633Sdes * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
272296633Sdes *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
273296633Sdes *      id-sha256(3) }
274296633Sdes */
275296633Sdesstatic const u_char id_sha512[] = {
276296633Sdes	0x30, 0x51, /* type Sequence, length 0x51 (81) */
277296633Sdes	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
278296633Sdes	0x06, 0x09, /* type OID, length 0x09 */
279296633Sdes	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */
280296633Sdes	0x05, 0x00, /* NULL */
281296633Sdes	0x04, 0x40  /* Octet string, length 0x40 (64), followed by sha512 hash */
282296633Sdes};
283296633Sdes
284106121Sdesstatic int
285296633Sdesrsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp)
286296633Sdes{
287296633Sdes	switch (hash_alg) {
288296633Sdes	case SSH_DIGEST_SHA1:
289296633Sdes		*oidp = id_sha1;
290296633Sdes		*oidlenp = sizeof(id_sha1);
291296633Sdes		break;
292296633Sdes	case SSH_DIGEST_SHA256:
293296633Sdes		*oidp = id_sha256;
294296633Sdes		*oidlenp = sizeof(id_sha256);
295296633Sdes		break;
296296633Sdes	case SSH_DIGEST_SHA512:
297296633Sdes		*oidp = id_sha512;
298296633Sdes		*oidlenp = sizeof(id_sha512);
299296633Sdes		break;
300296633Sdes	default:
301296633Sdes		return SSH_ERR_INVALID_ARGUMENT;
302296633Sdes	}
303296633Sdes	return 0;
304296633Sdes}
305296633Sdes
306296633Sdesstatic int
307294328Sdesopenssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
308294328Sdes    u_char *sigbuf, size_t siglen, RSA *rsa)
309106121Sdes{
310296633Sdes	size_t rsasize = 0, oidlen = 0, hlen = 0;
311296633Sdes	int ret, len, oidmatch, hashmatch;
312106121Sdes	const u_char *oid = NULL;
313106121Sdes	u_char *decrypted = NULL;
314106121Sdes
315296633Sdes	if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0)
316296633Sdes		return ret;
317294328Sdes	ret = SSH_ERR_INTERNAL_ERROR;
318296633Sdes	hlen = ssh_digest_bytes(hash_alg);
319106121Sdes	if (hashlen != hlen) {
320294328Sdes		ret = SSH_ERR_INVALID_ARGUMENT;
321106121Sdes		goto done;
322106121Sdes	}
323106121Sdes	rsasize = RSA_size(rsa);
324294328Sdes	if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
325294328Sdes	    siglen == 0 || siglen > rsasize) {
326294328Sdes		ret = SSH_ERR_INVALID_ARGUMENT;
327106121Sdes		goto done;
328106121Sdes	}
329294328Sdes	if ((decrypted = malloc(rsasize)) == NULL) {
330294328Sdes		ret = SSH_ERR_ALLOC_FAIL;
331294328Sdes		goto done;
332294328Sdes	}
333106121Sdes	if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
334106121Sdes	    RSA_PKCS1_PADDING)) < 0) {
335294328Sdes		ret = SSH_ERR_LIBCRYPTO_ERROR;
336106121Sdes		goto done;
337106121Sdes	}
338294328Sdes	if (len < 0 || (size_t)len != hlen + oidlen) {
339294328Sdes		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;
344294328Sdes	if (!oidmatch || !hashmatch) {
345294328Sdes		ret = SSH_ERR_SIGNATURE_INVALID;
346106121Sdes		goto done;
347106121Sdes	}
348294328Sdes	ret = 0;
349294328Sdesdone:
350294328Sdes	if (decrypted) {
351294328Sdes		explicit_bzero(decrypted, rsasize);
352294328Sdes		free(decrypted);
353106121Sdes	}
354106121Sdes	return ret;
355106121Sdes}
356294332Sdes#endif /* WITH_OPENSSL */
357