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