diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-06-21 06:21:04 +0200 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-06-21 06:24:35 +0200 |
commit | 4f7a56d5e02e3d04ab69eac1213817a7536d0562 (patch) | |
tree | b17da67f20831b53f9b00c6647c5eb1bdf88d626 | |
parent | upstream: print the correct AuthorizedPrincipalsCommand rather than (diff) | |
download | openssh-4f7a56d5e02e3d04ab69eac1213817a7536d0562.tar.xz openssh-4f7a56d5e02e3d04ab69eac1213817a7536d0562.zip |
upstream: Add protection for private keys at rest in RAM against
speculation and memory sidechannel attacks like Spectre, Meltdown, Rowhammer
and Rambleed. This change encrypts private keys when they are not in use with
a symmetic key that is derived from a relatively large "prekey" consisting of
random data (currently 16KB).
Attackers must recover the entire prekey with high accuracy before
they can attempt to decrypt the shielded private key, but the current
generation of attacks have bit error rates that, when applied
cumulatively to the entire prekey, make this unlikely.
Implementation-wise, keys are encrypted "shielded" when loaded and then
automatically and transparently unshielded when used for signatures or
when being saved/serialised.
Hopefully we can remove this in a few years time when computer
architecture has become less unsafe.
been in snaps for a bit already; thanks deraadt@
ok dtucker@ deraadt@
OpenBSD-Commit-ID: 19767213c312e46f94b303a512ef8e9218a39bd4
-rw-r--r-- | authfd.c | 4 | ||||
-rw-r--r-- | authfd.h | 4 | ||||
-rw-r--r-- | krl.c | 4 | ||||
-rw-r--r-- | krl.h | 4 | ||||
-rw-r--r-- | ssh-agent.c | 7 | ||||
-rw-r--r-- | ssh-keygen.c | 4 | ||||
-rw-r--r-- | sshconnect.c | 4 | ||||
-rw-r--r-- | sshconnect.h | 4 | ||||
-rw-r--r-- | sshd.c | 10 | ||||
-rw-r--r-- | sshkey.c | 299 | ||||
-rw-r--r-- | sshkey.h | 21 |
11 files changed, 324 insertions, 41 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.113 2018/12/27 23:02:11 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.114 2019/06/21 04:21:04 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -423,7 +423,7 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign) * This call is intended only for use by ssh-add(1) and like applications. */ int -ssh_add_identity_constrained(int sock, const struct sshkey *key, +ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment, u_int life, u_int confirm, u_int maxsign) { struct sshbuf *msg; @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.44 2018/07/12 04:35:25 djm Exp $ */ +/* $OpenBSD: authfd.h,v 1.45 2019/06/21 04:21:04 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -29,7 +29,7 @@ void ssh_close_authentication_socket(int sock); int ssh_lock_agent(int sock, int lock, const char *password); int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); void ssh_free_identitylist(struct ssh_identitylist *idl); -int ssh_add_identity_constrained(int sock, const struct sshkey *key, +int ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment, u_int life, u_int confirm, u_int maxsign); int ssh_remove_identity(int sock, struct sshkey *key); int ssh_update_card(int sock, int add, const char *reader_id, @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.42 2018/09/12 01:21:34 djm Exp $ */ +/* $OpenBSD: krl.c,v 1.43 2019/06/21 04:21:04 djm Exp $ */ #include "includes.h" @@ -732,7 +732,7 @@ revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf) int ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, - const struct sshkey **sign_keys, u_int nsign_keys) + struct sshkey **sign_keys, u_int nsign_keys) { int r = SSH_ERR_INTERNAL_ERROR; struct revoked_certs *rc; @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.h,v 1.6 2018/09/12 01:21:34 djm Exp $ */ +/* $OpenBSD: krl.h,v 1.7 2019/06/21 04:21:04 djm Exp $ */ #ifndef _KRL_H #define _KRL_H @@ -56,7 +56,7 @@ int ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len); int ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len); int ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key); int ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, - const struct sshkey **sign_keys, u_int nsign_keys); + struct sshkey **sign_keys, u_int nsign_keys); int ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, const struct sshkey **sign_ca_keys, size_t nsign_ca_keys); int ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key); diff --git a/ssh-agent.c b/ssh-agent.c index 4669b679c..4d7ab225f 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.235 2019/06/14 03:51:47 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.236 2019/06/21 04:21:04 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -423,7 +423,10 @@ process_add_identity(SocketEntry *e) error("%s: decode private key: %s", __func__, ssh_err(r)); goto err; } - + if ((r = sshkey_shield_private(k)) != 0) { + error("%s: shield private key: %s", __func__, ssh_err(r)); + goto err; + } while (sshbuf_len(e->request)) { if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { error("%s: buffer error: %s", __func__, ssh_err(r)); diff --git a/ssh-keygen.c b/ssh-keygen.c index 010667157..c95bc15cf 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.331 2019/06/06 05:13:13 otto Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.332 2019/06/21 04:21:04 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1654,7 +1654,7 @@ load_pkcs11_key(char *path) /* Signer for sshkey_certify_custom that uses the agent */ static int -agent_signer(const struct sshkey *key, u_char **sigp, size_t *lenp, +agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat, void *ctx) { diff --git a/sshconnect.c b/sshconnect.c index c57f1a0ff..2dc500b47 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.315 2019/05/03 03:27:38 dtucker Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.316 2019/06/21 04:21:04 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1401,7 +1401,7 @@ ssh_local_cmd(const char *args) } void -maybe_add_key_to_agent(char *authfile, const struct sshkey *private, +maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment, char *passphrase) { int auth_sock = -1, r; diff --git a/sshconnect.h b/sshconnect.h index 6e8989b27..b455d7c20 100644 --- a/sshconnect.h +++ b/sshconnect.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.h,v 1.37 2019/01/19 21:36:38 djm Exp $ */ +/* $OpenBSD: sshconnect.h,v 1.38 2019/06/21 04:21:05 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -52,4 +52,4 @@ void ssh_userauth2(struct ssh *ssh, const char *, const char *, int ssh_local_cmd(const char *); -void maybe_add_key_to_agent(char *, const struct sshkey *, char *, char *); +void maybe_add_key_to_agent(char *, struct sshkey *, char *, char *); @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.535 2019/06/06 05:13:13 otto Exp $ */ +/* $OpenBSD: sshd.c,v 1.536 2019/06/21 04:21:05 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1375,7 +1375,7 @@ set_process_rdomain(struct ssh *ssh, const char *name) static void accumulate_host_timing_secret(struct sshbuf *server_cfg, - const struct sshkey *key) + struct sshkey *key) { static struct ssh_digest_ctx *ctx; u_char *hash; @@ -1723,6 +1723,12 @@ main(int ac, char **av) &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) do_log2(ll, "Unable to load host key \"%s\": %s", options.host_key_files[i], ssh_err(r)); + if (r == 0 && (r = sshkey_shield_private(key)) != 0) { + do_log2(ll, "Unable to shield host key \"%s\": %s", + options.host_key_files[i], ssh_err(r)); + sshkey_free(key); + key = NULL; + } if ((r = sshkey_load_public(options.host_key_files[i], &pubkey, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) do_log2(ll, "Unable to load host key \"%s\": %s", @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.75 2019/05/20 00:20:35 djm Exp $ */ +/* $OpenBSD: sshkey.c,v 1.76 2019/06/21 04:21:05 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -78,7 +78,15 @@ /* Version identification string for SSH v1 identity files. */ #define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" -int sshkey_private_serialize_opt(const struct sshkey *key, +/* + * Constants relating to "shielding" support; protection of keys expected + * to remain in memory for long durations + */ +#define SSHKEY_SHIELD_PREKEY_LEN (16 * 1024) +#define SSHKEY_SHIELD_CIPHER "aes256-ctr" /* XXX want AES-EME* */ +#define SSHKEY_SHIELD_PREKEY_HASH SSH_DIGEST_SHA512 + +int sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, enum sshkey_serialize_rep); static int sshkey_from_blob_internal(struct sshbuf *buf, struct sshkey **keyp, int allow_cert); @@ -604,6 +612,8 @@ sshkey_free(struct sshkey *k) } if (sshkey_is_cert(k)) cert_free(k->cert); + freezero(k->shielded_private, k->shielded_len); + freezero(k->shield_prekey, k->shield_prekey_len); freezero(k, sizeof(*k)); } @@ -1869,6 +1879,218 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) return r; } +int +sshkey_is_shielded(struct sshkey *k) +{ + return k != NULL && k->shielded_private != NULL; +} + +int +sshkey_shield_private(struct sshkey *k) +{ + struct sshbuf *prvbuf = NULL; + u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH]; + struct sshcipher_ctx *cctx = NULL; + const struct sshcipher *cipher; + size_t i, enclen = 0; + struct sshkey *kswap = NULL, tmp; + int r = SSH_ERR_INTERNAL_ERROR; + +#ifdef DEBUG_PK + fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); +#endif + if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (cipher_keylen(cipher) + cipher_ivlen(cipher) > + ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + + /* Prepare a random pre-key, and from it an ephemeral key */ + if ((prekey = malloc(SSHKEY_SHIELD_PREKEY_LEN)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN); + if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, + prekey, SSHKEY_SHIELD_PREKEY_LEN, + keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: key+iv\n", __func__); + sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), + stderr); +#endif + if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), + keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0) + goto out; + + /* Serialise and encrypt the private key using the ephemeral key */ + if ((prvbuf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0) + goto out; + if ((r = sshkey_private_serialize_opt(k, prvbuf, + SSHKEY_SERIALIZE_FULL)) != 0) + goto out; + /* pad to cipher blocksize */ + i = 0; + while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) { + if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0) + goto out; + } +#ifdef DEBUG_PK + fprintf(stderr, "%s: serialised\n", __func__); + sshbuf_dump(prvbuf, stderr); +#endif + /* encrypt */ + enclen = sshbuf_len(prvbuf); + if ((enc = malloc(enclen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = cipher_crypt(cctx, 0, enc, + sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: encrypted\n", __func__); + sshbuf_dump_data(enc, enclen, stderr); +#endif + + /* Make a scrubbed, public-only copy of our private key argument */ + if ((r = sshkey_from_private(k, &kswap)) != 0) + goto out; + + /* Swap the private key out (it will be destroyed below) */ + tmp = *kswap; + *kswap = *k; + *k = tmp; + + /* Insert the shielded key into our argument */ + k->shielded_private = enc; + k->shielded_len = enclen; + k->shield_prekey = prekey; + k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN; + enc = prekey = NULL; /* transferred */ + enclen = 0; + + /* success */ + r = 0; + + out: + /* XXX behaviour on error - invalidate original private key? */ + cipher_free(cctx); + explicit_bzero(enc, enclen); + explicit_bzero(keyiv, sizeof(keyiv)); + explicit_bzero(&tmp, sizeof(tmp)); + freezero(prekey, SSHKEY_SHIELD_PREKEY_LEN); + sshkey_free(kswap); + sshbuf_free(prvbuf); + return r; +} + +int +sshkey_unshield_private(struct sshkey *k) +{ + struct sshbuf *prvbuf = NULL; + u_char pad, *cp, keyiv[SSH_DIGEST_MAX_LENGTH]; + struct sshcipher_ctx *cctx = NULL; + const struct sshcipher *cipher; + size_t i; + struct sshkey *kswap = NULL, tmp; + int r = SSH_ERR_INTERNAL_ERROR; + +#ifdef DEBUG_PK + fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); +#endif + if (!sshkey_is_shielded(k)) + return 0; /* nothing to do */ + + if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (cipher_keylen(cipher) + cipher_ivlen(cipher) > + ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + /* check size of shielded key blob */ + if (k->shielded_len < cipher_blocksize(cipher) || + (k->shielded_len % cipher_blocksize(cipher)) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* Calculate the ephemeral key from the prekey */ + if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, + k->shield_prekey, k->shield_prekey_len, + keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) + goto out; + if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), + keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: key+iv\n", __func__); + sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), + stderr); +#endif + + /* Decrypt and parse the shielded private key using the ephemeral key */ + if ((prvbuf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0) + goto out; + /* decrypt */ +#ifdef DEBUG_PK + fprintf(stderr, "%s: encrypted\n", __func__); + sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr); +#endif + if ((r = cipher_crypt(cctx, 0, cp, + k->shielded_private, k->shielded_len, 0, 0)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: serialised\n", __func__); + sshbuf_dump(prvbuf, stderr); +#endif + /* Parse private key */ + if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0) + goto out; + /* Check deterministic padding */ + i = 0; + while (sshbuf_len(prvbuf)) { + if ((r = sshbuf_get_u8(prvbuf, &pad)) != 0) + goto out; + if (pad != (++i & 0xff)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* Swap the parsed key back into place */ + tmp = *kswap; + *kswap = *k; + *k = tmp; + + /* success */ + r = 0; + + out: + cipher_free(cctx); + explicit_bzero(keyiv, sizeof(keyiv)); + explicit_bzero(&tmp, sizeof(tmp)); + sshkey_free(kswap); + sshbuf_free(prvbuf); + return r; +} + static int cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf) { @@ -2373,41 +2595,55 @@ sshkey_check_sigtype(const u_char *sig, size_t siglen, } int -sshkey_sign(const struct sshkey *key, +sshkey_sign(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat) { + int was_shielded = sshkey_is_shielded(key); + int r2, r = SSH_ERR_INTERNAL_ERROR; + if (sigp != NULL) *sigp = NULL; if (lenp != NULL) *lenp = 0; if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_unshield_private(key)) != 0) + return r; switch (key->type) { #ifdef WITH_OPENSSL case KEY_DSA_CERT: case KEY_DSA: - return ssh_dss_sign(key, sigp, lenp, data, datalen, compat); + r = ssh_dss_sign(key, sigp, lenp, data, datalen, compat); + break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: case KEY_ECDSA: - return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat); + r = ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat); + break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA_CERT: case KEY_RSA: - return ssh_rsa_sign(key, sigp, lenp, data, datalen, alg); + r = ssh_rsa_sign(key, sigp, lenp, data, datalen, alg); + break; #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: - return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); + r = ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); + break; #ifdef WITH_XMSS case KEY_XMSS: case KEY_XMSS_CERT: - return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat); + r = ssh_xmss_sign(key, sigp, lenp, data, datalen, compat); + break; #endif /* WITH_XMSS */ default: - return SSH_ERR_KEY_TYPE_UNKNOWN; + r = SSH_ERR_KEY_TYPE_UNKNOWN; + break; } + if (was_shielded && (r2 = sshkey_shield_private(key)) != 0) + return r2; + return r; } /* @@ -2652,7 +2888,7 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, } static int -default_key_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, +default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat, void *ctx) { @@ -2762,15 +2998,21 @@ sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l) } int -sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b, +sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, enum sshkey_serialize_rep opts) { int r = SSH_ERR_INTERNAL_ERROR; + int was_shielded = sshkey_is_shielded(key); + struct sshbuf *b = NULL; #ifdef WITH_OPENSSL const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q; const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key, *dsa_priv_key; #endif /* WITH_OPENSSL */ + if ((r = sshkey_unshield_private(key)) != 0) + return r; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0) goto out; switch (key->type) { @@ -2896,14 +3138,23 @@ sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b, r = SSH_ERR_INVALID_ARGUMENT; goto out; } - /* success */ + /* + * success (but we still need to append the output to buf after + * possibly re-shielding the private key) + */ r = 0; out: + if (was_shielded) + r = sshkey_shield_private(key); + if (r == 0) + r = sshbuf_putb(buf, b); + sshbuf_free(b); + return r; } int -sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) +sshkey_private_serialize(struct sshkey *key, struct sshbuf *b) { return sshkey_private_serialize_opt(key, b, SSHKEY_SERIALIZE_DEFAULT); @@ -3358,7 +3609,7 @@ sshkey_dump_ec_key(const EC_KEY *key) #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ static int -sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob, +sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob, const char *passphrase, const char *comment, const char *ciphername, int rounds) { @@ -3728,20 +3979,28 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, #ifdef WITH_OPENSSL /* convert SSH v2 key in OpenSSL PEM format */ static int -sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, +sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, const char *_passphrase, const char *comment) { + int was_shielded = sshkey_is_shielded(key); int success, r; int blen, len = strlen(_passphrase); u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; char *bptr; BIO *bio = NULL; + struct sshbuf *blob; if (len > 0 && len <= 4) return SSH_ERR_PASSPHRASE_TOO_SHORT; - if ((bio = BIO_new(BIO_s_mem())) == NULL) + if ((blob = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; + if ((bio = BIO_new(BIO_s_mem())) == NULL) { + sshbuf_free(blob); + return SSH_ERR_ALLOC_FAIL; + } + if ((r = sshkey_unshield_private(key)) != 0) + goto out; switch (key->type) { case KEY_DSA: @@ -3774,6 +4033,12 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, goto out; r = 0; out: + if (was_shielded) + r = sshkey_shield_private(key); + if (r == 0) + r = sshbuf_putb(buf, blob); + sshbuf_free(blob); + BIO_free(bio); return r; } @@ -4102,7 +4367,7 @@ sshkey_set_filename(struct sshkey *k, const char *filename) } #else int -sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b, +sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, u_int32_t maxsign, sshkey_printfn *pr) { return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT); @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.31 2019/01/20 22:51:37 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.32 2019/06/21 04:21:05 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -123,6 +123,10 @@ struct sshkey { u_char *xmss_sk; u_char *xmss_pk; struct sshkey_cert *cert; + u_char *shielded_private; + size_t shielded_len; + u_char *shield_prekey; + size_t shield_prekey_len; }; #define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES @@ -146,6 +150,11 @@ u_int sshkey_size(const struct sshkey *); int sshkey_generate(int type, u_int bits, struct sshkey **keyp); int sshkey_from_private(const struct sshkey *, struct sshkey **); + +int sshkey_is_shielded(struct sshkey *); +int sshkey_shield_private(struct sshkey *); +int sshkey_unshield_private(struct sshkey *); + int sshkey_type_from_name(const char *); int sshkey_is_cert(const struct sshkey *); int sshkey_type_is_cert(int); @@ -161,7 +170,7 @@ int sshkey_check_cert_sigtype(const struct sshkey *, const char *); int sshkey_certify(struct sshkey *, struct sshkey *, const char *); /* Variant allowing use of a custom signature function (e.g. for ssh-agent) */ -typedef int sshkey_certify_signer(const struct sshkey *, u_char **, size_t *, +typedef int sshkey_certify_signer(struct sshkey *, u_char **, size_t *, const u_char *, size_t, const char *, u_int, void *); int sshkey_certify_custom(struct sshkey *, struct sshkey *, const char *, sshkey_certify_signer *, void *); @@ -192,7 +201,7 @@ int sshkey_puts_opts(const struct sshkey *, struct sshbuf *, int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); -int sshkey_sign(const struct sshkey *, u_char **, size_t *, +int sshkey_sign(struct sshkey *, u_char **, size_t *, const u_char *, size_t, const char *, u_int); int sshkey_verify(const struct sshkey *, const u_char *, size_t, const u_char *, size_t, const char *, u_int); @@ -204,8 +213,8 @@ void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); void sshkey_dump_ec_key(const EC_KEY *); /* private key parsing and serialisation */ -int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); -int sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *buf, +int sshkey_private_serialize(struct sshkey *key, struct sshbuf *buf); +int sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, enum sshkey_serialize_rep); int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); @@ -231,7 +240,7 @@ int sshkey_set_filename(struct sshkey *, const char *); int sshkey_enable_maxsign(struct sshkey *, u_int32_t); u_int32_t sshkey_signatures_left(const struct sshkey *); int sshkey_forward_state(const struct sshkey *, u_int32_t, sshkey_printfn *); -int sshkey_private_serialize_maxsign(const struct sshkey *key, struct sshbuf *buf, +int sshkey_private_serialize_maxsign(struct sshkey *key, struct sshbuf *buf, u_int32_t maxsign, sshkey_printfn *pr); #ifdef SSHKEY_INTERNAL |