醉酒主教


42

在公共密钥密码术中,公共密钥指纹是用于标识较长公共密钥的一小段字节序列。

特别是在SSH中,它们可以用来验证服务器是否实际上是我希望与之通信的服务器,并且不受中间人攻击的攻击。

它们通常表示为十六进制数字字符串,因此将其与我期望的指纹进行比较可能相当无聊且乏味:

37:e4:6a:2d:48:38:1a:0a:f3:72:6d:d9:17:6b:bd:5e

为了使操作更简单,OpenSSH引入了一种将指纹可视化为ASCII艺术的方法,如下所示:

+-----------------+
|                 |
|                 |
|          .      |
|     .   o       |
|o . o . S +      |
|.+ + = . B .     |
|o + + o B o E    |
| o .   + . o     |
|         .o      |
+-----------------+

这样,我可以尝试记住ASCII艺术的粗略形状,然后(从理论上)在服务器的指纹更改并且图像看起来不同时识别出它。

这个怎么运作

取自Dirk Loss,Tobias Limmer,Alexander von Gernler。2009.醉酒的主教:OpenSSH指纹可视化算法分析

网格的宽度为17个字符,高度为9个字符。“主教”从第4行/第8列(中心)开始。每个位置可以表示为[x,y],即主教的起始位置为[8,4]。

            1111111
  01234567890123456
 +-----------------+
0|                 |
1|                 |
2|                 |
3|                 |
4|        S        |
5|                 |
6|                 |
7|                 |
8|                 |
 +-----------------+

主教使用指纹四处走动。它从左到右,从最低有效位到最高有效位按字节顺序读取它:

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

主教将按照以下计划行动:

Bits   Direction
-----------------
00     Up/Left
01     Up/Right
10     Down/Left
11     Down/Right

特别案例:

  • 如果主教在拐角处并且会再次移动到拐角处,那么他根本不会移动。即:主教在[0,0],他的下一步将是00。他留在[0,0]
  • 如果主教在角落或墙壁上并且要移入其中一堵墙壁,则他只能水平或垂直移动。即:主教在[0,5],他的下一步将是01。他不能向左走,所以他往上走[0,4]

每个位置都具有主教访问该字段的频率值:

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 |

值15(S)和16(E)的特殊之处在于它们分别标记主教的开始和结束位置,并覆盖相对位置的实际值。

目标

创建一个程序,该程序将字母数字指纹作为输入,并产生其ASCII艺术表现形式,如示例所示。

例子

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          |
|                 |
|                 |
|                 |
+-----------------+

规则

  • 这是。最少字节的代码将获胜。
  • 不能使用产生图像的现有库。
  • 使用您喜欢的任何一种语言!
  • 您的提交必须是完整的程序

3
我们是否可以假设没有单元会被访问超过14次?
马丁·恩德

2
在一些极少覆盖的极端情况下,将导致某些字段被访问14次以上。33:33:33:...:33cc:cc:cc:...:cc这就是例子。指纹通常是MD5哈希,因此很难获得这样的结果。我还没有找到任何可靠的方法来处理这些问题,所以现在我要说:假设没有一个单元被访问超过14次。
Padarom

Answers:


2

Pyth,125个字节

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

在线尝试:演示测试套件

我几天前写了,但是没有发表,因为我对此并不满意。

说明:

基本思想如下。我从一对开始(4, 8)(m1,m2)我的每一步都从(x, y)(x-1+2*m1, y-1+2*m2)。为了确保这些坐标不会超出边界,我将列出一些列表,对其进行排序,然后返回中间元素:(sorted(0,8,newx)[1], sorted(0,16,newy)[1])

我跟踪所有职位。在此职位列表中,我添加了所有可能职位的列表,对其进行排序并对其进行游程长度编码。这给了我每个职位的号码。使用这个数字,我可以选择coorect字符,最后覆盖开始和结束位置的字符。


9

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'}¨⍵⊂⍨':'≠⍵}

此函数将字符串作为其正确参数,并返回包含ASCII艺术作品表示的字符矩阵,例如:

      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.|
+-----------------+

说明:

  • ⎕ML←3:设置⎕ML3。这对于拆分字符串更加有用。

  • F←9 17⍴0:制作一个17×9的零矩阵。F代表每个位置被访问了多少次。

  • ⍵⊂⍨':'≠⍵:字符分割。

  • {... :每个组:
    • ¯1+⍵⍳⍨⎕D,'abcdef':查找字符串中每个字符的索引'01234567890abcdef'。减去1,因为APL默认为1索引。
    • (4/2)⊤:将值转换为其4位表示形式(现在应为2×4矩阵)。
    • ↓⊖4 2⍴⍉:旋转矩阵,改用元素填充2×4矩阵,水平镜像该矩阵,然后分别获取每一行。这为我们提供了4个2位值。
  • ⊃,/:将结果列表连接在一起,给出2位步骤的列表。
  • 5 9{... }:给出步骤列表,并从位置[9,5]开始:
    • (⍺⌷F)+←1:在中增加当前位置F
    • ×⍴⍵::如果步骤列表不为空:
      • ↑⍵:从清单中迈出第一步
      • ⍺-1 1-2×:获取该步骤的增量,并将其从当前位置减去
      • 1 1⌈9 17⌊:将运动限制在野外
      • (... )∇1↓⍵:继续执行新的职位和其余步骤
    • 如果空:
      • (⍺⌷F)←16F在最终位置设置为16
      • F[5;9]←15F在开始位置设为15
      • ' .o+=*BOX@%&#/^SE'[1+F]:将每个位置映射到相应的字符
      • K⍪(M,... ,M←'|')⍪K←'+','+',⍨17⍴'-':将结果换行

8

Perl,300 + 1(-n)= 301字节

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'

这个答案令人作呕,但这也是这个难题的第一个答案,所以现在就可以解决。

-n在STDIN上输入一行并填充$_

# 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

7

R,465个 459 410 393 382 357字节

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)}

缩进和换行符:

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)
}

用法:

> 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      |
+-----------------+

不能通过一次定义为“ m”来打高尔夫球“矩阵”功能?是否与“功能”相同?
CousinCocaine 2015年

我认为您不需要在“ cat”函数中使用“ sep =”
CousinCocaine 2015年

sep的默认值是空格,因此我需要覆盖它,但实际上我可以为矩阵加别名。
plannapus 2015年

另外,据我所知,您不能别名function
plannapus 2015年

5

八度,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]

说明:

%// 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]

样品运行:

>> 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.|
+-----------------+

3

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

在线尝试。

Pyth并不善于应对迭代挑战。我希望CJam可以轻松击败它。


3

JavaScript的(ES6)249 208

编辑添加缺少的边框

在任何符合EcmaScript 6的浏览器中测试运行以下代码段

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>


您的高尔夫代码本身也应该能够打印边框。目前,只有您的测试用例会打印中的上下边框forEach,垂直边框仍然缺失。
Padarom

@Padarom我误解了您的“合理”评论,以为没有要求过边境
edc65

我的意思是您使用哪种输入和输出方法是合理的。如果造成误导,我

3

Python中,381 328

-51感谢@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)

为了说明起见,略有偏离:

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)

这条线很乱:

r[p]+=1;p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]

在功能上等同于此:

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

但是将所有条件句嵌套在这种高尔夫捷径中:(false_value,true_value)[condition] 希望其余的条件不言而喻

测验

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          |
|                 |
|                 |
|                 |
+-----------------+
```

您好,欢迎来到PPCG。您可以使用一个字母的变量名,然后将单行换循环放入一行,从而编写代码。(1,0)[p%17==16]+(p%17!=16),甚至可能p%17!=16
乔纳森·弗雷奇

此外,中有多余的空间] for
乔纳森·弗雷奇

我认为fp应该是f
乔纳森·弗雷奇


2
为什么要使用~16?有点混淆永远不会伤害您的高尔夫!
乔纳森·弗雷希


2

C-488

必须有一种方法可以使它变小。

#include<stdio.h>
#define H ++p;h[i]|=(*p-(*p>58?87:48))<<
#define B ((h[j]>>n*2)&3)
#define L puts("+-----------------+")
#define F(m,s)for(m=0;m<s;m++)
int h[16],m[17][9],i,j,n,x=8,y=4;main(w,v)char**v;{char*p=v[1]-1,c[17]={32,46,111,43,61,42,66,79,88,64,37,38,35,47,94,83,69};for(;*p;p++,i++){H 4;H 0;}F(j,16)F(n,4){if(B&1)x+=!(x==16);else x-=!(x==0);if(B&2)y+=!(y==8);else y-=!(y==0);m[x][y]++;}m[8][4]=15;m[x][y]=16;L;F(i,9){printf("|");F(j,17)printf("%c",c[m[j][i]]);puts("|");}L;}

0

锈-509字节

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())})}

很大,但是...几乎接近C。通常,由于Rust不会自动将类型彼此转换的方式,因此已消耗了许多字节。但是可能还存在改进的余地...。可能会使用其他解决方案中的一些想法。

Ungolfed版本在Rust Playground在线上

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.