1 /* $NetBSD: ssh-rsa.c,v 1.16 2018/08/26 07:46:36 christos Exp $ */ 2 /* $OpenBSD: ssh-rsa.c,v 1.67 2018/07/03 11:39:54 djm Exp $ */ 3 4 /* 5 * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include "includes.h" 21 __RCSID("$NetBSD: ssh-rsa.c,v 1.16 2018/08/26 07:46:36 christos Exp $"); 22 #include <sys/types.h> 23 24 #include <openssl/evp.h> 25 #include <openssl/err.h> 26 27 #include <string.h> 28 29 #include "sshbuf.h" 30 #include "compat.h" 31 #include "ssherr.h" 32 #define SSHKEY_INTERNAL 33 #include "sshkey.h" 34 #include "digest.h" 35 #include "log.h" 36 37 static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); 38 39 static const char * 40 rsa_hash_alg_ident(int hash_alg) 41 { 42 switch (hash_alg) { 43 case SSH_DIGEST_SHA1: 44 return "ssh-rsa"; 45 case SSH_DIGEST_SHA256: 46 return "rsa-sha2-256"; 47 case SSH_DIGEST_SHA512: 48 return "rsa-sha2-512"; 49 } 50 return NULL; 51 } 52 53 /* 54 * Returns the hash algorithm ID for a given algorithm identifier as used 55 * inside the signature blob, 56 */ 57 static int 58 rsa_hash_id_from_ident(const char *ident) 59 { 60 if (strcmp(ident, "ssh-rsa") == 0) 61 return SSH_DIGEST_SHA1; 62 if (strcmp(ident, "rsa-sha2-256") == 0) 63 return SSH_DIGEST_SHA256; 64 if (strcmp(ident, "rsa-sha2-512") == 0) 65 return SSH_DIGEST_SHA512; 66 return -1; 67 } 68 69 /* 70 * Return the hash algorithm ID for the specified key name. This includes 71 * all the cases of rsa_hash_id_from_ident() but also the certificate key 72 * types. 73 */ 74 static int 75 rsa_hash_id_from_keyname(const char *alg) 76 { 77 int r; 78 79 if ((r = rsa_hash_id_from_ident(alg)) != -1) 80 return r; 81 if (strcmp(alg, "ssh-rsa-cert-v01@openssh.com") == 0) 82 return SSH_DIGEST_SHA1; 83 if (strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0) 84 return SSH_DIGEST_SHA256; 85 if (strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0) 86 return SSH_DIGEST_SHA512; 87 return -1; 88 } 89 90 static int 91 rsa_hash_alg_nid(int type) 92 { 93 switch (type) { 94 case SSH_DIGEST_SHA1: 95 return NID_sha1; 96 case SSH_DIGEST_SHA256: 97 return NID_sha256; 98 case SSH_DIGEST_SHA512: 99 return NID_sha512; 100 default: 101 return -1; 102 } 103 } 104 105 int 106 ssh_rsa_generate_additional_parameters(struct sshkey *key) 107 { 108 RSA *rsa; 109 BIGNUM *aux = NULL; 110 BN_CTX *ctx = NULL; 111 int r; 112 113 if (key == NULL || key->rsa == NULL || 114 sshkey_type_plain(key->type) != KEY_RSA) 115 return SSH_ERR_INVALID_ARGUMENT; 116 117 if ((ctx = BN_CTX_new()) == NULL) 118 return SSH_ERR_ALLOC_FAIL; 119 if ((aux = BN_new()) == NULL) { 120 r = SSH_ERR_ALLOC_FAIL; 121 goto out; 122 } 123 rsa = key->rsa; 124 BN_set_flags(aux, BN_FLG_CONSTTIME); 125 126 { 127 const BIGNUM *q, *d, *p; 128 BIGNUM *dmq1=NULL, *dmp1=NULL; 129 if ((dmq1 = BN_new()) == NULL || 130 (dmp1 = BN_new()) == NULL ) { 131 r = SSH_ERR_ALLOC_FAIL; 132 goto out; 133 } 134 RSA_get0_key(rsa, NULL, NULL, &d); 135 RSA_get0_factors(rsa, &p, &q); 136 if ((BN_sub(aux, q, BN_value_one()) == 0) || 137 (BN_mod(dmq1, d, aux, ctx) == 0) || 138 (BN_sub(aux, p, BN_value_one()) == 0) || 139 (BN_mod(dmp1, d, aux, ctx) == 0) || 140 RSA_set0_crt_params(rsa, dmp1, dmq1, NULL) == 0) { 141 r = SSH_ERR_LIBCRYPTO_ERROR; 142 BN_clear_free(dmp1); 143 BN_clear_free(dmq1); 144 goto out; 145 } 146 } 147 r = 0; 148 out: 149 BN_clear_free(aux); 150 BN_CTX_free(ctx); 151 return r; 152 } 153 154 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ 155 int 156 ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 157 const u_char *data, size_t datalen, const char *alg_ident) 158 { 159 u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; 160 size_t slen = 0; 161 u_int dlen, len; 162 int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 163 struct sshbuf *b = NULL; 164 165 if (lenp != NULL) 166 *lenp = 0; 167 if (sigp != NULL) 168 *sigp = NULL; 169 170 if (alg_ident == NULL || strlen(alg_ident) == 0) 171 hash_alg = SSH_DIGEST_SHA1; 172 else 173 hash_alg = rsa_hash_id_from_keyname(alg_ident); 174 if (key == NULL || key->rsa == NULL || hash_alg == -1 || 175 sshkey_type_plain(key->type) != KEY_RSA) 176 return SSH_ERR_INVALID_ARGUMENT; 177 if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) 178 return SSH_ERR_KEY_LENGTH; 179 slen = RSA_size(key->rsa); 180 if (slen == 0 || slen > SSHBUF_MAX_BIGNUM) 181 return SSH_ERR_INVALID_ARGUMENT; 182 183 /* hash the data */ 184 nid = rsa_hash_alg_nid(hash_alg); 185 if ((dlen = ssh_digest_bytes(hash_alg)) == 0) 186 return SSH_ERR_INTERNAL_ERROR; 187 if ((ret = ssh_digest_memory(hash_alg, data, datalen, 188 digest, sizeof(digest))) != 0) 189 goto out; 190 191 if ((sig = malloc(slen)) == NULL) { 192 ret = SSH_ERR_ALLOC_FAIL; 193 goto out; 194 } 195 196 if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { 197 ret = SSH_ERR_LIBCRYPTO_ERROR; 198 goto out; 199 } 200 if (len < slen) { 201 size_t diff = slen - len; 202 memmove(sig + diff, sig, len); 203 explicit_bzero(sig, diff); 204 } else if (len > slen) { 205 ret = SSH_ERR_INTERNAL_ERROR; 206 goto out; 207 } 208 /* encode signature */ 209 if ((b = sshbuf_new()) == NULL) { 210 ret = SSH_ERR_ALLOC_FAIL; 211 goto out; 212 } 213 if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 || 214 (ret = sshbuf_put_string(b, sig, slen)) != 0) 215 goto out; 216 len = sshbuf_len(b); 217 if (sigp != NULL) { 218 if ((*sigp = malloc(len)) == NULL) { 219 ret = SSH_ERR_ALLOC_FAIL; 220 goto out; 221 } 222 memcpy(*sigp, sshbuf_ptr(b), len); 223 } 224 if (lenp != NULL) 225 *lenp = len; 226 ret = 0; 227 out: 228 explicit_bzero(digest, sizeof(digest)); 229 freezero(sig, slen); 230 sshbuf_free(b); 231 return ret; 232 } 233 234 int 235 ssh_rsa_verify(const struct sshkey *key, 236 const u_char *sig, size_t siglen, const u_char *data, size_t datalen, 237 const char *alg) 238 { 239 char *sigtype = NULL; 240 int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR; 241 size_t len = 0, diff, modlen, dlen; 242 struct sshbuf *b = NULL; 243 u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; 244 245 if (key == NULL || key->rsa == NULL || 246 sshkey_type_plain(key->type) != KEY_RSA || 247 sig == NULL || siglen == 0) 248 return SSH_ERR_INVALID_ARGUMENT; 249 if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) 250 return SSH_ERR_KEY_LENGTH; 251 252 if ((b = sshbuf_from(sig, siglen)) == NULL) 253 return SSH_ERR_ALLOC_FAIL; 254 if (sshbuf_get_cstring(b, &sigtype, NULL) != 0) { 255 ret = SSH_ERR_INVALID_FORMAT; 256 goto out; 257 } 258 if ((hash_alg = rsa_hash_id_from_ident(sigtype)) == -1) { 259 ret = SSH_ERR_KEY_TYPE_MISMATCH; 260 goto out; 261 } 262 /* 263 * Allow ssh-rsa-cert-v01 certs to generate SHA2 signatures for 264 * legacy reasons, but otherwise the signature type should match. 265 */ 266 if (alg != NULL && strcmp(alg, "ssh-rsa-cert-v01@openssh.com") != 0) { 267 if ((want_alg = rsa_hash_id_from_keyname(alg)) == -1) { 268 ret = SSH_ERR_INVALID_ARGUMENT; 269 goto out; 270 } 271 if (hash_alg != want_alg) { 272 ret = SSH_ERR_SIGNATURE_INVALID; 273 goto out; 274 } 275 } 276 if (sshbuf_get_string(b, &sigblob, &len) != 0) { 277 ret = SSH_ERR_INVALID_FORMAT; 278 goto out; 279 } 280 if (sshbuf_len(b) != 0) { 281 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 282 goto out; 283 } 284 /* RSA_verify expects a signature of RSA_size */ 285 modlen = RSA_size(key->rsa); 286 if (len > modlen) { 287 ret = SSH_ERR_KEY_BITS_MISMATCH; 288 goto out; 289 } else if (len < modlen) { 290 diff = modlen - len; 291 osigblob = sigblob; 292 if ((sigblob = realloc(sigblob, modlen)) == NULL) { 293 sigblob = osigblob; /* put it back for clear/free */ 294 ret = SSH_ERR_ALLOC_FAIL; 295 goto out; 296 } 297 memmove(sigblob + diff, sigblob, len); 298 explicit_bzero(sigblob, diff); 299 len = modlen; 300 } 301 if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { 302 ret = SSH_ERR_INTERNAL_ERROR; 303 goto out; 304 } 305 if ((ret = ssh_digest_memory(hash_alg, data, datalen, 306 digest, sizeof(digest))) != 0) 307 goto out; 308 309 ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, 310 key->rsa); 311 out: 312 freezero(sigblob, len); 313 free(sigtype); 314 sshbuf_free(b); 315 explicit_bzero(digest, sizeof(digest)); 316 return ret; 317 } 318 319 /* 320 * See: 321 * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ 322 * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn 323 */ 324 325 /* 326 * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) 327 * oiw(14) secsig(3) algorithms(2) 26 } 328 */ 329 static const u_char id_sha1[] = { 330 0x30, 0x21, /* type Sequence, length 0x21 (33) */ 331 0x30, 0x09, /* type Sequence, length 0x09 */ 332 0x06, 0x05, /* type OID, length 0x05 */ 333 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ 334 0x05, 0x00, /* NULL */ 335 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ 336 }; 337 338 /* 339 * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 340 * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 341 * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 342 * id-sha256(1) } 343 */ 344 static const u_char id_sha256[] = { 345 0x30, 0x31, /* type Sequence, length 0x31 (49) */ 346 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 347 0x06, 0x09, /* type OID, length 0x09 */ 348 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ 349 0x05, 0x00, /* NULL */ 350 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ 351 }; 352 353 /* 354 * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 355 * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 356 * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 357 * id-sha256(3) } 358 */ 359 static const u_char id_sha512[] = { 360 0x30, 0x51, /* type Sequence, length 0x51 (81) */ 361 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 362 0x06, 0x09, /* type OID, length 0x09 */ 363 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ 364 0x05, 0x00, /* NULL */ 365 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ 366 }; 367 368 static int 369 rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) 370 { 371 switch (hash_alg) { 372 case SSH_DIGEST_SHA1: 373 *oidp = id_sha1; 374 *oidlenp = sizeof(id_sha1); 375 break; 376 case SSH_DIGEST_SHA256: 377 *oidp = id_sha256; 378 *oidlenp = sizeof(id_sha256); 379 break; 380 case SSH_DIGEST_SHA512: 381 *oidp = id_sha512; 382 *oidlenp = sizeof(id_sha512); 383 break; 384 default: 385 return SSH_ERR_INVALID_ARGUMENT; 386 } 387 return 0; 388 } 389 390 static int 391 openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, 392 u_char *sigbuf, size_t siglen, RSA *rsa) 393 { 394 size_t rsasize = 0, oidlen = 0, hlen = 0; 395 int ret, len, oidmatch, hashmatch; 396 const u_char *oid = NULL; 397 u_char *decrypted = NULL; 398 399 if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) 400 return ret; 401 ret = SSH_ERR_INTERNAL_ERROR; 402 hlen = ssh_digest_bytes(hash_alg); 403 if (hashlen != hlen) { 404 ret = SSH_ERR_INVALID_ARGUMENT; 405 goto done; 406 } 407 rsasize = RSA_size(rsa); 408 if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || 409 siglen == 0 || siglen > rsasize) { 410 ret = SSH_ERR_INVALID_ARGUMENT; 411 goto done; 412 } 413 if ((decrypted = malloc(rsasize)) == NULL) { 414 ret = SSH_ERR_ALLOC_FAIL; 415 goto done; 416 } 417 if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, 418 RSA_PKCS1_PADDING)) < 0) { 419 ret = SSH_ERR_LIBCRYPTO_ERROR; 420 goto done; 421 } 422 if (len < 0 || (size_t)len != hlen + oidlen) { 423 ret = SSH_ERR_INVALID_FORMAT; 424 goto done; 425 } 426 oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; 427 hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; 428 if (!oidmatch || !hashmatch) { 429 ret = SSH_ERR_SIGNATURE_INVALID; 430 goto done; 431 } 432 ret = 0; 433 done: 434 freezero(decrypted, rsasize); 435 return ret; 436 } 437