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