From 8682246d33f21e369a3e5b38fa2fdec58b1e4425 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 12 May 2015 14:35:44 +0100 Subject: [PATCH] Centralise SSH-2 key fingerprinting into sshpubk.c. There were ad-hoc functions for fingerprinting a bare key blob in both cmdgen.c and pageant.c, not quite doing the same thing. Also, every SSH-2 public key algorithm in the code base included a dedicated fingerprint() method, which is completely pointless since SSH-2 key fingerprints are computed in an algorithm-independent way (just hash the standard-format public key blob), so each of those methods was just duplicating the work of the public_blob() method with a less general output mechanism. Now sshpubk.c centrally provides an ssh2_fingerprint_blob() function that does all the real work, plus an ssh2_fingerprint() function that wraps it and deals with calling public_blob() to get something to fingerprint. And the fingerprint() method has been completely removed from ssh_signkey and all its implementations, and good riddance. --- cmdgen.c | 26 ++------------- pageant.c | 30 ++++------------- ssh.c | 2 +- ssh.h | 3 +- sshdss.c | 38 --------------------- sshecc.c | 85 ----------------------------------------------- sshpubk.c | 56 +++++++++++++++++++++++++++++++ sshrsa.c | 36 -------------------- windows/winpgen.c | 6 ++-- windows/winpgnt.c | 2 +- 10 files changed, 71 insertions(+), 213 deletions(-) diff --git a/cmdgen.c b/cmdgen.c index aa03cb54..df3fd9af 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -192,27 +192,6 @@ static int move(char *from, char *to) return TRUE; } -static char *blobfp(char *alg, int bits, unsigned char *blob, int bloblen) -{ - char buffer[128]; - unsigned char digest[16]; - struct MD5Context md5c; - int i; - - MD5Init(&md5c); - MD5Update(&md5c, blob, bloblen); - MD5Final(digest, &md5c); - - sprintf(buffer, "%s ", alg); - if (bits > 0) - sprintf(buffer + strlen(buffer), "%d ", bits); - for (i = 0; i < 16; i++) - sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "", - digest[i]); - - return dupstr(buffer); -} - int main(int argc, char **argv) { char *infile = NULL; @@ -980,10 +959,11 @@ int main(int argc, char **argv) rsa_fingerprint(fingerprint, 128, ssh1key); } else { if (ssh2key) { - fingerprint = ssh2key->alg->fingerprint(ssh2key->data); + fingerprint = ssh2_fingerprint(ssh2key->alg, + ssh2key->data); } else { assert(ssh2blob); - fingerprint = blobfp(ssh2alg, bits, ssh2blob, ssh2bloblen); + fingerprint = ssh2_fingerprint_blob(ssh2blob, ssh2bloblen); } } diff --git a/pageant.c b/pageant.c index 754af9c6..c249a3b7 100644 --- a/pageant.c +++ b/pageant.c @@ -259,25 +259,6 @@ void *pageant_make_keylist2(int *length) return ret; } -char *fingerprint_ssh2_blob(const void *blob, int bloblen) -{ - unsigned char digest[16]; - char fingerprint_str[16*3]; - unsigned stringlen; - int i; - - MD5Simple(blob, bloblen, digest); - for (i = 0; i < 16; i++) - sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":"); - - stringlen = GET_32BIT((const unsigned char *)blob); - if (stringlen < bloblen-4) - return dupprintf("%.*s %s", (int)stringlen, (const char *)blob + 4, - fingerprint_str); - else - return dupstr(fingerprint_str); -} - static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))) @@ -381,7 +362,8 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, int i; struct ssh2_userkey *skey; for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) { - char *fingerprint = skey->alg->fingerprint(skey->data); + char *fingerprint = ssh2_fingerprint(skey->alg, + skey->data); plog(logctx, logfn, "returned key: %s %s", fingerprint, skey->comment); sfree(fingerprint); @@ -528,7 +510,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, } data = p; if (logfn) { - char *fingerprint = fingerprint_ssh2_blob(b.blob, b.len); + char *fingerprint = ssh2_fingerprint_blob(b.blob, b.len); plog(logctx, logfn, "requested key: %s", fingerprint); sfree(fingerprint); } @@ -728,7 +710,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, key->comment = comment; if (logfn) { - char *fingerprint = key->alg->fingerprint(key->data); + char *fingerprint = ssh2_fingerprint(key->alg, key->data); plog(logctx, logfn, "submitted key: %s %s", fingerprint, key->comment); sfree(fingerprint); @@ -822,7 +804,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, p += b.len; if (logfn) { - char *fingerprint = fingerprint_ssh2_blob(b.blob, b.len); + char *fingerprint = ssh2_fingerprint_blob(b.blob, b.len); plog(logctx, logfn, "unwanted key: %s", fingerprint); sfree(fingerprint); } @@ -1688,7 +1670,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, sfree(keylist); return PAGEANT_ACTION_FAILURE; } - fingerprint = fingerprint_ssh2_blob(p, n); + fingerprint = ssh2_fingerprint_blob(p, n); cbkey.blob = p; cbkey.bloblen = n; p += n, keylistlen -= n; diff --git a/ssh.c b/ssh.c index 5d27d5ea..0f4712d8 100644 --- a/ssh.c +++ b/ssh.c @@ -7042,7 +7042,7 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, * Authenticate remote host: verify host key. (We've already * checked the signature of the exchange hash.) */ - s->fingerprint = ssh->hostkey->fingerprint(s->hkey); + s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey); logevent("Host key fingerprint is:"); logevent(s->fingerprint); /* First check against manually configured host keys. */ diff --git a/ssh.h b/ssh.h index 4e36ad45..1924d9f1 100644 --- a/ssh.h +++ b/ssh.h @@ -375,7 +375,6 @@ struct ssh_signkey { * openssh_private_npieces gives that information. */ int openssh_private_npieces; int (*pubkey_bits) (const void *blob, int len); - char *(*fingerprint) (void *key); int (*verifysig) (void *key, const char *sig, int siglen, const char *data, int datalen); unsigned char *(*sign) (void *key, const char *data, int datalen, @@ -722,6 +721,8 @@ char *ssh2_pubkey_openssh_str(struct ssh2_userkey *key); void ssh2_write_pubkey(FILE *fp, const char *comment, const void *v_pub_blob, int pub_len, int keytype); +char *ssh2_fingerprint_blob(const void *blob, int bloblen); +char *ssh2_fingerprint(const struct ssh_signkey *alg, void *data); int key_type(const Filename *filename); char *key_type_to_str(int type); diff --git a/sshdss.c b/sshdss.c index 5633e395..80056266 100644 --- a/sshdss.c +++ b/sshdss.c @@ -190,43 +190,6 @@ static char *dss_fmtkey(void *key) return p; } -static char *dss_fingerprint(void *key) -{ - struct dss_key *dss = (struct dss_key *) key; - struct MD5Context md5c; - unsigned char digest[16], lenbuf[4]; - char buffer[16 * 3 + 40]; - char *ret; - int numlen, i; - - MD5Init(&md5c); - MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-dss", 11); - -#define ADD_BIGNUM(bignum) \ - numlen = (bignum_bitcount(bignum)+8)/8; \ - PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \ - for (i = numlen; i-- ;) { \ - unsigned char c = bignum_byte(bignum, i); \ - MD5Update(&md5c, &c, 1); \ - } - ADD_BIGNUM(dss->p); - ADD_BIGNUM(dss->q); - ADD_BIGNUM(dss->g); - ADD_BIGNUM(dss->y); -#undef ADD_BIGNUM - - MD5Final(digest, &md5c); - - sprintf(buffer, "ssh-dss %d ", bignum_bitcount(dss->p)); - for (i = 0; i < 16; i++) - sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "", - digest[i]); - ret = snewn(strlen(buffer) + 1, char); - if (ret) - strcpy(ret, buffer); - return ret; -} - static int dss_verifysig(void *key, const char *sig, int siglen, const char *data, int datalen) { @@ -705,7 +668,6 @@ const struct ssh_signkey ssh_dss = { dss_openssh_fmtkey, 5 /* p,q,g,y,x */, dss_pubkey_bits, - dss_fingerprint, dss_verifysig, dss_sign, "ssh-dss", diff --git a/sshecc.c b/sshecc.c index 5cda17be..6915eaf0 100644 --- a/sshecc.c +++ b/sshecc.c @@ -3082,87 +3082,6 @@ static int ecdsa_pubkey_bits(const void *blob, int len) return ret; } -static char *ecdsa_fingerprint(void *key) -{ - struct ec_key *ec = (struct ec_key *) key; - struct MD5Context md5c; - unsigned char digest[16], lenbuf[4]; - char *ret; - unsigned char *name, *fullname; - int pointlen, namelen, fullnamelen, i, j; - - MD5Init(&md5c); - - namelen = ec_curve_to_name(EC_TYPE_DSA, ec->publicKey.curve, NULL, 0); - name = snewn(namelen, unsigned char); - ec_curve_to_name(EC_TYPE_DSA, ec->publicKey.curve, name, namelen); - - if (ec->publicKey.curve->type == EC_EDWARDS) { - unsigned char b; - - /* Do it with the weird encoding */ - PUT_32BIT(lenbuf, namelen); - MD5Update(&md5c, lenbuf, 4); - MD5Update(&md5c, name, namelen); - - pointlen = ec->publicKey.curve->fieldBits / 8; - PUT_32BIT(lenbuf, pointlen); - MD5Update(&md5c, lenbuf, 4); - for (i = 0; i < pointlen - 1; ++i) { - b = bignum_byte(ec->publicKey.y, i); - MD5Update(&md5c, &b, 1); - } - /* Unset last bit of y and set first bit of x in its place */ - b = bignum_byte(ec->publicKey.y, i) & 0x7f; - b |= bignum_bit(ec->publicKey.x, 0) << 7; - MD5Update(&md5c, &b, 1); - } else if (ec->publicKey.curve->type == EC_WEIERSTRASS) { - fullnamelen = ec_curve_to_name(EC_TYPE_CURVE, ec->publicKey.curve, NULL, 0); - fullname = snewn(namelen, unsigned char); - ec_curve_to_name(EC_TYPE_DSA, ec->publicKey.curve, fullname, fullnamelen); - - PUT_32BIT(lenbuf, fullnamelen); - MD5Update(&md5c, lenbuf, 4); - MD5Update(&md5c, fullname, fullnamelen); - sfree(fullname); - - PUT_32BIT(lenbuf, namelen); - MD5Update(&md5c, lenbuf, 4); - MD5Update(&md5c, name, namelen); - - pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8; - PUT_32BIT(lenbuf, 1 + (pointlen * 2)); - MD5Update(&md5c, lenbuf, 4); - MD5Update(&md5c, (const unsigned char *)"\x04", 1); - for (i = pointlen; i--; ) { - unsigned char c = bignum_byte(ec->publicKey.x, i); - MD5Update(&md5c, &c, 1); - } - for (i = pointlen; i--; ) { - unsigned char c = bignum_byte(ec->publicKey.y, i); - MD5Update(&md5c, &c, 1); - } - } else { - sfree(name); - return NULL; - } - - MD5Final(digest, &md5c); - - ret = snewn(namelen + 1 + (16 * 3), char); - - i = 0; - memcpy(ret, name, namelen); - i += namelen; - sfree(name); - ret[i++] = ' '; - for (j = 0; j < 16; j++) { - i += sprintf(ret + i, "%s%02x", j ? ":" : "", digest[j]); - } - - return ret; -} - static int ecdsa_verifysig(void *key, const char *sig, int siglen, const char *data, int datalen) { @@ -3545,7 +3464,6 @@ const struct ssh_signkey ssh_ecdsa_ed25519 = { ed25519_openssh_fmtkey, 2 /* point, private exponent */, ecdsa_pubkey_bits, - ecdsa_fingerprint, ecdsa_verifysig, ecdsa_sign, "ssh-ed25519", @@ -3563,7 +3481,6 @@ const struct ssh_signkey ssh_ecdsa_nistp256 = { ecdsa_openssh_fmtkey, 3 /* curve name, point, private exponent */, ecdsa_pubkey_bits, - ecdsa_fingerprint, ecdsa_verifysig, ecdsa_sign, "ecdsa-sha2-nistp256", @@ -3581,7 +3498,6 @@ const struct ssh_signkey ssh_ecdsa_nistp384 = { ecdsa_openssh_fmtkey, 3 /* curve name, point, private exponent */, ecdsa_pubkey_bits, - ecdsa_fingerprint, ecdsa_verifysig, ecdsa_sign, "ecdsa-sha2-nistp384", @@ -3599,7 +3515,6 @@ const struct ssh_signkey ssh_ecdsa_nistp521 = { ecdsa_openssh_fmtkey, 3 /* curve name, point, private exponent */, ecdsa_pubkey_bits, - ecdsa_fingerprint, ecdsa_verifysig, ecdsa_sign, "ecdsa-sha2-nistp521", diff --git a/sshpubk.c b/sshpubk.c index 053eeb6f..e5952a0a 100644 --- a/sshpubk.c +++ b/sshpubk.c @@ -1568,6 +1568,62 @@ void ssh2_write_pubkey(FILE *fp, const char *comment, } } +/* ---------------------------------------------------------------------- + * Utility functions to compute SSH-2 fingerprints in a uniform way. + */ +char *ssh2_fingerprint_blob(const void *blob, int bloblen) +{ + unsigned char digest[16]; + char fingerprint_str[16*3]; + const char *algstr; + int alglen; + const struct ssh_signkey *alg; + int i; + + /* + * The fingerprint hash itself is always just the MD5 of the blob. + */ + MD5Simple(blob, bloblen, digest); + for (i = 0; i < 16; i++) + sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":"); + + /* + * Identify the key algorithm, if possible. + */ + alglen = toint(GET_32BIT((const unsigned char *)blob)); + if (alglen > 0 && alglen < bloblen-4) { + algstr = (const char *)blob + 4; + + /* + * If we can actually identify the algorithm as one we know + * about, get hold of the key's bit count too. + */ + alg = find_pubkey_alg_len(alglen, algstr); + if (alg) { + int bits = alg->pubkey_bits(blob, bloblen); + return dupprintf("%.*s %d %s", alglen, algstr, + bits, fingerprint_str); + } else { + return dupprintf("%.*s %s", alglen, algstr, fingerprint_str); + } + } else { + /* + * No algorithm available (which means a seriously confused + * key blob, but there we go). Return only the hash. + */ + return dupstr(fingerprint_str); + } +} + +char *ssh2_fingerprint(const struct ssh_signkey *alg, void *data) +{ + int len; + unsigned char *blob = alg->public_blob(data, &len); + char *ret = ssh2_fingerprint_blob(blob, len); + sfree(blob); + return ret; +} + /* ---------------------------------------------------------------------- * Determine the type of a private key file. */ diff --git a/sshrsa.c b/sshrsa.c index 3adccfbe..9042c43a 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -775,41 +775,6 @@ static int rsa2_pubkey_bits(const void *blob, int len) return ret; } -static char *rsa2_fingerprint(void *key) -{ - struct RSAKey *rsa = (struct RSAKey *) key; - struct MD5Context md5c; - unsigned char digest[16], lenbuf[4]; - char buffer[16 * 3 + 40]; - char *ret; - int numlen, i; - - MD5Init(&md5c); - MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-rsa", 11); - -#define ADD_BIGNUM(bignum) \ - numlen = (bignum_bitcount(bignum)+8)/8; \ - PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \ - for (i = numlen; i-- ;) { \ - unsigned char c = bignum_byte(bignum, i); \ - MD5Update(&md5c, &c, 1); \ - } - ADD_BIGNUM(rsa->exponent); - ADD_BIGNUM(rsa->modulus); -#undef ADD_BIGNUM - - MD5Final(digest, &md5c); - - sprintf(buffer, "ssh-rsa %d ", bignum_bitcount(rsa->modulus)); - for (i = 0; i < 16; i++) - sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "", - digest[i]); - ret = snewn(strlen(buffer) + 1, char); - if (ret) - strcpy(ret, buffer); - return ret; -} - /* * This is the magic ASN.1/DER prefix that goes in the decoded * signature, between the string of FFs and the actual SHA hash @@ -945,7 +910,6 @@ const struct ssh_signkey ssh_rsa = { rsa2_openssh_fmtkey, 6 /* n,e,d,iqmp,q,p */, rsa2_pubkey_bits, - rsa2_fingerprint, rsa2_verifysig, rsa2_sign, "ssh-rsa", diff --git a/windows/winpgen.c b/windows/winpgen.c index 8806d75f..20146da6 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -753,9 +753,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, savecomment = state->ssh2key.comment; state->ssh2key.comment = NULL; - fp = - state->ssh2key.alg-> - fingerprint(state->ssh2key.data); + fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data); state->ssh2key.comment = savecomment; SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); @@ -1395,7 +1393,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, *state->commentptr = NULL; if (state->ssh2) { char *fp; - fp = state->ssh2key.alg->fingerprint(state->ssh2key.data); + fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data); SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); sfree(fp); } else { diff --git a/windows/winpgnt.c b/windows/winpgnt.c index a82888f6..70f8309e 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -297,7 +297,7 @@ void keylist_update(void) * nice alignment in the list box, until we encounter a : * meaning we're into the fingerprint proper. */ - p = skey->alg->fingerprint(skey->data); + p = ssh2_fingerprint(skey->alg, skey->data); listentry = dupprintf("%s\t%s", p, skey->comment); fp_len = strlen(listentry); sfree(p); -- 2.45.2