Annotation of src/usr.bin/ssh/ssh-rsa.c, Revision 1.52
1.52 ! djm 1: /* $OpenBSD: ssh-rsa.c,v 1.51 2014/02/02 03:44:31 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.1 markus 34: /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
35: int
1.52 ! djm 36: ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
! 37: const u_char *data, size_t datalen, u_int compat)
1.1 markus 38: {
1.50 djm 39: int hash_alg;
1.52 ! djm 40: u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
! 41: size_t slen;
! 42: u_int dlen, len;
! 43: int nid, ret = SSH_ERR_INTERNAL_ERROR;
! 44: struct sshbuf *b = NULL;
! 45:
! 46: if (lenp != NULL)
! 47: *lenp = 0;
! 48: if (sigp != NULL)
! 49: *sigp = NULL;
! 50:
! 51: if (key == NULL || key->rsa == NULL ||
! 52: sshkey_type_plain(key->type) != KEY_RSA)
! 53: return SSH_ERR_INVALID_ARGUMENT;
! 54: slen = RSA_size(key->rsa);
! 55: if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
! 56: return SSH_ERR_INVALID_ARGUMENT;
1.47 djm 57:
1.50 djm 58: /* hash the data */
59: hash_alg = SSH_DIGEST_SHA1;
1.49 djm 60: nid = NID_sha1;
1.52 ! djm 61: if ((dlen = ssh_digest_bytes(hash_alg)) == 0)
! 62: return SSH_ERR_INTERNAL_ERROR;
! 63: if ((ret = ssh_digest_memory(hash_alg, data, datalen,
! 64: digest, sizeof(digest))) != 0)
! 65: goto out;
! 66:
! 67: if ((sig = malloc(slen)) == NULL) {
! 68: ret = SSH_ERR_ALLOC_FAIL;
! 69: goto out;
1.50 djm 70: }
1.1 markus 71:
1.52 ! djm 72: if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) {
! 73: ret = SSH_ERR_LIBCRYPTO_ERROR;
! 74: goto out;
1.1 markus 75: }
76: if (len < slen) {
1.52 ! djm 77: size_t diff = slen - len;
1.1 markus 78: memmove(sig + diff, sig, len);
1.51 djm 79: explicit_bzero(sig, diff);
1.1 markus 80: } else if (len > slen) {
1.52 ! djm 81: ret = SSH_ERR_INTERNAL_ERROR;
! 82: goto out;
1.1 markus 83: }
84: /* encode signature */
1.52 ! djm 85: if ((b = sshbuf_new()) == NULL) {
! 86: ret = SSH_ERR_ALLOC_FAIL;
! 87: goto out;
! 88: }
! 89: if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 ||
! 90: (ret = sshbuf_put_string(b, sig, slen)) != 0)
! 91: goto out;
! 92: len = sshbuf_len(b);
! 93: if (sigp != NULL) {
! 94: if ((*sigp = malloc(len)) == NULL) {
! 95: ret = SSH_ERR_ALLOC_FAIL;
! 96: goto out;
! 97: }
! 98: memcpy(*sigp, sshbuf_ptr(b), len);
! 99: }
1.23 markus 100: if (lenp != NULL)
101: *lenp = len;
1.52 ! djm 102: ret = 0;
! 103: out:
! 104: explicit_bzero(digest, sizeof(digest));
! 105: if (sig != NULL) {
! 106: explicit_bzero(sig, slen);
! 107: free(sig);
1.23 markus 108: }
1.52 ! djm 109: if (b != NULL)
! 110: sshbuf_free(b);
1.1 markus 111: return 0;
112: }
113:
114: int
1.52 ! djm 115: ssh_rsa_verify(const struct sshkey *key,
! 116: const u_char *signature, size_t signaturelen,
! 117: const u_char *data, size_t datalen, u_int compat)
1.1 markus 118: {
1.52 ! djm 119: char *ktype = NULL;
! 120: int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
! 121: size_t len, diff, modlen, dlen;
! 122: struct sshbuf *b = NULL;
! 123: u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
! 124:
! 125: if (key == NULL || key->rsa == NULL ||
! 126: sshkey_type_plain(key->type) != KEY_RSA ||
! 127: BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
! 128: return SSH_ERR_INVALID_ARGUMENT;
! 129:
! 130: if ((b = sshbuf_from(signature, signaturelen)) == NULL)
! 131: return SSH_ERR_ALLOC_FAIL;
! 132: if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
! 133: ret = SSH_ERR_INVALID_FORMAT;
! 134: goto out;
! 135: }
1.1 markus 136: if (strcmp("ssh-rsa", ktype) != 0) {
1.52 ! djm 137: ret = SSH_ERR_KEY_TYPE_MISMATCH;
! 138: goto out;
1.1 markus 139: }
1.52 ! djm 140: if (sshbuf_get_string(b, &sigblob, &len) != 0) {
! 141: ret = SSH_ERR_INVALID_FORMAT;
! 142: goto out;
! 143: }
! 144: if (sshbuf_len(b) != 0) {
! 145: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
! 146: goto out;
1.19 markus 147: }
148: /* RSA_verify expects a signature of RSA_size */
149: modlen = RSA_size(key->rsa);
150: if (len > modlen) {
1.52 ! djm 151: ret = SSH_ERR_KEY_BITS_MISMATCH;
! 152: goto out;
1.19 markus 153: } else if (len < modlen) {
1.52 ! djm 154: diff = modlen - len;
! 155: osigblob = sigblob;
! 156: if ((sigblob = realloc(sigblob, modlen)) == NULL) {
! 157: sigblob = osigblob; /* put it back for clear/free */
! 158: ret = SSH_ERR_ALLOC_FAIL;
! 159: goto out;
! 160: }
1.19 markus 161: memmove(sigblob + diff, sigblob, len);
1.51 djm 162: explicit_bzero(sigblob, diff);
1.19 markus 163: len = modlen;
1.1 markus 164: }
1.50 djm 165: hash_alg = SSH_DIGEST_SHA1;
166: if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
1.52 ! djm 167: ret = SSH_ERR_INTERNAL_ERROR;
! 168: goto out;
1.7 markus 169: }
1.52 ! djm 170: if ((ret = ssh_digest_memory(hash_alg, data, datalen,
! 171: digest, sizeof(digest))) != 0)
! 172: goto out;
1.1 markus 173:
1.50 djm 174: ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
175: key->rsa);
1.52 ! djm 176: out:
! 177: if (sigblob != NULL) {
! 178: explicit_bzero(sigblob, len);
! 179: free(sigblob);
! 180: }
! 181: if (ktype != NULL)
! 182: free(ktype);
! 183: if (b != NULL)
! 184: sshbuf_free(b);
1.51 djm 185: explicit_bzero(digest, sizeof(digest));
1.25 markus 186: return ret;
187: }
188:
189: /*
190: * See:
191: * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
192: * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
193: */
194: /*
195: * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
196: * oiw(14) secsig(3) algorithms(2) 26 }
197: */
198: static const u_char id_sha1[] = {
199: 0x30, 0x21, /* type Sequence, length 0x21 (33) */
200: 0x30, 0x09, /* type Sequence, length 0x09 */
201: 0x06, 0x05, /* type OID, length 0x05 */
202: 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
203: 0x05, 0x00, /* NULL */
204: 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */
205: };
206:
207: static int
1.52 ! djm 208: openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
! 209: u_char *sigbuf, size_t siglen, RSA *rsa)
1.25 markus 210: {
1.52 ! djm 211: size_t ret, rsasize = 0, oidlen = 0, hlen = 0;
1.44 djm 212: int len, oidmatch, hashmatch;
1.25 markus 213: const u_char *oid = NULL;
214: u_char *decrypted = NULL;
215:
1.52 ! djm 216: ret = SSH_ERR_INTERNAL_ERROR;
1.50 djm 217: switch (hash_alg) {
218: case SSH_DIGEST_SHA1:
1.25 markus 219: oid = id_sha1;
220: oidlen = sizeof(id_sha1);
221: hlen = 20;
222: break;
223: default:
224: goto done;
225: }
226: if (hashlen != hlen) {
1.52 ! djm 227: ret = SSH_ERR_INVALID_ARGUMENT;
1.25 markus 228: goto done;
229: }
230: rsasize = RSA_size(rsa);
1.52 ! djm 231: if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
! 232: siglen == 0 || siglen > rsasize) {
! 233: ret = SSH_ERR_INVALID_ARGUMENT;
! 234: goto done;
! 235: }
! 236: if ((decrypted = malloc(rsasize)) == NULL) {
! 237: ret = SSH_ERR_ALLOC_FAIL;
1.25 markus 238: goto done;
239: }
240: if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
241: RSA_PKCS1_PADDING)) < 0) {
1.52 ! djm 242: ret = SSH_ERR_LIBCRYPTO_ERROR;
1.25 markus 243: goto done;
244: }
1.52 ! djm 245: if (len < 0 || (size_t)len != hlen + oidlen) {
! 246: ret = SSH_ERR_INVALID_FORMAT;
1.25 markus 247: goto done;
248: }
1.44 djm 249: oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
250: hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
1.52 ! djm 251: if (!oidmatch || !hashmatch) {
! 252: ret = SSH_ERR_SIGNATURE_INVALID;
1.25 markus 253: goto done;
254: }
1.52 ! djm 255: ret = 0;
! 256: done:
! 257: if (decrypted) {
! 258: explicit_bzero(decrypted, rsasize);
! 259: free(decrypted);
1.25 markus 260: }
1.1 markus 261: return ret;
262: }