1 /* $OpenBSD: ssh-rsa.c,v 1.53 2015/06/15 01:32:50 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 #ifdef WITH_OPENSSL 21 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 30 #include "sshbuf.h" 31 #include "compat.h" 32 #include "ssherr.h" 33 #define SSHKEY_INTERNAL 34 #include "sshkey.h" 35 #include "digest.h" 36 37 static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); 38 39 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ 40 int 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) 43 { 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; 50 51 if (lenp != NULL) 52 *lenp = 0; 53 if (sigp != NULL) 54 *sigp = NULL; 55 56 if (key == NULL || key->rsa == NULL || 57 sshkey_type_plain(key->type) != KEY_RSA) 58 return SSH_ERR_INVALID_ARGUMENT; 59 slen = RSA_size(key->rsa); 60 if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) 61 return SSH_ERR_INVALID_ARGUMENT; 62 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; 71 72 if ((sig = malloc(slen)) == NULL) { 73 ret = SSH_ERR_ALLOC_FAIL; 74 goto out; 75 } 76 77 if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { 78 ret = SSH_ERR_LIBCRYPTO_ERROR; 79 goto out; 80 } 81 if (len < slen) { 82 size_t diff = slen - len; 83 memmove(sig + diff, sig, len); 84 explicit_bzero(sig, diff); 85 } else if (len > slen) { 86 ret = SSH_ERR_INTERNAL_ERROR; 87 goto out; 88 } 89 /* encode signature */ 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 } 105 if (lenp != NULL) 106 *lenp = len; 107 ret = 0; 108 out: 109 explicit_bzero(digest, sizeof(digest)); 110 if (sig != NULL) { 111 explicit_bzero(sig, slen); 112 free(sig); 113 } 114 if (b != NULL) 115 sshbuf_free(b); 116 return ret; 117 } 118 119 int 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) 123 { 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; 129 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; 140 } 141 if (strcmp("ssh-rsa", ktype) != 0) { 142 ret = SSH_ERR_KEY_TYPE_MISMATCH; 143 goto out; 144 } 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; 152 } 153 /* RSA_verify expects a signature of RSA_size */ 154 modlen = RSA_size(key->rsa); 155 if (len > modlen) { 156 ret = SSH_ERR_KEY_BITS_MISMATCH; 157 goto out; 158 } else if (len < modlen) { 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 } 166 memmove(sigblob + diff, sigblob, len); 167 explicit_bzero(sigblob, diff); 168 len = modlen; 169 } 170 hash_alg = SSH_DIGEST_SHA1; 171 if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { 172 ret = SSH_ERR_INTERNAL_ERROR; 173 goto out; 174 } 175 if ((ret = ssh_digest_memory(hash_alg, data, datalen, 176 digest, sizeof(digest))) != 0) 177 goto out; 178 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)); 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 }; 211 212 static int 213 openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, 214 u_char *sigbuf, size_t siglen, RSA *rsa) 215 { 216 size_t ret, rsasize = 0, oidlen = 0, hlen = 0; 217 int len, oidmatch, hashmatch; 218 const u_char *oid = NULL; 219 u_char *decrypted = NULL; 220 221 ret = SSH_ERR_INTERNAL_ERROR; 222 switch (hash_alg) { 223 case SSH_DIGEST_SHA1: 224 oid = id_sha1; 225 oidlen = sizeof(id_sha1); 226 hlen = 20; 227 break; 228 default: 229 goto done; 230 } 231 if (hashlen != hlen) { 232 ret = SSH_ERR_INVALID_ARGUMENT; 233 goto done; 234 } 235 rsasize = RSA_size(rsa); 236 if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || 237 siglen == 0 || siglen > rsasize) { 238 ret = SSH_ERR_INVALID_ARGUMENT; 239 goto done; 240 } 241 if ((decrypted = malloc(rsasize)) == NULL) { 242 ret = SSH_ERR_ALLOC_FAIL; 243 goto done; 244 } 245 if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, 246 RSA_PKCS1_PADDING)) < 0) { 247 ret = SSH_ERR_LIBCRYPTO_ERROR; 248 goto done; 249 } 250 if (len < 0 || (size_t)len != hlen + oidlen) { 251 ret = SSH_ERR_INVALID_FORMAT; 252 goto done; 253 } 254 oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; 255 hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; 256 if (!oidmatch || !hashmatch) { 257 ret = SSH_ERR_SIGNATURE_INVALID; 258 goto done; 259 } 260 ret = 0; 261 done: 262 if (decrypted) { 263 explicit_bzero(decrypted, rsasize); 264 free(decrypted); 265 } 266 return ret; 267 } 268 #endif /* WITH_OPENSSL */ 269