···11+package randomart
22+33+/*
44+ * Draw an ASCII-Art representing the fingerprint so human brain can
55+ * profit from its built-in pattern recognition ability.
66+ * This technique is called "random art" and can be found in some
77+ * scientific publications like this original paper:
88+ *
99+ * "Hash Visualization: a New Technique to improve Real-World Security",
1010+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
1111+ * Techniques and E-Commerce (CrypTEC '99)
1212+ * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
1313+ *
1414+ * The subject came up in a talk by Dan Kaminsky, too.
1515+ *
1616+ * If you see the picture is different, the key is different.
1717+ * If the picture looks the same, you still know nothing.
1818+ *
1919+ * The algorithm used here is a worm crawling over a discrete plane,
2020+ * leaving a trace (augmenting the field) everywhere it goes.
2121+ * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
2222+ * makes the respective movement vector be ignored for this turn.
2323+ * Graphs are not unambiguous, because circles in graphs can be
2424+ * walked in either direction.
2525+ */
2626+2727+import (
2828+ "os"
2929+)
3030+3131+func MAX(a, b int) int {
3232+ if a > b {
3333+ return a
3434+ }
3535+ return b
3636+}
3737+3838+func MIN(a, b int) int {
3939+ if a < b {
4040+ return a
4141+ }
4242+ return b
4343+}
4444+4545+/*
4646+ * Field sizes for the random art. Have to be odd, so the starting point
4747+ * can be in the exact middle of the picture, and FLDBASE should be >=8 .
4848+ * Else pictures would be too dense, and drawing the frame would
4949+ * fail, too, because the key type would not fit in anymore.
5050+ */
5151+const (
5252+ FLDBASE = 8
5353+ FLDSIZE_Y = (FLDBASE + 1)
5454+ FLDSIZE_X = (FLDBASE*2 + 1)
5555+)
5656+5757+func FromString(str string) string {
5858+ ch := make(chan byte)
5959+6060+ go func() {
6161+ defer close(ch)
6262+ for _, v := range []byte(str) {
6363+ ch <- v
6464+ }
6565+ }()
6666+ return key_fingerprint_randomart(ch)
6767+}
6868+6969+func FromFile(file *os.File) string {
7070+ ch := make(chan byte)
7171+7272+ go func() {
7373+ defer close(ch)
7474+ // TODO make input a 1 element byte array
7575+ input := make([]byte, 1)
7676+ nread, err := file.Read(input)
7777+ for err == nil && nread > 0 {
7878+ ch <- input[0]
7979+ nread, err = file.Read(input)
8080+ }
8181+ }()
8282+ return key_fingerprint_randomart(ch)
8383+}
8484+8585+func key_fingerprint_randomart(ch chan byte) string {
8686+ /*
8787+ * Chars to be used after each other every time the worm
8888+ * intersects with itself. Matter of taste.
8989+ */
9090+ augment_string := " .o+=*BOX@%&#/^SE"
9191+ var field [FLDSIZE_X][FLDSIZE_Y]byte
9292+ len_aug := len(augment_string) - 1
9393+ var retval [(FLDSIZE_X + 3) * (FLDSIZE_Y + 2)]byte
9494+9595+ /* initialize field */
9696+ x := FLDSIZE_X / 2
9797+ y := FLDSIZE_Y / 2
9898+9999+ /* process raw key */
100100+ for input, ok := <-ch; ok; input, ok = <-ch {
101101+ /* each byte conveys four 2-bit move commands */
102102+ for b := 0; b < 4; b++ {
103103+ /* evaluate 2 bit, rest is shifted later */
104104+ if input&0x1 > 0 {
105105+ x += 1
106106+ } else {
107107+ x += -1
108108+ }
109109+110110+ if input&0x2 > 0 {
111111+ y++
112112+ } else {
113113+ y--
114114+ }
115115+116116+ /* assure we are still in bounds */
117117+ x = MAX(x, 0)
118118+ y = MAX(y, 0)
119119+ x = MIN(x, FLDSIZE_X-1)
120120+ y = MIN(y, FLDSIZE_Y-1)
121121+122122+ /* augment the field */
123123+ if int(field[x][y]) < len_aug-2 {
124124+ field[x][y]++
125125+ }
126126+ input = input >> 2
127127+ }
128128+ }
129129+130130+ /* mark starting point and end point*/
131131+ field[FLDSIZE_X/2][FLDSIZE_Y/2] = byte(len_aug - 1)
132132+ field[x][y] = byte(len_aug)
133133+134134+ i := 0
135135+ retval[i] = '+'
136136+ i++
137137+138138+ /* output upper border */
139139+ for x := 0; x < FLDSIZE_X; x++ {
140140+ retval[i] = '-'
141141+ i++
142142+ }
143143+ retval[i] = '+'
144144+ i++
145145+ retval[i] = '\n'
146146+ i++
147147+148148+ /* output content */
149149+ for y := 0; y < FLDSIZE_Y; y++ {
150150+ retval[i] = '|'
151151+ i++
152152+ for x := 0; x < FLDSIZE_X; x++ {
153153+ retval[i] = augment_string[MIN(int(field[x][y]), len_aug)]
154154+ i++
155155+ }
156156+ retval[i] = '|'
157157+ i++
158158+ retval[i] = '\n'
159159+ i++
160160+ }
161161+162162+ /* output lower border */
163163+ retval[i] = '+'
164164+ i++
165165+ for j := 0; j < FLDSIZE_X; j++ {
166166+ retval[i] = '-'
167167+ i++
168168+ }
169169+ retval[i] = '+'
170170+ i++
171171+172172+ return string(retval[0:i])
173173+}