C: AES FIPS-197 SubBytes tablosunu sabit zaman koduyla değiştirin


17

In FIPS-197 ( Gelişmiş Şifreleme Standardı AES olarak bilinen), bu ağır kullanımı yapıldığı SubByteshalinde uygulanması mümkündür,

unsigned char SubBytes(unsigned char x) {
static const unsigned char t[256] = {
  0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
  0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
  0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
  0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
  0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
  0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
  0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
  0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
  0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
  0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
  0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
  0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
  0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
  0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
  0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
  0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16};
return t[x];}

Bu işlev keyfi değildir; bir Galois Alanındaki bir inversiyon ve ardından bir afin dönüşümden oluşan geri dönüşümlü bir haritadır. Tüm detaylar FIPS-197 bölüm 5.1.1 veya burada bölüm 4.2.1'de (biraz farklı bir isim altında).

Tablo olarak uygulama ile ilgili bir sorun, önbellek zamanlama saldırılarına sözde açılmış olmasıdır .

Böylece göreviniz, SubBytes()sabit zamanlı davranış sergileyen yukarıdaki işlev için tam bir yedek oluşturmaktır ; Biz hiçbir şey girişine bağlı olarak zaman Bu durumda varsayıyoruz xarasında SubBytesya kullanılır:

  • dizi dizini olarak,
  • Kontrol işlene olarak if, while, for, case, veya operatör ?:;
  • operatörlerin herhangi işlenen olarak &&, ||, !, ==, !=, <, >, <=, >=, *, /, %;
  • Operatörlerin sağ terim olarak >>, <<, *=, /=, %=, <<=, >>=.

Kazanan tekli operatörler için 5 ağırlık ile giriş bağımlı veri yolu yürütülen işletmeci sayısında elde edilen en düşük maliyetle biri olacak -ve ~de için olduğu kadar <<1, >>1, +1, -1; diğer tüm operatörler için ağırlık 7, diğer sayılarla kaymalar veya diğer sabitler ekler / alçalır (tür yayınları ve promosyonlar ücretsizdir). Prensip olarak, bu maliyet döngüler (varsa) ve girişten bağımsız olarak değiştirilerek değiştirilmez x. Bir kravat kırıcı olarak, boşluk ve yorumları kaldırdıktan sonra en kısa kod ile cevap kazanır.

2013 yılında, UTC'de olabildiğince erken bir cevap vermeyi planlıyorum. Bildiğim dillerdeki cevapları, bunları boyut için optimize edilmeyen C'ye doğrudan bir çeviri olarak sıralayacağım.

Tercih edilen operatörlerin, serbest atmaların ve promosyonların ilk ihmali +1ve -1büyüklük ve boyut sıralamasından dolayı özür dileriz . *Hem tekli hem de çarpma olarak yasaklandığını unutmayın .


1
Aramaların ücretsiz olduğunu belirtmek gerekir, çünkü sabit olarak bunları satır içine alabilirsiniz.
Peter Taylor

"2013 yılının başlarında, UTC" - tarih saat diliminden daha ilginç olmaz mıydı?
Paŭlo Ebermann

@ PaŭloEbermann: Niyetim şimdi net olmalı.
fgrieu

Yanıtlar:


13

Puan: 940 933 926 910, saha kule yaklaşımı

public class SBox2
{
    public static void main(String[] args)
    {
        for (int i = 0; i < 256; i++) {
            int s = SubBytes(i);
            System.out.format("%02x  ", s);
            if (i % 16 == 15) System.out.println();
        }
    }

    private static int SubBytes(int x) {
        int fwd;
        fwd  = 0x010001 & -(x & 1); x >>= 1; //   7+5+7+5+ | 24+
        fwd ^= 0x1d010f & -(x & 1); x >>= 1; // 7+7+5+7+5+ | 31+
        fwd ^= 0x4f020b & -(x & 1); x >>= 1; // 7+7+5+7+5+ | 31+
        fwd ^= 0x450201 & -(x & 1); x >>= 1; // 7+7+5+7+5+ | 31+
        fwd ^= 0xce080d & -(x & 1); x >>= 1; // 7+7+5+7+5+ | 31+
        fwd ^= 0xa20f0f & -(x & 1); x >>= 1; // 7+7+5+7+5+ | 31+
        fwd ^= 0xc60805 & -(x & 1); x >>= 1; // 7+7+5+7+5+ | 31+
        fwd ^= 0x60070e & -x;                // 7+7+5+     | 19+

        // Running total so far: 229

        int p1;
        {
            int ma = fwd;
            int mb = fwd >> 16;         // 7+         | 7+
            p1  = ma & -(mb&1); ma<<=1; //   7+5+7+5+ | 24+
            p1 ^= ma & -(mb&2); ma<<=1; // 7+7+5+7+5+ | 31+
            p1 ^= ma & -(mb&4); ma<<=1; // 7+7+5+7+5+ | 31+
            p1 ^= ma & -(mb&8);         // 7+7+5+7+   | 26+
            int t = p1 >> 3;            // 7+         | 7+
            p1 ^= (t >> 1) ^ (t & 0xe); // 7+5+7+7+   | 26+
        }

        // Running total so far: 229 + 152 = 381

        int y3, y2, y1, y0;
        {
            int Kinv = (fwd >> 20) ^ p1;     // 7+7+
            int w0 = Kinv & 1; Kinv >>= 1;   // 7+5+
            int w1 = Kinv & 1; Kinv >>= 1;   // 7+5+
            int w2 = Kinv & 1; Kinv >>= 1;   // 7+5+
            int w3 = Kinv & 1;               // 7+

            int t0 = w1 ^ w0 ^ (w2 & w3);      // 7+7+7+
            int t1 = w2 ^ (w0 | w3);           // 7+7+
            int t2 = t0 ^ t1;                  // 7+

            y3 = t2 ^ (t1 & (w1 & w3));        // 7+7+7+
            y2 = t0 ^ (w0 | t2);               // 7+7+
            y1 = w0 ^ w3 ^ (t1 & t0);          // 7+7+7+
            y0 = w3 ^ (t0 | (w1 ^ (w0 | w2))); // 7+7+7+7


        }

        // Running total so far: 381 + 24*7 + 3*5 = 564

        int p2;
        {
            int ma = fwd;
            p2  = ma & -y0; ma<<=1;       //   7+5+5+ | 17+
            p2 ^= ma & -y1; ma<<=1;       // 7+7+5+5+ | 24+
            p2 ^= ma & -y2; ma<<=1;       // 7+7+5+5+ | 24+
            p2 ^= ma & -y3;               // 7+7+5+   | 19+
            int t = p2 >> 3;              // 7+       | 7+
            p2 ^= (t >> 1) ^ (t & 0xe0e); // 7+5+7+7+ | 26
        }

        // Running total so far: 564 + 117 = 681

        int inv8;
        inv8  =  31 & -(p2 & 1);           //   7+5+7+   | 19+
        inv8 ^= 178 & -(p2 & 2); p2 >>= 2; // 7+7+5+7+7+ | 33+
        inv8 ^= 171 & -(p2 & 1);           // 7+7+5+7+   | 26+
        inv8 ^=  54 & -(p2 & 2); p2 >>= 6; // 7+7+5+7+7+ | 33+
        inv8 ^= 188 & -(p2 & 1);           // 7+7+5+7+   | 26+
        inv8 ^=  76 & -(p2 & 2); p2 >>= 2; // 7+7+5+7+7+ | 33+
        inv8 ^= 127 & -(p2 & 1);           // 7+7+5+7+   | 26+
        inv8 ^= 222 & -(p2 & 2);           // 7+7+5+7    | 26+

        return inv8 ^ 0x63;                // 7+         | 7+

        // Grand total: 681 + 229 = 910
    }
}

Yapı esas olarak Boyar ve Peralta'nın uygulamasıyla aynıdır - GF'deki (2 ^ 8) ters çevrmeyi GF'deki (2 ^ 4) tersine çevirmek, doğrusal bir prologa, doğrusal olmayan bir gövdeye ve doğrusal bir epiloga ayırmak, ve ayrı ayrı en aza indirin. Bit çıkarımı için bazı cezalar ödüyorum, ancak paralel olarak işlemler yapabilmekle telafi ediyorum (parçalarının makul bir şekilde doldurulmasıyla fwd). Daha ayrıntılı olarak ...

Arka fon

Sorun açıklamasında belirtildiği gibi S-kutusu, Galois alanı GF'nin (2 ^ 8) belirli bir uygulamasında bir ters çevirme ve ardından bir afin dönüşümden oluşur. Her ikisinin de ne anlama geldiğini biliyorsanız, bu bölümü atlayın.

Afin (veya lineer) dönüşüm, ve f(x)'ye saygı duyan bir fonksiyondur .f(x + y) = f(x) + f(y)f(a*x) = a*f(x)

Alan, bir dizi Fdiyeceğiz iki özel unsurlar ile elementlerin 0ve 1diyeceğiz ve iki operatör, +ve *çeşitli özellikleri saygı, hangi. Bu bölümde, farz x, yve zunsurlarıdır F.

  • Unsurları Faltında form, değişmeli grubu +ile 0kimlik olarak: örneğin, x + ybir elemanıdır F; x + 0 = 0 + x = x; Her xbir karşılık gelen sahip -xolacak şekilde x + (-x) = (-x) + x = 0; x + (y + z) = (x + y) + z; ve x + y= y + x.
  • Unsurları Fdiğerinden daha 0altında şeklinde bir değişmeli grup *ile 1kimlik olarak.
  • Çarpma ek üzerinde dağıtır: x * (y + z) = (x * y) + (x * z).

Sonlu cisimler üzerinde oldukça ciddi sınırlamalar olduğu ortaya çıkıyor:

  • Asal bir güç olan bir dizi unsura sahip olmalıdırlar.
  • Aynı büyüklükteki diğer tüm sonlu alanlarla izomorfiktirler (yani, belirli bir büyüklükte gerçekten sadece bir sonlu alan vardır ve diğerleri sadece yeniden etiketleme anlamına gelir; bu alanı GF (p ^ k) polarak adlandırın, asal ve kgüç olarak adlandırın) .
  • F\{0}Altındaki çarpımsal grup *sikliktir; yani gher elementin gücü olacak şekilde en az bir element vardır g.
  • 1'den büyük güçler kiçin asal düzen alanının tek değişkenli polinomları olarak bir temsil vardır . Örneğin, GF (2 ^ 8), xGF (2) üzerindeki polinomlar açısından bir temsile sahiptir . Aslında genellikle birden fazla temsil vardır. GF'yi düşünün x^7 * x(2 ^ 8); bazı 7 numaralı polinomlara eşdeğer olmalıdır, ama hangisi? Doğru yapıyı veren birçok seçenek var; AES yapmayı seçer x^8 = x^4 + x^3 + x + 1(çalışan sözlükbilimsel olarak en küçük polinom).

Öyleyse, GF'nin (2 ^ 8) bu özel sunumunda tersi nasıl hesaplayabiliriz? Doğrudan mücadele etmek çok tıknaz bir sorun, bu yüzden onu yıkmamız gerekiyor.

Alan kuleleri: GF (2 ^ 8) cinsinden GF'yi (2 ^ 8) temsil eder

GF'yi (2 ^ 8) GF (2) üzerinde 8 terimli polinomlarla temsil etmek yerine, GF (2 ^ 4) üzerinde 2 terimli polinomlarla temsil edebiliriz. Bu kez için doğrusal bir polinom seçmeliyiz x^2. Seçtiğimizi varsayalım x^2 = px + q. Sonra (ax + b) * (cx + d) = (ad + bc + acp)x + (bd + acq).

Bu sunumda tersini bulmak daha mı kolay? Eğer (cx + d) = (ax + b)^-1simultane eşitlikleri elde

  • ad + (b + ap)c = 0
  • bd + (aq)c = 1

Bırakın D = [b(b+ap) + a^2 q]ve ayarlayın c = a * D^-1; d = (b + ap) * D^-1. Bu nedenle, GF'ye (2 ^ 4) dönüşüm maliyeti, GF'de (2 ^ 4) ters ve birkaç ekleme ve çarpma ve geri dönüş için GF'de (2 ^ 8) bir ters yapabiliriz. Tersi bir tablo aracılığıyla yapsak bile, tablo boyutunu 256'dan 16'ya düşürdük.

Uygulama ayrıntıları

GF'nin (4) bir temsilini oluşturmak için azaltmak için üç polinom arasından seçim yapabiliriz x^4:

  • x^4 = x + 1
  • x^4 = x^3 + 1
  • x^4 = x^3 + x^2 + x + 1

En önemli fark çarpma işlemidir. Üçünden herhangi biri için poly(3, 9, f'ye karşılık gelen ) aşağıdakiler işe yarayacaktır:

// 14x &, 7x unary -, 3x <<1, 3x >>1, 3x >>3, 6x ^ gives score 226
int mul(int a, int b) {
    // Call current values a = a0, b = b0
    int p = a & -(b & 1);
    a = ((a << 1) ^ (poly & -(a >> 3))) & 15;
    b >>= 1;
    // Now p = a0 * (b0 mod x); a = a0 x; b = b0 div x

    p ^= a & -(b & 1);
    a = ((a << 1) ^ (poly & -(a >> 3))) & 15;
    b >>= 1;
    // Now p = a0 * (b0 mod x^2); a = a0 x^2; b = b0 div x^2

    p ^= a & -(b & 1);
    a = ((a << 1) ^ (poly & -(a >> 3))) & 15;
    b >>= 1;
    // Now p = a0 * (b0 mod x^3); a = a0 x^3; b = b0 div x^3

    p ^= a & -(b & 1);
    // p = a0 * b0

    return p;
}

Bununla birlikte, seçersek poly = 3, taşmayı çok daha verimli bir şekilde yapabiliriz çünkü güzel bir yapıya sahiptir: çift taşma yoktur, çünkü iki giriş hem x^6 = x^2 (x + 1)küp hem de kübiktir . Buna ek olarak şu vardiyaları da kaydedebiliriz b: Taşma işlemini sonlandırdığımız için, veya 1'e a0 x^2karşılık gelen herhangi bir set biti yoktur xve böylece -1 yerine -4 ile maskeleyebiliriz. Sonuç

// 10x &, 4x unary -, 3x <<1, 1x >>1, 1x >>3, 5x ^ gives score 152
int mul(int a, int b) {
    int p;
    p  = a & -(b & 1); a <<= 1;
    p ^= a & -(b & 2); a <<= 1;
    p ^= a & -(b & 4); a <<= 1;
    p ^= a & -(b & 8);
    // Here p = a0 * b0 but with overflow, which we need to bring back down.

    int t = p >> 3;
    p ^= (t >> 1) ^ (t & 0xe);
    return p & 15;
}

Yine de GF'nin (2 ^ 8) GF (2 ^ 4) üzerindeki değerlerini pve qtemsilini seçmemiz gerekiyor , ancak bunlar üzerinde çok fazla kısıtlamamız yok. Önemli olan tek şey, orijinal temsilimizin bitlerinden çalışma temsilinin bitlerine kadar doğrusal bir fonksiyon elde edebilmemizdir. Bu iki nedenden ötürü önemlidir: ilk olarak doğrusal dönüşümler yapmak kolaydır, ancak doğrusal olmayan bir dönüşüm sadece tüm S kutusunu optimize etmekte güçlükle eşdeğer optimizasyon gerektirir; İkincisi, çünkü bazı yan faydalar elde edebiliriz. Yapıyı yeniden özetlemek için:

GF256 SubBytes(GF256 x) {
    GF16 a, b, t, D, Dinv, c, d;

    (a, b) = f(x); // f is linear

    t = b + a * p;
    D = b * t + a * a * q;
    Dinv = inverse_GF16(D);
    c = a * Dinv;
    d = t * Dinv;

    return finv(c, d); // finv is also linear
}

Bit ise xşunlardır x7 x6 ... x0dönüşümü beri o zaman olsun doğrusal a = f({x7}0000000 + 0{x6}000000 + ... + 0000000{x0}) = f({x7}0000000) + f(0{x6}000000) + ... + f(0000000{x0}). Karesini alın ve a^2 = f({x7}0000000)^2 + f(0{x6}000000)^2 + ... + f(0000000{x0})^2çapraz terimlerin iptal edildiği yere ulaşırız (çünkü GF (2) 'de 1 + 1 = 0). Böylece a^2doğrusal fonksiyon olarak da hesaplanabilir x. Almak için ileri doğrusal dönüşümü artırabiliriz:

GF256 SubBytes(GF256 x) {
    GF16 a, b, t, a2q, D, Dinv, c, d;

    (a, b, t, a2q) = faug(x);

    D = b * t + a2q;
    Dinv = inverse_GF16(D);
    c = a * Dinv;
    d = t * Dinv;

    return finv(c, d);
}

ve üç çarpma ve bir ekleme yaptık. İki çarpmayı Dinvparalel olarak yapmak için çarpma kodunu da genişletebiliriz . Yani toplam maliyetimiz ileri doğrusal dönüşüm, toplama, iki çarpma, GF'de ters (2 ^ 4) ve geri doğrusal dönüşümdür. S-kutusunun ters-sonrası doğrusal dönüşümünü yuvarlayabilir ve bunu esas olarak ücretsiz olarak alabiliriz.

Doğrusal dönüşümler için katsayıların hesaplanması çok ilginç değildir ve maskeyi buraya kaydetmek ve oraya doğru kaymak için mikro optimizasyon da değildir. Kalan ilginç kısım iseinverse_GF16. 4 bit ile 4 bit arasında 2 ^ 64 farklı işlev vardır, bu nedenle doğrudan optimizasyon çok fazla bellek ve zaman gerektirir. Yaptığım şey, 4 bitten 1 bit'e kadar 4 işlevi düşünmek, herhangi bir işlev için izin verilen toplam maliyete bir sınır koymak (işlev başına maksimum 63 maliyetle bir dakikadan kısa bir sürede tüm uygun ifadeleri numaralandırabilirim), ve her bir fonksiyon grubu için ortak alt ifade eliminasyonu yapar. 25 dakika gevrekleştikten sonra, bu kapakla mümkün olan en iyi tersin toplam maliyetinin 133 olduğunu (herhangi bir bit için en ucuz ifadenin 35 olduğunu düşünmek kötü değil, çıktı başına ortalama 33,25). .

Hala GF (2 ^ 4) inversiyonunu en aza indirmek için diğer yaklaşımları deniyorum ve yukarıdan aşağıya yerine aşağıdan yukarıya inşa eden bir yaklaşım 133'den 126'ya bir gelişme sağladı.


Fantastik! Çalıştığını onaylıyorum! Bir ayrıntı: 8 & 1kesilmiş olabilir (. Esp xolduğu unsigned char; CHAR_BITcodegolf içinde 8).
fgrieu

@fgrieu, iyi bir nokta.
Peter Taylor

8

Puan: 980 = 7 * 5 + 115 * 7 + 7 * 5 + 15 * 7, Boyar ve Peralta'nın minimizasyonu

Joan Boyar ve René Peralta'nın kriptolojiye uygulamaları ile yeni bir kombinasyonel mantık minimizasyon tekniği buldum , ki bu da (C formalizminden tasarruf edin) gerekli olanı yapıyor. Denklemlerini elde etmek için kullanılan teknik , ABD'den daha az olmayan bir patentle patentlidir . Denklemlerinin doğrudan C çevirisini yaptım , lütfen buraya bağlandım .

unsigned char SubBytes_Boyar_Peralta(unsigned char x7){
  unsigned char 
  x6=x7>>1,x5=x6>>1,x4=x5>>1,x3=x4>>1,x2=x3>>1,x1=x2>>1,x0=x1>>1,
  y14=x3^x5,y13=x0^x6,y9=x0^x3,y8=x0^x5,t0=x1^x2,y1=t0^x7,y4=y1^x3,y12=y13^y14,y2=y1^x0,
  y5=y1^x6,y3=y5^y8,t1=x4^y12,y15=t1^x5,y20=t1^x1,y6=y15^x7,y10=y15^t0,y11=y20^y9,y7=x7^y11,
  y17=y10^y11,y19=y10^y8,y16=t0^y11,y21=y13^y16,y18=x0^y16,t2=y12&y15,t3=y3&y6,t4=t3^t2,
  t5=y4&x7,t6=t5^t2,t7=y13&y16,t8=y5&y1,t9=t8^t7,t10=y2&y7,t11=t10^t7,t12=y9&y11,
  t13=y14&y17,t14=t13^t12,t15=y8&y10,t16=t15^t12,t17=t4^t14,t18=t6^t16,t19=t9^t14,
  t20=t11^t16,t21=t17^y20,t22=t18^y19,t23=t19^y21,t24=t20^y18,t25=t21^t22,t26=t21&t23,
  t27=t24^t26,t28=t25&t27,t29=t28^t22,t30=t23^t24,t31=t22^t26,t32=t31&t30,t33=t32^t24,
  t34=t23^t33,t35=t27^t33,t36=t24&t35,t37=t36^t34,t38=t27^t36,t39=t29&t38,t40=t25^t39,
  t41=t40^t37,t42=t29^t33,t43=t29^t40,t44=t33^t37,t45=t42^t41,z0=t44&y15,z1=t37&y6,
  z2=t33&x7,z3=t43&y16,z4=t40&y1,z5=t29&y7,z6=t42&y11,z7=t45&y17,z8=t41&y10,z9=t44&y12,
  z10=t37&y3,z11=t33&y4,z12=t43&y13,z13=t40&y5,z14=t29&y2,z15=t42&y9,z16=t45&y14,z17=t41&y8,
  t46=z15^z16,t47=z10^z11,t48=z5^z13,t49=z9^z10,t50=z2^z12,t51=z2^z5,t52=z7^z8,t53=z0^z3,
  t54=z6^z7,t55=z16^z17,t56=z12^t48,t57=t50^t53,t58=z4^t46,t59=z3^t54,t60=t46^t57,
  t61=z14^t57,t62=t52^t58,t63=t49^t58,t64=z4^t59,t65=t61^t62,t66=z1^t63,s0=t59^t63,
  s6=t56^t62,s7=t48^t60,t67=t64^t65,s3=t53^t66,s4=t51^t66,s5=t47^t65,s1=t64^s3,s2=t55^t67;
  return (((((((s0<<1|s1&1)<<1|s2&1)<<1|s3&1)<<1|s4&1)<<1|s5&1)<<1|s6&1)<<1|s7&1)^0x63;}

Vay canına, gerçekten işe yarıyor ve gerçekten ucuz. Sökerken, prolog, epilogie ve hareket talimatları hariç gerçekten 144 talimattır.
ugoren

5

Puan: 10965

Bu, dizi aramasını açma prensibini kullanır. Ek ödeme gerektirebilir.

Nasıl iyileştirileceğini işaret ettiği için ugoren'e teşekkürler is_zero.

// Cost: 255 * (5+7+24+7) = 10965
unsigned char SubBytes(unsigned char x) {
    unsigned char r = 0x63;
    char c = (char)x;
    c--; r ^= is_zero(c) & (0x63^0x7c); // 5+7+24+7 inlining the final xor
    c--; r ^= is_zero(c) & (0x63^0x77); // 5+7+24+7
    // ...
    c--; r ^= is_zero(c) & (0x63^0x16); // 5+7+24+7
    return r;
}

// Cost: 24
// Returns (unsigned char)-1 when input is 0 and 0 otherwise
unsigned char is_zero(char c) {
    // Shifting a signed number right is unspecified, so use unsigned
    unsigned char u;
    c |= -c;               // 7+5+
    u = (unsigned char)c;
    u >>= (CHAR_BITS - 1); // 7+
    c = (char)u;
    // c is 0 if we want -1 and 1 otherwise.
    c--;                   // 5
    return (unsigned char)c;
}

2
C tamsayısı (c|-c)>>31için, 0 için 0 ve aksi takdirde -1'dir.
ugoren

@ugoren, mantıklı dillerde, evet. C'de, imzasız bir türün sağa kaydırılması belirtilmemiş.
Peter Taylor

1
Sanırım imzalı demek istiyorsun. Ancak bu site, katı standartlara uygunluk açısından gerçekten ünlü değildir. Ayrıca, c >> 4bana doğru kaymış doğru vardiya gibi görünüyor. Ve eğer gerçekten ısrar ediyorsanız - ((unsigned int)(c|-c))>>31öyle c?1:0.
ugoren

@ugoren, haklısın, imzalamak istedim. c >>4Birlikte veya işaret uzantısı olmadan çalışır. Ama imzasız bir vardiya kullanarak iyi yakalamak: eve geldiğimde düzenleyecek ve bir telefon yerine uygun bir bilgisayar kullanabileceksiniz. Teşekkürler.
Peter Taylor

3

Puan: 9109, cebirsel yaklaşım

Herkesin büyük ölçüde iyileştirmesi durumunda arama yaklaşımından ayrılacağım, ancak iyi bir cebirsel yaklaşımın mümkün olduğu ortaya çıkıyor. Bu uygulama Öklid algoritmasını kullanarak çarpımsal tersi bulur . Java ile yazdım ama prensip olarak C'ye taşınabilir - sadece 8 bitlik tipler kullanarak yeniden çalışmak isterseniz değişecek parçaları yorumladım.

Uugoren'e, diğer cevabımla ilgili is_nonzerobir yorumda çekin nasıl kısaltılacağına işaret ettiği için teşekkürler .

public class SBox
{
    public static void main(String[] args)
    {
        for (int i = 0; i < 256; i++) {
            int s = SubBytes(i);
            System.out.format("%02x  ", s);
            if (i % 16 == 15) System.out.println();
        }
    }

    // Total cost: 9109
    public static int SubBytes(int x)
    {
        x = inv_euclid(x); // 9041
        x = affine(x);     // 68
        return x;
    }

    // Total cost: 68
    private static int affine(int s0) {
        int s = s0;
        s ^= (s0 << 1) ^ (s0 >> 7); // 5 + 7
        s ^= (s0 << 2) ^ (s0 >> 6); // 7 + 7
        s ^= (s0 << 3) ^ (s0 >> 5); // 7 + 7
        s ^= (s0 << 4) ^ (s0 >> 4); // 7 + 7
        return (s ^ 0x63) & 0xff;   // 7 + 7
    }

    // Does the inverse in the Galois field for a total cost of 24 + 9010 + 7 = 9041
    private static int inv_euclid(int s) {
        // The first part of handling the special case: cost of 24
        int zeromask = is_nonzero(s);

        // NB the special value of r would complicate the unrolling slightly with unsigned bytes
        int r = 0x11b, a = 0, b = 1;

        // Total cost of loop: 7*(29+233+566+503+28) - 503 = 9010
        for (int depth = 0; depth < 7; depth++) { // 7*(
            // Calculate mask to fake out when we're looping further than necessary: cost 29
            int mask = is_nonzero(s >> 1);

            // Cost: 233
            int ord = polynomial_order(s);

            // This next block does div/rem at a total cost of 6*(24+49) + 69 + 59 = 566
            int quot = 0, rem = r;
            for (int i = 7; i > 1; i--) {                   // 6*(
                int divmask = is_nonzero(ord & (rem >> i)); // 24+7+7
                quot ^= (1 << i) & divmask;                 // 7+0+7+ since 1<<i is inlined on unrolling
                rem ^= (s << i) & divmask;                  // 7+7+7) +
            }
            int divmask1 = is_nonzero(ord & (rem >> 1));    // 24+7+5
            quot ^= 2 & divmask1;                           // 7+7+
            rem ^= (s << 1) & divmask1;                     // 7+5+7+
            int divmask0 = is_nonzero(ord & rem);           // 24+7
            quot ^= 1 & divmask0;                           // 7+7+
            rem ^= s & divmask0;                            // 7+7

            // This next block does the rest for the cost of a mul (503) plus 28
            // When depth = 0, b = 1 so we can skip the mul on unrolling
            r = s;
            s = rem;
            quot = mul(quot, b) ^ a;
            a = b;
            b ^= (quot ^ b) & mask;
        }

        // The rest of handling the special case: cost 7
        return b & zeromask;
    }

    // Gets the highest set bit in the input. Assumes that it's always at least 1<<1
    // Cost: 233
    private static int polynomial_order(int s) {
        int ord = 2;
        ord ^= 6 & -((s >> 2) & 1);           // 7+7+5+7+7 = 33 +
        ord ^= (ord ^ 8) & -((s >> 3) & 1);   // 7+7+7+5+7+7 = 40 +
        ord ^= (ord ^ 16) & -((s >> 4) & 1);  // 40 +
        ord ^= (ord ^ 32) & -((s >> 5) & 1);  // 40 +
        ord ^= (ord ^ 64) & -((s >> 6) & 1);  // 40 +
        ord ^= (ord ^ 128) & -((s >> 7) & 1); // 40
        return ord;
    }

    // Returns 0 if c is 0 and -1 otherwise
    // Cost: 24
    private static int is_nonzero(int c) {
        c |= -c;   // 7+5+
        c >>>= 31; // 7+ (simulating a cast to unsigned and right shift by CHAR_BIT)
        c = -c;    // 5+ (could be saved assuming a particular implementation of signed right shift)
        return c;
    }

    // Performs a multiplication in the Rijndael finite field
    // Cost: 503 (496 if working with unsigned bytes)
    private static int mul(int a, int b) {
        int p = 0;
        for (int counter = 0; counter < 8; counter++) { // 8*(_+_
            p ^= a & -(b & 1);                          // +7+7+5+7
            a = (a << 1) ^ (0x1b & -(a >> 7));          // +5+7+7+5+7
            b >>= 1;                                    // +5)
        }
        p &= 0xff;                                      // +7 avoidable with unsigned bytes
        return p;
    }
}

2

Puan: 256 * (7+ (8 * (7 + 7 + 7) - (2 + 2)) + 5 + 7 + 7) = 48640 (döngülerin açık olduğu varsayılarak)

unsigned char SubBytes(unsigned char x) {
static const unsigned char t[256] = {
  0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
  0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
  0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
  0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
  0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
  0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
  0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
  0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
  0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
  0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
  0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
  0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
  0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
  0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
  0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
  0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16};

unsigned char ret_val = 0;
int i,j;
for(i=0;i<256;i++) {
  unsigned char is_index = (x ^ ((unsigned char) i));
  for(j=0;j<8;j++) {
   is_index |= (is_index << (1 << j)) | (is_index >> (1 << j));
  }
  is_index = ~is_index;
  ret_val |= is_index & t[i];
}

return ret_val;}

Açıklama:

Esasen, bir dizi araması bitsel operatörler kullanılarak yeniden uygulandı ve her zaman tüm diziyi işledi. Diziyi ve xher dizini xor ile döngüye alırız, ardından sonucu mantıksal olarak olumsuzlamak için bitsel işleçleri kullanırız, böylece 255 zaman x=ive 0 aksi takdirde alırız . Biz bitwise -ve bu dizi-değeri ile, böylece seçilen değer değişmez ve diğerleri 0 olur. Sonra bu dizinin bitsel-veya veya almak, böylece sadece seçilen değeri çeker.

İki 1 << jişlem, döngüyü açmanın bir parçası olarak, 1'den 128'e 2'nin güçleriyle değiştirilecekti.


Şimdi matematiği bitsel işleçleri kullanarak gerçekten yapmanın mümkün olup olmadığını görmek için.
histocrat

Buradaki algoritmaya baktığımızda , polinom zamanının bazı adımlarının yerine en az bir kez tüm baytlar üzerinde döngü yapmadan polinom tersinin uygulanmasının mümkün olacağından şüpheliyim. Yani bu herhangi bir "akıllı" çözümü yenebilir. Bu sabit zamanlı dizi arama ayar daha umut verici bir avenue şüpheli.
histokrat

Güzel. Aes.c işlevi rj_sbox burada (olduğu gibi, bu soruyu uymuyor, rağmen) ilham verebilir.
fgrieu

Nerede gelmez -(2+2)senin puanlama hesaplamada geliyor? Düzenleme: ah, satır içi a <<1ve a oluşturur >>1.
Peter Taylor

0

Arama tablosunu kullanarak 1968 1692 puanı

Not: Bu çözüm nedeniyle kriterleri geçmez w >> b.

Arama tablosunu kullanma, ancak bir seferde 8 bayt okuma.
3 * 7 + 32 * (6 * 7 + 2 * 5) + 7 = 692

unsigned char SubBytes(unsigned char x){

static const unsigned char t[256] = {
  0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
  0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
  0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
  0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
  0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
  0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
  0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
  0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
  0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
  0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
  0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
  0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
  0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
  0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
  0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
  0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16};

  unsigned long long *t2 = (unsigned long long *)t;
  int a = x>>3, b=(x&7)<<3;                       // 7+7+7
  int i;
  int ret = 0;
  for (i=0;i<256/8;i++) {                         // 32 *
      unsigned long long w = t2[i];
      int badi = ((unsigned int)(a|-a))>>31;      // 7+7+5
      w &= (badi-1);                              // +7+7
      a--;                                        // +5
      ret |= w >> b;                              // +7+7
  }
  return ret & 0xff;                              // +7
}

Bunun sorudaki sabit zaman tanımını karşıladığını düşünmüyorum, çünkü w>>bRHS hesaplandıx
Peter Taylor

Birkaç ihlalleri vardır: w >> bnerede bgirişine bağlıdır; ayrıca x/8,, x%8ve *= (1-badi). Birincisi özellikle düşük-uç CPU'larda zamanlama bağımlılığına dönüşecektir. Bununla birlikte, geniş değişkenleri kullanma fikri kesinlikle potansiyele sahiptir.
fgrieu

Talimatları yeterince dikkatle okumadım. Sorunların çoğunu düzeltebilirim, ancak w >> boldukça önemlidir (her şeyi yeniden yazmadan düzeltilip düzeltilemeyeceğini görmek gerekir.
ugoren

0

Tablo arama ve maske, puan = 256 * (5 * 7 + 1 * 5) = 10240

Yalnızca istediğimiz sonucu seçmek için tablo aramasını maske ile kullanır. j|-jNegatif (i! = X olduğunda) veya sıfır (i == x olduğunda) gerçeğini kullanır . Vites değiştirme, yalnızca istediğimiz girişi seçmek için kullanılan hepsi bir veya tamamen sıfır maskesi yapar.

static const unsigned char t[256] = {
  0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
  0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
  0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
  0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
  0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
  0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
  0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
  0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
  0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
  0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
  0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
  0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
  0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
  0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
  0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
  0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16};

unsigned char SubBytes(unsigned char x) {
  unsigned char r = 255;
  for (int i = 0; i < 256; i++) {
    int j = i - x;
    r &= t[i] | ((j | -j) >> 31);
  }
  return r;
}

Bu daha az optimize edilmiş durum dışında ikinci cevabımla aynı değil mi?
Peter Taylor

Yakın sanırım. İmzalı vardiya kullanıyorum, böylece sonunda -1 yapmak zorunda değilim.
Keith Randall
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.