]> andersk Git - openssh.git/commitdiff
- grunk@cvs.openbsd.org 2008/06/11 21:01:35
authordtucker <dtucker>
Thu, 12 Jun 2008 18:40:35 +0000 (18:40 +0000)
committerdtucker <dtucker>
Thu, 12 Jun 2008 18:40:35 +0000 (18:40 +0000)
     [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@

ChangeLog
key.c
key.h
readconf.c
readconf.h
ssh-keygen.1
ssh-keygen.c
ssh_config.5
sshconnect.c

index 43ddc276f4b225fd37374f2a7d5f22fc116583ac..51748937a3593c2341414c96ac71b360905f3c06 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,27 @@
    - 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]
diff --git a/key.c b/key.c
index 62bf8361d6791caea9bae2be052eb3ae125cc269..5d357a8d6b22995112812408cde646b856dae6fa 100644 (file)
--- a/key.c
+++ b/key.c
@@ -1,4 +1,4 @@
-/* $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
@@ -35,6 +35,7 @@
 
 #include "includes.h"
 
+#include <sys/param.h>
 #include <sys/types.h>
 
 #include <openssl/evp.h>
@@ -295,6 +296,105 @@ key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len)
        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)
 {
@@ -312,6 +412,9 @@ 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);
diff --git a/key.h b/key.h
index 6873dd7933e947e176f18d0f1b3239dae2c10093..14aac79c2de07d69cf9be3d6640cfecad378852c 100644 (file)
--- a/key.h
+++ b/key.h
@@ -1,4 +1,4 @@
-/* $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.
@@ -42,7 +42,8 @@ enum fp_type {
 };
 enum fp_rep {
        SSH_FP_HEX,
-       SSH_FP_BUBBLEBABBLE
+       SSH_FP_BUBBLEBABBLE,
+       SSH_FP_RANDOMART
 };
 
 /* key is stored in external hardware */
index 3ddb4d392c55d668606f6f795df7651ee0e4e87a..1d61145c4edbb00d704db095c543c5c0e711bc50 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
@@ -452,7 +452,23 @@ parse_flag:
 
        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;
index 6257f4b2f522415ff9dc3b127fbc140d3928c9cd..5c16a0ba6c89e9ace88b93922e9156b828e01f5a 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -123,6 +123,10 @@ typedef struct {
 
 }       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
index 5ce2ba45aebdbc626bcb8a9d9de5ad85794ffd8c..f9247bf30e92de9f2ad7d1b8ecb336ada7bb0bfb 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $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 -*-
 .\"
@@ -257,11 +257,12 @@ RFC 4716 SSH Public Key File Format.
 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.
index a03c6575d67f0b5b5c478edd19750c8baf14cc6e..c22e814dacd87c91787cff46a4445199a7646097 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
@@ -504,7 +504,7 @@ do_fingerprint(struct passwd *pw)
 {
        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;
@@ -522,9 +522,12 @@ 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, 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);
        }
@@ -582,8 +585,11 @@ do_fingerprint(struct passwd *pw)
                        }
                        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;
@@ -603,12 +609,14 @@ print_host(FILE *f, const char *name, Key *public, int hash)
        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)
@@ -1451,10 +1459,15 @@ passphrase_again:
 
        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);
        }
 
index 2956f8b5e014ed5a5d10be85468ec10760da6e3c..5ac2ced2ee1299ddbdfb2be9f3f16afa1e0666fc 100644 (file)
@@ -34,7 +34,7 @@
 .\" (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
@@ -161,6 +161,10 @@ will additionally check the host IP address in the
 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
index a604c9724aa0512f2b7732ab509c1142364fd6dc..1512996144e6c98a06cb5220289e43df48db9006 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
@@ -602,7 +602,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
        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;
@@ -740,6 +740,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
                                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:
@@ -775,6 +782,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
                                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)
@@ -789,10 +798,11 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
                        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;
@@ -1063,18 +1073,20 @@ static int
 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);
This page took 1.01043 seconds and 5 git commands to generate.