From 995e2f7164733ef7b7d677ed59bad562de638a99 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 13 Mar 2021 10:15:29 +0000 Subject: [PATCH] Add API for getting all of a key's fingerprints. ssh2_all_fingerprints() and friends will return a small 'char **' array, containing all the fingerprints of a key that we know how to generate, indexed by the FingerprintType enum. The result requires complex freeing, so there's an ssh2_free_all_fingerprints as well. For SSH-1 RSA keys, we refuse to generate any fingerprint except the old SSH-1 MD5 version, because there's no other fingerprint type I know of that anyone else uses. So I've got a function that returns the same 'char **' for an SSH-1 key, but it only fills in the MD5 slot, and leaves the rest NULL. As a result, I also need a dynamic function that takes a fingerprint list and returns the id of the most preferred fingerprint type in it _that actually exists_. NFC: this API is introduced, but not yet used. --- ssh.h | 8 ++++++++ sshpubk.c | 24 ++++++++++++++++++++++++ sshrsa.c | 13 +++++++++++++ utils.c | 21 +++++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/ssh.h b/ssh.h index 378dcb90..acffbe32 100644 --- a/ssh.h +++ b/ssh.h @@ -567,6 +567,7 @@ mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key); bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key, strbuf *outbuf); char *rsastr_fmt(RSAKey *key); char *rsa_ssh1_fingerprint(RSAKey *key); +char **rsa_ssh1_fake_all_fingerprints(RSAKey *key); bool rsa_verify(RSAKey *key); void rsa_ssh1_public_blob(BinarySink *bs, RSAKey *key, RsaSsh1Order order); int rsa_ssh1_public_blob_len(ptrlen data); @@ -1337,6 +1338,10 @@ typedef enum { #define SSH_FPTYPE_DEFAULT SSH_FPTYPE_MD5 #define SSH_N_FPTYPES (SSH_FPTYPE_SHA256 + 1) +FingerprintType ssh2_pick_fingerprint(char **fingerprints, + FingerprintType preferred_type); +FingerprintType ssh2_pick_default_fingerprint(char **fingerprints); + char *ssh1_pubkey_str(RSAKey *ssh1key); void ssh1_write_pubkey(FILE *fp, RSAKey *ssh1key); char *ssh2_pubkey_openssh_str(ssh2_userkey *key); @@ -1345,6 +1350,9 @@ void ssh2_write_pubkey(FILE *fp, const char *comment, int keytype); char *ssh2_fingerprint_blob(ptrlen, FingerprintType); char *ssh2_fingerprint(ssh_key *key, FingerprintType); +char **ssh2_all_fingerprints_for_blob(ptrlen); +char **ssh2_all_fingerprints(ssh_key *key); +void ssh2_free_all_fingerprints(char **); int key_type(const Filename *filename); int key_type_s(BinarySource *src); const char *key_type_to_str(int type); diff --git a/sshpubk.c b/sshpubk.c index 031093d6..33671e3b 100644 --- a/sshpubk.c +++ b/sshpubk.c @@ -1794,6 +1794,14 @@ char *ssh2_fingerprint_blob(ptrlen blob, FingerprintType fptype) return strbuf_to_str(sb); } +char **ssh2_all_fingerprints_for_blob(ptrlen blob) +{ + char **fps = snewn(SSH_N_FPTYPES, char *); + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + fps[i] = ssh2_fingerprint_blob(blob, i); + return fps; +} + char *ssh2_fingerprint(ssh_key *data, FingerprintType fptype) { strbuf *blob = strbuf_new(); @@ -1803,6 +1811,22 @@ char *ssh2_fingerprint(ssh_key *data, FingerprintType fptype) return ret; } +char **ssh2_all_fingerprints(ssh_key *data) +{ + strbuf *blob = strbuf_new(); + ssh_key_public_blob(data, BinarySink_UPCAST(blob)); + char **ret = ssh2_all_fingerprints_for_blob(ptrlen_from_strbuf(blob)); + strbuf_free(blob); + return ret; +} + +void ssh2_free_all_fingerprints(char **fps) +{ + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + sfree(fps[i]); + sfree(fps); +} + /* ---------------------------------------------------------------------- * Determine the type of a private key file. */ diff --git a/sshrsa.c b/sshrsa.c index f58ca375..e3dcbdda 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -319,6 +319,19 @@ char *rsa_ssh1_fingerprint(RSAKey *key) return strbuf_to_str(out); } +/* + * Wrap the output of rsa_ssh1_fingerprint up into the same kind of + * structure that comes from ssh2_all_fingerprints. + */ +char **rsa_ssh1_fake_all_fingerprints(RSAKey *key) +{ + char **ret = snewn(SSH_N_FPTYPES, char *); + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + ret[i] = NULL; + ret[SSH_FPTYPE_MD5] = rsa_ssh1_fingerprint(key); + return ret; +} + /* * Verify that the public data in an RSA key matches the private * data. We also check the private data itself: we ensure that p > diff --git a/utils.c b/utils.c index 3f0cf20e..30b326ee 100644 --- a/utils.c +++ b/utils.c @@ -17,6 +17,7 @@ #include "defs.h" #include "misc.h" +#include "ssh.h" /* * Parse a string block size specification. This is approximately a @@ -1099,3 +1100,23 @@ void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size) } } } + +FingerprintType ssh2_pick_fingerprint( + char **fingerprints, FingerprintType preferred_type) +{ + /* + * Keys are either SSH-2, in which case we have all fingerprint + * types, or SSH-1, in which case we have only MD5. So we return + * the default type if we can, or MD5 if that's all we have; no + * need for a fully general preference-list system. + */ + FingerprintType fptype = fingerprints[preferred_type] ? + preferred_type : SSH_FPTYPE_MD5; + assert(fingerprints[fptype]); + return fptype; +} + +FingerprintType ssh2_pick_default_fingerprint(char **fingerprints) +{ + return ssh2_pick_fingerprint(fingerprints, SSH_FPTYPE_DEFAULT); +}