Rijndael'in S-box'ını uygulayın


15

Rijndael'in S-kutusu , AES şifreleme ve şifre çözme işlemlerinde sıkça kullanılan bir işlemdir . Genellikle 256 baytlık arama tablosu olarak uygulanır. Bu hızlı, ancak kodunuzda 256 baytlık bir arama tablosu numaralandırmanız gerektiği anlamına gelir. Bahse girerim bu matematiğin altında yatan matematik yapısı göz önüne alındığında daha az kodla bunu yapabilirdi.

En sevdiğiniz dilde Rijndael'in S-kutusunu uygulayan bir işlev yazın. En kısa kod kazanır.


1
Elde edilen fonksiyon sabit zaman ise (yani verilere bağlı kod yolları veya dizi erişimi veya dilinizin desteklediği her ne olursa olsun) bonus puanları (benden oylar).
Paŭlo Ebermann

@ PaŭloEbermann dizi erişimleri birçok dilde sabit sürelidir (bir işaretçiye (ölçeklendirilmiş) değer ekler ve kayıttan çıkarır, bu yüzden bir arama tablosu çok hızlıdır)
cırcır ucube

@ratchetfreak Dizi erişimleri O (1), ancak gerçek erişim süresi, örneğin AES'de yan kanal saldırılarına yol açan önbellek isabetlerine veya özlemlerine bağlıdır.
Paŭlo Ebermann

@ PaŭloEbermann, ancak bir arama tablosunu doldurmak için daha kısa bir kod kullanabilirsiniz, bu daha sonra bir bellek sayfasının altına sığacaktır.
Peter Taylor

@ PaŭloEbermann ve 256 uzunluklu tablo kod boyunca saklanırsa (derleme zamanında oluşturulan numara olarak) neredeyse bir önbellek isabetini garanti ettiniz
mandal ucube

Yanıtlar:


6

Ruby, 161 karakter

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}

Çıktıyı kontrol etmek için aşağıdaki kodu tablo biçiminde yazdırmak için kullanabilirsiniz:

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

7

GolfScript, 60 karakter

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

Bu kod, Sbayt alan ve Rijndael S-box'ı uygulayan adlı bir işlevi tanımlar . (Ayrıca rbirkaç karakter kaydetmek için adlandırılmış bir dahili yardımcı işlevi kullanır .)

Bu uygulama, Thomas Pornin tarafından önerildiği gibi GF (2 8 ) tersini hesaplamak için bir logaritma tablosu kullanır . Birkaç karakter kaydetmek için, tüm logaritma tablosu her girdi baytı için yeniden hesaplanır; buna rağmen ve GolfScript genel olarak çok yavaş bir dil olmasına rağmen, bu kod eski dizüstü bilgisayarımdaki bir baytı işlemek için sadece 10 ms sürüyor. Logaritma tablosunu (as ) önceden hesaplamak, bayt başına yaklaşık 0,5 ms'ye kadar hızlar ve üç karakter daha mütevazı bir maliyetle:L

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

Kolaylık için, Syukarıda tanımlandığı gibi, S kutusunun tamamını Wikipedia'da olduğu gibi onaltılık olarak hesaplamak ve yazdırmak için çağıran basit bir test takımı :

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

Bu kodu çevrimiçi deneyin.

(Çevrimiçi demo, çok fazla zaman almamak için logaritma tablosunu önceden hesaplar. Buna rağmen, çevrimiçi GolfScript sitesi bazen rastgele zaman aşımına uğrayabilir; bu, siteyle ilgili bilinen bir sorundur ve yeniden yükleme genellikle bunu düzeltir.)

Açıklama:

Logaritma tablosu hesaplamasıyla ve özellikle yardımcı işleviyle başlayalım r:

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

Bu işlev yığın üzerinde iki giriş alır: bir bayt ve bir azaltma bit maskesi (256 ve 511 arasında bir sabit). Girdi baytını çoğaltır, kopyayı 2 ile çarpar ve sonuç 255'i aşarsa, 256'nın altına geri getirmek için bitmask ile XORs yapar.

Günlük tablosu oluşturma kodu içinde, işlev r, 283 = 0x11b azaltma bitmaskiyle çağrılır ( Rijndael GF'ye karşılık gelir (2 8 ) azaltma polinomu x 8 + x 4 + x 3 + x + 1'e karşılık gelir) ile çağrılır ve sonuç XORed olur orijinal bayt ile etkin bir şekilde Rijndael sonlu alanında 3 (= x + 1, bir polinom olarak) ile çarparak . Bu çarpma, bayt 1'den başlayarak 255 kez tekrarlanır ve sonuçlar (artı bir başlangıç ​​sıfır baytı), Lşuna benzeyen 257 elemanlı bir diziye toplanır (orta kısım atlanır):

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

257 elementin olmasının nedeni, yukarıda belirtilen 0 ve 1'in iki kez meydana gelmesiyle, herhangi bir baytın modüler tersini, sadece bu dizideki (sıfır temelli) dizinine bakarak, reddederek ve bakarak bulabiliriz. aynı dizideki reddedilen dizindeki baytı yukarı kaldırın. (GolfScript'te, diğer birçok programlama dilinde olduğu gibi, negatif dizi dizinleri, dizinin sonundan geriye doğru sayar.) Aslında, L?~)L=işlevin başındaki kod tam olarak budur S.

Kodun kalan yardımcı işlevini çağırır razalma ile dört kez 257 = 2 bit maskesi 8 ters giriş bayt dört bit döndürülmüş kopya oluşturmak için + 1. Bunların tümü 99 = 0x63 sabiti ile birlikte bir dizi halinde toplanır ve nihai çıktıyı üretmek için birlikte XORed edilir.


7

x86-64 Makine kodu - 23 22 20 19 bayt

AES-NI komut setini kullanır

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 çağrı kurallarını kullanarak bir bayt alır ve bir bayt verir. ShiftRowsİlk baytı etkilemediği için tersine çevirmek gerekli değildir.


2
Bir kez, x86_64 bir mathematica çeker ve bunun için bir yerleşik vardır.
moonheart08

6

Tablo, logaritmalar kullanılarak sonlu GF (256) alanındaki ters hesaplanmadan oluşturulabilir. Şöyle görünecektir (Java kodu, intimzalı türle ilgili sorunları önlemek için kullanma 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;
}

Fikir, 3'ün GF (256) * 'nın çarpımsal bir jeneratörü olduğu. Tablo t[]şekildedir t[x]3 eşittir x ; 3 255 = 1 olduğundan, 1 / (3 x ) = 3 255-x elde ederiz .


olmamalı 0x1B(onaltılık harflerle bir 1) yerine0x11B
cırcır ucube

@ ratchetfreak: hayır, 0x11B olmalı (denedim). intTip Java 32 bit olduğu; Daha yüksek biti iptal etmeliyim.
Thomas Pornin

ah bunu fark etmedi
cırcır ucube

Bu, 4. satırdaki >> yerine >>> mı?
Joe Z.

@JoeZeng: Her ikisi de işe yarar. Java'da '>>>' "imzasız vardiya", ">>" ise "imzalı vardiya" dır. İşaret bitini nasıl ele aldıklarına göre farklılık gösterirler. Burada, değerler hiçbir zaman işaret bitinin sıfır olmaması için yeterince geniş olmayacaktır, bu yüzden gerçek bir fark yaratmaz.
Thomas Pornin

6

GolfScript (82 karakter)

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

Global değişkenleri kullanır Ave Bişlevi global değişken olarak oluşturur S.

Galois inversiyonu kaba kuvvettir; mulİnversiyon sonrası afin dönüşümü için tekrar kullanılabilen ayrı bir fonksiyona sahip olmayı denedim, ancak farklı taşma davranışı nedeniyle daha pahalı olduğu ortaya çıktı.

Bu, çevrimiçi bir demo için çok yavaştır - tablonun ilk iki satırında bile zaman aşımına uğrar.


Benimki daha hızlı (ve daha kısa;). Yine de +1.
Ilmari Karonen

4

Python, 176 karakter

Bu cevap Paŭlo Ebermann'ın fonksiyonu sürekli hale getirme hakkındaki yorum sorusu içindir. Bu kod faturaya uyar.

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

Çarpma süresinin sabit olması platforma bağlıdır (32 bit platformlarda bile, örneğin ARM Cortex M0). Bu ilgili soruya
fgrieu

1
@fgrieu Elbette, ancak bunların tümü sabitler tarafından çarpmalardır ve bunlar vardiyalar ve eklemeler kullanılarak sabit zamanda kolayca uygulanabilir.
Keith Randall

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;

}

Bu derleme zamanında arama tablosu oluşturabilir, ubyte genel bir param yaparak biraz tasarruf edebilirsiniz

düzenlemek doğrudan ubyteiçin ubyte, dizi aramaları olmadan dallanma ve tam unrollable döngüler

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 arama tablosunu oluşturmak için @Thomas 'algo kullanılır


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.