- jmc@cvs.openbsd.org 2008/06/11 07:30:37
[sshd.8]
kill trailing whitespace;
+ - grunk@cvs.openbsd.org 2008/06/11 21:01:35
+ [ssh_config.5 key.h readconf.c readconf.h ssh-keygen.1 ssh-keygen.c key.c
+ sshconnect.c]
+ Introduce SSH Fingerprint ASCII Visualization, a technique inspired by the
+ graphical hash visualization schemes known as "random art", and by
+ Dan Kaminsky's musings on the subject during a BlackOp talk at the
+ 23C3 in Berlin.
+ Scientific publication (original paper):
+ "Hash Visualization: a New Technique to improve Real-World Security",
+ Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ Techniques and E-Commerce (CrypTEC '99)
+ http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+ The algorithm used here is a worm crawling over a discrete plane,
+ leaving a trace (augmenting the field) everywhere it goes.
+ Movement is taken from dgst_raw 2bit-wise. Bumping into walls
+ makes the respective movement vector be ignored for this turn,
+ thus switching to the other color of the chessboard.
+ Graphs are not unambiguous for now, because circles in graphs can be
+ walked in either direction.
+ discussions with several people,
+ help, corrections and ok markus@ djm@
20080611
- (djm) [channels.c configure.ac]
-/* $OpenBSD: key.c,v 1.69 2007/07/12 05:48:05 ray Exp $ */
+/* $OpenBSD: key.c,v 1.70 2008/06/11 21:01:35 grunk Exp $ */
/*
* read_bignum():
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#include "includes.h"
+#include <sys/param.h>
#include <sys/types.h>
#include <openssl/evp.h>
return retval;
}
+/*
+ * Draw an ASCII-Art representing the fingerprint so human brain can
+ * profit from its built-in pattern recognition ability.
+ * This technique is called "random art" and can be found in some
+ * scientific publications like this original paper:
+ *
+ * "Hash Visualization: a New Technique to improve Real-World Security",
+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ * Techniques and E-Commerce (CrypTEC '99)
+ * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+ *
+ * The subject came up in a talk by Dan Kaminsky, too.
+ *
+ * If you see the picture is different, the key is different.
+ * If the picture looks the same, you still know nothing.
+ *
+ * The algorithm used here is a worm crawling over a discrete plane,
+ * leaving a trace (augmenting the field) everywhere it goes.
+ * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
+ * makes the respective movement vector be ignored for this turn.
+ * Graphs are not unambiguous, because circles in graphs can be
+ * walked in either direction.
+ */
+#define FLDSIZE_Y 8
+#define FLDSIZE_X FLDSIZE_Y * 2
+static char *
+key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len)
+{
+ /*
+ * Chars to be used after each other every time the worm
+ * intersects with itself. Matter of taste.
+ */
+ char *augmentation_string = " .o+=*BOX@%&#/^";
+ char *retval, *p;
+ char field[FLDSIZE_X][FLDSIZE_Y];
+ u_int i, b;
+ int x, y;
+
+ retval = xcalloc(1, (FLDSIZE_X + 3) * (FLDSIZE_Y + 2));
+
+ /* initialize field */
+ memset(field, ' ', FLDSIZE_X * FLDSIZE_Y * sizeof(char));
+ x = FLDSIZE_X / 2;
+ y = FLDSIZE_Y / 2;
+ field[x][y] = '.';
+
+ /* process raw key */
+ for (i = 0; i < dgst_raw_len; i++) {
+ int input;
+ /* each byte conveys four 2-bit move commands */
+ input = dgst_raw[i];
+ for (b = 0; b < 4; b++) {
+ /* evaluate 2 bit, rest is shifted later */
+ x += (input & 0x1) ? 1 : -1;
+ y += (input & 0x2) ? 1 : -1;
+
+ /* assure we are still in bounds */
+ x = MAX(x, 0);
+ y = MAX(y, 0);
+ x = MIN(x, FLDSIZE_X - 1);
+ y = MIN(y, FLDSIZE_Y - 1);
+
+ /* augment the field */
+ p = strchr(augmentation_string, field[x][y]);
+ if (*++p != '\0')
+ field[x][y] = *p;
+
+ input = input >> 2;
+ }
+ }
+
+ /* fill in retval */
+ p = retval;
+
+ /* output upper border */
+ *p++ = '+';
+ for (i = 0; i < FLDSIZE_X; i++)
+ *p++ = '-';
+ *p++ = '+';
+ *p++ = '\n';
+
+ /* output content */
+ for (y = 0; y < FLDSIZE_Y; y++) {
+ *p++ = '|';
+ for (x = 0; x < FLDSIZE_X; x++)
+ *p++ = field[x][y];
+ *p++ = '|';
+ *p++ = '\n';
+ }
+
+ /* output lower border */
+ *p++ = '+';
+ for (i = 0; i < FLDSIZE_X; i++)
+ *p++ = '-';
+ *p++ = '+';
+
+ return retval;
+}
+
char *
key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
{
case SSH_FP_BUBBLEBABBLE:
retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
break;
+ case SSH_FP_RANDOMART:
+ retval = key_fingerprint_randomart(dgst_raw, dgst_raw_len);
+ break;
default:
fatal("key_fingerprint_ex: bad digest representation %d",
dgst_rep);
-/* $OpenBSD: key.h,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: key.h,v 1.27 2008/06/11 21:01:35 grunk Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
};
enum fp_rep {
SSH_FP_HEX,
- SSH_FP_BUBBLEBABBLE
+ SSH_FP_BUBBLEBABBLE,
+ SSH_FP_RANDOMART
};
/* key is stored in external hardware */
-/* $OpenBSD: readconf.c,v 1.165 2008/01/19 23:09:49 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.166 2008/06/11 21:01:35 grunk Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
case oCheckHostIP:
intptr = &options->check_host_ip;
- goto parse_flag;
+ arg = strdelim(&s);
+ if (!arg || *arg == '\0')
+ fatal("%.200s line %d: Missing CheckHostIP argument.",
+ filename, linenum);
+ value = 0; /* To avoid compiler warning... */
+ if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
+ value = SSHCTL_CHECKHOSTIP_YES;
+ else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
+ value = SSHCTL_CHECKHOSTIP_NO;
+ else if (strcmp(arg, "fingerprint") == 0)
+ value = SSHCTL_CHECKHOSTIP_FPR;
+ else
+ fatal("%.200s line %d: Bad CheckHostIP argument.",
+ filename, linenum);
+ if (*activep && *intptr == -1)
+ *intptr = value;
+ break;
case oVerifyHostKeyDNS:
intptr = &options->verify_host_key_dns;
-/* $OpenBSD: readconf.h,v 1.72 2008/01/19 23:09:49 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.73 2008/06/11 21:01:35 grunk Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
} Options;
+#define SSHCTL_CHECKHOSTIP_NO 0
+#define SSHCTL_CHECKHOSTIP_YES 1
+#define SSHCTL_CHECKHOSTIP_FPR 2
+
#define SSHCTL_MASTER_NO 0
#define SSHCTL_MASTER_YES 1
#define SSHCTL_MASTER_AUTO 2
-.\" $OpenBSD: ssh-keygen.1,v 1.75 2007/05/31 19:20:16 jmc Exp $
+.\" $OpenBSD: ssh-keygen.1,v 1.76 2008/06/11 21:01:35 grunk Exp $
.\"
.\" -*- nroff -*-
.\"
This option allows importing keys from several commercial
SSH implementations.
.It Fl l
-Show fingerprint of specified public key file.
+Show fingerprint and ASCII art representation of specified public key file.
Private RSA1 keys are also supported.
For RSA and DSA keys
.Nm
-tries to find the matching public key file and prints its fingerprint.
+tries to find the matching public key file and prints its fingerprint
+and representation.
.It Fl M Ar memory
Specify the amount of memory to use (in megabytes) when generating
candidate moduli for DH-GEX.
-/* $OpenBSD: ssh-keygen.c,v 1.166 2008/05/19 15:46:31 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.167 2008/06/11 21:01:35 grunk Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
{
FILE *f;
Key *public;
- char *comment = NULL, *cp, *ep, line[16*1024], *fp;
+ 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;
public = key_load_public(identity_file, &comment);
if (public != NULL) {
fp = key_fingerprint(public, fptype, rep);
+ ra = key_fingerprint(public, fptype, rep);
printf("%u %s %s\n", key_size(public), fp, comment);
+ verbose("%s\n", ra);
key_free(public);
xfree(comment);
+ xfree(ra);
xfree(fp);
exit(0);
}
}
comment = *cp ? cp : comment;
fp = key_fingerprint(public, fptype, rep);
+ ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART);
printf("%u %s %s\n", key_size(public), fp,
comment ? comment : "no comment");
+ verbose("%s\n", ra);
+ xfree(ra);
xfree(fp);
key_free(public);
invalid = 0;
if (print_fingerprint) {
enum fp_rep rep;
enum fp_type fptype;
- char *fp;
+ char *fp, *ra;
fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
fp = key_fingerprint(public, fptype, rep);
- printf("%u %s %s\n", key_size(public), fp, name);
+ ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART);
+ printf("%u %s %s\n%s\n", key_size(public), fp, name, ra);
+ xfree(ra);
xfree(fp);
} else {
if (hash && (name = host_hash(name, NULL, 0)) == NULL)
if (!quiet) {
char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);
+ char *ra = key_fingerprint(public, SSH_FP_MD5,
+ SSH_FP_RANDOMART);
printf("Your public key has been saved in %s.\n",
identity_file);
printf("The key fingerprint is:\n");
printf("%s %s\n", fp, comment);
+ printf("The key's randomart image is:\n");
+ printf("%s\n", ra);
+ xfree(ra);
xfree(fp);
}
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh_config.5,v 1.106 2008/06/10 18:21:24 dtucker Exp $
+.\" $OpenBSD: ssh_config.5,v 1.107 2008/06/11 21:01:35 grunk Exp $
.Dd $Mdocdate$
.Dt SSH_CONFIG 5
.Os
file.
This allows ssh to detect if a host key changed due to DNS spoofing.
If the option is set to
+.Dq fingerprint ,
+not only the host IP address will be checked, but also an ASCII art
+representation of the key will be printed.
+If the option is set to
.Dq no ,
the check will not be executed.
The default is
-/* $OpenBSD: sshconnect.c,v 1.203 2007/12/27 14:22:08 dtucker Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.204 2008/06/11 21:01:35 grunk Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
Key *file_key;
const char *type = key_type(host_key);
char *ip = NULL, *host = NULL;
- char hostline[1000], *hostp, *fp;
+ char hostline[1000], *hostp, *fp, *ra;
HostStatus host_status;
HostStatus ip_status;
int r, local = 0, host_ip_differ = 0;
logit("Warning: Permanently added the %s host "
"key for IP address '%.128s' to the list "
"of known hosts.", type, ip);
+ } else if (options.check_host_ip == SSHCTL_CHECKHOSTIP_FPR) {
+ fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+ ra = key_fingerprint(host_key, SSH_FP_MD5,
+ SSH_FP_RANDOMART);
+ logit("Host key fingerprint is %s\n%s\n", fp, ra);
+ xfree(ra);
+ xfree(fp);
}
break;
case HOST_NEW:
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);
msg2[0] = '\0';
if (options.verify_host_key_dns) {
if (matching_host_key_dns)
snprintf(msg, sizeof(msg),
"The authenticity of host '%.200s (%s)' can't be "
"established%s\n"
- "%s key fingerprint is %s.\n%s"
+ "%s key fingerprint is %s.\n%s\n%s"
"Are you sure you want to continue connecting "
"(yes/no)? ",
- host, ip, msg1, type, fp, msg2);
+ host, ip, msg1, type, fp, ra, msg2);
+ xfree(ra);
xfree(fp);
if (!confirm(msg))
goto fail;
show_key_from_file(const char *file, const char *host, int keytype)
{
Key *found;
- char *fp;
+ char *fp, *ra;
int line, ret;
found = key_new(keytype);
if ((ret = lookup_key_in_hostfile_by_type(file, host,
keytype, found, &line))) {
fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
+ ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART);
logit("WARNING: %s key found for host %s\n"
"in %s:%d\n"
- "%s key fingerprint %s.",
+ "%s key fingerprint %s.\n%s\n",
key_type(found), host, file, line,
- key_type(found), fp);
+ key_type(found), fp, ra);
+ xfree(ra);
xfree(fp);
}
key_free(found);