upstream commit
Add FingerprintHash option to control algorithm used for
key fingerprints. Default changes from MD5 to SHA256 and format from hex to
base64.
Feedback and ok naddy@ markus@
diff --git a/sshkey.c b/sshkey.c
index cf12662..a32bd36 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.c,v 1.6 2014/12/10 01:24:09 djm Exp $ */
+/* $OpenBSD: sshkey.c,v 1.7 2014/12/21 22:27:55 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@@ -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 @@
}
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 @@
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 @@
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 @@
}
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;
- 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);
+ 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;
}
+ /* 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 @@
#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 @@
* 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 @@
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 @@
*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 @@
/* 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 @@
}
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);