123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /* $OpenBSD: ssh-rsa.c,v 1.67 2018/07/03 11:39:54 djm Exp $ */
- /*
- * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include "includes.h"
- #ifdef WITH_OPENSSL
- #include <sys/types.h>
- #include <openssl/evp.h>
- #include <openssl/err.h>
- #include <stdarg.h>
- #include <string.h>
- #include "sshbuf.h"
- #include "compat.h"
- #include "ssherr.h"
- #define SSHKEY_INTERNAL
- #include "sshkey.h"
- #include "digest.h"
- #include "log.h"
- #include "openbsd-compat/openssl-compat.h"
- static int openssh_RSA_verify(int, const u_char *, size_t, u_char *, size_t, EVP_PKEY *);
- static const char *
- rsa_hash_alg_ident(int hash_alg)
- {
- switch (hash_alg) {
- case SSH_DIGEST_SHA1:
- return "ssh-rsa";
- case SSH_DIGEST_SHA256:
- return "rsa-sha2-256";
- case SSH_DIGEST_SHA512:
- return "rsa-sha2-512";
- }
- return NULL;
- }
- /*
- * Returns the hash algorithm ID for a given algorithm identifier as used
- * inside the signature blob,
- */
- static int
- rsa_hash_id_from_ident(const char *ident)
- {
- if (strcmp(ident, "ssh-rsa") == 0)
- return SSH_DIGEST_SHA1;
- if (strcmp(ident, "rsa-sha2-256") == 0)
- return SSH_DIGEST_SHA256;
- if (strcmp(ident, "rsa-sha2-512") == 0)
- return SSH_DIGEST_SHA512;
- return -1;
- }
- /*
- * Return the hash algorithm ID for the specified key name. This includes
- * all the cases of rsa_hash_id_from_ident() but also the certificate key
- * types.
- */
- static int
- rsa_hash_id_from_keyname(const char *alg)
- {
- int r;
- if ((r = rsa_hash_id_from_ident(alg)) != -1)
- return r;
- if (strcmp(alg, "ssh-rsa-cert-v01@openssh.com") == 0)
- return SSH_DIGEST_SHA1;
- if (strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0)
- return SSH_DIGEST_SHA256;
- if (strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0)
- return SSH_DIGEST_SHA512;
- return -1;
- }
- int
- ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp)
- {
- const BIGNUM *rsa_p, *rsa_q, *rsa_d;
- BIGNUM *aux = NULL, *d_consttime = NULL;
- BIGNUM *rsa_dmq1 = NULL, *rsa_dmp1 = NULL, *rsa_iqmp = NULL;
- BN_CTX *ctx = NULL;
- int r;
- if (key == NULL || key->rsa == NULL ||
- sshkey_type_plain(key->type) != KEY_RSA)
- return SSH_ERR_INVALID_ARGUMENT;
- RSA_get0_key(key->rsa, NULL, NULL, &rsa_d);
- RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
- if ((ctx = BN_CTX_new()) == NULL)
- return SSH_ERR_ALLOC_FAIL;
- if ((aux = BN_new()) == NULL ||
- (rsa_dmq1 = BN_new()) == NULL ||
- (rsa_dmp1 = BN_new()) == NULL)
- return SSH_ERR_ALLOC_FAIL;
- if ((d_consttime = BN_dup(rsa_d)) == NULL ||
- (rsa_iqmp = BN_dup(iqmp)) == NULL) {
- r = SSH_ERR_ALLOC_FAIL;
- goto out;
- }
- BN_set_flags(aux, BN_FLG_CONSTTIME);
- BN_set_flags(d_consttime, BN_FLG_CONSTTIME);
- if ((BN_sub(aux, rsa_q, BN_value_one()) == 0) ||
- (BN_mod(rsa_dmq1, d_consttime, aux, ctx) == 0) ||
- (BN_sub(aux, rsa_p, BN_value_one()) == 0) ||
- (BN_mod(rsa_dmp1, d_consttime, aux, ctx) == 0)) {
- debug("((BN_sub(aux, rsa_q, BN_value_one()) == 0) || (BN_mod(rsa_dmq1, d_consttime, aux, ctx) == 0) || (BN_sub(aux, rsa_p, BN_value_one()) == 0) || (BN_mod(rsa_dmp1, d_consttime, aux, ctx) == 0))");
- r = SSH_ERR_LIBCRYPTO_ERROR;
- goto out;
- }
- if (!RSA_set0_crt_params(key->rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp)) {
- debug("(!RSA_set0_crt_params(key->rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp))");
- r = SSH_ERR_LIBCRYPTO_ERROR;
- goto out;
- }
- rsa_dmp1 = rsa_dmq1 = rsa_iqmp = NULL; /* transferred */
- /* success */
- r = 0;
- out:
- BN_clear_free(aux);
- BN_clear_free(d_consttime);
- BN_clear_free(rsa_dmp1);
- BN_clear_free(rsa_dmq1);
- BN_clear_free(rsa_iqmp);
- BN_CTX_free(ctx);
- return r;
- }
- /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
- int
- ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
- const u_char *data, size_t datalen, const char *alg_ident)
- {
- EVP_PKEY *pkey = NULL;
- u_char *sig = NULL;
- int len, slen = 0;
- int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
- struct sshbuf *b = NULL;
- if (lenp != NULL)
- *lenp = 0;
- if (sigp != NULL)
- *sigp = NULL;
- if (alg_ident == NULL || strlen(alg_ident) == 0)
- hash_alg = SSH_DIGEST_SHA1;
- else
- hash_alg = rsa_hash_id_from_keyname(alg_ident);
- if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
- sshkey_type_plain(key->type) != KEY_RSA)
- return SSH_ERR_INVALID_ARGUMENT;
- slen = RSA_size(key->rsa);
- if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE)
- return SSH_ERR_KEY_LENGTH;
- if ((pkey = EVP_PKEY_new()) == NULL ||
- EVP_PKEY_set1_RSA(pkey, key->rsa) != 1)
- return SSH_ERR_ALLOC_FAIL;
- ret = sshkey_calculate_signature(pkey, hash_alg, &sig, &len, data,
- datalen);
- EVP_PKEY_free(pkey);
- if (ret < 0) {
- goto out;
- }
- if (len < slen) {
- size_t diff = slen - len;
- memmove(sig + diff, sig, len);
- explicit_bzero(sig, diff);
- } else if (len > slen) {
- ret = SSH_ERR_INTERNAL_ERROR;
- goto out;
- }
- /* encode signature */
- if ((b = sshbuf_new()) == NULL) {
- ret = SSH_ERR_ALLOC_FAIL;
- goto out;
- }
- if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 ||
- (ret = sshbuf_put_string(b, sig, slen)) != 0)
- goto out;
- len = sshbuf_len(b);
- if (sigp != NULL) {
- if ((*sigp = malloc(len)) == NULL) {
- ret = SSH_ERR_ALLOC_FAIL;
- goto out;
- }
- memcpy(*sigp, sshbuf_ptr(b), len);
- }
- if (lenp != NULL)
- *lenp = len;
- ret = 0;
- out:
- freezero(sig, slen);
- sshbuf_free(b);
- return ret;
- }
- int
- ssh_rsa_verify(const struct sshkey *key,
- const u_char *sig, size_t siglen, const u_char *data, size_t datalen,
- const char *alg)
- {
- EVP_PKEY *pkey = NULL;
- char *sigtype = NULL;
- int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR;
- size_t len = 0, diff, modlen;
- struct sshbuf *b = NULL;
- u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
- if (key == NULL || key->rsa == NULL ||
- sshkey_type_plain(key->type) != KEY_RSA ||
- sig == NULL || siglen == 0)
- return SSH_ERR_INVALID_ARGUMENT;
- if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE)
- return SSH_ERR_KEY_LENGTH;
- if ((b = sshbuf_from(sig, siglen)) == NULL)
- return SSH_ERR_ALLOC_FAIL;
- if (sshbuf_get_cstring(b, &sigtype, NULL) != 0) {
- ret = SSH_ERR_INVALID_FORMAT;
- goto out;
- }
- if ((hash_alg = rsa_hash_id_from_ident(sigtype)) == -1) {
- ret = SSH_ERR_KEY_TYPE_MISMATCH;
- goto out;
- }
- /*
- * Allow ssh-rsa-cert-v01 certs to generate SHA2 signatures for
- * legacy reasons, but otherwise the signature type should match.
- */
- if (alg != NULL && strcmp(alg, "ssh-rsa-cert-v01@openssh.com") != 0) {
- if ((want_alg = rsa_hash_id_from_keyname(alg)) == -1) {
- ret = SSH_ERR_INVALID_ARGUMENT;
- goto out;
- }
- if (hash_alg != want_alg) {
- ret = SSH_ERR_SIGNATURE_INVALID;
- goto out;
- }
- }
- if (sshbuf_get_string(b, &sigblob, &len) != 0) {
- ret = SSH_ERR_INVALID_FORMAT;
- goto out;
- }
- if (sshbuf_len(b) != 0) {
- ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
- goto out;
- }
- /* RSA_verify expects a signature of RSA_size */
- modlen = RSA_size(key->rsa);
- if (len > modlen) {
- ret = SSH_ERR_KEY_BITS_MISMATCH;
- goto out;
- } else if (len < modlen) {
- diff = modlen - len;
- osigblob = sigblob;
- if ((sigblob = realloc(sigblob, modlen)) == NULL) {
- sigblob = osigblob; /* put it back for clear/free */
- ret = SSH_ERR_ALLOC_FAIL;
- goto out;
- }
- memmove(sigblob + diff, sigblob, len);
- explicit_bzero(sigblob, diff);
- len = modlen;
- }
- if ((pkey = EVP_PKEY_new()) == NULL ||
- EVP_PKEY_set1_RSA(pkey, key->rsa) != 1) {
- ret = SSH_ERR_ALLOC_FAIL;
- goto out;
- }
- ret = openssh_RSA_verify(hash_alg, data, datalen, sigblob, len, pkey);
- EVP_PKEY_free(pkey);
- out:
- freezero(sigblob, len);
- free(sigtype);
- sshbuf_free(b);
- explicit_bzero(digest, sizeof(digest));
- return ret;
- }
- static int
- openssh_RSA_verify(int hash_alg, const u_char *data, size_t datalen,
- u_char *sigbuf, size_t siglen, EVP_PKEY *pkey)
- {
- size_t rsasize = 0;
- const RSA *rsa;
- int ret;
- rsa = EVP_PKEY_get0_RSA(pkey);
- rsasize = RSA_size(rsa);
- if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
- siglen == 0 || siglen > rsasize) {
- ret = SSH_ERR_INVALID_ARGUMENT;
- goto done;
- }
- ret = sshkey_verify_signature(pkey, hash_alg, data, datalen,
- sigbuf, siglen);
- done:
- return ret;
- }
- #endif /* WITH_OPENSSL */
|