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