[PATCH 1/3] Add private key protection information extraction to ssh-keygen

Loïc loic at venez.fr
Sat Apr 25 10:58:53 AEST 2020


Add private key protection information extraction to shh-keygen using -v
option on top of -y option which is already parsing the private key.

Technically, the passphrase isn't necessary to do this, but it is the
most logical thing to do for me.

Adding this to -l option is not appropriate because fingerprinting is
using the .pub file when available.

An other idea is to add a new option, I can do it if you prefer.

Also, I'm laking information for information extraction from PEM and
PKCS8 file format, I'm OK to have a pointer to implement this correctly.

This patch is also adding a regression test for the functionnality.

---

 authfile.c                            |  16 ++--
 authfile.h                            |   7 +-
 regress/Makefile                      |   3 +-
 regress/keygen-private-information.sh |  81 +++++++++++++++++++++
 ssh-keygen.c                          |  44 +++++++----
 ssh-keysign.c                         |   2 +-
 sshconnect2.c                         |   2 +-
 sshd.c                                |   2 +-
 sshkey.c                              | 101 +++++++++++++++++++++++---
 sshkey.h                              |  14 +++-
 10 files changed, 234 insertions(+), 38 deletions(-)
 create mode 100644 regress/keygen-private-information.sh

diff --git a/authfile.c b/authfile.c
index 35ccf576c2b5..6c79369ebfc1 100644
--- a/authfile.c
+++ b/authfile.c
@@ -116,7 +116,7 @@ sshkey_perm_ok(int fd, const char *filename)
 
 int
 sshkey_load_private_type(int type, const char *filename, const char
*passphrase,
-    struct sshkey **keyp, char **commentp)
+    struct sshkey **keyp, char **commentp, struct sshkey_vault
**vault_infop)
 {
     int fd, r;
 
@@ -124,6 +124,8 @@ sshkey_load_private_type(int type, const char
*filename, const char *passphrase,
         *keyp = NULL;
     if (commentp != NULL)
         *commentp = NULL;
+    if (vault_infop != NULL)
+        *vault_infop = NULL;
 
     if ((fd = open(filename, O_RDONLY)) == -1)
         return SSH_ERR_SYSTEM_ERROR;
@@ -132,7 +134,7 @@ sshkey_load_private_type(int type, const char
*filename, const char *passphrase,
     if (r != 0)
         goto out;
 
-    r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
+    r = sshkey_load_private_type_fd(fd, type, passphrase, keyp,
commentp, vault_infop);
     if (r == 0 && keyp && *keyp)
         r = sshkey_set_filename(*keyp, filename);
  out:
@@ -142,15 +144,15 @@ sshkey_load_private_type(int type, const char
*filename, const char *passphrase,
 
 int
 sshkey_load_private(const char *filename, const char *passphrase,
-    struct sshkey **keyp, char **commentp)
+    struct sshkey **keyp, char **commentp, struct sshkey_vault
**vault_infop)
 {
     return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase,
-        keyp, commentp);
+        keyp, commentp, vault_infop);
 }
 
 int
 sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
-    struct sshkey **keyp, char **commentp)
+    struct sshkey **keyp, char **commentp, struct sshkey_vault
**vault_infop)
 {
     struct sshbuf *buffer = NULL;
     int r;
@@ -159,7 +161,7 @@ sshkey_load_private_type_fd(int fd, int type, const
char *passphrase,
         *keyp = NULL;
     if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
         (r = sshkey_parse_private_fileblob_type(buffer, type,
-        passphrase, keyp, commentp)) != 0)
+        passphrase, keyp, commentp, vault_infop)) != 0)
         goto out;
 
     /* success */
@@ -334,7 +336,7 @@ sshkey_load_private_cert(int type, const char
*filename, const char *passphrase,
     }
 
     if ((r = sshkey_load_private_type(type, filename,
-        passphrase, &key, NULL)) != 0 ||
+        passphrase, &key, NULL, NULL)) != 0 ||
         (r = sshkey_load_cert(filename, &cert)) != 0)
         goto out;
 
diff --git a/authfile.h b/authfile.h
index 1db067a813a1..85f78ac78edf 100644
--- a/authfile.h
+++ b/authfile.h
@@ -29,6 +29,7 @@
 
 struct sshbuf;
 struct sshkey;
+struct sshkey_vault;
 
 /* XXX document these */
 /* XXX some of these could probably be merged/retired */
@@ -37,13 +38,13 @@ int sshkey_save_private(struct sshkey *, const char *,
     const char *, const char *, int, const char *, int);
 int sshkey_load_cert(const char *, struct sshkey **);
 int sshkey_load_public(const char *, struct sshkey **, char **);
-int sshkey_load_private(const char *, const char *, struct sshkey **,
char **);
+int sshkey_load_private(const char *, const char *, struct sshkey **,
char **, struct sshkey_vault **);
 int sshkey_load_private_cert(int, const char *, const char *,
     struct sshkey **);
 int sshkey_load_private_type(int, const char *, const char *,
-    struct sshkey **, char **);
+    struct sshkey **, char **, struct sshkey_vault **);
 int sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
-    struct sshkey **keyp, char **commentp);
+    struct sshkey **keyp, char **commentp, struct sshkey_vault
**vault_infop);
 int sshkey_perm_ok(int, const char *);
 int sshkey_in_file(struct sshkey *, const char *, int, int);
 int sshkey_check_revoked(struct sshkey *key, const char
*revoked_keys_file);
diff --git a/regress/Makefile b/regress/Makefile
index 62794d25fc42..ae6f4dd09edc 100644
--- a/regress/Makefile
+++ b/regress/Makefile
@@ -92,7 +92,8 @@ LTESTS=     connect \
         allow-deny-users \
         authinfo \
         sshsig \
-        keygen-comment
+        keygen-comment \
+        keygen-private-information
 
 
 INTEROP_TESTS=    putty-transfer putty-ciphers putty-kex conch-ciphers
diff --git a/regress/keygen-private-information.sh
b/regress/keygen-private-information.sh
new file mode 100644
index 000000000000..a9959e919fd1
--- /dev/null
+++ b/regress/keygen-private-information.sh
@@ -0,0 +1,81 @@
+#    Placed in the Public Domain.
+
+tid="Test information extraction from private key"
+
+check_private_key () {
+    file="$1"
+    format="$2"
+    comment="$3"
+    secret="$4"
+    rounds="$5"
+
+    # construct expected output in $exp file
+    exp=$OBJ/$t-expected
+    # default format is RFC4716
+    test -z "$format" && format="RFC4716"
+    # Currently PKCS8 is detected as PEM, should be fixed in ssh-keygen
+    test "$format" = "PKCS8" && format="PEM"
+    cat > $exp << EOF
+$comment
+Key protection details:
+File format: $format
+EOF
+    if [ -z "$secret" -o "$format" = "PEM" ]; then
+        # For PEM format, passphrase is not detected yet, should be
fixed in ssh-keygen
+        echo "no passphrase" >> $exp
+    else
+        cat >> $exp << EOF
+cipher: aes256-ctr
+kdf: bcrypt
+rounds: $rounds
+EOF
+    fi
+
+    if ! ${SSHKEYGEN} -yv -P "${secret}" -f $file > $OBJ/$t-pub ; then
+        fail "ssh-keygen -y failed for $t-key"
+    fi
+    if ! sed '1 s/[^ ]* [^ ]* \?//' $OBJ/$t-pub > $OBJ/$t-tmp ; then
+        fail "sed failed for $t-key"
+    fi
+    if ! cmp $OBJ/$t-tmp $exp > /dev/null 2>&1; then
+        fail "ssh-keygen -yv output is not the expected value for $t-key"
+        diff $exp $OBJ/$t-tmp
+    fi
+    rm -f $OBJ/$t-pub $OBJ/$t-tmp $exp
+}
+
+for fmt in '' PKCS8 PEM ; do
+    for secret in '' 'secret1'; do
+        rounds_list="0"
+        test -n "$secret" -a -z "$fmt" && rounds_list="2 16"
+        for rounds in $rounds_list; do
+            for t in $SSH_KEYTYPES; do
+                trace "generating $t key in '$fmt' format with
'$secret' passphrase and '$rounds' rounds"
+                rm -f $OBJ/$t-key*
+                oldfmt=""
+                case "$fmt" in
+                PKCS8|PEM) oldfmt=1 ;;
+                esac
+                # Some key types like ssh-ed25519 and *@openssh.com are
never
+                # stored in old formats.
+                case "$t" in
+                ssh-ed25519|*openssh.com) test -z "$oldfmt" || continue ;;
+                esac
+                comment="foo bar"
+                fmtarg=""
+                test -z "$fmt" || fmtarg="-m $fmt"
+                test "$rounds" = "0" || roundarg="-a $rounds"
+                ${SSHKEYGEN} $fmtarg $roundarg -N "${secret}" -C
"${comment}" \
+                    -t $t -f $OBJ/$t-key >/dev/null 2>&1 || \
+                    fatal "keygen of $t in format $fmt failed"
+                rm -f $OBJ/$t-key.pub # .pub file not used, remove it
to be sure it is not used
+                if [ ! -z "$oldfmt" ] ; then
+                    # Comment cannot be recovered from old format keys.
+                    comment=""
+                fi
+                check_private_key $OBJ/$t-key "${fmt}" "${comment}"
"${secret}" "${rounds}"
+                rm -f $OBJ/$t-key*
+            done
+        done
+    done
+done
diff --git a/ssh-keygen.c b/ssh-keygen.c
index d50ca5f28c51..6dd17c48be5e 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -310,7 +310,7 @@ ask_filename(struct passwd *pw, const char *prompt)
 }
 
 static struct sshkey *
-load_identity(const char *filename, char **commentp)
+load_identity(const char *filename, char **commentp, struct
sshkey_vault **vault_infop)
 {
     char *pass;
     struct sshkey *prv;
@@ -318,7 +318,9 @@ load_identity(const char *filename, char **commentp)
 
     if (commentp != NULL)
         *commentp = NULL;
-    if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0)
+    if (vault_infop != NULL)
+        *vault_infop = NULL;
+    if ((r = sshkey_load_private(filename, "", &prv, commentp,
vault_infop)) == 0)
         return prv;
     if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
         fatal("Load key \"%s\": %s", filename, ssh_err(r));
@@ -326,7 +328,7 @@ load_identity(const char *filename, char **commentp)
         pass = xstrdup(identity_passphrase);
     else
         pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN);
-    r = sshkey_load_private(filename, pass, &prv, commentp);
+    r = sshkey_load_private(filename, pass, &prv, commentp, vault_infop);
     freezero(pass, strlen(pass));
     if (r != 0)
         fatal("Load key \"%s\": %s", filename, ssh_err(r));
@@ -429,7 +431,7 @@ do_convert_to(struct passwd *pw)
     if (stat(identity_file, &st) == -1)
         fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
     if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
-        k = load_identity(identity_file, NULL);
+        k = load_identity(identity_file, NULL, NULL);
     switch (convert_format) {
     case FMT_RFC4716:
         do_convert_to_ssh2(pw, k);
@@ -806,19 +808,33 @@ do_print_public(struct passwd *pw)
     struct stat st;
     int r;
     char *comment = NULL;
+    struct sshkey_vault *vault_info = NULL;
 
     if (!have_identity)
         ask_filename(pw, "Enter file in which the key is");
     if (stat(identity_file, &st) == -1)
         fatal("%s: %s", identity_file, strerror(errno));
-    prv = load_identity(identity_file, &comment);
+    prv = load_identity(identity_file, &comment, &vault_info);
     if ((r = sshkey_write(prv, stdout)) != 0)
         error("sshkey_write failed: %s", ssh_err(r));
     sshkey_free(prv);
     if (comment != NULL && *comment != '\0')
-        fprintf(stdout, " %s", comment);
-    fprintf(stdout, "\n");
+        printf(" %s", comment);
+    printf("\n");
+    if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) {
+        printf("Key protection details:\n");
+        printf("File format: %s\n",
sshkey_format_name(vault_info->format));
+        if ( (vault_info->ciphername == NULL ||
strcmp(vault_info->ciphername, "none") == 0)
+          || (vault_info->kdfname == NULL ||
strcmp(vault_info->kdfname, "none") == 0)) {
+            printf("no passphrase\n");
+        } else {
+            printf("cipher: %s\n", vault_info->ciphername);
+            printf("kdf: %s\n", vault_info->kdfname);
+            printf("rounds: %d\n", vault_info->rounds);
+        }
+    }
     free(comment);
+    sshkey_vault_free(vault_info);
     exit(0);
 }
 
@@ -920,7 +936,7 @@ fingerprint_private(const char *path)
     if (pubkey == NULL || comment == NULL || *comment == '\0') {
         free(comment);
         if ((r = sshkey_load_private(path, NULL,
-            &privkey, &comment)) != 0)
+            &privkey, &comment, NULL)) != 0)
             debug("load private \"%s\": %s", path, ssh_err(r));
     }
     if (pubkey == NULL && privkey == NULL)
@@ -1416,7 +1432,7 @@ do_change_passphrase(struct passwd *pw)
     if (stat(identity_file, &st) == -1)
         fatal("%s: %s", identity_file, strerror(errno));
     /* Try to load the file with empty passphrase. */
-    r = sshkey_load_private(identity_file, "", &private, &comment);
+    r = sshkey_load_private(identity_file, "", &private, &comment, NULL);
     if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
         if (identity_passphrase)
             old_passphrase = xstrdup(identity_passphrase);
@@ -1425,7 +1441,7 @@ do_change_passphrase(struct passwd *pw)
                 read_passphrase("Enter old passphrase: ",
                 RP_ALLOW_STDIN);
         r = sshkey_load_private(identity_file, old_passphrase,
-            &private, &comment);
+            &private, &comment, NULL);
         freezero(old_passphrase, strlen(old_passphrase));
         if (r != 0)
             goto badkey;
@@ -1525,7 +1541,7 @@ do_change_comment(struct passwd *pw, const char
*identity_comment)
     if (stat(identity_file, &st) == -1)
         fatal("%s: %s", identity_file, strerror(errno));
     if ((r = sshkey_load_private(identity_file, "",
-        &private, &comment)) == 0)
+        &private, &comment, NULL)) == 0)
         passphrase = xstrdup("");
     else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
         fatal("Cannot load private key \"%s\": %s.",
@@ -1540,7 +1556,7 @@ do_change_comment(struct passwd *pw, const char
*identity_comment)
                 RP_ALLOW_STDIN);
         /* Try to load using the passphrase. */
         if ((r = sshkey_load_private(identity_file, passphrase,
-            &private, &comment)) != 0) {
+            &private, &comment, NULL)) != 0) {
             freezero(passphrase, strlen(passphrase));
             fatal("Cannot load private key \"%s\": %s.",
                 identity_file, ssh_err(r));
@@ -1785,7 +1801,7 @@ do_ca_sign(struct passwd *pw, const char
*ca_key_path, int prefer_agent,
         ca->flags |= SSHKEY_FLAG_EXT;
     } else {
         /* CA key is assumed to be a private key on the filesystem */
-        ca = load_identity(tmp, NULL);
+        ca = load_identity(tmp, NULL, NULL);
     }
     free(tmp);
 
@@ -2494,7 +2510,7 @@ load_sign_key(const char *keypath, const struct
sshkey *pubkey)
         debug("%s: %s looks like a public key, using private key "
             "path %s instead", __func__, keypath, privpath);
     }
-    if ((privkey = load_identity(privpath, NULL)) == NULL) {
+    if ((privkey = load_identity(privpath, NULL, NULL)) == NULL) {
         error("Couldn't load identity %s", keypath);
         goto done;
     }
diff --git a/ssh-keysign.c b/ssh-keysign.c
index 3e3ea3e1481d..c9c20483b9a5 100644
--- a/ssh-keysign.c
+++ b/ssh-keysign.c
@@ -225,7 +225,7 @@ main(int argc, char **argv)
         if (key_fd[i] == -1)
             continue;
         r = sshkey_load_private_type_fd(key_fd[i], KEY_UNSPEC,
-            NULL, &key, NULL);
+            NULL, &key, NULL, NULL);
         close(key_fd[i]);
         if (r != 0)
             debug("parse key %d: %s", i, ssh_err(r));
diff --git a/sshconnect2.c b/sshconnect2.c
index 1a6545edf026..7947f2da6584 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1472,7 +1472,7 @@ load_identity_file(Identity *id)
             }
         }
         switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
-            passphrase, &private, &comment))) {
+            passphrase, &private, &comment, NULL))) {
         case 0:
             break;
         case SSH_ERR_KEY_WRONG_PASSPHRASE:
diff --git a/sshd.c b/sshd.c
index 6f8f11a3bdac..42c19089a225 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1789,7 +1789,7 @@ main(int ac, char **av)
         if (options.host_key_files[i] == NULL)
             continue;
         if ((r = sshkey_load_private(options.host_key_files[i], "",
-            &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
+            &key, NULL, 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 (sshkey_is_sk(key) &&
diff --git a/sshkey.c b/sshkey.c
index 1571e3d93878..4c1948a3752e 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -93,6 +93,26 @@ int    sshkey_private_serialize_opt(struct sshkey *key,
 static int sshkey_from_blob_internal(struct sshbuf *buf,
     struct sshkey **keyp, int allow_cert);
 
+/* Supported format types */
+const char *
+sshkey_format_name(enum sshkey_private_format format) {
+    const char *format_str;
+    switch (format) {
+    case SSHKEY_PRIVATE_OPENSSH:
+        format_str = "RFC4716";
+        break;
+    case SSHKEY_PRIVATE_PKCS8:
+        format_str = "PKCS8";
+        break;
+    case SSHKEY_PRIVATE_PEM:
+        format_str = "PEM";
+        break;
+    default:
+        format_str = "unknown";
+    }
+    return format_str;
+}
+
 /* Supported key types */
 struct keytype {
     const char *name;
@@ -679,6 +699,29 @@ sshkey_free(struct sshkey *k)
     freezero(k, sizeof(*k));
 }
 
+struct sshkey_vault *
+sshkey_vault_new()
+{
+    struct sshkey_vault *k = calloc(1, sizeof(*k));
+    if (k == NULL)
+        return NULL;
+    k->format = SSHKEY_PRIVATE_OPENSSH;
+    k->ciphername = NULL;
+    k->kdfname = NULL;
+    k->rounds = -1;
+    return k;
+}
+
+void
+sshkey_vault_free(struct sshkey_vault *k)
+{
+    if (k == NULL)
+        return;
+    free(k->kdfname);
+    free(k);
+    return;
+}
+
 static int
 cert_compare(struct sshkey_cert *a, struct sshkey_cert *b)
 {
@@ -4029,7 +4072,7 @@ private2_uudecode(struct sshbuf *blob, struct
sshbuf **decodedp)
 
 static int
 private2_decrypt(struct sshbuf *decoded, const char *passphrase,
-    struct sshbuf **decryptedp, struct sshkey **pubkeyp)
+    struct sshbuf **decryptedp, struct sshkey **pubkeyp, struct
sshkey_vault **vault_infop)
 {
     char *ciphername = NULL, *kdfname = NULL;
     const struct sshcipher *cipher = NULL;
@@ -4038,12 +4081,21 @@ private2_decrypt(struct sshbuf *decoded, const
char *passphrase,
     struct sshbuf *kdf = NULL, *decrypted = NULL;
     struct sshcipher_ctx *ciphercontext = NULL;
     struct sshkey *pubkey = NULL;
+    struct sshkey_vault *vault_info = NULL;
     u_char *key = NULL, *salt = NULL, *dp;
     u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
 
     if (decoded == NULL || decryptedp == NULL || pubkeyp == NULL)
         return SSH_ERR_INVALID_ARGUMENT;
 
+    if (vault_infop != NULL) {
+        *vault_infop = NULL;
+    }
+    if ((vault_info = sshkey_vault_new()) == NULL) {
+        r = SSH_ERR_ALLOC_FAIL;
+        goto out;
+    }
+
     *decryptedp = NULL;
     *pubkeyp = NULL;
 
@@ -4074,10 +4126,18 @@ private2_decrypt(struct sshbuf *decoded, const
char *passphrase,
         r = SSH_ERR_KEY_UNKNOWN_CIPHER;
         goto out;
     }
+    if ((vault_info->ciphername = strdup(ciphername)) == NULL) {
+        r = SSH_ERR_ALLOC_FAIL;
+        goto out;
+    }
     if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) {
         r = SSH_ERR_KEY_UNKNOWN_CIPHER;
         goto out;
     }
+    if ((vault_info->kdfname = strdup(kdfname)) == NULL) {
+        r = SSH_ERR_ALLOC_FAIL;
+        goto out;
+    }
     if (strcmp(kdfname, "none") == 0 && strcmp(ciphername, "none") != 0) {
         r = SSH_ERR_INVALID_FORMAT;
         goto out;
@@ -4108,6 +4168,7 @@ private2_decrypt(struct sshbuf *decoded, const
char *passphrase,
         if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 ||
             (r = sshbuf_get_u32(kdf, &rounds)) != 0)
             goto out;
+        vault_info->rounds = rounds;
         if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
             key, keylen + ivlen, rounds) < 0) {
             r = SSH_ERR_INVALID_FORMAT;
@@ -4155,6 +4216,10 @@ private2_decrypt(struct sshbuf *decoded, const
char *passphrase,
     decrypted = NULL;
     *pubkeyp = pubkey;
     pubkey = NULL;
+    if (vault_infop != NULL) {
+        *vault_infop = vault_info;
+        vault_info = NULL;
+    }
     r = 0;
  out:
     cipher_free(ciphercontext);
@@ -4171,6 +4236,7 @@ private2_decrypt(struct sshbuf *decoded, const
char *passphrase,
     }
     sshbuf_free(kdf);
     sshbuf_free(decrypted);
+    sshkey_vault_free(vault_info);
     return r;
 }
 
@@ -4201,7 +4267,7 @@ private2_check_padding(struct sshbuf *decrypted)
 
 static int
 sshkey_parse_private2(struct sshbuf *blob, int type, const char
*passphrase,
-    struct sshkey **keyp, char **commentp)
+    struct sshkey **keyp, char **commentp, struct sshkey_vault
**vault_infop)
 {
     char *comment = NULL;
     int r = SSH_ERR_INTERNAL_ERROR;
@@ -4216,7 +4282,7 @@ sshkey_parse_private2(struct sshbuf *blob, int
type, const char *passphrase,
     /* Undo base64 encoding and decrypt the private section */
     if ((r = private2_uudecode(blob, &decoded)) != 0 ||
         (r = private2_decrypt(decoded, passphrase,
-        &decrypted, &pubkey)) != 0)
+        &decrypted, &pubkey, vault_infop)) != 0)
         goto out;
 
     if (type != KEY_UNSPEC &&
@@ -4521,15 +4587,18 @@ pem_passphrase_cb(char *buf, int size, int
rwflag, void *u)
 
 static int
 sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
-    const char *passphrase, struct sshkey **keyp)
+    const char *passphrase, struct sshkey **keyp, struct sshkey_vault
**vault_infop)
 {
     EVP_PKEY *pk = NULL;
     struct sshkey *prv = NULL;
+    struct sshkey_vault *vault_info = NULL;
     BIO *bio = NULL;
     int r;
 
     if (keyp != NULL)
         *keyp = NULL;
+    if (vault_infop != NULL)
+        *vault_infop = NULL;
 
     if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
         return SSH_ERR_ALLOC_FAIL;
@@ -4538,6 +4607,13 @@ sshkey_parse_private_pem_fileblob(struct sshbuf
*blob, int type,
         r = SSH_ERR_ALLOC_FAIL;
         goto out;
     }
+    if ((vault_info = sshkey_vault_new()) == NULL) {
+        r = SSH_ERR_ALLOC_FAIL;
+        goto out;
+    }
+    // TODO: identify correctly PEM and PKCS8 format
+    vault_info->format = SSHKEY_PRIVATE_PEM;
+    // TODO: put the correct ciphername, kdfname and round if a
passphrase is used
 
     clear_libcrypto_errors();
     if ((pk = PEM_read_bio_PrivateKey(bio, NULL, pem_passphrase_cb,
@@ -4614,17 +4690,22 @@ sshkey_parse_private_pem_fileblob(struct sshbuf
*blob, int type,
         *keyp = prv;
         prv = NULL;
     }
+    if (vault_infop != NULL) {
+        *vault_infop = vault_info;
+        vault_info = NULL;
+    }
  out:
     BIO_free(bio);
     EVP_PKEY_free(pk);
     sshkey_free(prv);
+    sshkey_vault_free(vault_info);
     return r;
 }
 #endif /* WITH_OPENSSL */
 
 int
 sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
-    const char *passphrase, struct sshkey **keyp, char **commentp)
+    const char *passphrase, struct sshkey **keyp, char **commentp,
struct sshkey_vault **vault_infop)
 {
     int r = SSH_ERR_INTERNAL_ERROR;
 
@@ -4632,22 +4713,24 @@ sshkey_parse_private_fileblob_type(struct sshbuf
*blob, int type,
         *keyp = NULL;
     if (commentp != NULL)
         *commentp = NULL;
+    if (vault_infop != NULL)
+        *vault_infop = NULL;
 
     switch (type) {
     case KEY_ED25519:
     case KEY_XMSS:
         /* No fallback for new-format-only keys */
         return sshkey_parse_private2(blob, type, passphrase,
-            keyp, commentp);
+            keyp, commentp, vault_infop);
     default:
         r = sshkey_parse_private2(blob, type, passphrase, keyp,
-            commentp);
+            commentp, vault_infop);
         /* Only fallback to PEM parser if a format error occurred. */
         if (r != SSH_ERR_INVALID_FORMAT)
             return r;
 #ifdef WITH_OPENSSL
         return sshkey_parse_private_pem_fileblob(blob, type,
-            passphrase, keyp);
+            passphrase, keyp, vault_infop);
 #else
         return SSH_ERR_INVALID_FORMAT;
 #endif /* WITH_OPENSSL */
@@ -4664,7 +4747,7 @@ sshkey_parse_private_fileblob(struct sshbuf
*buffer, const char *passphrase,
         *commentp = NULL;
 
     return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
-        passphrase, keyp, commentp);
+        passphrase, keyp, commentp, NULL);
 }
 
 void
diff --git a/sshkey.h b/sshkey.h
index 9c1d4f6372f6..7b7de828d9ce 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -99,6 +99,8 @@ enum sshkey_private_format {
     SSHKEY_PRIVATE_PEM = 1,
     SSHKEY_PRIVATE_PKCS8 = 2,
 };
+const char *
+sshkey_format_name(enum sshkey_private_format);
 
 /* key is stored in external hardware */
 #define SSHKEY_FLAG_EXT        0x0001
@@ -162,6 +164,16 @@ struct sshkey_sig_details {
     uint8_t sk_flags;    /* U2F signature flags; see ssh-sk.h */
 };
 
+/* Key storage parameters in private key file */
+struct sshkey_vault {
+    enum sshkey_private_format format;
+    char *ciphername;
+    char *kdfname;
+    int rounds;
+};
+struct sshkey_vault    *sshkey_vault_new();
+void        sshkey_vault_free(struct sshkey_vault *);
+
 struct sshkey    *sshkey_new(int);
 void         sshkey_free(struct sshkey *);
 int         sshkey_equal_public(const struct sshkey *,
@@ -258,7 +270,7 @@ int    sshkey_private_to_fileblob(struct sshkey
*key, struct sshbuf *blob,
 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,
-    const char *passphrase, struct sshkey **keyp, char **commentp);
+    const char *passphrase, struct sshkey **keyp, char **commentp,
struct sshkey_vault **vault_infop);
 int    sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob,
     int type, struct sshkey **pubkeyp);
 
-- 
2.17.1




More information about the openssh-unix-dev mailing list