···1+package randomart
2+3+/*
4+ * Draw an ASCII-Art representing the fingerprint so human brain can
5+ * profit from its built-in pattern recognition ability.
6+ * This technique is called "random art" and can be found in some
7+ * scientific publications like this original paper:
8+ *
9+ * "Hash Visualization: a New Technique to improve Real-World Security",
10+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
11+ * Techniques and E-Commerce (CrypTEC '99)
12+ * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
13+ *
14+ * The subject came up in a talk by Dan Kaminsky, too.
15+ *
16+ * If you see the picture is different, the key is different.
17+ * If the picture looks the same, you still know nothing.
18+ *
19+ * The algorithm used here is a worm crawling over a discrete plane,
20+ * leaving a trace (augmenting the field) everywhere it goes.
21+ * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
22+ * makes the respective movement vector be ignored for this turn.
23+ * Graphs are not unambiguous, because circles in graphs can be
24+ * walked in either direction.
25+ */
26+27+import (
28+ "os"
29+)
30+31+func MAX(a, b int) int {
32+ if a > b {
33+ return a
34+ }
35+ return b
36+}
37+38+func MIN(a, b int) int {
39+ if a < b {
40+ return a
41+ }
42+ return b
43+}
44+45+/*
46+ * Field sizes for the random art. Have to be odd, so the starting point
47+ * can be in the exact middle of the picture, and FLDBASE should be >=8 .
48+ * Else pictures would be too dense, and drawing the frame would
49+ * fail, too, because the key type would not fit in anymore.
50+ */
51+const (
52+ FLDBASE = 8
53+ FLDSIZE_Y = (FLDBASE + 1)
54+ FLDSIZE_X = (FLDBASE*2 + 1)
55+)
56+57+func FromString(str string) string {
58+ ch := make(chan byte)
59+60+ go func() {
61+ defer close(ch)
62+ for _, v := range []byte(str) {
63+ ch <- v
64+ }
65+ }()
66+ return key_fingerprint_randomart(ch)
67+}
68+69+func FromFile(file *os.File) string {
70+ ch := make(chan byte)
71+72+ go func() {
73+ defer close(ch)
74+ // TODO make input a 1 element byte array
75+ input := make([]byte, 1)
76+ nread, err := file.Read(input)
77+ for err == nil && nread > 0 {
78+ ch <- input[0]
79+ nread, err = file.Read(input)
80+ }
81+ }()
82+ return key_fingerprint_randomart(ch)
83+}
84+85+func key_fingerprint_randomart(ch chan byte) string {
86+ /*
87+ * Chars to be used after each other every time the worm
88+ * intersects with itself. Matter of taste.
89+ */
90+ augment_string := " .o+=*BOX@%&#/^SE"
91+ var field [FLDSIZE_X][FLDSIZE_Y]byte
92+ len_aug := len(augment_string) - 1
93+ var retval [(FLDSIZE_X + 3) * (FLDSIZE_Y + 2)]byte
94+95+ /* initialize field */
96+ x := FLDSIZE_X / 2
97+ y := FLDSIZE_Y / 2
98+99+ /* process raw key */
100+ for input, ok := <-ch; ok; input, ok = <-ch {
101+ /* each byte conveys four 2-bit move commands */
102+ for b := 0; b < 4; b++ {
103+ /* evaluate 2 bit, rest is shifted later */
104+ if input&0x1 > 0 {
105+ x += 1
106+ } else {
107+ x += -1
108+ }
109+110+ if input&0x2 > 0 {
111+ y++
112+ } else {
113+ y--
114+ }
115+116+ /* assure we are still in bounds */
117+ x = MAX(x, 0)
118+ y = MAX(y, 0)
119+ x = MIN(x, FLDSIZE_X-1)
120+ y = MIN(y, FLDSIZE_Y-1)
121+122+ /* augment the field */
123+ if int(field[x][y]) < len_aug-2 {
124+ field[x][y]++
125+ }
126+ input = input >> 2
127+ }
128+ }
129+130+ /* mark starting point and end point*/
131+ field[FLDSIZE_X/2][FLDSIZE_Y/2] = byte(len_aug - 1)
132+ field[x][y] = byte(len_aug)
133+134+ i := 0
135+ retval[i] = '+'
136+ i++
137+138+ /* output upper border */
139+ for x := 0; x < FLDSIZE_X; x++ {
140+ retval[i] = '-'
141+ i++
142+ }
143+ retval[i] = '+'
144+ i++
145+ retval[i] = '\n'
146+ i++
147+148+ /* output content */
149+ for y := 0; y < FLDSIZE_Y; y++ {
150+ retval[i] = '|'
151+ i++
152+ for x := 0; x < FLDSIZE_X; x++ {
153+ retval[i] = augment_string[MIN(int(field[x][y]), len_aug)]
154+ i++
155+ }
156+ retval[i] = '|'
157+ i++
158+ retval[i] = '\n'
159+ i++
160+ }
161+162+ /* output lower border */
163+ retval[i] = '+'
164+ i++
165+ for j := 0; j < FLDSIZE_X; j++ {
166+ retval[i] = '-'
167+ i++
168+ }
169+ retval[i] = '+'
170+ i++
171+172+ return string(retval[0:i])
173+}