[Question]
  [
> 
> In public-key cryptography, a public key fingerprint is a short sequence of bytes used to identify a longer public key.
> 
> 
> 
In SSH in particular they can be used to verify that a server is in fact the server I'm expecting to communicate with and I'm not targeted by a man-in-the-middle attack. 
They are usually represented as a string of hexadecimal digits, so it can be rather boring and tedious to compare it with the fingerprint I would expect:
```
37:e4:6a:2d:48:38:1a:0a:f3:72:6d:d9:17:6b:bd:5e
```
To make it a little easier, OpenSSH has introduced a method to visualize fingerprints as ASCII art, that would look like the following:
```
+-----------------+
|                 |
|                 |
|          .      |
|     .   o       |
|o . o . S +      |
|.+ + = . B .     |
|o + + o B o E    |
| o .   + . o     |
|         .o      |
+-----------------+
```
With this, I could try to remember the rough shape of the ASCII art and would then (theoretically) recognize it when the fingerprint of the server changed and the image looks different.
# How it works
*Taken from [Dirk Loss, Tobias Limmer, Alexander von Gernler. 2009. The drunken bishop: An analysis of the OpenSSH
fingerprint visualization algorithm](http://www.dirk-loss.de/sshvis/drunken_bishop.pdf).*
The grid has a width of 17 characters and a height of 9 characters. The "bishop" starts at row 4/column 8 (the center). Each position can be denoted as [x,y], i.e. [8,4] for the starting position of the bishop.
```
            1111111
  01234567890123456
 +-----------------+
0|                 |
1|                 |
2|                 |
3|                 |
4|        S        |
5|                 |
6|                 |
7|                 |
8|                 |
 +-----------------+
```
The bishop uses the fingerprint to move around. It reads it byte-wise from left to right and from the least significant bit to the most significant bit:
```
Fingerprint      37      :       e4      :       6a      :  ...  :       5e
Bits        00 11 01 11  :  11 10 01 00  :  01 10 10 10  :  ...  :  01 01 11 10
             |  |  |  |      |  |  |  |      |  |  |  |              |  |  |  |
Step         4  3  2  1      8  7  6  5     12 11 10  9             64 63 62 61
```
The bishop will move by the following plan:
```
Bits   Direction
-----------------
00     Up/Left
01     Up/Right
10     Down/Left
11     Down/Right
```
Special cases:
* If the bishop is in a corner and would move into the corner again, he doesn't move at all. *i.e: The bishop is at `[0,0]` and his next step would be `00`. He remains at `[0,0]`*
* If the bishop is in a corner or at a wall and would move into one of the walls, he moves horizontally or vertically only. *i.e: The bishop is at `[0,5]` and his next step would be `01`. He cannot go left, so he just moves up, to `[0,4]`.*
Each position holds a value of how often the bishop has visited this field:
```
Value      | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16|
Character  |   | . | o | + | = | * | B | O | X | @ | % | & | # | / | ^ | S | E |
```
The values 15 (S) and 16 (E) are special in that they mark the start and end position of the bishop respectively and overwrite the real value of the respecting position.
# Goal
Create a program, that takes an alphanumeric fingerprint as input and produces its ASCII art representation as shown in the examples.
# Examples
```
Input:
16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
Output:
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+
Input:
b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b
Output:
+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+
Input:
05:1e:1e:c1:ac:b9:d1:1c:6a:60:ce:0f:77:6c:78:47
Output:
+-----------------+
|       o=.       |
|    o  o++E      |
|   + . Ooo.      |
|    + O B..      |
|     = *S.       |
|      o          |
|                 |
|                 |
|                 |
+-----------------+
```
# Rules
* This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"). The code in the fewest bytes wins.
* You can **not** use an existing library that produces the image.
* Use whichever language you prefer!
* Your submission has to be a complete program
  
[Answer]
# Dyalog APL (178)
```
{⎕ML←3⋄F←9 17⍴0⋄5 9{(⍺⌷F)+←1⋄×⍴⍵:(1 1⌈9 17⌊⍺-1 1-2×↑⍵)∇1↓⍵⋄(⍺⌷F)←16⋄F[5;9]←15⋄K⍪(M,' .o+=*BOX@%&#/^SE'[1+F],M←'|')⍪K←'+','+',⍨17⍴'-'}⊃,/{↓⊖4 2⍴⍉(4/2)⊤¯1+⍵⍳⍨⎕D,'abcdef'}¨⍵⊂⍨':'≠⍵}
```
This is a function that takes the string as its right argument, and returns a character matrix containing the ASCII art representation, e.g.:
```
      F←{⎕ML←3⋄F←9 17⍴0⋄5 9{(⍺⌷F)+←1⋄×⍴⍵:(1 1⌈9 17⌊⍺-1 1-2×↑⍵)∇1↓⍵⋄(⍺⌷F)←16⋄F[5;9]←15⋄K⍪(M,' .o+=*BOX@%&#/^SE'[1+F],M←'|')⍪K←'+','+',⍨17⍴'-'}⊃,/{↓⊖4 2⍴⍉(4/2)⊤¯1+⍵⍳⍨⎕D,'abcdef'}¨⍵⊂⍨':'≠⍵}
      F '16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48'
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+
      F 'b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b'
+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+
```
Explanation:
* `⎕ML←3`: set `⎕ML` to `3`. This makes `⊂` more useful for splitting strings.
* `F←9 17⍴0`: make a 17-by-9 matrix of zeroes. `F` represents how many times each position has been visited.
* `⍵⊂⍨':'≠⍵`: split `⍵` on `:` characters.
* `{`...`}¨`: for each group:
	+ `¯1+⍵⍳⍨⎕D,'abcdef'`: find the index of each character in the string `'01234567890abcdef'`. Subtract 1, because APL is 1-indexed by default.
	+ `(4/2)⊤`: convert the values to their 4-bit representations (there should now be 2-by-4 matrix).
	+ `↓⊖4 2⍴⍉`: rotate the matrix, use the elements to fill a 2-by-4 matrix instead, mirror that matrix horizontally, and then get each line separately. This gives us the 4 2-bit values we need.
* `⊃,/`: join the resulting lists together, giving a list of 2-bit steps.
* `5 9{`...`}`: given the list of steps, and starting at position [9,5]:
	+ `(⍺⌷F)+←1`: increment the current position in `F`.
	+ `×⍴⍵:`: if the list of steps is not empty:
		- `↑⍵`: take the first step from the list
		- `⍺-1 1-2×`: get the delta for that step, and subtract it from the current position
		- `1 1⌈9 17⌊`: restrict movement to within the field
		- `(`...`)∇1↓⍵`: continue with the new position and the rest of the steps
	+ If it *is* empty:
		- `(⍺⌷F)←16`: set `F` to 16 at the final position
		- `F[5;9]←15`: set `F` to 15 at the start position
		- `' .o+=*BOX@%&#/^SE'[1+F]`: map each position to the corresponding character
		- `K⍪(M,`...`,M←'|')⍪K←'+','+',⍨17⍴'-'`: wrap the result in lines
[Answer]
# Perl, 300 + 1 (-n) = 301 bytes
```
perl -ne 'sub b{$b=$_[0]+$_[1];$_[0]=$b<0?0:$b>$_[2]?$_[2]:$b}$v=pack"(H2)*",/\w\w/g;($x,$y)=(8,4);$a[b($y,($_&2)-1,8)*17+b($x,($_&1)*2-1,16)]++for map{vec$v,$_,2}0..63;@a[76,$y*17+$x]=(15,16);$c=" .o+=*BOX@%&#/^SE";print$d="+".("-"x17)."+\n",(map{+"|",(map{substr$c,$_,1}@a[$_*17..($_+1)*17-1]),"|\n"}0..8),$d'
```
This answer is disgusting, but it's also the first one for this puzzle, so it'll do for now.
`-n` to take a line of input on STDIN and fill `$_`.
```
# b($v, -1 or 1, max) modifies $v within 0..max
sub b{$b=$_[0]+$_[1];$_[0]=$b<0?0:$b>$_[2]?$_[2]:$b}
# turn $_ into a binary string
$v=pack"(H2)*",/\w\w/g;
# initialize cursor
($x,$y)=(8,4);
# find an element of single-dimensional buffer @a
$a[
    # y += (bitpair & 2) - 1, within 8
    b($y,($_&2)-1,8) * 17
    # x += (bitpair & 1) * 2 - 1, within 17
  + b($x,($_&1)*2-1,16)
# and increment it
]++
# for each bit pair (in the right order!)
  for map{vec$v,$_,2}0..63;
# overwrite the starting and ending positions
@a[76,$y*17+$x]=(15,16);
# ascii art lookup table
$c=" .o+=*BOX@%&#/^SE";
# output
print
  # the top row, saving it for later
  $d="+".("-"x17)."+\n",
  # each of the eight middle rows
  (map{+
    # converting each character in @a in this row as appropriate
    "|",(map{substr$c,$_,1}@a[$_*17..($_+1)*17-1]),"|\n"
  }0..8),
  # the bottom row
  $d
```
[Answer]
# R, ~~465~~ ~~459~~ ~~410~~ ~~393~~ ~~382~~ 357 bytes
```
f=function(a){s=strsplit;C=matrix(as.integer(sapply(strtoi(el(s(a,":")),16),intToBits)[1:8,]),2);C[!C]=-1;n=c(17,9);R=array(0,n);w=c(9,5);for(i in 1:64){w=w+C[,i];w[w<1]=1;w[w>n]=n[w>n];x=w[1];y=w[2];R[x,y]=R[x,y]+1};R[]=el(s(" .o+=*BOX@%&#/^",""))[R+1];R[9,5]="S";R[x,y]="E";z="+-----------------+\n";cat(z);for(i in 1:9)cat("|",R[,i],"|\n",sep="");cat(z)}
```
With indentations and newlines:
```
f=function(a){
    s=strsplit
    C=matrix(as.integer(sapply(strtoi(el(s(a,":")),16),intToBits)[1:8,]),2)
    C[!C]=-1
    n=c(17,9)
    R=array(0,n)
    w=c(9,5)
    for(i in 1:64){
        w=w+C[,i]
        w[w<1]=1
        w[w>n]=n[w>n]
        x=w[1]
        y=w[2]
        R[x,y]=R[x,y]+1
    }
    R[]=el(s(" .o+=*BOX@%&#/^",""))[R+1]
    R[9,5]="S"
    R[x,y]="E"
    z="+-----------------+\n"
    cat(z)
    for(i in 1:9)cat("|",R[,i],"|\n",sep="")
    cat(z)
}
```
Usage:
```
> f("16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48")
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+
> f("37:e4:6a:2d:48:38:1a:0a:f3:72:6d:d9:17:6b:bd:5e")
+-----------------+
|                 |
|                 |
|          .      |
|     .   o       |
|o . o . S +      |
|.+ + = . B .     |
|o + + o B o E    |
| o .   + . o     |
|         .o      |
+-----------------+
```
[Answer]
# Octave, 277
```
d=reshape(rot90(dec2bin(hex2dec(strsplit(input('','s'),':'))))>'0',2,[])*2-1;p=[9;5];for m=1:64 p=[max(min(p(:,1)+d(:,m),[17;9]),1) p];end;A=' .o+=*BOX@%&#/^SE';F=A(sparse(p(2,:),p(1,:),1,9,17)+1);F(5,9)='S';F(p(2,1),p(1,1))='E';[a='+-----------------+';b=['|||||||||']' F b;a]
```
### Explanation:
```
%// convert the input to binary and rearrange it to be
%//   an array of vectors: [x_displacement; y_displacement]
d=reshape(rot90(dec2bin(hex2dec(strsplit(input('','s'),':'))))>'0',2,[])*2-1;
%// start position array with vector for the start position
p=[9;5];
%// for each move, add displacement, clamping to valid values
for m=1:64 p=[max(min(p(:,1)+d(:,m),[17;9]),1) p];end;
%// alphabet for our fingerprint
A=' .o+=*BOX@%&#/^SE';
%// create a sparse matrix, accumulating values for duplicate
%// positions, and replace counts with symbols
F=A(sparse(p(2,:),p(1,:),1,9,17)+1);
%// correct the start and end symbols and construct the final output
F(5,9)='S';F(p(2,1),p(1,1))='E';
[a='+-----------------+';b=['|||||||||']' F b;a]
```
### Sample run:
```
>> bish
b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b
ans =
+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+
```
[Answer]
# Pyth, ~~145~~ ~~143~~ 140
```
Jm*17]09A,4K8FYcz\:V4AmhtS[0^2d+@,HGdtyv@+_.BiY16*7\0+-4dyN),3 4 X@JGHh@@JGH; X@J4K15 X@JGH16
=Y++\+*17\-\+VJ++\|s@L" .o+=*BOX@%&#/^SE"N\|)Y
```
[Try it online.](https://pyth.herokuapp.com/?code=Jm*17]09A%2C4%3Dk8FYcz%5C%3AK%2B_.BiY16*7%5C0V4AmhtS[0%5E2hd%2Bedtyv%40K%2B-4hdyN%29%2C%2C3G%2C4H+X%40JGHh%40%40JGH%3B+X%40J4k15+X%40JGH16%0A%3DY%2B%2B%5C%2B*17%5C-%5C%2BVJ%2B%2B%5C|s%40L%22+.o%2B%3D*BOX%40%25%26%23%2F%5ESE%22N%5C|%29Y&input=37%3Ae4%3A6a%3A2d%3A48%3A38%3A1a%3A0a%3Af3%3A72%3A6d%3Ad9%3A17%3A6b%3Abd%3A5e&debug=0)
Pyth isn't really good at challenges with iteration. I'm expecting CJam to beat it easily.
[Answer]
# JavaScript (ES6) 249 ~~208~~
**Edit** Added missing border
Test running the snippet below in any EcmaScript 6 compliant browser
```
B=f=>f.replace(/\w+/g,b=>{for(b=`0x1${b}`;b-1;b>>=2)++g[p=(q=(p=(q=p+~-(b&2)*18)>0&q<162?q:p)+b%2*2-1)%18?q:p]},p=81,z=`+${'-'.repeat(17)}+`,g=Array(162).fill(0))&&g.map((v,q)=>q?q-81?q-p?q%18?' .o+=*BOX@%&#/^'[v]:`|
|`:'E':'S':z+`
|`).join``+`|
`+z
// TEST
console.log=x=>O.innerHTML+=x+'\n'
;['37:e4:6a:2d:48:38:1a:0a:f3:72:6d:d9:17:6b:bd:5e'
,'16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48'
,'b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b'
,'05:1e:1e:c1:ac:b9:d1:1c:6a:60:ce:0f:77:6c:78:47'  
].forEach(t=>console.log(t+'\n'+B(t)+'\n'))
// Less golfed
BB=f=>(
  p = 81,
  g = Array(162).fill(0),
  f.replace(/\w+/g, b => {
    for(b = `0x1${b}`;b != 1; b >>= 2)
      q = p+~-(b&2)*18,
      p = q>0&q<162?q:p,
      p = (q=p+b%2*2-1)%18?q:p,
      ++g[p]
  }),
  g.map((v,q) => q-81?q-p?q%18?' .o+=*BOX@%&#/^'[v]:'\n':'E':'S')
  .join``
)
```
```
pre { font-family: menlo,consolas; font-size:13px }
```
```
<pre id=O></pre>
```
[Answer]
# Python, ~~381~~328
-51 thanks to @JonathanFrech
```
def h(f):
 s=[f'{int(o,16)>>s&3:02b}'for o in f.split(':')for s in(0,2,4,6)];r=[0]*153;p=76;w=17
 for d in s:r[p]+=1;p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]
 r[76]=15;r[p]=16;b='+'+'-'*w+'+';print(b);i=0
 while i<153:print(f"|{''.join(' .o+=*BOX@%&#/^SE'[c]for c in r[i:i+w])}|");i+=w
 print(b)
```
Slightly ungolfed for the sake of explanation:
```
def h_(f):
 #Alias 17 because it gets used enough times for this to save bytes
 w=17
 #Input parsing
 s=[f'{int(o,16)>>s&3:02b}'for o in f.split(':')for s in(0,2,4,6)]
 #Room setup
 r=[0]*153
 p=76
 #Apply movements
 for d in s:
  r[p]+=1
  p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]
 r[76]=15 #Set start position
 r[p]=16 #Set end position
 #Display result
 b='+'+'-'*w+'+'
 print(b)
 i=0
 while i<153:
  print(f"|{''.join(' .o+=*BOX@%&#/^SE'[c]for c in r[i:i+w])}|")
  i+=w
 print(b)
```
This mess of a line:
```
r[p]+=1;p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]
```
​
is functionally equivalent to this:
```
if int(d[0]): #Down, Y+
  if p//17!=8:
    p+=17
else: #Up, Y-
  if p//17!=0:
    p-=17
​
if int(d[1]): #Right, X+
  if p%17!=16:
    p+=1
else: #Left, X-
  if p%17!=0:
    p-=1
```
but nests all of the conditionals in this style of golf shortcut: `(false_value,true_value)[condition]`
Hopefully the rest is fairly self-explanatory
# Tests
```
h('16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48')
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+
h("b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b")
+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+
h("05:1e:1e:c1:ac:b9:d1:1c:6a:60:ce:0f:77:6c:78:47")
+-----------------+
|       o=.       |
|    o  o++E      |
|   + . Ooo.      |
|    + O B..      |
|     = *S.       |
|      o          |
|                 |
|                 |
|                 |
+-----------------+
```
```
[Answer]
# Pyth, 125 bytes
```
Jj*17\-"++"JVc9XXsm@"^ .o+=*BOX@%&#/"hdrS+*U9U17K.u.e@S[0b*8hk)1.b+tNyYNYsm_c4.[08jxsM^.HM16 2d2cz\:,4 8 8ieK17\E76\SjN"||")J
```
Try it online: [Demonstration](http://pyth.herokuapp.com/?code=Jj*17%5C-%22%2B%2B%22JVc9XXsm%40%22%5E+.o%2B%3D*BOX%40%25%26%23%2F%22hdrS%2B*U9U17K.u.e%40S[0b*8hk%291.b%2BtNyYNYsm_c4.[08jxsM%5E.HM16+2d2cz%5C%3A%2C4+8+8ieK17%5CE76%5CSjN%22||%22%29J&input=37%3Ae4%3A6a%3A2d%3A48%3A38%3A1a%3A0a%3Af3%3A72%3A6d%3Ad9%3A17%3A6b%3Abd%3A5e&test_suite_input=16%3A27%3Aac%3Aa5%3A76%3A28%3A2d%3A36%3A63%3A1b%3A56%3A4d%3Aeb%3Adf%3Aa6%3A48%0Ab6%3Add%3Ab7%3A1f%3Abc%3A25%3A31%3Ad3%3A12%3Af4%3A92%3A1c%3A0b%3A93%3A5f%3A4b%0A05%3A1e%3A1e%3Ac1%3Aac%3Ab9%3Ad1%3A1c%3A6a%3A60%3Ace%3A0f%3A77%3A6c%3A78%3A47&debug=0) or [Test-Suite](http://pyth.herokuapp.com/?code=Jj*17%5C-%22%2B%2B%22JVc9XXsm%40%22%5E%20.o%2B%3D*BOX%40%25%26%23%2F%22hdrS%2B*U9U17K.u.e%40S[0b*8hk%291.b%2BtNyYNYsm_c4.[08jxsM%5E.HM16%202d2cz%5C%3A%2C4%208%208ieK17%5CE76%5CSjN%22%7C%7C%22%29J&input=37%3Ae4%3A6a%3A2d%3A48%3A38%3A1a%3A0a%3Af3%3A72%3A6d%3Ad9%3A17%3A6b%3Abd%3A5e&test_suite=1&test_suite_input=16%3A27%3Aac%3Aa5%3A76%3A28%3A2d%3A36%3A63%3A1b%3A56%3A4d%3Aeb%3Adf%3Aa6%3A48%0Ab6%3Add%3Ab7%3A1f%3Abc%3A25%3A31%3Ad3%3A12%3Af4%3A92%3A1c%3A0b%3A93%3A5f%3A4b%0A05%3A1e%3A1e%3Ac1%3Aac%3Ab9%3Ad1%3A1c%3A6a%3A60%3Ace%3A0f%3A77%3A6c%3A78%3A47&debug=0)
I wrote a few days ago, but didn't post it, because I wasn't really happy about it.
### Explanation:
The basic idea is the following. I start with the pair `(4, 8)`. In each move `(m1,m2)` I go from the `(x, y)` to `(x-1+2*m1, y-1+2*m2)`. To make sure, that these coordinates don't go outside the boarders, I'll make some lists, sort them and return the middle element: `(sorted(0,8,newx)[1], sorted(0,16,newy)[1])`. 
I keep track of all positions. To this list of positions I add a list of all possible positions, sort them and run-length-encode them. Which gives me a number for each position. With this number I can choose the coorect char, and at the end overwrite the chars of the start and end position.
[Answer]
# Ruby 288
```
->k{w=17
r=[z=?++?-*w+?+]
(0...w*9).each_slice(w).map{|o|r<<?|+o.map{|x|c=76
q=0
k.split(?:).flat_map{|b|(0..7).map{|i|b.to_i(16)[i]}}.each_slice(2){|h,v|v<1?(c>w&&c-=w):c<w*8&&c+=w
c+=h<1?c%w>0?-1:0:c%w<16?1:0
c==x&&q+=1}
x==76?'S':c==x ?'E':' .o+=*BOX@%&#/^'[q]}.join+?|}
(r+[z]).join'
'}
```
Try it online: <http://ideone.com/QOHAnM>
The readable version (the one I started golfing from) is here: <http://ideone.com/XR64km>
[Answer]
# C 361
**Thanks to @ceilingcat for some very nice pieces of golfing - now even shorter**
```
#define H h[i]|=*++p-48-*p/59*39
#define L puts("+-----------------+")
#define F(m,s)for(m=0;m<s;m++)
h[16],m[17][9],i,j,n,x=8,y=4;main(w,v)char**v;{for(char*p=v[1]-1;i<16;p++,i++)H<<4,H;F(j,16)F(n,4)x+=h[j]>>n*2&1?x!=16:-!!x,y+=h[j]>>n*2&2?y!=8:-!!y,m[x][y]++;m[8][4]=15;m[x][y]=L-4;F(i,9){printf("|");F(j,17)printf(L" .o+=*BOX@%&#/^SE"+m[j][i]);puts("|");}L;}
```
[Try it online!](https://tio.run/##XVDLboJAFN37FYCpYZihOjDymGG0aVLjgqSLbppMpgnyKJiARK2FqN9Osdqm6d3d87jn5sTmexx33TBJs6JKlaWSi0KeuAFhbRLPNOrx1Ddsf/AjCJX6Y7/TNWj@H6iBX9VCL9EOZJutXvIJK4MdKyEEg1xgR6JSYFcKX6ICrVGFGu6hlhNWRkWlf6IDiPNoaxgHdrz4v5eaHwSWJmZFgB1WQ4iK/twyCAhasoW@RtgBC71CBDSQ52ItZ7PKsEZ43qgcO9RU1Qa1fxlr3qrcuxBt/04jRSshZKXwpCCS4ym7gTw0SR9QIB8c621R7TNdO2ngmumCGxRqyv0GcuPx@fXhbjQcv708abDsw/oqAbv2dXGdQ3buus52aUqoE1ErocSjtkdxRCcRzWzqWtRJaOJT7FJnRVcJnaZf "C (gcc) – Try It Online")
[Answer]
# [C (gcc)](https://gcc.gnu.org/), ~~333 326 314 292~~ 288 bytes
```
f[17][9],i,x=8,y=4;main(n){for(;i++%4||read(0,&n,3)*sscanf(&n,"%x",&n);n/=4)f[x+=n%2?x<16:-!!x][y+=n&2?y<8:-!!y]+=f[x][y]<14;f[8][4]=15;for(f[x][y]=puts(n="+-----------------+")-4;++i<75;puts("|"))for(x=!printf("|");x<17;)putchar(" .o+=*BOX@%&#/^SE"[y=fmin(f[x++][i-66],16)]);puts(n);}
```
[Try it online!](https://tio.run/##XY9Bb8IwDIX/yshU1JB2kDakxWnENInzDrtMijIptIRVgoBaJrUa@@vrUrbTfPPnZ/u9Mt6X5TBYRTOtVjqqo07mUS@ZOJrahQ5/2lMTipqQgF2vzc5U4SKauijFs7YtjbOhb1DQIQ@xcHPJsFUdkS5I1l1BOcSTSadV78k0WfdFPoJeE@lVHuuCMmFVrhXTki7F@OxvIs8flzZ0EpH4fxGEYyYIqYtsKW4ydEUYj8udnJyb2l3sDQlvIRPYS8p304To7uFE5Ozp@fUxmN7P3142SPXSHn3Q0TTRqo451xHlWOPfwz7U1zCkGewYcANJBSyHNAdqYGHAppAlwCuoVkAz4FvYVrDcfZf2YPbtEB@OPw "C (gcc) – Try It Online")
Golfed from openssh source code `key_fingerprint_randomart()`. 
Edit: Now a complete program.
Less golfed:
```
/* initialize field */
f[17][9],i,x=8,y=4;
main(n){
  /* process raw key */
  /* each byte conveys four 2-bit move commands */
  for(;i%4||read(0,&n,3)+sscanf(&n,"%x",&n);n/=4)
    /* evaluate 2 bit, rest is shifted later */
    /* assure we are still in bounds; augment the field */
    f[x+=n%2?x<16:-!!x][y+=n&2?y<8:-!!y]+=f[x][y]<14;
  /* mark starting point and end point; output upper border */
  f[8][4]=15;
  f[x][y]=puts(n="+-----------------+")-4
  /* output content */
  for(;++i<75;puts("|"))
    for(x=!printf("|");x<17;)
      /*
       * Chars to be used after each other every time the worm
       * intersects with itself.
       */
      putchar(" .o+=*BOX@%&#/^SE"[y=fmin(f[x++][i-66],16)]);
  /* output lower border */
  puts(n);
}
```
[Answer]
# [Burlesque](https://github.com/FMNSSun/Burlesque), 200 bytes
```
':;;m{b6b28'0P[2cob2^p}m{{-1 1}Jcpj!!}{4 8}+]qr{?+q0cy{>.}Z]{8 16}cy{<.}Z]}pal_g_jf:u[j" .o+=*BOX@%&#/^"XXbcjq!!Z]z[{(D!)_+p^}m[' 17.*9.*)XXje!j'SD!j'ED!"|\n|"IC'|+]'|[+\['+'-17.*_+<-"+\n".+J<-#rCL\[Q
```
[Try it online!](https://tio.run/##FcpbT4MwGIDhv0K7aB0NSAGBfcNDHF64mKjxhqwcQoGZkLEBhgst/e243bzJk7xiHA71Tz/W80xgvW6l8IQdEOuD2@VJ2FmnWikNpjG1LbsGISVdLVA07Qf5SHur/JUPptqlMtCYp84KL1Jdcci/82YPI2@wZp7ovf78Hj9dXS9uMxzHomx6hHbpH5c3EVrmtMtUy4nGfFNfmfoyjpsaNeQrOuclQnhKjhN@3ZCJpmTiNOGEEuMy5zQ0ME2O2KTb0FgMm7eEf86z40PtgleAXYEbgBMAK8AqYO@Ab4NXQbUC5oMnQFRwV/8D "Burlesque – Try It Online")
```
':;;                # Split at colons
m{                  # Map over these
 b6b2               # Read as hex, then to binary
 8'0P[              # Pad to 8 bit
 2co                # Chunks of 2
 b2                 # Read as binary
 ^p}                # Push each
m{                  # Map over these
 {-1 1}Jcp          # Construct direction array {{- -}{- +}{+ -}{+ +}}
 j!!                # Select the block indexed by binary val
}
{4 8}+]             # Push starting pos
qr{                 # Boxed reduce
 ?+                 # Add together
 q0cy{>.}Z]         # If < 0 -> 0
 {8 16}cy{<.}Z]     # If > box -> boxwidth
pa                  # Apply on inits
l_g_j               # Take start and end for later
f:                  # Calculate frequency of everything but head and tail
u[j                 # Unzip to {pos} {counts}
" .o+=*BOX@%&#/^"XXbcj # Magic string
q!!Z]               # Select element based on count
z[                  # Rezip to {pos icon}
{(D!)_+p^}m[        # Add D! (set at pos) to the each
' 17.*9.*)XX        # Create 17x9 array of spaces
je!                 # Evaluate block of ({pos icon D!}, ...)
j'SD!j'ED!          # Set start and end to S & E
"|\n|"IC            # Intercalate surrounding v-bars 
'|+]'|[+            # Add first and last
\[                  # Concatenate
'+'-17.*_+<-"+\n".+ # Create top-bar
J<-                 # Duplicate and reverse (putting newline at the front
#r                  # Rotate stack
CL                  # Collect stack as list
\[                  # Concatenate
Q                   # Pretty print
```
[Answer]
# [Perl 5](https://www.perl.org/), ~~283~~ ~~276~~ ~~266~~ 254 + 1 (-p) = 255 bytes
```
$x=8;$y=4;for$n(map{hex}split(/:/)){$n&2?$y<8&&$y++:$y&&$y--,$n&1?$x<16&&$x++:$x&&$x--,$a[$x][$y]++,$n>>=2 for 1..4}$a[8][4]=15;$a[$x][$y]=16;map{$y=$_;$s.="|";$s.=substr' .o+=*BOX@%&#/^SE',$a[$_][$y],1 for 0..16;$s.="|
"}0..8;$l='-'x17;$_="+$l+
$s+$l+
"
```
Uses `-p` to take a line of input into `$_`, and to print the (modified) value of `$_` at the end.
[Try it online!](https://tio.run/##RU/BToNAEL33KwiO0LplcSksZLbbGhPPHryYECQgNDZBIIDJktpfFxc8eHpv5s2bN9OWXRVMEygZCRilL05NB/X6M2svH6W69m11HtYuupvNBWrLO8K4jywLRkIQxpk4zlYL7Ahqz7huqFlRM5mVLAaVxDAmhOixw0F6hg4wGKX@VYtREvuJZIH4H5SMizldHwOpgJ5K89tcsP/K@6GzDdoQeff4/Ppwa924by9P9hKTLu4tW/bfU6rX/JlX5lWX@rtK2o6tWCgglSaBiqygX8CcJsbRCzF7xyzAUPMIvQJ3HPkOWY4BR7/AMsfihJnm0U/TDuem7ien/QU "Perl 5 – Try It Online")
Ungolfed:
```
# Implicit from running with perl -p:
# read line from standard input
#while($_ = <STDIN>) {
# Initialise starting coordinates
$x=8;
$y=4;
# Split input line at colons, convert each element (2-digit hex value) to integer,
iterate over the list of integers
for $n (map { hex($_) } split(/:/))
{
    # for each integer, iterate 4 times, looking at 2 of the 8 bits each time
    for(1..4) 
    {
        # if second-last bit is set
        if($n & 2) {
            # move down, unless we are already at the bottom
            $y++ if($y < 8);
        }
        else {
            # move up, unless we are already at the top
            $y-- if($y);
        }
        # if last bit is set
        if($n & 1) {
            # move right, unless already at the right edge
            $x++ if($x<16);
        }
        else {
            # move left, unless already at the left edge
            $x-- if($x);
        }
        # Increment the counter for this position
        $a[$x][$y]++;
        # Shift number right two bits for next iteration
        $n = $n >> 2
    }
}
# Set fixed values (15 and 16) for start and end positions respectively
$a[8][4] = 15;
$a[$x][$y] = 16;
# Iterate over the rows of the grid, building a string containing the output rows
for(0..8)
{
    # save the y-coordinate for use in inner loop below
    $y = $_;
    # append the line start
    $s .= "|";
    # iterate over the columns of the row
    for(0..16)
    {
        # get the value for this position
        $v = $a[$_][$y];
        # map the value to the appropriate character
        $s .= substr(' .o+=*BOX@%&#/^SE', $v, 1)
    }
    # append the line end
    $s .= "|\n"
}
# build the hyphen lines for the top and bottom rows
$l = '-' x 17;
# build the full output string and assign to $_
$_ = "+$l+\n$s+$l+\n"
# Implicit from running under perl -p
#print $_;
#}
```
[Answer]
# Java 11, ~~460~~ ~~453~~ ~~452~~ 449 bytes
```
interface M{static void main(String[]a){var m=new char[9][17];Integer x=4,y=8,i=0,s;for(var p:a[0].split(":"))for(p=x.toString(x.parseInt(p,16),2),i=4;i-->0;m[x-=s<2?x>0?1:0:x/8-1][y-=s%2>0?y/16-1:y>0?1:0]++)s=x.decode(("0".repeat(8-p.length())+p).split("(?<=\\G..)")[i]);String t="+"+"-".repeat(m[x][y]=16)+"-+\n",r=t+"|";for(m[4][8]=15;++i<153;r+=y>15?"|\n"+(x<8?"|":""):"")r+=" .o+=*BOX@%&#/^SE".charAt(m[x=i/17][y=i%17]);System.out.print(r+t);}}
```
-11 bytes thanks to *@ceilingcat*.
[Try it online.](https://tio.run/##PZBfa9swFMW/itHIkCZbsR3Hca@jZBuMsYexh74MFA/UWEnV1X@QtdSm7WfPbho2hJA45@rcn@6DPunoof59PtvWG3fQexN8fx689nYfnDpbB422Lb31zrZHVWn2fNIuaGRrnoL9vXbqplLJqiq/4eujccEos3CSRWhlHA7loXP0Ut@DVnElhv7RekqAMHZxejkK312j6Sh67QaDObQPk5yFKcOQrLRRtInLRo2RHNbpdtzE2wRiGOdFlFRqQnWWojbNkzxKYLraFedswPTa7LvaUEpiIpzpjfa0iHrxaNqjv6eM8Z79g6LbtdztvgrBCFO2YuWVK/CScFzR/wBEwb6VREaU@a4loZOekxfy9t1GZZUq0F6WnNt1slyUjstpkyy35AWLOR3XBV5xCoRdNrokEB2XHz7/@Plx9v7d/NftFyIuw/301k3aOU5YTdLO8ESwafCmEd0fL3pE9NRxz8rX1/P5vFiBySDXkNaQFbAoINEQazgsYJVCXkN9A8kK8ju4q2Fp/gI)
**Explanation:**
```
interface M{              // Class
  static void main(String[]a){
                          //  Mandatory main method
    var m=new char[9][17];//   Create a character-matrix of size 9 by 17
    Integer x=4,y=8,      //   Current position, starting at 4,8
            i=0,s;        //   Temp integers
    for(var p:a[0].split(":"))
                          //   Loop over the argument-String, split by ":"
      for(p=x.toString(x.parseInt(p,16),
                          //    Convert the current part from hexadecimal String to integer
                       2),//    And then from integer to binary String
          i=4;i-->0       //    Inner loop `i` in the range (4,0]:
          ;               //      After every iteration:
           m[x-=          //       Update the `x` position:
                s<2?      //        If `s` is 0 or 1:
                 x>0?     //         If we're not out of bounds yet at the top:
                  1       //          Go up
                 :        //         Else:
                  0       //          Stay at the top border
                :         //        Else (s is 10 or 11):
                 x/8      //         If we're not out of bounds yet at the bottom:
                  -1]     //          Go down
                          //         Else:
                          //          Stay at the bottom border
            [y-=          //       Update the `y` position:
                s%2>0?    //        If `s` is odd (1 or 11):
                 y/16     //         If we're not out of bounds yet at the right side:
                  -1      //          Go right
                          //         Else:
                          //          Stay at the right border
                :         //        Else (s is 0 or 10):
                 y>0?     //         If we're not out of bounds yet at the left side:
                  1       //          Go left
                 :        //         Else:
                  0]      //          Stay at the left border
                    ++)   //       And increase the value at x,y cell by 1
        s=                //     Set `s` to:
          x.decode(("0".repeat(8-p.length())+p)
                          //      Left-pad the binary-String `p` with 0s up to length 8
                   .split("(?<=\\G..)")
                          //      Split it into parts of size 2
                   [i]    //      Get the `i`'th part
                      );  //      And convert it from binary-String to integer
                          //   After the nested loops are done:
    String t="+"+"-".repeat(m[x][y]=16
                          //   Set the value at position x,y to 16
                      )+"-+\n",
                          //   Push a temp String for the top/bottom borders
           r=t+"|";       //   Result-String, starting with the top border + one "|"
    for(m[4][8]=15;       //   Set the value at position 4,8 to 15
        ;++i<153          //   Loop over all cells:
        ;                 //     After every iteration:
         r+=              //      Append the following to the result-String:
            y>15?         //       If we're done with a row:
             "|\n"        //        Append "|" and a newline
             +(x<8?       //        And unless it's the last row:
                "|"       //         Also append a "|"
               :          //        If it is the last row:
                "")       //         Append nothing
            :             //       And if we're not done with the row yet:
             "")          //        Append nothing
      r+=                 //    Append the following to the result-String:
         " .o+=*BOX@%&#/^SE".charAt(m[x=i/17][y=i%17]);
                          //     The `i`'th character of the String " .o+=*BOX@%&#/^SE"
                          //     where `i` is the current value of cell x,y
    System.out.print(r    //   And finally output this result-String,
      +t);}}              //   including its bottom border
```
[Answer]
# Rust - 509 bytes
```
fn b(s:&str)->String{let(mut v,mut b)=([[0;11];20],[9,5]);v[19]=[19;11];for i in 0..16{let mut c=usize::from_str_radix(&s[i*3..i*3+2],16).unwrap();for k in 0..4{for j in 0..2{v[j*18][i%9+1]=18;v[i+k][j*10]=[17,3][(i+k+17)%18/17];b[j]=match(if c&(j+1)==j+1{b[j]+1}else{b[j]-1},j,){(0,_)=>1,(18,0)=>17,(10,1)=>9,x@_=>x.0 as usize,}}v[b[0]][b[1]]+=1;c>>=2;}}v[9][5]=15;v[b[0]][b[1]]=16;(0..220).fold("\n".to_string(),|s,i|{format!("{}{}",s," .o+=*BOX@%&#/^SE-|\n".chars().nth(v[i%20][i/20] as usize).unwrap())})}
```
Large but... almost close to C. As usual there are many bytes used up due to the way Rust does not automagically cast types into each other. But there is also probably room for improvement.... could probably use some ideas from other solutions.
[ungolfed version is on the Rust Playground online](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=24ecd8e1acef083f22629543508afa3c)
]