]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: Add protection for private keys at rest in RAM against
authordjm@openbsd.org <djm@openbsd.org>
Fri, 21 Jun 2019 04:21:04 +0000 (04:21 +0000)
committerDamien Miller <djm@mindrot.org>
Fri, 21 Jun 2019 04:24:35 +0000 (14:24 +1000)
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

authfd.c
authfd.h
krl.c
krl.h
ssh-agent.c
ssh-keygen.c
sshconnect.c
sshconnect.h
sshd.c
sshkey.c
sshkey.h

index 95348abfceca72e947ccfd915b40ad9e6f829223..327a333d261efe986b35f116787b8fa93171155d 100644 (file)
--- a/authfd.c
+++ b/authfd.c
@@ -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;
index a032fd5428fb2c1c637587480b0e81afe133d025..060bed63f74222346d39f85e1fba71ece9492480 100644 (file)
--- a/authfd.h
+++ b/authfd.h
@@ -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,
diff --git a/krl.c b/krl.c
index 8e2d5d5df9f8283eb27b3fdf02be34357bb6e314..bb960882fd28084d23d16995426c88ca1a177b48 100644 (file)
--- a/krl.c
+++ b/krl.c
@@ -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;
diff --git a/krl.h b/krl.h
index 815a1df4ebab7c3825f89a395b424ca3459b2541..ce534a11136dfc93e3f3f789892b57ca0a62ed61 100644 (file)
--- a/krl.h
+++ b/krl.h
@@ -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);
index 4669b679c555d34a29579f69ff389239bb646c14..4d7ab225f1885d23bf7ddddfecf0a15c223d5207 100644 (file)
@@ -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));
index 010667157966b6fa6826390ed2cfd3c1d2c04c4c..c95bc15cfefdaacb6b34ac9b89ff055f6f889b74 100644 (file)
@@ -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)
 {
index c57f1a0fff8c08f5d62e4cf9ef3b8ddd84d13bc5..2dc500b472f521f7269ec47750bdd5212dbf4ee8 100644 (file)
@@ -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;
index 6e8989b27b80400f7a16ba082fbe44e4f0ad96ca..b455d7c20b59c9d3a2e64454058539401935df2e 100644 (file)
@@ -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 *);
diff --git a/sshd.c b/sshd.c
index be23fbc80eb69ca231c3668bb8a692a3cd7a3f02..735a11060a81d1497b6b622c971a72ce827fb694 100644 (file)
--- a/sshd.c
+++ b/sshd.c
@@ -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",
index 379a579cf83d8d43520ef45a92b8656d04429293..7aa7e772c6591c66c998e7c9cb29c9dcd84296d1 100644 (file)
--- a/sshkey.c
+++ b/sshkey.c
@@ -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.
 /* 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);
index a91e60436358f49370a9962b70a2e7d204ac92f5..41d159a1bf4f1cab6d65962ee50e8ccadfc2b6b2 100644 (file)
--- a/sshkey.h
+++ b/sshkey.h
@@ -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