1 /* $NetBSD: ssh-rsa.c,v 1.10 2016/08/02 13:45:12 christos Exp $ */ 2 /* $OpenBSD: ssh-rsa.c,v 1.59 2016/04/21 06:08:02 djm Exp $ */ 3 /* 4 * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "includes.h" 20 __RCSID("$NetBSD: ssh-rsa.c,v 1.10 2016/08/02 13:45:12 christos Exp $"); 21 #include <sys/types.h> 22 23 #include <openssl/evp.h> 24 #include <openssl/err.h> 25 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 static const char * 38 rsa_hash_alg_ident(int hash_alg) 39 { 40 switch (hash_alg) { 41 case SSH_DIGEST_SHA1: 42 return "ssh-rsa"; 43 case SSH_DIGEST_SHA256: 44 return "rsa-sha2-256"; 45 case SSH_DIGEST_SHA512: 46 return "rsa-sha2-512"; 47 } 48 return NULL; 49 } 50 51 static int 52 rsa_hash_alg_from_ident(const char *ident) 53 { 54 if (strcmp(ident, "ssh-rsa") == 0) 55 return SSH_DIGEST_SHA1; 56 if (strcmp(ident, "rsa-sha2-256") == 0) 57 return SSH_DIGEST_SHA256; 58 if (strcmp(ident, "rsa-sha2-512") == 0) 59 return SSH_DIGEST_SHA512; 60 return -1; 61 } 62 63 static int 64 rsa_hash_alg_nid(int type) 65 { 66 switch (type) { 67 case SSH_DIGEST_SHA1: 68 return NID_sha1; 69 case SSH_DIGEST_SHA256: 70 return NID_sha256; 71 case SSH_DIGEST_SHA512: 72 return NID_sha512; 73 default: 74 return -1; 75 } 76 } 77 78 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ 79 int 80 ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 81 const u_char *data, size_t datalen, const char *alg_ident) 82 { 83 u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; 84 size_t slen; 85 u_int dlen, len; 86 int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 87 struct sshbuf *b = NULL; 88 89 if (lenp != NULL) 90 *lenp = 0; 91 if (sigp != NULL) 92 *sigp = NULL; 93 94 if (alg_ident == NULL || strlen(alg_ident) == 0 || 95 strncmp(alg_ident, "ssh-rsa-cert", strlen("ssh-rsa-cert")) == 0) 96 hash_alg = SSH_DIGEST_SHA1; 97 else 98 hash_alg = rsa_hash_alg_from_ident(alg_ident); 99 if (key == NULL || key->rsa == NULL || hash_alg == -1 || 100 sshkey_type_plain(key->type) != KEY_RSA || 101 BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) 102 return SSH_ERR_INVALID_ARGUMENT; 103 slen = RSA_size(key->rsa); 104 if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) 105 return SSH_ERR_INVALID_ARGUMENT; 106 107 /* hash the data */ 108 nid = rsa_hash_alg_nid(hash_alg); 109 if ((dlen = ssh_digest_bytes(hash_alg)) == 0) 110 return SSH_ERR_INTERNAL_ERROR; 111 if ((ret = ssh_digest_memory(hash_alg, data, datalen, 112 digest, sizeof(digest))) != 0) 113 goto out; 114 115 if ((sig = malloc(slen)) == NULL) { 116 ret = SSH_ERR_ALLOC_FAIL; 117 goto out; 118 } 119 120 if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { 121 ret = SSH_ERR_LIBCRYPTO_ERROR; 122 goto out; 123 } 124 if (len < slen) { 125 size_t diff = slen - len; 126 memmove(sig + diff, sig, len); 127 explicit_bzero(sig, diff); 128 } else if (len > slen) { 129 ret = SSH_ERR_INTERNAL_ERROR; 130 goto out; 131 } 132 /* encode signature */ 133 if ((b = sshbuf_new()) == NULL) { 134 ret = SSH_ERR_ALLOC_FAIL; 135 goto out; 136 } 137 if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 || 138 (ret = sshbuf_put_string(b, sig, slen)) != 0) 139 goto out; 140 len = sshbuf_len(b); 141 if (sigp != NULL) { 142 if ((*sigp = malloc(len)) == NULL) { 143 ret = SSH_ERR_ALLOC_FAIL; 144 goto out; 145 } 146 memcpy(*sigp, sshbuf_ptr(b), len); 147 } 148 if (lenp != NULL) 149 *lenp = len; 150 ret = 0; 151 out: 152 explicit_bzero(digest, sizeof(digest)); 153 if (sig != NULL) { 154 explicit_bzero(sig, slen); 155 free(sig); 156 } 157 sshbuf_free(b); 158 return ret; 159 } 160 161 int 162 ssh_rsa_verify(const struct sshkey *key, 163 const u_char *sig, size_t siglen, const u_char *data, size_t datalen) 164 { 165 char *ktype = NULL; 166 int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 167 size_t len, diff, modlen, dlen; 168 struct sshbuf *b = NULL; 169 u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; 170 171 if (key == NULL || key->rsa == NULL || 172 sshkey_type_plain(key->type) != KEY_RSA || 173 BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE || 174 sig == NULL || siglen == 0) 175 return SSH_ERR_INVALID_ARGUMENT; 176 177 if ((b = sshbuf_from(sig, siglen)) == NULL) 178 return SSH_ERR_ALLOC_FAIL; 179 if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { 180 ret = SSH_ERR_INVALID_FORMAT; 181 goto out; 182 } 183 if ((hash_alg = rsa_hash_alg_from_ident(ktype)) == -1) { 184 ret = SSH_ERR_KEY_TYPE_MISMATCH; 185 goto out; 186 } 187 if (sshbuf_get_string(b, &sigblob, &len) != 0) { 188 ret = SSH_ERR_INVALID_FORMAT; 189 goto out; 190 } 191 if (sshbuf_len(b) != 0) { 192 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 193 goto out; 194 } 195 /* RSA_verify expects a signature of RSA_size */ 196 modlen = RSA_size(key->rsa); 197 if (len > modlen) { 198 ret = SSH_ERR_KEY_BITS_MISMATCH; 199 goto out; 200 } else if (len < modlen) { 201 diff = modlen - len; 202 osigblob = sigblob; 203 if ((sigblob = realloc(sigblob, modlen)) == NULL) { 204 sigblob = osigblob; /* put it back for clear/free */ 205 ret = SSH_ERR_ALLOC_FAIL; 206 goto out; 207 } 208 memmove(sigblob + diff, sigblob, len); 209 explicit_bzero(sigblob, diff); 210 len = modlen; 211 } 212 if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { 213 ret = SSH_ERR_INTERNAL_ERROR; 214 goto out; 215 } 216 if ((ret = ssh_digest_memory(hash_alg, data, datalen, 217 digest, sizeof(digest))) != 0) 218 goto out; 219 220 ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, 221 key->rsa); 222 out: 223 if (sigblob != NULL) { 224 explicit_bzero(sigblob, len); 225 free(sigblob); 226 } 227 free(ktype); 228 sshbuf_free(b); 229 explicit_bzero(digest, sizeof(digest)); 230 return ret; 231 } 232 233 /* 234 * See: 235 * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ 236 * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn 237 */ 238 239 /* 240 * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) 241 * oiw(14) secsig(3) algorithms(2) 26 } 242 */ 243 static const u_char id_sha1[] = { 244 0x30, 0x21, /* type Sequence, length 0x21 (33) */ 245 0x30, 0x09, /* type Sequence, length 0x09 */ 246 0x06, 0x05, /* type OID, length 0x05 */ 247 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ 248 0x05, 0x00, /* NULL */ 249 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ 250 }; 251 252 /* 253 * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 254 * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 255 * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 256 * id-sha256(1) } 257 */ 258 static const u_char id_sha256[] = { 259 0x30, 0x31, /* type Sequence, length 0x31 (49) */ 260 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 261 0x06, 0x09, /* type OID, length 0x09 */ 262 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ 263 0x05, 0x00, /* NULL */ 264 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ 265 }; 266 267 /* 268 * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 269 * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 270 * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 271 * id-sha256(3) } 272 */ 273 static const u_char id_sha512[] = { 274 0x30, 0x51, /* type Sequence, length 0x51 (81) */ 275 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 276 0x06, 0x09, /* type OID, length 0x09 */ 277 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ 278 0x05, 0x00, /* NULL */ 279 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ 280 }; 281 282 static int 283 rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) 284 { 285 switch (hash_alg) { 286 case SSH_DIGEST_SHA1: 287 *oidp = id_sha1; 288 *oidlenp = sizeof(id_sha1); 289 break; 290 case SSH_DIGEST_SHA256: 291 *oidp = id_sha256; 292 *oidlenp = sizeof(id_sha256); 293 break; 294 case SSH_DIGEST_SHA512: 295 *oidp = id_sha512; 296 *oidlenp = sizeof(id_sha512); 297 break; 298 default: 299 return SSH_ERR_INVALID_ARGUMENT; 300 } 301 return 0; 302 } 303 304 static int 305 openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, 306 u_char *sigbuf, size_t siglen, RSA *rsa) 307 { 308 size_t rsasize = 0, oidlen = 0, hlen = 0; 309 int ret, len, oidmatch, hashmatch; 310 const u_char *oid = NULL; 311 u_char *decrypted = NULL; 312 313 if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) 314 return ret; 315 ret = SSH_ERR_INTERNAL_ERROR; 316 hlen = ssh_digest_bytes(hash_alg); 317 if (hashlen != hlen) { 318 ret = SSH_ERR_INVALID_ARGUMENT; 319 goto done; 320 } 321 rsasize = RSA_size(rsa); 322 if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || 323 siglen == 0 || siglen > rsasize) { 324 ret = SSH_ERR_INVALID_ARGUMENT; 325 goto done; 326 } 327 if ((decrypted = malloc(rsasize)) == NULL) { 328 ret = SSH_ERR_ALLOC_FAIL; 329 goto done; 330 } 331 if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, 332 RSA_PKCS1_PADDING)) < 0) { 333 ret = SSH_ERR_LIBCRYPTO_ERROR; 334 goto done; 335 } 336 if (len < 0 || (size_t)len != hlen + oidlen) { 337 ret = SSH_ERR_INVALID_FORMAT; 338 goto done; 339 } 340 oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; 341 hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; 342 if (!oidmatch || !hashmatch) { 343 ret = SSH_ERR_SIGNATURE_INVALID; 344 goto done; 345 } 346 ret = 0; 347 done: 348 if (decrypted) { 349 explicit_bzero(decrypted, rsasize); 350 free(decrypted); 351 } 352 return ret; 353 } 354