From 5d6a14bd4a3aeda6b651d850d5e80d54c29c3829 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Jul 23 2019 07:49:22 +0000 Subject: Use the upstream version of the PKCS#8 PEM support (#1722285) --- diff --git a/openssh-8.0p1-openssl-pem.patch b/openssh-8.0p1-openssl-pem.patch index 28becd0..7e4fa81 100644 --- a/openssh-8.0p1-openssl-pem.patch +++ b/openssh-8.0p1-openssl-pem.patch @@ -1,14 +1,176 @@ -commit 2fe812887139ce32eeca52f9a0c141bdc7c4c8af -Author: Jakub Jelen -Date: Wed May 22 17:25:22 2019 +0200 +From eb0d8e708a1f958aecd2d6e2ff2450af488d4c2a Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Mon, 15 Jul 2019 13:16:29 +0000 +Subject: [PATCH] upstream: support PKCS8 as an optional format for storage of - New PEM export format withou MD5 +private keys, enabled via "ssh-keygen -m PKCS8" on operations that save +private keys to disk. +The OpenSSH native key format remains the default, but PKCS8 is a +superior format to PEM if interoperability with non-OpenSSH software +is required, as it may use a less terrible KDF (IIRC PEM uses a single +round of MD5 as a KDF). + +adapted from patch by Jakub Jelen via bz3013; ok markus + +OpenBSD-Commit-ID: 027824e3bc0b1c243dc5188504526d73a55accb1 +--- + authfile.c | 6 ++-- + ssh-keygen.1 | 9 +++--- + ssh-keygen.c | 25 +++++++++-------- + sshkey.c | 78 +++++++++++++++++++++++++++++++++++++--------------- + sshkey.h | 11 ++++++-- + 5 files changed, 87 insertions(+), 42 deletions(-) + +diff --git a/authfile.c b/authfile.c +index 2166c1689..851c1a8a1 100644 +--- a/authfile.c ++++ b/authfile.c +@@ -74,7 +74,7 @@ sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) + int + sshkey_save_private(struct sshkey *key, const char *filename, + const char *passphrase, const char *comment, +- int force_new_format, const char *new_format_cipher, int new_format_rounds) ++ int format, const char *openssh_format_cipher, int openssh_format_rounds) + { + struct sshbuf *keyblob = NULL; + int r; +@@ -82,7 +82,7 @@ sshkey_save_private(struct sshkey *key, const char *filename, + if ((keyblob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, +- force_new_format, new_format_cipher, new_format_rounds)) != 0) ++ format, openssh_format_cipher, openssh_format_rounds)) != 0) + goto out; + if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) + goto out; +diff --git a/ssh-keygen.1 b/ssh-keygen.1 +index f42127c60..8184a1797 100644 +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -419,11 +419,12 @@ The supported key formats are: + .Dq RFC4716 + (RFC 4716/SSH2 public or private key), + .Dq PKCS8 +-(PEM PKCS8 public key) ++(PKCS8 public or private key) + or + .Dq PEM + (PEM public key). +-The default conversion format is ++By default OpenSSH will write newly-generated private keys in its own ++format, but when converting public keys for export the default format is + .Dq RFC4716 . + Setting a format of + .Dq PEM +diff --git a/ssh-keygen.c b/ssh-keygen.c +index b019a02ff..5dcad1f61 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -147,11 +147,11 @@ static char *key_type_name = NULL; + /* Load key from this PKCS#11 provider */ + static char *pkcs11provider = NULL; + +-/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */ +-static int use_new_format = 1; ++/* Format for writing private keys */ ++static int private_key_format = SSHKEY_PRIVATE_OPENSSH; + + /* Cipher for new-format private keys */ +-static char *new_format_cipher = NULL; ++static char *openssh_format_cipher = NULL; + + /* + * Number of KDF rounds to derive new format keys / +@@ -1048,7 +1048,8 @@ do_gen_all_hostkeys(struct passwd *pw) + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, + hostname); + if ((r = sshkey_save_private(private, prv_tmp, "", +- comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ comment, private_key_format, openssh_format_cipher, ++ rounds)) != 0) { + error("Saving key \"%s\" failed: %s", + prv_tmp, ssh_err(r)); + goto failnext; +@@ -1391,7 +1392,7 @@ do_change_passphrase(struct passwd *pw) + + /* Save the file using the new passphrase. */ + if ((r = sshkey_save_private(private, identity_file, passphrase1, +- comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ comment, private_key_format, openssh_format_cipher, rounds)) != 0) { + error("Saving key \"%s\" failed: %s.", + identity_file, ssh_err(r)); + explicit_bzero(passphrase1, strlen(passphrase1)); +@@ -1480,7 +1481,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) + } + + if (private->type != KEY_ED25519 && private->type != KEY_XMSS && +- !use_new_format) { ++ private_key_format != SSHKEY_PRIVATE_OPENSSH) { + error("Comments are only supported for keys stored in " + "the new format (-o)."); + explicit_bzero(passphrase, strlen(passphrase)); +@@ -1514,7 +1515,8 @@ do_change_comment(struct passwd *pw, const char *identity_comment) + + /* Save the file using the new passphrase. */ + if ((r = sshkey_save_private(private, identity_file, passphrase, +- new_comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ new_comment, private_key_format, openssh_format_cipher, ++ rounds)) != 0) { + error("Saving key \"%s\" failed: %s", + identity_file, ssh_err(r)); + explicit_bzero(passphrase, strlen(passphrase)); +@@ -2525,11 +2527,12 @@ main(int argc, char **argv) + } + if (strcasecmp(optarg, "PKCS8") == 0) { + convert_format = FMT_PKCS8; ++ private_key_format = SSHKEY_PRIVATE_PKCS8; + break; + } + if (strcasecmp(optarg, "PEM") == 0) { + convert_format = FMT_PEM; +- use_new_format = 0; ++ private_key_format = SSHKEY_PRIVATE_PEM; + break; + } + fatal("Unsupported conversion format \"%s\"", optarg); +@@ -2567,7 +2570,7 @@ main(int argc, char **argv) + add_cert_option(optarg); + break; + case 'Z': +- new_format_cipher = optarg; ++ openssh_format_cipher = optarg; + break; + case 'C': + identity_comment = optarg; +@@ -2912,7 +2915,7 @@ main(int argc, char **argv) + + /* Save the key with the given passphrase and comment. */ + if ((r = sshkey_save_private(private, identity_file, passphrase1, +- comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ comment, private_key_format, openssh_format_cipher, rounds)) != 0) { + error("Saving key \"%s\" failed: %s", + identity_file, ssh_err(r)); + explicit_bzero(passphrase1, strlen(passphrase1)); diff --git a/sshkey.c b/sshkey.c -index b95ed0b1..1a271512 100644 +index 6b5ff0485..a0cea9257 100644 --- a/sshkey.c +++ b/sshkey.c -@@ -3805,26 +3805,28 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, +@@ -3975,10 +3975,10 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, + + + #ifdef WITH_OPENSSL +-/* convert SSH v2 key in OpenSSL PEM format */ ++/* convert SSH v2 key to PEM or PKCS#8 format */ + static int +-sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, +- const char *_passphrase, const char *comment) ++sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *blob, ++ int format, const char *_passphrase, const char *comment) + { + int success, r; + int blen, len = strlen(_passphrase); +@@ -3988,26 +3988,46 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; char *bptr; BIO *bio = NULL; @@ -16,47 +178,68 @@ index b95ed0b1..1a271512 100644 if (len > 0 && len <= 4) return SSH_ERR_PASSPHRASE_TOO_SHORT; - if ((bio = BIO_new(BIO_s_mem())) == NULL) - return SSH_ERR_ALLOC_FAIL; -+ if ((pkey = EVP_PKEY_new()) == NULL) { -+ BIO_free(bio); -+ return SSH_ERR_ALLOC_FAIL; -+ } +- if ((bio = BIO_new(BIO_s_mem())) == NULL) +- return SSH_ERR_ALLOC_FAIL; ++ if ((bio = BIO_new(BIO_s_mem())) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if (format == SSHKEY_PRIVATE_PKCS8 && (pkey = EVP_PKEY_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } switch (key->type) { case KEY_DSA: - success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, - cipher, passphrase, len, NULL, NULL); -+ success = EVP_PKEY_set1_DSA(pkey, key->dsa); ++ if (format == SSHKEY_PRIVATE_PEM) { ++ success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, ++ cipher, passphrase, len, NULL, NULL); ++ } else { ++ success = EVP_PKEY_set1_DSA(pkey, key->dsa); ++ } break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: - success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, - cipher, passphrase, len, NULL, NULL); -+ success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa); ++ if (format == SSHKEY_PRIVATE_PEM) { ++ success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, ++ cipher, passphrase, len, NULL, NULL); ++ } else { ++ success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa); ++ } break; #endif case KEY_RSA: - success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, - cipher, passphrase, len, NULL, NULL); -+ success = EVP_PKEY_set1_RSA(pkey, key->rsa); ++ if (format == SSHKEY_PRIVATE_PEM) { ++ success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, ++ cipher, passphrase, len, NULL, NULL); ++ } else { ++ success = EVP_PKEY_set1_RSA(pkey, key->rsa); ++ } break; default: success = 0; -@@ -3834,6 +3836,12 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, +@@ -4023,6 +4040,13 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } -+ success = PEM_write_bio_PrivateKey(bio, pkey, -+ cipher, passphrase, len, NULL, NULL); -+ if (success == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; ++ if (format == SSHKEY_PRIVATE_PKCS8) { ++ if ((success = PEM_write_bio_PrivateKey(bio, pkey, cipher, ++ passphrase, len, NULL, NULL)) == 0) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } + } if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) { r = SSH_ERR_INTERNAL_ERROR; goto out; -@@ -3842,6 +3850,7 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, +@@ -4035,6 +4059,7 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, goto out; r = 0; out: @@ -64,4 +247,78 @@ index b95ed0b1..1a271512 100644 BIO_free(bio); return r; } +@@ -4046,29 +4071,38 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, + int + sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, +- int force_new_format, const char *new_format_cipher, int new_format_rounds) ++ int format, const char *openssh_format_cipher, int openssh_format_rounds) + { + switch (key->type) { + #ifdef WITH_OPENSSL + case KEY_DSA: + case KEY_ECDSA: + case KEY_RSA: +- if (force_new_format) { +- return sshkey_private_to_blob2(key, blob, passphrase, +- comment, new_format_cipher, new_format_rounds); +- } +- return sshkey_private_pem_to_blob(key, blob, +- passphrase, comment); ++ break; /* see below */ + #endif /* WITH_OPENSSL */ + case KEY_ED25519: + #ifdef WITH_XMSS + case KEY_XMSS: + #endif /* WITH_XMSS */ + return sshkey_private_to_blob2(key, blob, passphrase, +- comment, new_format_cipher, new_format_rounds); ++ comment, openssh_format_cipher, openssh_format_rounds); + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } ++ ++#ifdef WITH_OPENSSL ++ switch (format) { ++ case SSHKEY_PRIVATE_OPENSSH: ++ return sshkey_private_to_blob2(key, blob, passphrase, ++ comment, openssh_format_cipher, openssh_format_rounds); ++ case SSHKEY_PRIVATE_PEM: ++ case SSHKEY_PRIVATE_PKCS8: ++ return sshkey_private_to_blob_pem_pkcs8(key, blob, ++ format, passphrase, comment); ++ default: ++ return SSH_ERR_INVALID_ARGUMENT; ++ } ++#endif /* WITH_OPENSSL */ + } + + +diff --git a/sshkey.h b/sshkey.h +index 41d159a1b..d30a69cc9 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -88,6 +88,13 @@ enum sshkey_serialize_rep { + SSHKEY_SERIALIZE_INFO = 254, + }; + ++/* Private key disk formats */ ++enum sshkey_private_format { ++ SSHKEY_PRIVATE_OPENSSH = 0, ++ SSHKEY_PRIVATE_PEM = 1, ++ SSHKEY_PRIVATE_PKCS8 = 2, ++}; ++ + /* key is stored in external hardware */ + #define SSHKEY_FLAG_EXT 0x0001 + +@@ -221,7 +228,7 @@ int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); + /* private key file format parsing and serialisation */ + int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, +- int force_new_format, const char *new_format_cipher, int new_format_rounds); ++ int format, const char *openssh_format_cipher, int openssh_format_rounds); + int sshkey_parse_private_fileblob(struct sshbuf *buffer, + const char *passphrase, struct sshkey **keyp, char **commentp); + int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,