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 <sys/types.h> 19 20 #include <openssl/evp.h> 21 #include <openssl/err.h> 22 23 #include <string.h> 24 25 #include "sshbuf.h" 26 #include "compat.h" 27 #include "ssherr.h" 28 #define SSHKEY_INTERNAL 29 #include "sshkey.h" 30 #include "digest.h" 31 32 static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); 33 34 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ 35 int 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) 38 { 39 int hash_alg; 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; 57 58 /* hash the data */ 59 hash_alg = SSH_DIGEST_SHA1; 60 nid = NID_sha1; 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; 70 } 71 72 if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { 73 ret = SSH_ERR_LIBCRYPTO_ERROR; 74 goto out; 75 } 76 if (len < slen) { 77 size_t diff = slen - len; 78 memmove(sig + diff, sig, len); 79 explicit_bzero(sig, diff); 80 } else if (len > slen) { 81 ret = SSH_ERR_INTERNAL_ERROR; 82 goto out; 83 } 84 /* encode signature */ 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 } 100 if (lenp != NULL) 101 *lenp = len; 102 ret = 0; 103 out: 104 explicit_bzero(digest, sizeof(digest)); 105 if (sig != NULL) { 106 explicit_bzero(sig, slen); 107 free(sig); 108 } 109 if (b != NULL) 110 sshbuf_free(b); 111 return 0; 112 } 113 114 int 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) 118 { 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 } 136 if (strcmp("ssh-rsa", ktype) != 0) { 137 ret = SSH_ERR_KEY_TYPE_MISMATCH; 138 goto out; 139 } 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; 147 } 148 /* RSA_verify expects a signature of RSA_size */ 149 modlen = RSA_size(key->rsa); 150 if (len > modlen) { 151 ret = SSH_ERR_KEY_BITS_MISMATCH; 152 goto out; 153 } else if (len < modlen) { 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 } 161 memmove(sigblob + diff, sigblob, len); 162 explicit_bzero(sigblob, diff); 163 len = modlen; 164 } 165 hash_alg = SSH_DIGEST_SHA1; 166 if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { 167 ret = SSH_ERR_INTERNAL_ERROR; 168 goto out; 169 } 170 if ((ret = ssh_digest_memory(hash_alg, data, datalen, 171 digest, sizeof(digest))) != 0) 172 goto out; 173 174 ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, 175 key->rsa); 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); 185 explicit_bzero(digest, sizeof(digest)); 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 208 openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, 209 u_char *sigbuf, size_t siglen, RSA *rsa) 210 { 211 size_t ret, rsasize = 0, oidlen = 0, hlen = 0; 212 int len, oidmatch, hashmatch; 213 const u_char *oid = NULL; 214 u_char *decrypted = NULL; 215 216 ret = SSH_ERR_INTERNAL_ERROR; 217 switch (hash_alg) { 218 case SSH_DIGEST_SHA1: 219 oid = id_sha1; 220 oidlen = sizeof(id_sha1); 221 hlen = 20; 222 break; 223 default: 224 goto done; 225 } 226 if (hashlen != hlen) { 227 ret = SSH_ERR_INVALID_ARGUMENT; 228 goto done; 229 } 230 rsasize = RSA_size(rsa); 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; 238 goto done; 239 } 240 if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, 241 RSA_PKCS1_PADDING)) < 0) { 242 ret = SSH_ERR_LIBCRYPTO_ERROR; 243 goto done; 244 } 245 if (len < 0 || (size_t)len != hlen + oidlen) { 246 ret = SSH_ERR_INVALID_FORMAT; 247 goto done; 248 } 249 oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; 250 hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; 251 if (!oidmatch || !hashmatch) { 252 ret = SSH_ERR_SIGNATURE_INVALID; 253 goto done; 254 } 255 ret = 0; 256 done: 257 if (decrypted) { 258 explicit_bzero(decrypted, rsasize); 259 free(decrypted); 260 } 261 return ret; 262 } 263