diff options
Diffstat (limited to 'ssh-rsa.c')
-rw-r--r-- | ssh-rsa.c | 281 |
1 files changed, 232 insertions, 49 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-rsa.c,v 1.52 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: ssh-rsa.c,v 1.67 2018/07/03 11:39:54 djm Exp $ */ /* * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> * @@ -33,19 +33,144 @@ #define SSHKEY_INTERNAL #include "sshkey.h" #include "digest.h" +#include "log.h" + +#include "openbsd-compat/openssl-compat.h" static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); +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; +} + +static int +rsa_hash_alg_nid(int type) +{ + switch (type) { + case SSH_DIGEST_SHA1: + return NID_sha1; + case SSH_DIGEST_SHA256: + return NID_sha256; + case SSH_DIGEST_SHA512: + return NID_sha512; + default: + 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; + } +#if !defined(OPENSSL_IS_BORINGSSL) + BN_set_flags(aux, BN_FLG_CONSTTIME); + BN_set_flags(d_consttime, BN_FLG_CONSTTIME); +#endif + + 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)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (!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, u_int compat) + const u_char *data, size_t datalen, const char *alg_ident) { - int hash_alg; + const BIGNUM *rsa_n; u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; - size_t slen; + size_t slen = 0; u_int dlen, len; - int nid, ret = SSH_ERR_INTERNAL_ERROR; + int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; if (lenp != NULL) @@ -53,16 +178,22 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, if (sigp != NULL) *sigp = NULL; - if (key == NULL || key->rsa == 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; + RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); + if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; slen = RSA_size(key->rsa); if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) return SSH_ERR_INVALID_ARGUMENT; /* hash the data */ - hash_alg = SSH_DIGEST_SHA1; - nid = NID_sha1; + nid = rsa_hash_alg_nid(hash_alg); if ((dlen = ssh_digest_bytes(hash_alg)) == 0) return SSH_ERR_INTERNAL_ERROR; if ((ret = ssh_digest_memory(hash_alg, data, datalen, @@ -91,7 +222,7 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, ret = SSH_ERR_ALLOC_FAIL; goto out; } - if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 || + 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); @@ -107,41 +238,55 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, ret = 0; out: explicit_bzero(digest, sizeof(digest)); - if (sig != NULL) { - explicit_bzero(sig, slen); - free(sig); - } - if (b != NULL) - sshbuf_free(b); - return 0; + freezero(sig, slen); + sshbuf_free(b); + return ret; } int ssh_rsa_verify(const struct sshkey *key, - const u_char *signature, size_t signaturelen, - const u_char *data, size_t datalen, u_int compat) + const u_char *sig, size_t siglen, const u_char *data, size_t datalen, + const char *alg) { - char *ktype = NULL; - int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; - size_t len, diff, modlen, dlen; + const BIGNUM *rsa_n; + char *sigtype = NULL; + int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR; + size_t len = 0, diff, modlen, dlen; 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 || - BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + sig == NULL || siglen == 0) return SSH_ERR_INVALID_ARGUMENT; + RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); + if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; - if ((b = sshbuf_from(signature, signaturelen)) == NULL) + if ((b = sshbuf_from(sig, siglen)) == NULL) return SSH_ERR_ALLOC_FAIL; - if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + if (sshbuf_get_cstring(b, &sigtype, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } - if (strcmp("ssh-rsa", ktype) != 0) { + 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; @@ -167,7 +312,6 @@ ssh_rsa_verify(const struct sshkey *key, explicit_bzero(sigblob, diff); len = modlen; } - hash_alg = SSH_DIGEST_SHA1; if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { ret = SSH_ERR_INTERNAL_ERROR; goto out; @@ -179,14 +323,9 @@ ssh_rsa_verify(const struct sshkey *key, ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, key->rsa); out: - if (sigblob != NULL) { - explicit_bzero(sigblob, len); - free(sigblob); - } - if (ktype != NULL) - free(ktype); - if (b != NULL) - sshbuf_free(b); + freezero(sigblob, len); + free(sigtype); + sshbuf_free(b); explicit_bzero(digest, sizeof(digest)); return ret; } @@ -196,6 +335,7 @@ ssh_rsa_verify(const struct sshkey *key, * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn */ + /* * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) * oiw(14) secsig(3) algorithms(2) 26 } @@ -209,25 +349,71 @@ static const u_char id_sha1[] = { 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ }; +/* + * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html + * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) + * id-sha256(1) } + */ +static const u_char id_sha256[] = { + 0x30, 0x31, /* type Sequence, length 0x31 (49) */ + 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ + 0x06, 0x09, /* type OID, length 0x09 */ + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ + 0x05, 0x00, /* NULL */ + 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ +}; + +/* + * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html + * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) + * id-sha256(3) } + */ +static const u_char id_sha512[] = { + 0x30, 0x51, /* type Sequence, length 0x51 (81) */ + 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ + 0x06, 0x09, /* type OID, length 0x09 */ + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ + 0x05, 0x00, /* NULL */ + 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ +}; + +static int +rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) +{ + switch (hash_alg) { + case SSH_DIGEST_SHA1: + *oidp = id_sha1; + *oidlenp = sizeof(id_sha1); + break; + case SSH_DIGEST_SHA256: + *oidp = id_sha256; + *oidlenp = sizeof(id_sha256); + break; + case SSH_DIGEST_SHA512: + *oidp = id_sha512; + *oidlenp = sizeof(id_sha512); + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + return 0; +} + static int openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, u_char *sigbuf, size_t siglen, RSA *rsa) { - size_t ret, rsasize = 0, oidlen = 0, hlen = 0; - int len, oidmatch, hashmatch; + size_t rsasize = 0, oidlen = 0, hlen = 0; + int ret, len, oidmatch, hashmatch; const u_char *oid = NULL; u_char *decrypted = NULL; + if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) + return ret; ret = SSH_ERR_INTERNAL_ERROR; - switch (hash_alg) { - case SSH_DIGEST_SHA1: - oid = id_sha1; - oidlen = sizeof(id_sha1); - hlen = 20; - break; - default: - goto done; - } + hlen = ssh_digest_bytes(hash_alg); if (hashlen != hlen) { ret = SSH_ERR_INVALID_ARGUMENT; goto done; @@ -259,10 +445,7 @@ openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, } ret = 0; done: - if (decrypted) { - explicit_bzero(decrypted, rsasize); - free(decrypted); - } + freezero(decrypted, rsasize); return ret; } #endif /* WITH_OPENSSL */ |