Blob Blame History Raw
diff --git a/auth-rsa.c b/auth-rsa.c
index e9f4ede..ff7a132 100644
--- a/auth-rsa.c
+++ b/auth-rsa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-rsa.c,v 1.88 2014/07/15 15:54:14 millert Exp $ */
+/* $OpenBSD: auth-rsa.c,v 1.89 2014/12/21 22:27:56 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -236,7 +236,8 @@ rsa_key_allowed_in_file(struct passwd *pw, char *file,
 			    "actual %d vs. announced %d.",
 			    file, linenum, BN_num_bits(key->rsa->n), bits);
 
-		fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+		fp = key_fingerprint(key, options.fingerprint_hash,
+		    SSH_FP_DEFAULT);
 		debug("matching key found: file %s, line %lu %s %s",
 		    file, linenum, key_type(key), fp);
 		free(fp);
diff --git a/auth.c b/auth.c
index 5e60682..5a9acd3 100644
--- a/auth.c
+++ b/auth.c
@@ -702,7 +702,7 @@ auth_key_is_revoked(Key *key)
 	case 1:
  revoked:
 		/* Key revoked */
-		key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+		key_fp = key_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);
 		error("WARNING: authentication attempt with a revoked "
 		    "%s key %s ", key_type(key), key_fp);
 		free(key_fp);
diff --git a/auth2-hostbased.c b/auth2-hostbased.c
index 6787e4c..b7ae353 100644
--- a/auth2-hostbased.c
+++ b/auth2-hostbased.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-hostbased.c,v 1.18 2014/07/15 15:54:14 millert Exp $ */
+/* $OpenBSD: auth2-hostbased.c,v 1.19 2014/12/21 22:27:56 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -208,13 +208,14 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
 	if (host_status == HOST_OK) {
 		if (key_is_cert(key)) {
 			fp = key_fingerprint(key->cert->signature_key,
-			    SSH_FP_MD5, SSH_FP_HEX);
+			    options.fingerprint_hash, SSH_FP_DEFAULT);
 			verbose("Accepted certificate ID \"%s\" signed by "
 			    "%s CA %s from %s@%s", key->cert->key_id,
 			    key_type(key->cert->signature_key), fp,
 			    cuser, lookup);
 		} else {
-			fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+			fp = key_fingerprint(key, options.fingerprint_hash,
+			    SSH_FP_DEFAULT);
 			verbose("Accepted %s public key %s from %s@%s",
 			    key_type(key), fp, cuser, lookup);
 		}
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index f3ca965..3f4f789 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -213,7 +213,7 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
 
 	if (key_is_cert(key)) {
 		fp = key_fingerprint(key->cert->signature_key,
-		    SSH_FP_MD5, SSH_FP_HEX);
+		    options.fingerprint_hash, SSH_FP_DEFAULT);
 		auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", 
 		    key_type(key), key->cert->key_id,
 		    (unsigned long long)key->cert->serial,
@@ -221,7 +221,8 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
 		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
 		free(fp);
 	} else {
-		fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+		fp = key_fingerprint(key, options.fingerprint_hash,
+		    SSH_FP_DEFAULT);
 		auth_info(authctxt, "%s %s%s%s", key_type(key), fp,
 		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
 		free(fp);
@@ -365,8 +366,8 @@ check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
 				continue;
 			if (!key_is_cert_authority)
 				continue;
-			fp = key_fingerprint(found, SSH_FP_MD5,
-			    SSH_FP_HEX);
+			fp = key_fingerprint(found, options.fingerprint_hash,
+			    SSH_FP_DEFAULT);
 			debug("matching CA found: file %s, line %lu, %s %s",
 			    file, linenum, key_type(found), fp);
 			/*
@@ -406,7 +407,8 @@ check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
 			if (key_is_cert_authority)
 				continue;
 			found_key = 1;
-			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
+			fp = key_fingerprint(found, options.fingerprint_hash,
+			    SSH_FP_DEFAULT);
 			debug("matching key found: file %s, line %lu %s %s",
 			    file, linenum, key_type(found), fp);
 			free(fp);
@@ -432,7 +434,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key)
 		return 0;
 
 	ca_fp = key_fingerprint(key->cert->signature_key,
-	    SSH_FP_MD5, SSH_FP_HEX);
+	    options.fingerprint_hash, SSH_FP_DEFAULT);
 
 	if (key_in_file(key->cert->signature_key,
 	    options.trusted_user_ca_keys, 1) != 1) {
diff --git a/digest-libc.c b/digest-libc.c
index 1b4423a..169ded0 100644
--- a/digest-libc.c
+++ b/digest-libc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: digest-libc.c,v 1.3 2014/06/24 01:13:21 djm Exp $ */
+/* $OpenBSD: digest-libc.c,v 1.4 2014/12/21 22:27:56 djm Exp $ */
 /*
  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
  * Copyright (c) 2014 Markus Friedl.  All rights reserved.
@@ -126,6 +126,26 @@ ssh_digest_by_alg(int alg)
 	return &(digests[alg]);
 }
 
+int
+ssh_digest_alg_by_name(const char *name)
+{
+	int alg;
+
+	for (alg = 0; alg < SSH_DIGEST_MAX; alg++) {
+		if (strcasecmp(name, digests[alg].name) == 0)
+			return digests[alg].id;
+	}
+	return -1;
+}
+
+const char *
+ssh_digest_alg_name(int alg)
+{
+	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
+
+	return digest == NULL ? NULL : digest->name;
+}
+
 size_t
 ssh_digest_bytes(int alg)
 {
diff --git a/digest-openssl.c b/digest-openssl.c
index 02b1703..bb58ff2 100644
--- a/digest-openssl.c
+++ b/digest-openssl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: digest-openssl.c,v 1.4 2014/07/03 03:26:43 djm Exp $ */
+/* $OpenBSD: digest-openssl.c,v 1.5 2014/12/21 22:27:56 djm Exp $ */
 /*
  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
  *
@@ -74,6 +74,26 @@ ssh_digest_by_alg(int alg)
 	return &(digests[alg]);
 }
 
+int
+ssh_digest_alg_by_name(const char *name)
+{
+	int alg;
+
+	for (alg = 0; digests[alg].id != -1; alg++) {
+		if (strcasecmp(name, digests[alg].name) == 0)
+			return digests[alg].id;
+	}
+	return -1;
+}
+
+const char *
+ssh_digest_alg_name(int alg)
+{
+	const struct ssh_digest *digest = ssh_digest_by_alg(alg);
+
+	return digest == NULL ? NULL : digest->name;
+}
+
 size_t
 ssh_digest_bytes(int alg)
 {
diff --git a/digest.h b/digest.h
index 6afb197..3fe0734 100644
--- a/digest.h
+++ b/digest.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: digest.h,v 1.6 2014/07/03 04:36:45 djm Exp $ */
+/* $OpenBSD: digest.h,v 1.7 2014/12/21 22:27:56 djm Exp $ */
 /*
  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
  *
@@ -33,6 +33,12 @@
 struct sshbuf;
 struct ssh_digest_ctx;
 
+/* Looks up a digest algorithm by name */
+int ssh_digest_alg_by_name(const char *name);
+
+/* Returns the algorithm name for a digest identifier */
+const char *ssh_digest_alg_name(int alg);
+
 /* Returns the algorithm's digest length in bytes or 0 for invalid algorithm */
 size_t ssh_digest_bytes(int alg);
 
diff --git a/dns.c b/dns.c
index c4d073c..4b8ae44 100644
--- a/dns.c
+++ b/dns.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dns.c,v 1.31 2014/06/24 01:13:21 djm Exp $ */
+/* $OpenBSD: dns.c,v 1.32 2014/12/21 22:27:56 djm Exp $ */
 
 /*
  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -41,6 +41,7 @@
 #include "key.h"
 #include "dns.h"
 #include "log.h"
+#include "digest.h"
 
 static const char *errset_text[] = {
 	"success",		/* 0 ERRSET_SUCCESS */
@@ -80,7 +81,7 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
     u_char **digest, u_int *digest_len, Key *key)
 {
 	int success = 0;
-	enum fp_type fp_type = 0;
+	int fp_alg = -1;
 
 	switch (key->type) {
 	case KEY_RSA:
@@ -110,17 +111,17 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
 
 	switch (*digest_type) {
 	case SSHFP_HASH_SHA1:
-		fp_type = SSH_FP_SHA1;
+		fp_alg = SSH_DIGEST_SHA1;
 		break;
 	case SSHFP_HASH_SHA256:
-		fp_type = SSH_FP_SHA256;
+		fp_alg = SSH_DIGEST_SHA256;
 		break;
 	default:
 		*digest_type = SSHFP_HASH_RESERVED; /* 0 */
 	}
 
 	if (*algorithm && *digest_type) {
-		*digest = key_fingerprint_raw(key, fp_type, digest_len);
+		*digest = key_fingerprint_raw(key, fp_alg, digest_len);
 		if (*digest == NULL)
 			fatal("dns_read_key: null from key_fingerprint_raw()");
 		success = 1;
diff --git a/key.c b/key.c
index 2060761..780be1c 100644
--- a/key.c
+++ b/key.c
@@ -40,8 +40,7 @@ key_new_private(int type)
 }
 
 u_char*
-key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
-    u_int *dgst_raw_length)
+key_fingerprint_raw(const Key *k, int dgst_alg, u_int *dgst_raw_length)
 {
 	u_char *ret = NULL;
 	size_t dlen;
@@ -49,7 +48,7 @@ key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
 
 	if (dgst_raw_length != NULL)
 		*dgst_raw_length = 0;
-	if ((r = sshkey_fingerprint_raw(k, dgst_type, &ret, &dlen)) != 0)
+	if ((r = sshkey_fingerprint_raw(k, dgst_alg, &ret, &dlen)) != 0)
 		fatal("%s: %s", __func__, ssh_err(r));
 	if (dlen > INT_MAX)
 		fatal("%s: giant len %zu", __func__, dlen);
diff --git a/key.h b/key.h
index c6401a5..e1a3625 100644
--- a/key.h
+++ b/key.h
@@ -67,7 +67,7 @@ void	 key_add_private(Key *);
 Key	*key_new_private(int);
 void	 key_free(Key *);
 Key	*key_demote(const Key *);
-u_char	*key_fingerprint_raw(const Key *, enum fp_type, u_int *);
+u_char	*key_fingerprint_raw(const Key *, int, u_int *);
 int	 key_write(const Key *, FILE *);
 int	 key_read(Key *, char **);
 
diff --git a/krl.c b/krl.c
index eb31df9..4abed7e 100644
--- a/krl.c
+++ b/krl.c
@@ -36,6 +36,7 @@
 #include "misc.h"
 #include "log.h"
 #include "xmalloc.h"
+#include "digest.h"
 
 #include "krl.h"
 
@@ -406,7 +407,7 @@ ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const Key *key)
 	u_int len;
 
 	debug3("%s: revoke type %s by sha1", __func__, key_type(key));
-	if ((blob = key_fingerprint_raw(key, SSH_FP_SHA1, &len)) == NULL)
+	if ((blob = key_fingerprint_raw(key, SSH_DIGEST_SHA1, &len)) == NULL)
 		return -1;
 	return revoke_blob(&krl->revoked_sha1s, blob, len);
 }
@@ -1119,7 +1120,7 @@ is_key_revoked(struct ssh_krl *krl, const Key *key)
 
 	/* Check explicitly revoked hashes first */
 	memset(&rb, 0, sizeof(rb));
-	if ((rb.blob = key_fingerprint_raw(key, SSH_FP_SHA1, &rb.len)) == NULL)
+	if ((rb.blob = key_fingerprint_raw(key, SSH_DIGEST_SHA1, &rb.len)) == NULL)
 		return -1;
 	erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
 	free(rb.blob);
diff --git a/readconf.c b/readconf.c
index 7948ce1..3f5c58b 100644
--- a/readconf.c
+++ b/readconf.c
@@ -56,6 +56,7 @@
 #include "kex.h"
 #include "mac.h"
 #include "uidswap.h"
+#include "digest.h"
 
 /* Format of the configuration file:
 
@@ -151,6 +152,7 @@ typedef enum {
 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
 	oStreamLocalBindMask, oStreamLocalBindUnlink,
+	oFingerprintHash,
 	oIgnoredUnknownOption, oDeprecated, oUnsupported
 } OpCodes;
 
@@ -265,6 +267,7 @@ static struct {
 	{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
 	{ "streamlocalbindmask", oStreamLocalBindMask },
 	{ "streamlocalbindunlink", oStreamLocalBindUnlink },
+	{ "fingerprinthash", oFingerprintHash },
 	{ "ignoreunknown", oIgnoreUnknown },
 
 	{ NULL, oBadOption }
@@ -1433,6 +1436,18 @@ parse_int:
 		intptr = &options->fwd_opts.streamlocal_bind_unlink;
 		goto parse_flag;
 
+	case oFingerprintHash:
+		arg = strdelim(&s);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing argument.",
+			    filename, linenum);
+		if ((value = ssh_digest_alg_by_name(arg)) == -1)
+			fatal("%.200s line %d: Invalid hash algorithm \"%s\".",
+			    filename, linenum, arg);
+		if (*activep)
+			options->fingerprint_hash = value;
+		break;
+
 	case oDeprecated:
 		debug("%s line %d: Deprecated option \"%s\"",
 		    filename, linenum, keyword);
@@ -1609,6 +1624,7 @@ initialize_options(Options * options)
 	options->canonicalize_max_dots = -1;
 	options->canonicalize_fallback_local = -1;
 	options->canonicalize_hostname = -1;
+	options->fingerprint_hash = -1;
 }
 
 /*
@@ -1786,6 +1802,9 @@ fill_default_options(Options * options)
 		options->canonicalize_fallback_local = 1;
 	if (options->canonicalize_hostname == -1)
 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
+	if (options->fingerprint_hash == -1)
+		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
+
 #define CLEAR_ON_NONE(v) \
 	do { \
 		if (option_clear_or_none(v)) { \
diff --git a/readconf.h b/readconf.h
index 0b9cb77..a028306 100644
--- a/readconf.h
+++ b/readconf.h
@@ -144,6 +144,8 @@ typedef struct {
 	int	num_permitted_cnames;
 	struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS];
 
+	int	fingerprint_hash;
+
 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
 }       Options;
 
diff --git a/regress/Makefile b/regress/Makefile
index 3feb7a9..2905a0d 100644
--- a/regress/Makefile
+++ b/regress/Makefile
@@ -1,6 +1,6 @@
-#	$OpenBSD: Makefile,v 1.70 2014/06/24 01:14:17 djm Exp $
+#	$OpenBSD: Makefile,v 1.71 2014/12/22 02:15:52 djm Exp $
 
-REGRESS_TARGETS=	unit t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t-exec
+REGRESS_TARGETS=	unit t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t-exec
 tests:		$(REGRESS_TARGETS)
 
 # Interop tests are not run by default
@@ -119,7 +119,7 @@ t3:
 	${TEST_SSH_SSHKEYGEN} -if $(OBJ)/t3.out | diff - ${.CURDIR}/rsa_openssh.pub
 
 t4:
-	${TEST_SSH_SSHKEYGEN} -lf ${.CURDIR}/rsa_openssh.pub |\
+	${TEST_SSH_SSHKEYGEN} -E md5 -lf ${.CURDIR}/rsa_openssh.pub |\
 		awk '{print $$2}' | diff - ${.CURDIR}/t4.ok
 
 t5:
@@ -164,6 +164,10 @@ t10: $(OBJ)/t10.out
 	${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t10.out > /dev/null
 	${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t10.out > /dev/null
 
+t11:
+	${TEST_SSH_SSHKEYGEN} -E sha256 -lf ${.CURDIR}/rsa_openssh.pub |\
+		awk '{print $$2}' | diff - ${.CURDIR}/t11.ok
+
 t-exec:	${LTESTS:=.sh}
 	@if [ "x$?" = "x" ]; then exit 0; fi; \
 	for TEST in ""$?; do \
diff --git a/regress/t11.ok b/regress/t11.ok
new file mode 100644
index 0000000..1925bb4
--- /dev/null
+++ b/regress/t11.ok
@@ -0,0 +1 @@
+SHA256:4w1rnrek3klTJOTVhwuCIFd5k+pq9Bfo5KTxxb8BqbY
diff --git a/regress/t4.ok b/regress/t4.ok
index 8c4942b..4631ea8 100644
--- a/regress/t4.ok
+++ b/regress/t4.ok
@@ -1 +1 @@
-3b:dd:44:e9:49:18:84:95:f1:e7:33:6b:9d:93:b1:36
+MD5:3b:dd:44:e9:49:18:84:95:f1:e7:33:6b:9d:93:b1:36
diff --git a/regress/unittests/sshkey/test_file.c b/regress/unittests/sshkey/test_file.c
index 764f7fb..9c38a7c 100644
--- a/regress/unittests/sshkey/test_file.c
+++ b/regress/unittests/sshkey/test_file.c
@@ -1,4 +1,4 @@
-/* 	$OpenBSD: test_file.c,v 1.1 2014/06/24 01:14:18 djm Exp $ */
+/* 	$OpenBSD: test_file.c,v 1.2 2014/12/22 02:15:52 djm Exp $ */
 /*
  * Regress test for sshkey.h key management API
  *
@@ -33,6 +33,7 @@
 #include "authfile.h"
 #include "sshkey.h"
 #include "sshbuf.h"
+#include "digest.h"
 
 #include "common.h"
 
@@ -81,7 +82,7 @@ sshkey_file_tests(void)
 
 	TEST_START("RSA1 key hex fingerprint");
 	buf = load_text_file("rsa1_1.fp");
-	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -90,7 +91,7 @@ sshkey_file_tests(void)
 
 	TEST_START("RSA1 key bubblebabble fingerprint");
 	buf = load_text_file("rsa1_1.fp.bb");
-	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -164,7 +165,7 @@ sshkey_file_tests(void)
 
 	TEST_START("RSA key hex fingerprint");
 	buf = load_text_file("rsa_1.fp");
-	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -173,7 +174,7 @@ sshkey_file_tests(void)
 
 	TEST_START("RSA cert hex fingerprint");
 	buf = load_text_file("rsa_1-cert.fp");
-	cp = sshkey_fingerprint(k2, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -183,7 +184,7 @@ sshkey_file_tests(void)
 
 	TEST_START("RSA key bubblebabble fingerprint");
 	buf = load_text_file("rsa_1.fp.bb");
-	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -257,7 +258,7 @@ sshkey_file_tests(void)
 
 	TEST_START("DSA key hex fingerprint");
 	buf = load_text_file("dsa_1.fp");
-	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -266,7 +267,7 @@ sshkey_file_tests(void)
 
 	TEST_START("DSA cert hex fingerprint");
 	buf = load_text_file("dsa_1-cert.fp");
-	cp = sshkey_fingerprint(k2, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -276,7 +277,7 @@ sshkey_file_tests(void)
 
 	TEST_START("DSA key bubblebabble fingerprint");
 	buf = load_text_file("dsa_1.fp.bb");
-	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -357,7 +358,7 @@ sshkey_file_tests(void)
 
 	TEST_START("ECDSA key hex fingerprint");
 	buf = load_text_file("ecdsa_1.fp");
-	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -366,7 +367,7 @@ sshkey_file_tests(void)
 
 	TEST_START("ECDSA cert hex fingerprint");
 	buf = load_text_file("ecdsa_1-cert.fp");
-	cp = sshkey_fingerprint(k2, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -376,7 +377,7 @@ sshkey_file_tests(void)
 
 	TEST_START("ECDSA key bubblebabble fingerprint");
 	buf = load_text_file("ecdsa_1.fp.bb");
-	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -424,7 +425,7 @@ sshkey_file_tests(void)
 
 	TEST_START("Ed25519 key hex fingerprint");
 	buf = load_text_file("ed25519_1.fp");
-	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -433,7 +434,7 @@ sshkey_file_tests(void)
 
 	TEST_START("Ed25519 cert hex fingerprint");
 	buf = load_text_file("ed25519_1-cert.fp");
-	cp = sshkey_fingerprint(k2, SSH_FP_MD5, SSH_FP_HEX);
+	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
@@ -443,7 +444,7 @@ sshkey_file_tests(void)
 
 	TEST_START("Ed25519 key bubblebabble fingerprint");
 	buf = load_text_file("ed25519_1.fp.bb");
-	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
+	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
 	ASSERT_PTR_NE(cp, NULL);
 	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
 	sshbuf_free(buf);
diff --git a/regress/unittests/sshkey/testdata/dsa_1-cert.fp b/regress/unittests/sshkey/testdata/dsa_1-cert.fp
index 56ee1f8..b26145b 100644
--- a/regress/unittests/sshkey/testdata/dsa_1-cert.fp
+++ b/regress/unittests/sshkey/testdata/dsa_1-cert.fp
@@ -1 +1 @@
-5a:4a:41:8c:4e:fa:4c:52:19:f9:39:49:31:fb:fd:74
+MD5:5a:4a:41:8c:4e:fa:4c:52:19:f9:39:49:31:fb:fd:74
diff --git a/regress/unittests/sshkey/testdata/dsa_1.fp b/regress/unittests/sshkey/testdata/dsa_1.fp
index 56ee1f8..b26145b 100644
--- a/regress/unittests/sshkey/testdata/dsa_1.fp
+++ b/regress/unittests/sshkey/testdata/dsa_1.fp
@@ -1 +1 @@
-5a:4a:41:8c:4e:fa:4c:52:19:f9:39:49:31:fb:fd:74
+MD5:5a:4a:41:8c:4e:fa:4c:52:19:f9:39:49:31:fb:fd:74
diff --git a/regress/unittests/sshkey/testdata/dsa_2.fp b/regress/unittests/sshkey/testdata/dsa_2.fp
index ba9de82..8226574 100644
--- a/regress/unittests/sshkey/testdata/dsa_2.fp
+++ b/regress/unittests/sshkey/testdata/dsa_2.fp
@@ -1 +1 @@
-72:5f:50:6b:e5:64:c5:62:21:92:3f:8b:10:9b:9f:1a
+MD5:72:5f:50:6b:e5:64:c5:62:21:92:3f:8b:10:9b:9f:1a
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp b/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp
index a56dbc8..c3d747a 100644
--- a/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp
+++ b/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp
@@ -1 +1 @@
-f7:be:4c:02:65:ed:4c:11:af:ab:a8:dd:0a:92:e7:44
+MD5:f7:be:4c:02:65:ed:4c:11:af:ab:a8:dd:0a:92:e7:44
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.fp b/regress/unittests/sshkey/testdata/ecdsa_1.fp
index a56dbc8..c3d747a 100644
--- a/regress/unittests/sshkey/testdata/ecdsa_1.fp
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.fp
@@ -1 +1 @@
-f7:be:4c:02:65:ed:4c:11:af:ab:a8:dd:0a:92:e7:44
+MD5:f7:be:4c:02:65:ed:4c:11:af:ab:a8:dd:0a:92:e7:44
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.fp b/regress/unittests/sshkey/testdata/ecdsa_2.fp
index eb4bbdf..fe7526b 100644
--- a/regress/unittests/sshkey/testdata/ecdsa_2.fp
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.fp
@@ -1 +1 @@
-51:bd:ff:2b:6d:26:9b:90:f9:e1:4a:ca:a0:29:8e:70
+MD5:51:bd:ff:2b:6d:26:9b:90:f9:e1:4a:ca:a0:29:8e:70
diff --git a/regress/unittests/sshkey/testdata/ed25519_1-cert.fp b/regress/unittests/sshkey/testdata/ed25519_1-cert.fp
index e6d23d0..fbde87a 100644
--- a/regress/unittests/sshkey/testdata/ed25519_1-cert.fp
+++ b/regress/unittests/sshkey/testdata/ed25519_1-cert.fp
@@ -1 +1 @@
-19:08:8e:7e:4d:e5:de:86:2a:09:47:65:eb:0a:51:2f
+MD5:19:08:8e:7e:4d:e5:de:86:2a:09:47:65:eb:0a:51:2f
diff --git a/regress/unittests/sshkey/testdata/ed25519_1.fp b/regress/unittests/sshkey/testdata/ed25519_1.fp
index e6d23d0..fbde87a 100644
--- a/regress/unittests/sshkey/testdata/ed25519_1.fp
+++ b/regress/unittests/sshkey/testdata/ed25519_1.fp
@@ -1 +1 @@
-19:08:8e:7e:4d:e5:de:86:2a:09:47:65:eb:0a:51:2f
+MD5:19:08:8e:7e:4d:e5:de:86:2a:09:47:65:eb:0a:51:2f
diff --git a/regress/unittests/sshkey/testdata/ed25519_2.fp b/regress/unittests/sshkey/testdata/ed25519_2.fp
index 02c684f..ec1cdbb 100644
--- a/regress/unittests/sshkey/testdata/ed25519_2.fp
+++ b/regress/unittests/sshkey/testdata/ed25519_2.fp
@@ -1 +1 @@
-5c:c9:ae:a3:0c:aa:28:29:b8:fc:7c:64:ba:6e:e9:c9
+MD5:5c:c9:ae:a3:0c:aa:28:29:b8:fc:7c:64:ba:6e:e9:c9
diff --git a/regress/unittests/sshkey/testdata/rsa1_1.fp b/regress/unittests/sshkey/testdata/rsa1_1.fp
index 782ece0..2e1068c 100644
--- a/regress/unittests/sshkey/testdata/rsa1_1.fp
+++ b/regress/unittests/sshkey/testdata/rsa1_1.fp
@@ -1 +1 @@
-a8:82:9b:98:c5:e6:19:d6:83:39:9f:4d:3a:8f:7c:80
+MD5:a8:82:9b:98:c5:e6:19:d6:83:39:9f:4d:3a:8f:7c:80
diff --git a/regress/unittests/sshkey/testdata/rsa1_2.fp b/regress/unittests/sshkey/testdata/rsa1_2.fp
index c332537..cd00393 100644
--- a/regress/unittests/sshkey/testdata/rsa1_2.fp
+++ b/regress/unittests/sshkey/testdata/rsa1_2.fp
@@ -1 +1 @@
-c0:83:1c:97:5f:32:77:7e:e4:e3:e9:29:b9:eb:76:9c
+MD5:c0:83:1c:97:5f:32:77:7e:e4:e3:e9:29:b9:eb:76:9c
diff --git a/regress/unittests/sshkey/testdata/rsa_1-cert.fp b/regress/unittests/sshkey/testdata/rsa_1-cert.fp
index bf9c2e3..1cf780d 100644
--- a/regress/unittests/sshkey/testdata/rsa_1-cert.fp
+++ b/regress/unittests/sshkey/testdata/rsa_1-cert.fp
@@ -1 +1 @@
-be:27:4c:16:27:f5:04:03:62:a8:b7:91:df:a5:b1:3b
+MD5:be:27:4c:16:27:f5:04:03:62:a8:b7:91:df:a5:b1:3b
diff --git a/regress/unittests/sshkey/testdata/rsa_1.fp b/regress/unittests/sshkey/testdata/rsa_1.fp
index bf9c2e3..1cf780d 100644
--- a/regress/unittests/sshkey/testdata/rsa_1.fp
+++ b/regress/unittests/sshkey/testdata/rsa_1.fp
@@ -1 +1 @@
-be:27:4c:16:27:f5:04:03:62:a8:b7:91:df:a5:b1:3b
+MD5:be:27:4c:16:27:f5:04:03:62:a8:b7:91:df:a5:b1:3b
diff --git a/regress/unittests/sshkey/testdata/rsa_2.fp b/regress/unittests/sshkey/testdata/rsa_2.fp
index 53939f4..8d43676 100644
--- a/regress/unittests/sshkey/testdata/rsa_2.fp
+++ b/regress/unittests/sshkey/testdata/rsa_2.fp
@@ -1 +1 @@
-fb:8f:7b:26:3d:42:40:ef:ed:f1:ed:ee:66:9e:ba:b0
+MD5:fb:8f:7b:26:3d:42:40:ef:ed:f1:ed:ee:66:9e:ba:b0
diff --git a/servconf.c b/servconf.c
index b7f3294..e3ebaac 100644
--- a/servconf.c
+++ b/servconf.c
@@ -54,6 +54,7 @@
 #include "packet.h"
 #include "hostfile.h"
 #include "auth.h"
+#include "digest.h"
 
 static void add_listen_addr(ServerOptions *, char *, int);
 static void add_one_listen_addr(ServerOptions *, char *, int);
@@ -157,6 +158,7 @@ initialize_server_options(ServerOptions *options)
 	options->ip_qos_interactive = -1;
 	options->ip_qos_bulk = -1;
 	options->version_addendum = NULL;
+	options->fingerprint_hash = -1;
 }
 
 void
@@ -312,6 +314,8 @@ fill_default_server_options(ServerOptions *options)
 		options->fwd_opts.streamlocal_bind_mask = 0177;
 	if (options->fwd_opts.streamlocal_bind_unlink == -1)
 		options->fwd_opts.streamlocal_bind_unlink = 0;
+	if (options->fingerprint_hash == -1)
+		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
 	/* Turn privilege separation on by default */
 	if (use_privsep == -1)
 		use_privsep = PRIVSEP_NOSANDBOX;
@@ -361,7 +365,7 @@ typedef enum {
 	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
 	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
-	sAllowStreamLocalForwarding,
+	sAllowStreamLocalForwarding, sFingerprintHash,
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
@@ -492,6 +496,7 @@ static struct {
 	{ "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
 	{ "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL },
 	{ "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
+	{ "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
 	{ NULL, sBadOption, 0 }
 };
 
@@ -1663,6 +1668,18 @@ process_server_config_line(ServerOptions *options, char *line,
 		intptr = &options->fwd_opts.streamlocal_bind_unlink;
 		goto parse_flag;
 
+	case sFingerprintHash:
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing argument.",
+			    filename, linenum);
+		if ((value = ssh_digest_alg_by_name(arg)) == -1)
+			fatal("%.200s line %d: Invalid hash algorithm \"%s\".",
+			    filename, linenum, arg);
+		if (*activep)
+			options->fingerprint_hash = value;
+		break;
+
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
@@ -1905,6 +1922,8 @@ fmt_intarg(ServerOpCodes code, int val)
 		return fmt_multistate_int(val, multistate_tcpfwd);
 	case sAllowStreamLocalForwarding:
 		return fmt_multistate_int(val, multistate_tcpfwd);
+	case sFingerprintHash:
+		return ssh_digest_alg_name(val);
 	case sProtocol:
 		switch (val) {
 		case SSH_PROTO_1:
@@ -2066,6 +2085,7 @@ dump_config(ServerOptions *o)
 	dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding);
 	dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding);
 	dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep);
+	dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash);
 
 	/* string arguments */
 	dump_cfg_string(sPidFile, o->pid_file);
diff --git a/servconf.h b/servconf.h
index 766db3a..49b228b 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.114 2014/07/15 15:54:14 millert Exp $ */
+/* $OpenBSD: servconf.h,v 1.115 2014/12/21 22:27:56 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -185,6 +185,8 @@ typedef struct {
 
 	u_int	num_auth_methods;
 	char   *auth_methods[MAX_AUTH_METHODS];
+
+	int	fingerprint_hash;
 }       ServerOptions;
 
 /* Information about the incoming connection as used by Match */
diff --git a/ssh-add.1 b/ssh-add.1
index 4812448..04d1840 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -44,6 +44,7 @@
 .Sh SYNOPSIS
 .Nm ssh-add
 .Op Fl cDdkLlXx
+.Op Fl E Ar fingerprint_hash
 .Op Fl t Ar life
 .Op Ar
 .Nm ssh-add
@@ -108,6 +109,14 @@ If no public key is found at a given path,
 will append
 .Pa .pub
 and retry.
+.It Fl E Ar fingerprint_hash
+Specifies the hash algorithm used when displaying key fingerprints.
+Valid options are:
+.Dq md5
+and
+.Dq sha256 .
+The default is
+.Dq sha256 .
 .It Fl e Ar pkcs11
 Remove keys provided by the PKCS#11 shared library
 .Ar pkcs11 .
diff --git a/ssh-add.c b/ssh-add.c
index 78a3359..5d6a5f4 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -63,6 +63,7 @@
 #include "pathnames.h"
 #include "misc.h"
 #include "ssherr.h"
+#include "digest.h"
 
 /* argv0 */
 extern char *__progname;
@@ -79,6 +80,8 @@ static char *default_files[] = {
 	NULL
 };
 
+static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
+
 /* Default lifetime (0 == forever) */
 static int lifetime = 0;
 
@@ -340,8 +343,8 @@ list_identities(AuthenticationConnection *ac, int do_fp)
 		    key = ssh_get_next_identity(ac, &comment, version)) {
 			had_identities = 1;
 			if (do_fp) {
-				fp = key_fingerprint(key, SSH_FP_MD5,
-				    SSH_FP_HEX);
+				fp = key_fingerprint(key, fingerprint_hash,
+				    SSH_FP_DEFAULT);
 				printf("%d %s %s (%s)\n",
 				    key_size(key), fp, comment, key_type(key));
 				free(fp);
@@ -408,6 +411,7 @@ usage(void)
 	fprintf(stderr, "usage: %s [options] [file ...]\n", __progname);
 	fprintf(stderr, "Options:\n");
 	fprintf(stderr, "  -l          List fingerprints of all identities.\n");
+	fprintf(stderr, "  -E hash     Specify hash algorithm used for fingerprints.\n");
 	fprintf(stderr, "  -L          List public key parameters of all identities.\n");
 	fprintf(stderr, "  -k          Load only keys and not certificates.\n");
 	fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
@@ -428,6 +432,7 @@ main(int argc, char **argv)
 	AuthenticationConnection *ac = NULL;
 	char *pkcs11provider = NULL;
 	int i, ch, deleting = 0, ret = 0, key_only = 0;
+	int xflag = 0, lflag = 0, Dflag = 0;
 
 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
 	sanitise_stdfd();
@@ -446,21 +451,28 @@ main(int argc, char **argv)
 		    "Could not open a connection to your authentication agent.\n");
 		exit(2);
 	}
-	while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) {
+	while ((ch = getopt(argc, argv, "klLcdDxXE:e:s:t:")) != -1) {
 		switch (ch) {
+		case 'E':
+			fingerprint_hash = ssh_digest_alg_by_name(optarg);
+			if (fingerprint_hash == -1)
+				fatal("Invalid hash algorithm \"%s\"", optarg);
+			break;
 		case 'k':
 			key_only = 1;
 			break;
 		case 'l':
 		case 'L':
-			if (list_identities(ac, ch == 'l' ? 1 : 0) == -1)
-				ret = 1;
-			goto done;
+			if (lflag != 0)
+				fatal("-%c flag already specified", lflag);
+			lflag = ch;
+			break;
 		case 'x':
 		case 'X':
-			if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1)
-				ret = 1;
-			goto done;
+			if (xflag != 0)
+				fatal("-%c flag already specified", xflag);
+			xflag = ch;
+			break;
 		case 'c':
 			confirm = 1;
 			break;
@@ -468,9 +480,8 @@ main(int argc, char **argv)
 			deleting = 1;
 			break;
 		case 'D':
-			if (delete_all(ac) == -1)
-				ret = 1;
-			goto done;
+			Dflag = 1;
+			break;
 		case 's':
 			pkcs11provider = optarg;
 			break;
@@ -491,6 +502,23 @@ main(int argc, char **argv)
 			goto done;
 		}
 	}
+
+	if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
+		fatal("Invalid combination of actions");
+	else if (xflag) {
+		if (lock_agent(ac, xflag == 'x' ? 1 : 0) == -1)
+			ret = 1;
+		goto done;
+	} else if (lflag) {
+		if (list_identities(ac, lflag == 'l' ? 1 : 0) == -1)
+			ret = 1;
+		goto done;
+	} else if (Dflag) {
+		if (delete_all(ac) == -1)
+			ret = 1;
+		goto done;
+	}
+
 	argc -= optind;
 	argv += optind;
 	if (pkcs11provider != NULL) {
diff --git a/ssh-agent.1 b/ssh-agent.1
index a1e634f..d7e791b 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -45,6 +45,7 @@
 .Op Fl c | s
 .Op Fl d
 .Op Fl a Ar bind_address
+.Op Fl E Ar fingerprint_hash
 .Op Fl t Ar life
 .Op Ar command Op Ar arg ...
 .Nm ssh-agent
@@ -96,6 +97,14 @@ Debug mode.
 When this option is specified
 .Nm
 will not fork.
+.It Fl E Ar fingerprint_hash
+Specifies the hash algorithm used when displaying key fingerprints.
+Valid options are:
+.Dq md5
+and
+.Dq sha256 .
+The default is
+.Dq sha256 .
 .It Fl k
 Kill the current agent (given by the
 .Ev SSH_AGENT_PID
diff --git a/ssh-agent.c b/ssh-agent.c
index 25f10c5..c8036c8 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -142,6 +142,8 @@ extern char *__progname;
 /* Default lifetime in seconds (0 == forever) */
 static long lifetime = 0;
 
+static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
+
 static void
 close_socket(SocketEntry *e)
 {
@@ -203,7 +205,7 @@ confirm_key(Identity *id)
 	char *p;
 	int ret = -1;
 
-	p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX);
+	p = key_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT);
 	if (ask_permission("Allow use of key %s?\nKey fingerprint %s.",
 	    id->comment, p))
 		ret = 0;
@@ -1026,7 +1028,7 @@ usage(void)
 {
 	fprintf(stderr,
 	    "usage: ssh-agent [-c | -s] [-d] [-a bind_address] [-t life]\n"
-	    "                 [command [arg ...]]\n"
+	    "                 [-E fingerprint_hash] [command [arg ...]]\n"
 	    "       ssh-agent [-c | -s] -k\n");
 	exit(1);
 }
@@ -1069,8 +1071,13 @@ main(int ac, char **av)
 	__progname = ssh_get_progname(av[0]);
 	seed_rng();
 
-	while ((ch = getopt(ac, av, "cdksa:t:")) != -1) {
+	while ((ch = getopt(ac, av, "cdksE:a:t:")) != -1) {
 		switch (ch) {
+		case 'E':
+			fingerprint_hash = ssh_digest_alg_by_name(optarg);
+			if (fingerprint_hash == -1)
+				fatal("Invalid hash algorithm \"%s\"", optarg);
+			break;
 		case 'c':
 			if (s_flag)
 				usage();
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index 723a016..276dacc 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -73,6 +73,7 @@
 .Op Fl f Ar keyfile
 .Nm ssh-keygen
 .Fl l
+.Op Fl E Ar fingerprint_hash
 .Op Fl f Ar input_keyfile
 .Nm ssh-keygen
 .Fl B
@@ -269,6 +270,14 @@ When used in combination with
 this option indicates that a CA key resides in a PKCS#11 token (see the
 .Sx CERTIFICATES
 section for details).
+.It Fl E Ar fingerprint_hash
+Specifies the hash algorithm used when displaying key fingerprints.
+Valid options are:
+.Dq md5
+and
+.Dq sha256 .
+The default is
+.Dq sha256 .
 .It Fl e
 This option will read a private or public OpenSSH key file and
 print to stdout the key in one of the formats specified by the
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 23058ee..64fa217 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -53,6 +53,7 @@
 #include "ssh-pkcs11.h"
 #include "atomicio.h"
 #include "krl.h"
+#include "digest.h"
 
 /* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
 #define DEFAULT_BITS		2048
@@ -90,6 +91,9 @@ int show_cert = 0;
 int print_fingerprint = 0;
 int print_bubblebabble = 0;
 
+/* Hash algorithm to use for fingerprints. */
+int fingerprint_hash = SSH_FP_HASH_DEFAULT;
+
 /* The identity file name, given on the command line or entered by the user. */
 char identity_file[1024];
 int have_identity = 0;
@@ -749,11 +753,11 @@ do_download(struct passwd *pw)
 	Key **keys = NULL;
 	int i, nkeys;
 	enum fp_rep rep;
-	enum fp_type fptype;
+	int fptype;
 	char *fp, *ra;
 
-	fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
-	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
+	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
+	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
 
 	pkcs11_init(0);
 	nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
@@ -762,7 +766,7 @@ do_download(struct passwd *pw)
 	for (i = 0; i < nkeys; i++) {
 		if (print_fingerprint) {
 			fp = key_fingerprint(keys[i], fptype, rep);
-			ra = key_fingerprint(keys[i], SSH_FP_MD5,
+			ra = key_fingerprint(keys[i], fingerprint_hash,
 			    SSH_FP_RANDOMART);
 			printf("%u %s %s (PKCS11 key)\n", key_size(keys[i]),
 			    fp, key_type(keys[i]));
@@ -792,12 +796,11 @@ do_fingerprint(struct passwd *pw)
 	char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra;
 	int i, skip = 0, num = 0, invalid = 1;
 	enum fp_rep rep;
-	enum fp_type fptype;
+	int fptype;
 	struct stat st;
 
-	fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
-	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
-
+	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
+	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
 	if (!have_identity)
 		ask_filename(pw, "Enter file in which the key is");
 	if (stat(identity_file, &st) < 0) {
@@ -807,7 +810,8 @@ do_fingerprint(struct passwd *pw)
 	public = key_load_public(identity_file, &comment);
 	if (public != NULL) {
 		fp = key_fingerprint(public, fptype, rep);
-		ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART);
+		ra = key_fingerprint(public, fingerprint_hash,
+		    SSH_FP_RANDOMART);
 		printf("%u %s %s (%s)\n", key_size(public), fp, comment,
 		    key_type(public));
 		if (log_level >= SYSLOG_LEVEL_VERBOSE)
@@ -873,7 +877,8 @@ do_fingerprint(struct passwd *pw)
 		}
 		comment = *cp ? cp : comment;
 		fp = key_fingerprint(public, fptype, rep);
-		ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART);
+		ra = key_fingerprint(public, fingerprint_hash,
+		    SSH_FP_RANDOMART);
 		printf("%u %s %s (%s)\n", key_size(public), fp,
 		    comment ? comment : "no comment", key_type(public));
 		if (log_level >= SYSLOG_LEVEL_VERBOSE)
@@ -991,13 +996,15 @@ printhost(FILE *f, const char *name, Key *public, int ca, int revoked, int hash)
 {
 	if (print_fingerprint) {
 		enum fp_rep rep;
-		enum fp_type fptype;
+		int fptype;
 		char *fp, *ra;
 
-		fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
-		rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
+		fptype = print_bubblebabble ?
+		    SSH_DIGEST_SHA1 : fingerprint_hash;
+		rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
 		fp = key_fingerprint(public, fptype, rep);
-		ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART);
+		ra = key_fingerprint(public, fingerprint_hash,
+		    SSH_FP_RANDOMART);
 		printf("%u %s %s (%s)\n", key_size(public), fp, name,
 		    key_type(public));
 		if (log_level >= SYSLOG_LEVEL_VERBOSE)
@@ -1906,9 +1913,9 @@ do_show_cert(struct passwd *pw)
 		fatal("%s is not a certificate", identity_file);
 	v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00;
 
-	key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+	key_fp = key_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT);
 	ca_fp = key_fingerprint(key->cert->signature_key,
-	    SSH_FP_MD5, SSH_FP_HEX);
+	    fingerprint_hash, SSH_FP_DEFAULT);
 
 	printf("%s:\n", identity_file);
 	printf("        Type: %s %s certificate\n", key_ssh_name(key),
@@ -2187,7 +2194,7 @@ usage(void)
 	    "       ssh-keygen -e [-m key_format] [-f input_keyfile]\n"
 	    "       ssh-keygen -y [-f input_keyfile]\n"
 	    "       ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n"
-	    "       ssh-keygen -l [-f input_keyfile]\n"
+	    "       ssh-keygen -l [-E fingerprint_hash] [-f input_keyfile]\n"
 	    "       ssh-keygen -B [-f input_keyfile]\n");
 #ifdef ENABLE_PKCS11
 	fprintf(stderr,
@@ -2256,9 +2263,10 @@ main(int argc, char **argv)
 		exit(1);
 	}
 
-	/* Remaining characters: EUYdw */
+	/* Remaining characters: UYdw */
 	while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
-	    "C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
+	    "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:"
+	    "a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
 		switch (opt) {
 		case 'A':
 			gen_all_hostkeys = 1;
@@ -2269,6 +2277,11 @@ main(int argc, char **argv)
 				fatal("Bits has bad value %s (%s)",
 					optarg, errstr);
 			break;
+		case 'E':
+			fingerprint_hash = ssh_digest_alg_by_name(optarg);
+			if (fingerprint_hash == -1)
+				fatal("Invalid hash algorithm \"%s\"", optarg);
+			break;
 		case 'F':
 			find_host = 1;
 			rr_hostname = optarg;
@@ -2700,8 +2713,9 @@ passphrase_again:
 	fclose(f);
 
 	if (!quiet) {
-		char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);
-		char *ra = key_fingerprint(public, SSH_FP_MD5,
+		char *fp = key_fingerprint(public, fingerprint_hash,
+		    SSH_FP_DEFAULT);
+		char *ra = key_fingerprint(public, fingerprint_hash,
 		    SSH_FP_RANDOMART);
 		printf("Your public key has been saved in %s.\n",
 		    identity_file);
diff --git a/ssh-keysign.c b/ssh-keysign.c
index d95bb7d..3526d7d 100644
--- a/ssh-keysign.c
+++ b/ssh-keysign.c
@@ -246,7 +246,8 @@ main(int argc, char **argv)
 		}
 	}
 	if (!found) {
-		fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+		fp = key_fingerprint(key, options.fingerprint_hash,
+		    SSH_FP_DEFAULT);
 		fatal("no matching hostkey found for key %s %s",
 		    key_type(key), fp);
 	}
diff --git a/ssh.1 b/ssh.1
index fa5cfb2..d3198a1 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1083,7 +1083,7 @@ Fingerprints can be determined using
 If the fingerprint is already known, it can be matched
 and the key can be accepted or rejected.
 Because of the difficulty of comparing host keys
-just by looking at hex strings,
+just by looking at fingerprint strings,
 there is also support to compare host keys visually,
 using
 .Em random art .
diff --git a/sshconnect.c b/sshconnect.c
index ac09eae..7db31e6 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -915,9 +915,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
 				    "key for IP address '%.128s' to the list "
 				    "of known hosts.", type, ip);
 		} else if (options.visual_host_key) {
-			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
-			ra = key_fingerprint(host_key, SSH_FP_MD5,
-			    SSH_FP_RANDOMART);
+			fp = key_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_DEFAULT);
+			ra = key_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_RANDOMART);
 			logit("Host key fingerprint is %s\n%s\n", fp, ra);
 			free(ra);
 			free(fp);
@@ -956,9 +957,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
 			else
 				snprintf(msg1, sizeof(msg1), ".");
 			/* The default */
-			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
-			ra = key_fingerprint(host_key, SSH_FP_MD5,
-			    SSH_FP_RANDOMART);
+			fp = key_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_DEFAULT);
+			ra = key_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_RANDOMART);
 			msg2[0] = '\0';
 			if (options.verify_host_key_dns) {
 				if (matching_host_key_dns)
@@ -1222,7 +1224,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
 	char *fp;
 	Key *plain = NULL;
 
-	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+	fp = key_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT);
 	debug("Server host key: %s %s", key_type(host_key), fp);
 	free(fp);
 
@@ -1356,8 +1358,10 @@ show_other_keys(struct hostkeys *hostkeys, Key *key)
 			continue;
 		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
 			continue;
-		fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX);
-		ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART);
+		fp = key_fingerprint(found->key,
+		    options.fingerprint_hash, SSH_FP_DEFAULT);
+		ra = key_fingerprint(found->key,
+		    options.fingerprint_hash, SSH_FP_RANDOMART);
 		logit("WARNING: %s key found for host %s\n"
 		    "in %s:%lu\n"
 		    "%s key fingerprint %s.",
@@ -1378,7 +1382,8 @@ warn_changed_key(Key *host_key)
 {
 	char *fp;
 
-	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+	fp = key_fingerprint(host_key, options.fingerprint_hash,
+	    SSH_FP_DEFAULT);
 
 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
diff --git a/sshconnect2.c b/sshconnect2.c
index 68f7f4f..4724b66 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -582,7 +582,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt)
 		    key->type, pktype);
 		goto done;
 	}
-	fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+	fp = key_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);
 	debug2("input_userauth_pk_ok: fp %s", fp);
 	free(fp);
 
@@ -991,7 +991,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
 	int have_sig = 1;
 	char *fp;
 
-	fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX);
+	fp = key_fingerprint(id->key, options.fingerprint_hash, SSH_FP_DEFAULT);
 	debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp);
 	free(fp);
 
diff --git a/sshd_config.5 b/sshd_config.5
index fd44abe..0449eeb 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -483,6 +483,15 @@ and finally
 See PATTERNS in
 .Xr ssh_config 5
 for more information on patterns.
+.It Cm FingerprintHash
+Specifies the hash algorithm used when logging key fingerprints.
+Valid options are:
+.Dq md5
+and
+.Dq sha256 .
+The default is
+.Dq sha256 .
+.Pp
 .It Cm ForceCommand
 Forces the execution of the command specified by
 .Cm ForceCommand ,
diff --git a/sshkey.c b/sshkey.c
index fdd0c8a..70df758 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -29,6 +29,7 @@
 
 #include <sys/param.h>
 #include <sys/types.h>
+#include <netinet/in.h>
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
@@ -852,29 +853,18 @@ sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
 }
 
 int
-sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type,
+sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
     u_char **retp, size_t *lenp)
 {
 	u_char *blob = NULL, *ret = NULL;
 	size_t blob_len = 0;
-	int hash_alg = -1, r = SSH_ERR_INTERNAL_ERROR;
+	int r = SSH_ERR_INTERNAL_ERROR;
 
 	if (retp != NULL)
 		*retp = NULL;
 	if (lenp != NULL)
 		*lenp = 0;
-
-	switch (dgst_type) {
-	case SSH_FP_MD5:
-		hash_alg = SSH_DIGEST_MD5;
-		break;
-	case SSH_FP_SHA1:
-		hash_alg = SSH_DIGEST_SHA1;
-		break;
-	case SSH_FP_SHA256:
-		hash_alg = SSH_DIGEST_SHA256;
-		break;
-	default:
+	if (ssh_digest_bytes(dgst_alg) == 0) {
 		r = SSH_ERR_INVALID_ARGUMENT;
 		goto out;
 	}
@@ -899,7 +889,7 @@ sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type,
 		r = SSH_ERR_ALLOC_FAIL;
 		goto out;
 	}
-	if ((r = ssh_digest_memory(hash_alg, blob, blob_len,
+	if ((r = ssh_digest_memory(dgst_alg, blob, blob_len,
 	    ret, SSH_DIGEST_MAX_LENGTH)) != 0)
 		goto out;
 	/* success */
@@ -908,7 +898,7 @@ sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type,
 		ret = NULL;
 	}
 	if (lenp != NULL)
-		*lenp = ssh_digest_bytes(hash_alg);
+		*lenp = ssh_digest_bytes(dgst_alg);
 	r = 0;
  out:
 	free(ret);
@@ -920,21 +910,45 @@ sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type,
 }
 
 static char *
-fingerprint_hex(u_char *dgst_raw, size_t dgst_raw_len)
+fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
 {
-	char *retval;
-	size_t i;
+	char *ret;
+	size_t plen = strlen(alg) + 1;
+	size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1;
+	int r;
 
-	if ((retval = calloc(1, dgst_raw_len * 3 + 1)) == NULL)
+	if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL)
+		return NULL;
+	strlcpy(ret, alg, rlen);
+	strlcat(ret, ":", rlen);
+	if (dgst_raw_len == 0)
+		return ret;
+	if ((r = b64_ntop(dgst_raw, dgst_raw_len,
+	    ret + plen, rlen - plen)) == -1) {
+		explicit_bzero(ret, rlen);
+		free(ret);
 		return NULL;
-	for (i = 0; i < dgst_raw_len; i++) {
-		char hex[4];
-		snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]);
-		strlcat(retval, hex, dgst_raw_len * 3 + 1);
 	}
+	/* Trim padding characters from end */
+	ret[strcspn(ret, "=")] = '\0';
+	return ret;
+}
 
-	/* Remove the trailing ':' character */
-	retval[(dgst_raw_len * 3) - 1] = '\0';
+static char *
+fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
+{
+	char *retval, hex[5];
+	size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2;
+
+	if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL)
+		return NULL;
+	strlcpy(retval, alg, rlen);
+	strlcat(retval, ":", rlen);
+	for (i = 0; i < dgst_raw_len; i++) {
+		snprintf(hex, sizeof(hex), "%s%02x",
+		    i > 0 ? ":" : "", dgst_raw[i]);
+		strlcat(retval, hex, rlen);
+	}
 	return retval;
 }
 
@@ -1020,7 +1034,7 @@ fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len)
 #define	FLDSIZE_Y	(FLDBASE + 1)
 #define	FLDSIZE_X	(FLDBASE * 2 + 1)
 static char *
-fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
+fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
     const struct sshkey *k)
 {
 	/*
@@ -1028,9 +1042,9 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
 	 * intersects with itself.  Matter of taste.
 	 */
 	char	*augmentation_string = " .o+=*BOX@%&#/^SE";
-	char	*retval, *p, title[FLDSIZE_X];
+	char	*retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X];
 	u_char	 field[FLDSIZE_X][FLDSIZE_Y];
-	size_t	 i, tlen;
+	size_t	 i, tlen, hlen;
 	u_int	 b;
 	int	 x, y, r;
 	size_t	 len = strlen(augmentation_string) - 1;
@@ -1075,8 +1089,12 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
 		sshkey_type(k), sshkey_size(k));
 	/* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */
 	if (r < 0 || r > (int)sizeof(title))
-		snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
-	tlen = strlen(title);
+		r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
+	tlen = (r <= 0) ? 0 : strlen(title);
+
+	/* assemble hash ID. */
+	r = snprintf(hash, sizeof(hash), "[%s]", alg);
+	hlen = (r <= 0) ? 0 : strlen(hash);
 
 	/* output upper border */
 	p = retval;
@@ -1085,7 +1103,7 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
 		*p++ = '-';
 	memcpy(p, title, tlen);
 	p += tlen;
-	for (i = p - retval - 1; i < FLDSIZE_X; i++)
+	for (i += tlen; i < FLDSIZE_X; i++)
 		*p++ = '-';
 	*p++ = '+';
 	*p++ = '\n';
@@ -1101,7 +1119,11 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
 
 	/* output lower border */
 	*p++ = '+';
-	for (i = 0; i < FLDSIZE_X; i++)
+	for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++)
+		*p++ = '-';
+	memcpy(p, hash, hlen);
+	p += hlen;
+	for (i += hlen; i < FLDSIZE_X; i++)
 		*p++ = '-';
 	*p++ = '+';
 
@@ -1109,24 +1131,39 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
 }
 
 char *
-sshkey_fingerprint(const struct sshkey *k, enum sshkey_fp_type dgst_type,
+sshkey_fingerprint(const struct sshkey *k, int dgst_alg,
     enum sshkey_fp_rep dgst_rep)
 {
 	char *retval = NULL;
 	u_char *dgst_raw;
 	size_t dgst_raw_len;
 
-	if (sshkey_fingerprint_raw(k, dgst_type, &dgst_raw, &dgst_raw_len) != 0)
+	if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0)
 		return NULL;
 	switch (dgst_rep) {
+	case SSH_FP_DEFAULT:
+		if (dgst_alg == SSH_DIGEST_MD5) {
+			retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
+			    dgst_raw, dgst_raw_len);
+		} else {
+			retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
+			    dgst_raw, dgst_raw_len);
+		}
+		break;
 	case SSH_FP_HEX:
-		retval = fingerprint_hex(dgst_raw, dgst_raw_len);
+		retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
+		    dgst_raw, dgst_raw_len);
+		break;
+	case SSH_FP_BASE64:
+		retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
+		    dgst_raw, dgst_raw_len);
 		break;
 	case SSH_FP_BUBBLEBABBLE:
 		retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
 		break;
 	case SSH_FP_RANDOMART:
-		retval = fingerprint_randomart(dgst_raw, dgst_raw_len, k);
+		retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg),
+		    dgst_raw, dgst_raw_len, k);
 		break;
 	default:
 		explicit_bzero(dgst_raw, dgst_raw_len);
diff --git a/sshkey.h b/sshkey.h
index 450b30c..4554b09 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.h,v 1.1 2014/06/24 01:16:58 djm Exp $ */
+/* $OpenBSD: sshkey.h,v 1.2 2014/12/21 22:27:55 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -67,16 +67,14 @@ enum sshkey_types {
 	KEY_UNSPEC
 };
 
-/* Fingerprint hash algorithms */
-enum sshkey_fp_type {
-	SSH_FP_SHA1,
-	SSH_FP_MD5,
-	SSH_FP_SHA256
-};
+/* Default fingerprint hash */
+#define SSH_FP_HASH_DEFAULT	SSH_DIGEST_SHA256
 
 /* Fingerprint representation formats */
 enum sshkey_fp_rep {
+	SSH_FP_DEFAULT = 0,
 	SSH_FP_HEX,
+	SSH_FP_BASE64,
 	SSH_FP_BUBBLEBABBLE,
 	SSH_FP_RANDOMART
 };
@@ -124,9 +122,9 @@ int		 sshkey_equal_public(const struct sshkey *,
     const struct sshkey *);
 int		 sshkey_equal(const struct sshkey *, const struct sshkey *);
 char		*sshkey_fingerprint(const struct sshkey *,
-    enum sshkey_fp_type, enum sshkey_fp_rep);
+    int, enum sshkey_fp_rep);
 int		 sshkey_fingerprint_raw(const struct sshkey *k,
-    enum sshkey_fp_type dgst_type, u_char **retp, size_t *lenp);
+    int, u_char **retp, size_t *lenp);
 const char	*sshkey_type(const struct sshkey *);
 const char	*sshkey_cert_type(const struct sshkey *);
 int		 sshkey_write(const struct sshkey *, FILE *);