1323124Sdes/* $OpenBSD: ssh-rsa.c,v 1.59 2016/04/21 06:08:02 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 20295367Sdes#ifdef WITH_OPENSSL 21295367Sdes 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 30295367Sdes#include "sshbuf.h" 3176259Sgreen#include "compat.h" 32295367Sdes#include "ssherr.h" 33295367Sdes#define SSHKEY_INTERNAL 34295367Sdes#include "sshkey.h" 35262566Sdes#include "digest.h" 3676259Sgreen 37295367Sdesstatic int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); 38106121Sdes 39296781Sdesstatic const char * 40296781Sdesrsa_hash_alg_ident(int hash_alg) 41296781Sdes{ 42296781Sdes switch (hash_alg) { 43296781Sdes case SSH_DIGEST_SHA1: 44296781Sdes return "ssh-rsa"; 45296781Sdes case SSH_DIGEST_SHA256: 46296781Sdes return "rsa-sha2-256"; 47296781Sdes case SSH_DIGEST_SHA512: 48296781Sdes return "rsa-sha2-512"; 49296781Sdes } 50296781Sdes return NULL; 51296781Sdes} 52296781Sdes 53296781Sdesstatic int 54296781Sdesrsa_hash_alg_from_ident(const char *ident) 55296781Sdes{ 56296781Sdes if (strcmp(ident, "ssh-rsa") == 0) 57296781Sdes return SSH_DIGEST_SHA1; 58296781Sdes if (strcmp(ident, "rsa-sha2-256") == 0) 59296781Sdes return SSH_DIGEST_SHA256; 60296781Sdes if (strcmp(ident, "rsa-sha2-512") == 0) 61296781Sdes return SSH_DIGEST_SHA512; 62296781Sdes return -1; 63296781Sdes} 64296781Sdes 65296781Sdesstatic int 66296781Sdesrsa_hash_alg_nid(int type) 67296781Sdes{ 68296781Sdes switch (type) { 69296781Sdes case SSH_DIGEST_SHA1: 70296781Sdes return NID_sha1; 71296781Sdes case SSH_DIGEST_SHA256: 72296781Sdes return NID_sha256; 73296781Sdes case SSH_DIGEST_SHA512: 74296781Sdes return NID_sha512; 75296781Sdes default: 76296781Sdes return -1; 77296781Sdes } 78296781Sdes} 79296781Sdes 8076259Sgreen/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ 8176259Sgreenint 82295367Sdesssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 83296781Sdes const u_char *data, size_t datalen, const char *alg_ident) 8476259Sgreen{ 85295367Sdes u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; 86295367Sdes size_t slen; 87295367Sdes u_int dlen, len; 88296781Sdes int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 89295367Sdes struct sshbuf *b = NULL; 9076259Sgreen 91295367Sdes if (lenp != NULL) 92295367Sdes *lenp = 0; 93295367Sdes if (sigp != NULL) 94295367Sdes *sigp = NULL; 95262566Sdes 96296781Sdes if (alg_ident == NULL || strlen(alg_ident) == 0 || 97296781Sdes strncmp(alg_ident, "ssh-rsa-cert", strlen("ssh-rsa-cert")) == 0) 98296781Sdes hash_alg = SSH_DIGEST_SHA1; 99296781Sdes else 100296781Sdes hash_alg = rsa_hash_alg_from_ident(alg_ident); 101296781Sdes if (key == NULL || key->rsa == NULL || hash_alg == -1 || 102296781Sdes sshkey_type_plain(key->type) != KEY_RSA || 103296781Sdes BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) 104295367Sdes return SSH_ERR_INVALID_ARGUMENT; 105295367Sdes slen = RSA_size(key->rsa); 106295367Sdes if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) 107295367Sdes return SSH_ERR_INVALID_ARGUMENT; 108295367Sdes 109262566Sdes /* hash the data */ 110296781Sdes nid = rsa_hash_alg_nid(hash_alg); 111295367Sdes if ((dlen = ssh_digest_bytes(hash_alg)) == 0) 112295367Sdes return SSH_ERR_INTERNAL_ERROR; 113295367Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 114295367Sdes digest, sizeof(digest))) != 0) 115295367Sdes goto out; 116295367Sdes 117295367Sdes if ((sig = malloc(slen)) == NULL) { 118295367Sdes ret = SSH_ERR_ALLOC_FAIL; 119295367Sdes goto out; 12076259Sgreen } 12176259Sgreen 122295367Sdes if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { 123295367Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 124295367Sdes goto out; 12576259Sgreen } 12676259Sgreen if (len < slen) { 127295367Sdes size_t diff = slen - len; 12876259Sgreen memmove(sig + diff, sig, len); 129264377Sdes explicit_bzero(sig, diff); 13076259Sgreen } else if (len > slen) { 131295367Sdes ret = SSH_ERR_INTERNAL_ERROR; 132295367Sdes goto out; 13376259Sgreen } 13476259Sgreen /* encode signature */ 135295367Sdes if ((b = sshbuf_new()) == NULL) { 136295367Sdes ret = SSH_ERR_ALLOC_FAIL; 137295367Sdes goto out; 138295367Sdes } 139296781Sdes if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 || 140295367Sdes (ret = sshbuf_put_string(b, sig, slen)) != 0) 141295367Sdes goto out; 142295367Sdes len = sshbuf_len(b); 143295367Sdes if (sigp != NULL) { 144295367Sdes if ((*sigp = malloc(len)) == NULL) { 145295367Sdes ret = SSH_ERR_ALLOC_FAIL; 146295367Sdes goto out; 147295367Sdes } 148295367Sdes memcpy(*sigp, sshbuf_ptr(b), len); 149295367Sdes } 150106121Sdes if (lenp != NULL) 151106121Sdes *lenp = len; 152295367Sdes ret = 0; 153295367Sdes out: 154295367Sdes explicit_bzero(digest, sizeof(digest)); 155295367Sdes if (sig != NULL) { 156295367Sdes explicit_bzero(sig, slen); 157295367Sdes free(sig); 158106121Sdes } 159296781Sdes sshbuf_free(b); 160295367Sdes return ret; 16176259Sgreen} 16276259Sgreen 16376259Sgreenint 164295367Sdesssh_rsa_verify(const struct sshkey *key, 165296781Sdes const u_char *sig, size_t siglen, const u_char *data, size_t datalen) 16676259Sgreen{ 167295367Sdes char *ktype = NULL; 168295367Sdes int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; 169295367Sdes size_t len, diff, modlen, dlen; 170295367Sdes struct sshbuf *b = NULL; 171295367Sdes u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; 17276259Sgreen 173295367Sdes if (key == NULL || key->rsa == NULL || 174295367Sdes sshkey_type_plain(key->type) != KEY_RSA || 175323124Sdes BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE || 176323124Sdes sig == NULL || siglen == 0) 177295367Sdes return SSH_ERR_INVALID_ARGUMENT; 178262566Sdes 179296781Sdes if ((b = sshbuf_from(sig, siglen)) == NULL) 180295367Sdes return SSH_ERR_ALLOC_FAIL; 181295367Sdes if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { 182295367Sdes ret = SSH_ERR_INVALID_FORMAT; 183295367Sdes goto out; 18492555Sdes } 185296781Sdes if ((hash_alg = rsa_hash_alg_from_ident(ktype)) == -1) { 186295367Sdes ret = SSH_ERR_KEY_TYPE_MISMATCH; 187295367Sdes goto out; 18876259Sgreen } 189295367Sdes if (sshbuf_get_string(b, &sigblob, &len) != 0) { 190295367Sdes ret = SSH_ERR_INVALID_FORMAT; 191295367Sdes goto out; 19276259Sgreen } 193295367Sdes if (sshbuf_len(b) != 0) { 194295367Sdes ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 195295367Sdes goto out; 196295367Sdes } 19798675Sdes /* RSA_verify expects a signature of RSA_size */ 19898675Sdes modlen = RSA_size(key->rsa); 19998675Sdes if (len > modlen) { 200295367Sdes ret = SSH_ERR_KEY_BITS_MISMATCH; 201295367Sdes goto out; 20298675Sdes } else if (len < modlen) { 203295367Sdes diff = modlen - len; 204295367Sdes osigblob = sigblob; 205295367Sdes if ((sigblob = realloc(sigblob, modlen)) == NULL) { 206295367Sdes sigblob = osigblob; /* put it back for clear/free */ 207295367Sdes ret = SSH_ERR_ALLOC_FAIL; 208295367Sdes goto out; 209295367Sdes } 21098675Sdes memmove(sigblob + diff, sigblob, len); 211264377Sdes explicit_bzero(sigblob, diff); 21298675Sdes len = modlen; 21398675Sdes } 214262566Sdes if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { 215295367Sdes ret = SSH_ERR_INTERNAL_ERROR; 216295367Sdes goto out; 21776259Sgreen } 218295367Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 219295367Sdes digest, sizeof(digest))) != 0) 220295367Sdes goto out; 22176259Sgreen 222262566Sdes ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, 223262566Sdes key->rsa); 224295367Sdes out: 225295367Sdes if (sigblob != NULL) { 226295367Sdes explicit_bzero(sigblob, len); 227295367Sdes free(sigblob); 228295367Sdes } 229296781Sdes free(ktype); 230296781Sdes sshbuf_free(b); 231264377Sdes 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 */ 240296781Sdes 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 254296781Sdes/* 255296781Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 256296781Sdes * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 257296781Sdes * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 258296781Sdes * id-sha256(1) } 259296781Sdes */ 260296781Sdesstatic const u_char id_sha256[] = { 261296781Sdes 0x30, 0x31, /* type Sequence, length 0x31 (49) */ 262296781Sdes 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 263296781Sdes 0x06, 0x09, /* type OID, length 0x09 */ 264296781Sdes 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ 265296781Sdes 0x05, 0x00, /* NULL */ 266296781Sdes 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ 267296781Sdes}; 268296781Sdes 269296781Sdes/* 270296781Sdes * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html 271296781Sdes * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) 272296781Sdes * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) 273296781Sdes * id-sha256(3) } 274296781Sdes */ 275296781Sdesstatic const u_char id_sha512[] = { 276296781Sdes 0x30, 0x51, /* type Sequence, length 0x51 (81) */ 277296781Sdes 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ 278296781Sdes 0x06, 0x09, /* type OID, length 0x09 */ 279296781Sdes 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ 280296781Sdes 0x05, 0x00, /* NULL */ 281296781Sdes 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ 282296781Sdes}; 283296781Sdes 284106121Sdesstatic int 285296781Sdesrsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) 286296781Sdes{ 287296781Sdes switch (hash_alg) { 288296781Sdes case SSH_DIGEST_SHA1: 289296781Sdes *oidp = id_sha1; 290296781Sdes *oidlenp = sizeof(id_sha1); 291296781Sdes break; 292296781Sdes case SSH_DIGEST_SHA256: 293296781Sdes *oidp = id_sha256; 294296781Sdes *oidlenp = sizeof(id_sha256); 295296781Sdes break; 296296781Sdes case SSH_DIGEST_SHA512: 297296781Sdes *oidp = id_sha512; 298296781Sdes *oidlenp = sizeof(id_sha512); 299296781Sdes break; 300296781Sdes default: 301296781Sdes return SSH_ERR_INVALID_ARGUMENT; 302296781Sdes } 303296781Sdes return 0; 304296781Sdes} 305296781Sdes 306296781Sdesstatic int 307295367Sdesopenssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, 308295367Sdes u_char *sigbuf, size_t siglen, RSA *rsa) 309106121Sdes{ 310296781Sdes size_t rsasize = 0, oidlen = 0, hlen = 0; 311296781Sdes int ret, len, oidmatch, hashmatch; 312106121Sdes const u_char *oid = NULL; 313106121Sdes u_char *decrypted = NULL; 314106121Sdes 315296781Sdes if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) 316296781Sdes return ret; 317295367Sdes ret = SSH_ERR_INTERNAL_ERROR; 318296781Sdes hlen = ssh_digest_bytes(hash_alg); 319106121Sdes if (hashlen != hlen) { 320295367Sdes ret = SSH_ERR_INVALID_ARGUMENT; 321106121Sdes goto done; 322106121Sdes } 323106121Sdes rsasize = RSA_size(rsa); 324295367Sdes if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || 325295367Sdes siglen == 0 || siglen > rsasize) { 326295367Sdes ret = SSH_ERR_INVALID_ARGUMENT; 327106121Sdes goto done; 328106121Sdes } 329295367Sdes if ((decrypted = malloc(rsasize)) == NULL) { 330295367Sdes ret = SSH_ERR_ALLOC_FAIL; 331295367Sdes goto done; 332295367Sdes } 333106121Sdes if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, 334106121Sdes RSA_PKCS1_PADDING)) < 0) { 335295367Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 336106121Sdes goto done; 337106121Sdes } 338295367Sdes if (len < 0 || (size_t)len != hlen + oidlen) { 339295367Sdes 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; 344295367Sdes if (!oidmatch || !hashmatch) { 345295367Sdes ret = SSH_ERR_SIGNATURE_INVALID; 346106121Sdes goto done; 347106121Sdes } 348295367Sdes ret = 0; 349295367Sdesdone: 350295367Sdes if (decrypted) { 351295367Sdes explicit_bzero(decrypted, rsasize); 352295367Sdes free(decrypted); 353106121Sdes } 354106121Sdes return ret; 355106121Sdes} 356295367Sdes#endif /* WITH_OPENSSL */ 357