Gimli,让它更短吗?


25

我是Gimli的作者之一。我们已经在C中拥有2个tweet(280个字符)的版本,但是我想看看它有多小。

Gimli(论文网站)是一种高速,高安全级别的加密排列设计,将在2017年加密硬件和嵌入式系统大会(CHES)(9月25日至28日)展出。

任务

像往常一样:用您选择的语言来实现Gimli的较小可用的实现。

它应该能够将384位(或48个字节,或12个无符号int ...)作为输入返回(如果使用指针,则可以在适当位置进行修改)应用于这384位的Gimli结果

允许从十进制,十六进制,八进制或二进制的输入转换。

潜在的极端情况

整数编码被假定为低端(例如,您可能已经拥有的)。

您可以重命名Gimli为,G但是它仍然必须是函数调用。

谁赢?

这是代码高尔夫球,因此最短的答案以字节为单位!标准规则当然适用。

下面提供了参考实现。

注意

引起了一些关注:

“嗨,帮派,请用其他语言免费实施我的程序,这样我就不必了”(致谢@jstnthms)

我的回答如下:

我可以轻松地用Java,C#,JS,Ocaml来完成它……这很有趣。目前,我们(Gimli团队)已在AVR,Cortex-M0,Cortex-M3 / M4,Neon,SSE,SSE展开,AVX,AVX2,VHDL和Python3上实现(并优化)。:)


关于金利

状态

Gimli将一轮回合应用于384位状态。状态表示为尺寸为3×4×32的平行六面体,或等效地表示为32位字的3×4矩阵。

州

每个回合是三个操作的序列:

  • 非线性层,特别是应用于每列的96位SP盒;
  • 在第二轮中,形成一个线性混合层;
  • 在第四轮中,不断增加。

非线性层。

SP盒包含三个子操作:第一个单词和第二个单词的旋转;第二个单词的旋转。3输入非线性T函数;以及第一个和第三个单词的交换。

SP

线性层。

线性层包含两个交换操作,即小交换和大交换。从第1轮开始,每4轮进行一次小交换。从第3轮开始,每4轮进行一次大掉期。

线性的

舍入常数。

Gimli有24发子弹,编号为24,23,...,1。当舍入数r为24、20、16、12、8、4时,我们将舍入常数(0x9e377900 XOR r)与第一个状态字进行异或。

在此处输入图片说明

C语言参考源

#include <stdint.h>

uint32_t rotate(uint32_t x, int bits)
{
  if (bits == 0) return x;
  return (x << bits) | (x >> (32 - bits));
}

extern void gimli(uint32_t *state)
{
  int round;
  int column;
  uint32_t x;
  uint32_t y;
  uint32_t z;

  for (round = 24; round > 0; --round)
  {
    for (column = 0; column < 4; ++column)
    {
      x = rotate(state[    column], 24);
      y = rotate(state[4 + column],  9);
      z =        state[8 + column];

      state[8 + column] = x ^ (z << 1) ^ ((y&z) << 2);
      state[4 + column] = y ^ x        ^ ((x|z) << 1);
      state[column]     = z ^ y        ^ ((x&y) << 3);
    }

    if ((round & 3) == 0) { // small swap: pattern s...s...s... etc.
      x = state[0];
      state[0] = state[1];
      state[1] = x;
      x = state[2];
      state[2] = state[3];
      state[3] = x;
    }
    if ((round & 3) == 2) { // big swap: pattern ..S...S...S. etc.
      x = state[0];
      state[0] = state[2];
      state[2] = x;
      x = state[1];
      state[1] = state[3];
      state[3] = x;
    }

    if ((round & 3) == 0) { // add constant: pattern c...c...c... etc.
      state[0] ^= (0x9e377900 | round);
    }
  }
}

C中的可推文版本

这可能不是最小的可用实现,但是我们希望有一个C标准版本(因此没有UB,并且在库中“可用”)。

#include<stdint.h>
#define P(V,W)x=V,V=W,W=x
void gimli(uint32_t*S){for(long r=24,c,x,y,z;r;--r%2?P(*S,S[1+y/2]),P(S[3],S[2-y/2]):0,*S^=y?0:0x9e377901+r)for(c=4;c--;y=r%4)x=S[c]<<24|S[c]>>8,y=S[c+4]<<9|S[c+4]>>23,z=S[c+8],S[c]=z^y^8*(x&y),S[c+4]=y^x^2*(x|z),S[c+8]=x^2*z^4*(y&z);}

测试向量

以下输入由生成

for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;

和“印刷”值

for (i = 0;i < 12;++i) {
  printf("%08x ",x[i])
  if (i % 4 == 3) printf("\n");
}

从而:

00000000 9e3779ba 3c6ef37a daa66d46 
78dde724 1715611a b54cdb2e 53845566 
f1bbcfc8 8ff34a5a 2e2ac522 cc624026 

应该返回:

ba11c85a 91bad119 380ce880 d24c2c68 
3eceffea 277a921c 4f73a0bd da5a9cd8 
84b673f0 34e52ff7 9e2bef49 f41bb8d6 

3
一条推文是140个字符,而不是280个字符
Stan Strum

1
我知道,这就是为什么它适合2;)twitter.com/TweetGimli的原因
起搏

10
“嘿帮派,请用其他语言免费实施我的程序,这样我就不必这样做”
jstnthms

hahaha Nah我已经在Python中拥有它,并且可以在Java,C#,JS中轻松实现。它更有趣。:)
起搏

5
参考代码的网站上有一个关键的错误,-round而不是--round手段,它永远不会终止。--代码中可能不建议转换为破折号:)
orlp

Answers:


3

果酱(114个字符)

{24{[4/z{[8ZT].{8\#*G8#:Mmd+}__)2*\+.^W%\[_~;&8*\~@1$|2*@@&4*].^Mf%}%z([7TGT]R=4e!=\f=(2654435608R-_4%!*^\@]e_}fR}

这是一个匿名块(函数):如果要命名,G则添加append :G。在CJam中,分配的名称只能是单个大写字母。可以在其中添加注释,e# Gimli in CJam并在单个推文中保留字符。

在线测试

解剖

{                                e# Define a block
  24{                            e# For R=0 to 23...
    [                            e#   Collect values in an array
      4/z                        e#     Transpose to columns
      {                          e#     Map over each column
        [8ZT].{8\#*G8#:Mmd+}     e#       Rotations, giving [x y z]
        __)2*\+.^W%\             e#       => [y^z x^y x^z*2] [x y z]
        [_~;&8*\~@1$|2*@@&4*].^  e#       => [x' y' z']
        Mf%                      e#       Map out any bits which overflowed
      }%
      z                          e#    Transpose to rows
      ([7TGT]R=4e!=\f=           e#    Permute first row
      (2654435608R-_4%!*^        e#    Apply round constant to first element
      \@                         e#    Put the parts in the right order
    ]e_                          e#  Finish collecting in array and flatten
  }fR
}

有一段时间我对输出不在十六进制中(在在线测试中)的事实感到不满。:)
起搏

15

C(gcc),237个字节

#define P(a,l)x=a;a=S[c=l>>r%4*2&3];S[c]=x;
r,c,x,y,z;G(unsigned*S){
for(r=24;r;*S^=r--%4?0:0x9e377901+r){
for(c=4;c--;*S++=z^y^8*(x&y))
x=*S<<24|*S>>8,y=S[4]<<9|S[4]>>23,z=S[8],S[8]=x^2*z^4*(y&z),S[4]=y^x^2*(x|z);
S-=4;P(*S,33)P(S[3],222)}}

我可能通过交换方法获得了字节,但是它太可爱了,无法使用。


丢失还是获得?
HyperNeutrino

@HyperNeutrino获得,使我成为失败者:)
orlp

好的:P有意义:P:P
HyperNeutrino

当然,这仍然是一个改进,但是使用unsigned代替uint32_t(并且OP的代码使用有点欺骗long)有点欺骗,因为密码背后的想法是它具有高度的可移植性。(实际上,从根本上讲,这仅节省了8个字节)。
彼得·泰勒

1
@PeterTaylor即使我的代码相似,我也不是真正在与OP的代码竞争。我在PPCG的规则下工作,在该规则下,它必须至少与平台上的实现兼容,并且必须与gcc32位或64位Intel CPU(可能还有更多)兼容。
orlp

4

C,使用uint32_t 268个字符(268个字节)

注意:由于原始代码使用<stdint.h>和类型S为as uint32_t *,我认为使用long欺骗是要以可移植性为代价来获取280个字符,这是uint32_t首先使用它的原因。如果为了比较的公平起见,我们需要一致地使用uint32_t和显式签名void gimli(uint32_t *),则原始代码实际上是284个字符,而orlp的代码是276个字符。

#include<stdint.h>
#define R(V)x=S[V],S[V]=S[V^y],S[V^y]=x,
void gimli(uint32_t*S){for(uint32_t r=24,x,y,z,*T;r--;y=72>>r%4*2&3,R(0)R(3)*S^=y&1?0x9e377901+r:0)for(T=S+4;T-->S;*T=z^y^8*(x&y),T[4]=y^x^2*(x|z),T[8]=x^2*z^4*(y&z))x=*T<<24|*T>>8,y=T[4]<<9|T[4]>>23,z=T[8];}

可以将其分为两个带有继续标记的tweet

#include<stdint.h>
#define R(V)x=S[V],S[V]=S[V^y],S[V^y]=x,
void gimli(uint32_t*S){for(uint32_t r=24,x,y,z,*T;r--;y=72>>r%4*2&3,R(0)R(3)// 1

*S^=y&1?0x9e377901+r:0)for(T=S+4;T-->S;*T=z^y^8*(x&y),T[4]=y^x^2*(x|z),T[8]=x^2*z^4*(y&z))x=*T<<24|*T>>8,y=T[4]<<9|T[4]>>23,z=T[8];}// 2/2

使用long我的版本是安全的(相对于便携性),因为长的最小尺寸是标准的32位(相对int)。的旋转xy投进入前完成long的任务,使他们的安全(如签署值右移是CC相关)。返回uint32_t* S)时的转换会去除高位,并使我们处于正确的状态:)。
起搏

2

的Java(OpenJDK的8) 351 343 339 320 318 247 + 56个字节

只需从参考的接近1:1的端口开始打高尔夫球。

void f(int[]x,int y,int z){int q=x[y];x[y]=x[z];x[z]=q;}

s->{for(int r=24,c,x,y,z;r>0;--r){for(c=0;c<4;x=s[c]<<24|s[c]>>>8,y=s[4+c]<<9|s[4+c]>>>23,z=s[8+c],s[8+c]=x^z<<1^(y&z)<<2,s[4+c]=y^x^(x|z)<<1,s[c++]=z^y^(x&y)<<3);if((r&3)==2){f(s,0,2);f(s,1,3);}if((r&3)<1){f(s,0,1);f(s,2,3);s[0]^=0x9e377900|r;}}}

在线尝试!


1
为什么要使用Integer?o_O由于您未使用任何Integer方法,因此这里没有理由不使用ints
OlivierGrégoire17年

@OlivierGrégoire我认为尝试Integer.divideUnsigned只是我的残余,但我意识到我可以拥有>>>
Roberto Graham

s[0]^=(0x9e377900|r);(最后)-您不能删除多余的括号吗?
Clashsoft

与相同s[4+c]>>>(23)
Clashsoft

1
你可以少得多的变化,并得到300: void P(int[]S,int a,int b){int x=S[a];S[a]=S[b];S[b]=x;}void gimli(int[]S){for(int r=24,c,x,y,z;r>0;S[0]^=y<1?0x9e377901+r:0){for(c=4;c-->0;){x=S[c]<<24|S[c]>>>8;y=S[c+4]<<9|S[c+4]>>>23;z=S[c+8];S[c]=z^y^8*(x&y);S[c+4]=y^x^2*(x|z);S[c+8]=x^2*z^4*(y&z);}y=r%4;if(--r%2>0){P(S,0,1+y/2);P(S,3,2-y/2);}}}。我基本上已经进行了最小的更改,使其可以编译。Java的优先级规则与C的区别不大。
彼得·泰勒

2

JavaScript(ES6),231个字节

s=>{for(r=25;--r;[a,b,c,d,...e]=s,s=r&1?s:r&2?[c,d,a,b,...e]:[b,a,d,c,...e],s[0]^=r&3?0:0x9e377900|r)for(c=4;c--;x=s[c]<<24|s[c]>>>8,y=s[j=c+4]<<9|s[j]>>>23,z=s[c+8],s[c+8]=x^z*2^(y&z)*4,s[j]=y^x^(x|z)*2,s[c]=z^y^(x&y)*8);return s}

演示版


0

32位x86汇编器(112字节)

(__cdecl调用约定)

            pusha
            mov     ecx, 9E377918h
    loc_6:  mov     esi, [esp+24h]
            push    esi
            push    4
            pop     ebx
    loc_E:  lodsd
            ror     eax, 8
            mov     ebp, [esi+0Ch]
            rol     ebp, 9
            mov     edx, [esi+1Ch]
            push    eax
            push    ebp
            lea     edi, [edx+edx]
            and     ebp, edx
            shl     ebp, 2
            xor     edi, ebp
            xor     eax, edi
            mov     [esi+1Ch], eax
            pop     ebp
            pop     eax
            push    eax
            push    ebp
            xor     ebp, eax
            or      eax, edx
            shl     eax, 1
            xor     ebp, eax
            mov     [esi+0Ch], ebp
            pop     ebp
            pop     eax
            xor     edx, ebp
            and     eax, ebp
            shl     eax, 3
            xor     edx, eax
            push    edx
            dec     ebx
            jnz     short loc_E
            pop     esi
            pop     ebp
            pop     ebx
            pop     eax
            pop     edi
            mov     dl, cl
            and     dl, 3
            jnz     short loc_5B
            xchg    eax, ebx
            xchg    esi, ebp
            xor     eax, ecx
    loc_5B: cmp     dl, 2
            jnz     short loc_63
            xchg    eax, ebp
            xchg    esi, ebx
    loc_63: stosd
            xchg    eax, ebx
            stosd
            xchg    eax, ebp
            stosd
            xchg    eax, esi
            stosd
            dec     cl
            jnz     short loc_6
            popa
            retn

可发布的版本(z85格式的Base85编码):

v7vb1h> C} HbQuA91y51A:oWYw48G)?I = H /] rGf9Na> sA.DWu06 {6f#TEC ^ CM:#IeA-cstx7:>!VfVf#u * YB&mP(tuCl * + 7eENBP)$ :) Lh!k } t $ ^ wM51j%LDf $ HMAg2bB ^ MQP
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.