1323134Sdes/* $OpenBSD: ssh-rsa.c,v 1.60 2016/09/12 23:39:34 djm Exp $ */ 276259Sgreen/* 3124208Sdes * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> 476259Sgreen * 5124208Sdes * Permission to use, copy, modify, and distribute this software for any 6124208Sdes * purpose with or without fee is hereby granted, provided that the above 7124208Sdes * copyright notice and this permission notice appear in all copies. 876259Sgreen * 9124208Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10124208Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11124208Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12124208Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13124208Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14124208Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15124208Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1676259Sgreen */ 17162852Sdes 1876259Sgreen#include "includes.h" 1976259Sgreen 20294332Sdes#ifdef WITH_OPENSSL 21294332Sdes 22162852Sdes#include <sys/types.h> 23162852Sdes 2476259Sgreen#include <openssl/evp.h> 2576259Sgreen#include <openssl/err.h> 2676259Sgreen 27162852Sdes#include <stdarg.h> 28162852Sdes#include <string.h> 29162852Sdes 30294328Sdes#include "sshbuf.h" 3176259Sgreen#include "compat.h" 32294328Sdes#include "ssherr.h" 33294328Sdes#define SSHKEY_INTERNAL 34294328Sdes#include "sshkey.h" 35261320Sdes#include "digest.h" 3676259Sgreen 37294328Sdesstatic int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); 38106121Sdes 39296633Sdesstatic const char * 40296633Sdesrsa_hash_alg_ident(int hash_alg) 41296633Sdes{ 42296633Sdes switch (hash_alg) { 43296633Sdes case SSH_DIGEST_SHA1: 44296633Sdes return "ssh-rsa"; 45296633Sdes case SSH_DIGEST_SHA256: 46296633Sdes return "rsa-sha2-256"; 47296633Sdes case SSH_DIGEST_SHA512: 48296633Sdes return "rsa-sha2-512"; 49296633Sdes } 50296633Sdes return NULL; 51296633Sdes} 52296633Sdes 53296633Sdesstatic int 54296633Sdesrsa_hash_alg_from_ident(const char *ident) 55296633Sdes{ 56323134Sdes if (strcmp(ident, "ssh-rsa") == 0 || 57323134Sdes strcmp(ident, "ssh-rsa-cert-v01@openssh.com") == 0) 58296633Sdes return SSH_DIGEST_SHA1; 59296633Sdes if (strcmp(ident, "rsa-sha2-256") == 0) 60296633Sdes return SSH_DIGEST_SHA256; 61296633Sdes if (strcmp(ident, "rsa-sha2-512") == 0) 62296633Sdes return SSH_DIGEST_SHA512; 63296633Sdes return -1; 64296633Sdes} 65296633Sdes 66296633Sdesstatic int 67296633Sdesrsa_hash_alg_nid(int type) 68296633Sdes{ 69296633Sdes switch (type) { 70296633Sdes case SSH_DIGEST_SHA1: 71296633Sdes return NID_sha1; 72296633Sdes case SSH_DIGEST_SHA256: 73296633Sdes return NID_sha256; 74296633Sdes case SSH_DIGEST_SHA512: 75296633Sdes return NID_sha512; 76296633Sdes default: 77296633Sdes return -1; 78296633Sdes } 79296633Sdes} 80296633Sdes 8176259Sgreen/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ 8276259Sgreenint 83294328Sdesssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 84296633Sdes const u_char *data, size_t datalen, const char *alg_ident) 8576259Sgreen{ 86294328Sdes u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; 87294328Sdes size_t slen; 88294328Sdes u_int dlen, len; 89296633Sdes int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 90294328Sdes struct sshbuf *b = NULL; 9176259Sgreen 92294328Sdes if (lenp != NULL) 93294328Sdes *lenp = 0; 94294328Sdes if (sigp != NULL) 95294328Sdes *sigp = NULL; 96261320Sdes 97323134Sdes if (alg_ident == NULL || strlen(alg_ident) == 0) 98296633Sdes hash_alg = SSH_DIGEST_SHA1; 99296633Sdes else 100296633Sdes hash_alg = rsa_hash_alg_from_ident(alg_ident); 101296633Sdes if (key == NULL || key->rsa == NULL || hash_alg == -1 || 102296633Sdes sshkey_type_plain(key->type) != KEY_RSA || 103296633Sdes BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) 104294328Sdes return SSH_ERR_INVALID_ARGUMENT; 105294328Sdes slen = RSA_size(key->rsa); 106294328Sdes if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) 107294328Sdes return SSH_ERR_INVALID_ARGUMENT; 108294328Sdes 109261320Sdes /* hash the data */ 110296633Sdes nid = rsa_hash_alg_nid(hash_alg); 111294328Sdes if ((dlen = ssh_digest_bytes(hash_alg)) == 0) 112294328Sdes return SSH_ERR_INTERNAL_ERROR; 113294328Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 114294328Sdes digest, sizeof(digest))) != 0) 115294328Sdes goto out; 116294328Sdes 117294328Sdes if ((sig = malloc(slen)) == NULL) { 118294328Sdes ret = SSH_ERR_ALLOC_FAIL; 119294328Sdes goto out; 12076259Sgreen } 12176259Sgreen 122294328Sdes if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { 123294328Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 124294328Sdes goto out; 12576259Sgreen } 12676259Sgreen if (len < slen) { 127294328Sdes size_t diff = slen - len; 12876259Sgreen memmove(sig + diff, sig, len); 129263712Sdes explicit_bzero(sig, diff); 13076259Sgreen } else if (len > slen) { 131294328Sdes ret = SSH_ERR_INTERNAL_ERROR; 132294328Sdes goto out; 13376259Sgreen } 13476259Sgreen /* encode signature */ 135294328Sdes if ((b = sshbuf_new()) == NULL) { 136294328Sdes ret = SSH_ERR_ALLOC_FAIL; 137294328Sdes goto out; 138294328Sdes } 139296633Sdes if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 || 140294328Sdes (ret = sshbuf_put_string(b, sig, slen)) != 0) 141294328Sdes goto out; 142294328Sdes len = sshbuf_len(b); 143294328Sdes if (sigp != NULL) { 144294328Sdes if ((*sigp = malloc(len)) == NULL) { 145294328Sdes ret = SSH_ERR_ALLOC_FAIL; 146294328Sdes goto out; 147294328Sdes } 148294328Sdes memcpy(*sigp, sshbuf_ptr(b), len); 149294328Sdes } 150106121Sdes if (lenp != NULL) 151106121Sdes *lenp = len; 152294328Sdes ret = 0; 153294328Sdes out: 154294328Sdes explicit_bzero(digest, sizeof(digest)); 155294328Sdes if (sig != NULL) { 156294328Sdes explicit_bzero(sig, slen); 157294328Sdes free(sig); 158106121Sdes } 159296633Sdes sshbuf_free(b); 160294336Sdes return ret; 16176259Sgreen} 16276259Sgreen 16376259Sgreenint 164294328Sdesssh_rsa_verify(const struct sshkey *key, 165296633Sdes const u_char *sig, size_t siglen, const u_char *data, size_t datalen) 16676259Sgreen{ 167294328Sdes char *ktype = NULL; 168294328Sdes int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 169294328Sdes size_t len, diff, modlen, dlen; 170294328Sdes struct sshbuf *b = NULL; 171294328Sdes u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; 17276259Sgreen 173294328Sdes if (key == NULL || key->rsa == NULL || 174294328Sdes sshkey_type_plain(key->type) != KEY_RSA || 175323129Sdes BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE || 176323129Sdes sig == NULL || siglen == 0) 177294328Sdes return SSH_ERR_INVALID_ARGUMENT; 178261320Sdes 179296633Sdes if ((b = sshbuf_from(sig, siglen)) == NULL) 180294328Sdes return SSH_ERR_ALLOC_FAIL; 181294328Sdes if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { 182294328Sdes ret = SSH_ERR_INVALID_FORMAT; 183294328Sdes goto out; 18492555Sdes } 185296633Sdes if ((hash_alg = rsa_hash_alg_from_ident(ktype)) == -1) { 186294328Sdes ret = SSH_ERR_KEY_TYPE_MISMATCH; 187294328Sdes goto out; 18876259Sgreen } 189294328Sdes if (sshbuf_get_string(b, &sigblob, &len) != 0) { 190294328Sdes ret = SSH_ERR_INVALID_FORMAT; 191294328Sdes goto out; 19276259Sgreen } 193294328Sdes if (sshbuf_len(b) != 0) { 194294328Sdes ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 195294328Sdes goto out; 196294328Sdes } 19798675Sdes /* RSA_verify expects a signature of RSA_size */ 19898675Sdes modlen = RSA_size(key->rsa); 19998675Sdes if (len > modlen) { 200294328Sdes ret = SSH_ERR_KEY_BITS_MISMATCH; 201294328Sdes goto out; 20298675Sdes } else if (len < modlen) { 203294328Sdes diff = modlen - len; 204294328Sdes osigblob = sigblob; 205294328Sdes if ((sigblob = realloc(sigblob, modlen)) == NULL) { 206294328Sdes sigblob = osigblob; /* put it back for clear/free */ 207294328Sdes ret = SSH_ERR_ALLOC_FAIL; 208294328Sdes goto out; 209294328Sdes } 21098675Sdes memmove(sigblob + diff, sigblob, len); 211263712Sdes explicit_bzero(sigblob, diff); 21298675Sdes len = modlen; 21398675Sdes } 214261320Sdes if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { 215294328Sdes ret = SSH_ERR_INTERNAL_ERROR; 216294328Sdes goto out; 21776259Sgreen } 218294328Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 219294328Sdes digest, sizeof(digest))) != 0) 220294328Sdes goto out; 22176259Sgreen 222261320Sdes ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, 223261320Sdes key->rsa); 224294328Sdes out: 225294328Sdes if (sigblob != NULL) { 226294328Sdes explicit_bzero(sigblob, len); 227294328Sdes free(sigblob); 228294328Sdes } 229296633Sdes free(ktype); 230296633Sdes sshbuf_free(b); 231263712Sdes explicit_bzero(digest, sizeof(digest)); 23276259Sgreen return ret; 23376259Sgreen} 234106121Sdes 235106121Sdes/* 236106121Sdes * See: 237106121Sdes * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ 238106121Sdes * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn 239106121Sdes */ 240296633Sdes 241106121Sdes/* 242106121Sdes * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) 243106121Sdes * oiw(14) secsig(3) algorithms(2) 26 } 244106121Sdes */ 245106121Sdesstatic const u_char id_sha1[] = { 246106121Sdes 0x30, 0x21, /* type Sequence, length 0x21 (33) */ 247106121Sdes 0x30, 0x09, /* type Sequence, length 0x09 */ 248106121Sdes 0x06, 0x05, /* type OID, length 0x05 */ 249106121Sdes 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ 250106121Sdes 0x05, 0x00, /* NULL */ 251106121Sdes 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ 252106121Sdes}; 253106121Sdes 254296633Sdes/* 255296633Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 256296633Sdes * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 257296633Sdes * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 258296633Sdes * id-sha256(1) } 259296633Sdes */ 260296633Sdesstatic const u_char id_sha256[] = { 261296633Sdes 0x30, 0x31, /* type Sequence, length 0x31 (49) */ 262296633Sdes 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 263296633Sdes 0x06, 0x09, /* type OID, length 0x09 */ 264296633Sdes 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ 265296633Sdes 0x05, 0x00, /* NULL */ 266296633Sdes 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ 267296633Sdes}; 268296633Sdes 269296633Sdes/* 270296633Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 271296633Sdes * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 272296633Sdes * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 273296633Sdes * id-sha256(3) } 274296633Sdes */ 275296633Sdesstatic const u_char id_sha512[] = { 276296633Sdes 0x30, 0x51, /* type Sequence, length 0x51 (81) */ 277296633Sdes 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 278296633Sdes 0x06, 0x09, /* type OID, length 0x09 */ 279296633Sdes 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ 280296633Sdes 0x05, 0x00, /* NULL */ 281296633Sdes 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ 282296633Sdes}; 283296633Sdes 284106121Sdesstatic int 285296633Sdesrsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) 286296633Sdes{ 287296633Sdes switch (hash_alg) { 288296633Sdes case SSH_DIGEST_SHA1: 289296633Sdes *oidp = id_sha1; 290296633Sdes *oidlenp = sizeof(id_sha1); 291296633Sdes break; 292296633Sdes case SSH_DIGEST_SHA256: 293296633Sdes *oidp = id_sha256; 294296633Sdes *oidlenp = sizeof(id_sha256); 295296633Sdes break; 296296633Sdes case SSH_DIGEST_SHA512: 297296633Sdes *oidp = id_sha512; 298296633Sdes *oidlenp = sizeof(id_sha512); 299296633Sdes break; 300296633Sdes default: 301296633Sdes return SSH_ERR_INVALID_ARGUMENT; 302296633Sdes } 303296633Sdes return 0; 304296633Sdes} 305296633Sdes 306296633Sdesstatic int 307294328Sdesopenssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, 308294328Sdes u_char *sigbuf, size_t siglen, RSA *rsa) 309106121Sdes{ 310296633Sdes size_t rsasize = 0, oidlen = 0, hlen = 0; 311296633Sdes int ret, len, oidmatch, hashmatch; 312106121Sdes const u_char *oid = NULL; 313106121Sdes u_char *decrypted = NULL; 314106121Sdes 315296633Sdes if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) 316296633Sdes return ret; 317294328Sdes ret = SSH_ERR_INTERNAL_ERROR; 318296633Sdes hlen = ssh_digest_bytes(hash_alg); 319106121Sdes if (hashlen != hlen) { 320294328Sdes ret = SSH_ERR_INVALID_ARGUMENT; 321106121Sdes goto done; 322106121Sdes } 323106121Sdes rsasize = RSA_size(rsa); 324294328Sdes if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || 325294328Sdes siglen == 0 || siglen > rsasize) { 326294328Sdes ret = SSH_ERR_INVALID_ARGUMENT; 327106121Sdes goto done; 328106121Sdes } 329294328Sdes if ((decrypted = malloc(rsasize)) == NULL) { 330294328Sdes ret = SSH_ERR_ALLOC_FAIL; 331294328Sdes goto done; 332294328Sdes } 333106121Sdes if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, 334106121Sdes RSA_PKCS1_PADDING)) < 0) { 335294328Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 336106121Sdes goto done; 337106121Sdes } 338294328Sdes if (len < 0 || (size_t)len != hlen + oidlen) { 339294328Sdes ret = SSH_ERR_INVALID_FORMAT; 340106121Sdes goto done; 341106121Sdes } 342215116Sdes oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; 343215116Sdes hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; 344294328Sdes if (!oidmatch || !hashmatch) { 345294328Sdes ret = SSH_ERR_SIGNATURE_INVALID; 346106121Sdes goto done; 347106121Sdes } 348294328Sdes ret = 0; 349294328Sdesdone: 350294328Sdes if (decrypted) { 351294328Sdes explicit_bzero(decrypted, rsasize); 352294328Sdes free(decrypted); 353106121Sdes } 354106121Sdes return ret; 355106121Sdes} 356294332Sdes#endif /* WITH_OPENSSL */ 357