实施Rijndael的S-box


15

Rijndael的S-boxAES加密和解密中经常使用的操作。它通常实现为256字节查找表。这很快,但是这意味着您需要在代码中枚举256字节的查找表。我敢打赌,鉴于基本的数学结构,这种人群中的某人可以用更少的代码来完成这项工作。

用您喜欢的语言编写一个实现Rijndael的S-box的函数。最短的代码获胜。


1
如果生成的函数是恒定时间的(例如,没有数据相关的代码路径或数组访问或您的语言支持的任何内容),则可获得加分(由我投票)。
圣保罗Ebermann

@PaŭloEbermann数组访问在许多语言中都是固定时间的(它向指针添加了(缩放的)值并取消引用,这就是为什么查找表是如此之快的原因)
棘手问题

@ratchetfreak数组访问为O(1),但是实际访问时间取决于例如高速缓存的命中或未命中,这会导致对AES的侧通道攻击。
圣保罗Ebermann

@PaŭloEbermann,但您可以使用较短的代码来填充查找表,然后该表将很好地位于内存页面的下方。
彼得·泰勒

@PaŭloEbermann,如果256长度的表是沿着代码存储的(作为在编译时生成的枚举),则几乎可以保证出现高速缓存命中
棘手怪胎

Answers:


6

Ruby,161个字符

R=0..255
S=R.map{|t|t=b=R.select{|y|x=t;z=0;8.times{z^=y*(x&1);x/=2;y*=2};r=283<<8;8.times{r/=2;z^r<z/2&&z^=r};z==1}[0]||0;4.times{|r|t^=b<<1+r^b>>4+r};t&255^99}

为了检查输出,您可以使用以下代码以表格形式打印输出:

S.map{|x|"%02x"%x}.each_slice(16){|l|puts l*' '}

7

GolfScript,60个字符

{[[0 1{.283{1$2*.255>@*^}:r~^}255*].@?~)={257r}4*99]{^}*}:S;

该代码定义了一个名为name的函数S,该函数接收一个字节并将Rijndael S-box应用于该字节。(它也使用一个名为的内部帮助器函数r来保存一些字符。)

Thomas Pornin建议的,此实现使用对数表来计算GF(2 8)逆。为了节省一些字符,将为每个输入字节重新计算整个对数表。即便如此,尽管GolfScript通常是一种非常慢的语言,但是此代码仅用约10毫秒即可处理旧笔记本电脑上的一个字节。预先计算对数表(as )可以将其提高至每字节约0.5毫秒,而仅需花费三个字符即可:L

[0 1{.283{1$2*.255>@*^}:r~^}255*]:L;{[L?~)L={257r}4*99]{^}*}:S;

为了方便起见,下面是一个简单的测试工具,它调用S如上定义的函数,以像Wikipedia上那样以十六进制形式计算和打印出整个S-box :

"0123456789abcdef"1/:h; 256, {S .16/h= \16%h= " "++ }% 16/ n*

在线尝试此代码。

(在线演示程序会预先计算对数表,以避免花费过多时间。即使如此,在线GolfScript网站有时可能会随机超时;这是该网站的已知问题,通常可以通过重新加载来解决。)

说明:

让我们从对数表计算开始,特别是从helper函数开始r

{1$2*.255>@*^}:r

此函数在堆栈上有两个输入:一个字节和一个缩减位掩码(介于256和511之间的常数)。它复制输入字节,将副本乘以2,如果结果超过255,则将其与位掩码进行XOR运算,以使其回到256以下。

在日志表生成代码中,r使用约简位掩码283 = 0x11b(对应于Rijndael GF(2 8)约简多项式 x 8 + x 4 + x 3 + x + 1)调用该函数,并对结果进行异或运算与原始字节有效地乘以Rijndael有限域中的3(= x + 1,作为多项式)。从字节1开始,此乘法重复255次,结果(加上一个初始零字节)被收集到一个257个元素的数组中L,看起来像这样(中间部分省略):

[0 1 3 5 15 17 51 85 255 26 46 ... 180 199 82 246 1]

之所以有257个元素,是因为在0和1出现两次的情况下,我们可以简单地通过在此数组中查找(从零开始的)索引,对其求反,然后查找来查找任何给定字节的模逆。在同一数组中取反索引处的字节。(在GolfScript中,就像在许多其他编程语言中一样,负数组索引从数组末尾开始倒数。)确实,这正是L?~)L=函数开头的代码S所做的。

其余代码r用缩减位掩码257 = 2 8 +1 调用了辅助函数四次,以创建反相输入字节的四个位旋转副本。这些都与常量99 = 0x63一起被收集到一个数组中,并进行异或运算以产生最终输出。


7

x86-64机器码-23 22 20 19字节

使用AES-NI指令集

66 0F 6E C1          movd        xmm0,ecx
66 0F 38 DD C1       aesenclast  xmm0,xmm1
0F 57 C1             xorps       xmm0,xmm1  
66 0F 3A 14 C0 00    pextrb      eax,xmm0,0
C3                   ret

使用Windows调用约定,接收一个字节并输出一个字节。不必反转,ShiftRows因为它不会影响第一个字节。


2
x86_64一次只提取了一个matha,并为此内置了一个内置函数。
moonheart08

6

通过使用对数,可以在不计算有限域GF(256)中的逆的情况下生成表。看起来像这样(Java代码,int用于避免带符号的byte类型出现问题):

int[] t = new int[256];
for (int i = 0, x = 1; i < 256; i ++) {
    t[i] = x;
    x ^= (x << 1) ^ ((x >>> 7) * 0x11B);
}
int[] S = new int[256];
S[0] = 0x63;
for (int i = 0; i < 255; i ++) {
    int x = t[255 - i];
    x |= x << 8;
    x ^= (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7);
    S[t[i]] = (x ^ 0x63) & 0xFF;
}

这个想法是3是GF(256)*的乘法生成器。该表t[]是这样的:t[x]等于3 X ; 因为3 255 = 1,我们得到1 /(3 x)= 3 255-x


应该不是0x1B(十六进制文字中的1),而不是0x11B
棘轮怪胎

@ratchetfreak:不,它必须是0x11B(我尝试过)。intJava中的类型为32位;我必须取消较高的位。
Thomas Pornin 2011年

啊没有意识到
棘轮怪胎

这是>>>而不是第4行中的>>吗?
Joe Z.

@JoeZeng:两者都会起作用。在Java中,“ >>>”是“无符号移位”,“ >>”是“有符号移位”。它们在如何处理符号位方面有所不同。在这里,值永远不会足够宽,以至于符号位不为零,因此没有任何实际区别。
Thomas Pornin

6

GolfScript(82个字符)

{256:B,{0\2${@1$3$1&*^@2/@2*.B/283*^}8*;;1=},\+0=B)*:A.2*^4A*^8A*^128/A^99^B(&}:S;

使用全局变量AB,并将函数创建为全局变量S

Galois反演是蛮力的。我尝试过使用一个单独的mul函数,该函数可以在反演后仿射变换中重复使用,但是由于溢出行为不同,结果证明它更昂贵。

对于在线演示而言,这太慢了-即使在表的前两行也会超时。


我的更快(更短;)。还是+1。
Ilmari Karonen 2015年

4

Python,176个字符

这个答案是针对PaŭloEbermann关于使函数恒定时间的注释问题。此代码符合要求。

def S(x):
 i=0
 for y in range(256):
  p,a,b=0,x,y
  for j in range(8):p^=b%2*a;a*=2;a^=a/256*283;b/=2
  m=(p^1)-1>>8;i=y&m|i&~m
 i|=i*256;return(i^i/16^i/32^i/64^i/128^99)&255

恒定时间乘法取决于平台(即使在32位平台上,例如ARM Cortex M0)。看到这个相关问题
fgrieu

1
@fgrieu可以,但是这些都是常数的乘积,可以很容易地在恒定时间内使用移位和加法实现。
基思·兰德尔

2

d

ubyte[256] getLookup(){

    ubyte[256] t=void;
    foreach(i;0..256){
        t[i] = x;
        x ^= (x << 1) ^ ((x >>> 7) * 0x1B);
    }
    ubyte[256] S=void;
    S[0] = 0x63;
    foreach(i;0..255){
        int x = t[255 - i];
        x |= x << 8;
        x ^= (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7);
        S[t[i]] = cast(ubyte)(x & 0xFF) ^ 0x63 ;
    }
    return S;

}

这可以在编译时生成查找表,我可以通过将ubyte用作通用参数来节省一些

直接编辑ubyteubyte无数组查找,无分支和完全展开的循环

B[256] S(B:ubyte)(B i){
    B mulInv(B x){
        B r;
        foreach(i;0..256){
            B p=0,h,a=i,b=x;
            foreach(c;0..8){
                p^=(b&1)*a;
                h=a>>>7;
                a<<=1;
                a^=h*0x1b;//h is 0 or 1
                b>>=1;
            }
            if(p==1)r=i;//happens 1 or less times over 256 iterations
        }
        return r;
    }
    B s= x=mulInv(i);
    foreach(j,0..4){
        x^=(s=s<<1|(s>>>7));
    }
    return x^99;
}

edit2使用@Thomas算法创建查找表


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.