C: ganti tabel AES FIPS-197 SubBytes dengan kode waktu-konstan

17

Dalam FIPS-197 ( Advanced Encryption Standard , dikenal sebagai AES), ia digunakan secara berlebihan SubBytes, yang dapat diimplementasikan sebagai

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];}

Fungsi ini tidak sewenang-wenang; ini adalah pemetaan yang dapat dibalik, yang terdiri dari inversi di Galois Field diikuti oleh transformasi affine. Semua detail ada di FIPS-197 bagian 5.1.1 atau di sini bagian 4.2.1 (dengan nama yang sedikit berbeda).

Satu masalah dengan implementasi sebagai tabel adalah bahwa ia terbuka untuk serangan yang disebut cache-timing .

Dengan demikian misi Anda adalah untuk merancang pengganti yang tepat untuk SubBytes()fungsi di atas , yang menunjukkan perilaku waktu-konstan; kita akan berasumsi bahwa yang terjadi ketika tidak tergantung pada masukan xdari SubBytesdigunakan baik:

  • sebagai indeks array,
  • sebagai kontrol operand dari if, while, for, case, atau operator ?:;
  • karena setiap operan dari operator &&, ||, !, ==, !=, <, >, <=, >=, *, /, %;
  • sebagai operan kanan operator >>, <<, *=, /=, %=, <<=, >>=.

Masuknya menang akan menjadi satu dengan biaya terendah, yang diperoleh dari jumlah operator dieksekusi di jalur data input-dependent, dengan berat 5 untuk operator unary -dan ~juga untuk <<1, >>1, +1, -1; bobot 7 untuk semua operator lain, shift dengan hitungan lain, atau tambah / subs konstanta lain (tipe gips dan promosi gratis). Secara prinsip, biaya itu tidak berubah dengan membuka gulungan (jika ada), dan independen dari input x. Sebagai tie-breaker, jawaban dengan kode terpendek setelah menghapus spasi dan komentar menang.

Saya berencana untuk menunjuk sebuah entri sedini mungkin di tahun 2013, UTC. Saya akan mempertimbangkan jawaban dalam bahasa yang saya ketahui, rangking sebagai terjemahan langsung ke C yang tidak dioptimalkan untuk ukuran.

Permintaan maaf atas kelalaian awal +1dan -1dalam operator yang disukai, gips dan promosi gratis, dan peringkat ukuran. Catatan yang *dilarang baik saat unary, maupun sebagai penggandaan.

fgrieu
sumber
1
Patut dicatat bahwa pencarian gratis karena Anda dapat memasukkannya sebagai konstanta.
Peter Taylor
"awal tahun 2013, UTC" - bukankah tanggalnya akan lebih menarik daripada zona waktu?
Paŭlo Ebermann
@ PaŭloEbermann: Niat saya harus jelas sekarang.
fgrieu

Jawaban:

13

Nilai: 940 933 926 910, pendekatan menara lapangan

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

Struktur ini pada dasarnya sama dengan implementasi Boyar dan Peralta - mengurangi inversi di GF (2 ^ 8) menjadi inversi di GF (2 ^ 4), memecahnya menjadi prolog linier, badan non-linear, dan epilog linier, dan kemudian meminimalkannya secara terpisah. Saya membayar penalti untuk ekstraksi bit, tetapi saya mengimbanginya dengan bisa melakukan operasi secara paralel (dengan beberapa padding bit yang bijaksana fwd). Lebih detail ...

Latar Belakang

Seperti yang disebutkan dalam deskripsi masalah, S-box terdiri dari inversi dalam implementasi khusus bidang Galois GF (2 ^ 8) diikuti oleh transformasi affine. Jika Anda tahu apa arti keduanya, lewati bagian ini.

Transformasi affine (atau linear) adalah fungsi f(x)yang menghormati f(x + y) = f(x) + f(y)dan f(a*x) = a*f(x).

Bidang adalah serangkaian Felemen dengan dua elemen khusus, yang akan kita panggil 0dan 1, dan dua operator, yang akan kita panggil +dan *, yang menghormati berbagai properti. Pada bagian ini, menganggap bahwa x, y, dan zunsur F.

  • Unsur-unsur Fbentuk kelompok abelian di bawah +dengan 0sebagai identitas: yaitu x + yunsur F; x + 0 = 0 + x = x; masing-masing xmemiliki yang sesuai -xsehingga x + (-x) = (-x) + x = 0; x + (y + z) = (x + y) + z; dan x + y= y + x.
  • Unsur-unsur Fselain 0membentuk kelompok abelian di bawah *dengan 1sebagai identitas.
  • Perkalian mendistribusikan lebih Selain itu: x * (y + z) = (x * y) + (x * z).

Ternyata ada beberapa batasan yang cukup parah pada bidang terbatas:

  • Mereka harus memiliki sejumlah elemen yang merupakan kekuatan prima.
  • Mereka isomorfik dengan semua bidang terbatas lainnya dengan ukuran yang sama (yaitu benar-benar hanya ada satu bidang terbatas dengan ukuran tertentu, dan yang lainnya hanya relabellings; sebutlah bidang tersebut GF (p ^ k) di mana pyang utama dan kkekuatannya) .
  • Kelompok multiplikasi di F\{0}bawah *adalah siklik; yaitu ada setidaknya satu elemen gsedemikian rupa sehingga setiap elemen adalah kekuatan g.
  • Untuk kekuatan lebih besar dari 1 ada representasi sebagai polinomial tatanan univariat kbidang tatanan utama. Misalnya GF (2 ^ 8) memiliki representasi dalam hal polinomial xlebih dari GF (2). Bahkan biasanya ada lebih dari satu representasi. Pertimbangkan x^7 * xdalam GF (2 ^ 8); itu harus sama dengan beberapa polinomial order-7, tetapi yang mana? Ada banyak pilihan yang memberikan struktur yang tepat; AES memilih untuk membuat x^8 = x^4 + x^3 + x + 1(polinomial terkecil yang bekerja secara leksikografis).

Jadi bagaimana kita menghitung invers dalam representasi GF tertentu (2 ^ 8)? Ini masalah yang terlalu besar untuk ditangani secara langsung, jadi kita harus menguraikannya.

Menara menara: mewakili GF (2 ^ 8) dalam hal GF (2 ^ 4)

Alih-alih merepresentasikan GF (2 ^ 8) dengan polinomial 8 istilah di atas GF (2) kita bisa mewakilinya dengan polinomial 2 istilah di atas GF (2 ^ 4). Kali ini kita perlu memilih polinomial linier untuk x^2. Misalkan kita memilih x^2 = px + q. Lalu (ax + b) * (cx + d) = (ad + bc + acp)x + (bd + acq).

Apakah lebih mudah menemukan invers dalam representasi ini? Jika (cx + d) = (ax + b)^-1kita mendapatkan persamaan simultan

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

Biarkan D = [b(b+ap) + a^2 q]dan atur c = a * D^-1; d = (b + ap) * D^-1. Jadi kita dapat melakukan invers dalam GF (2 ^ 8) untuk biaya konversi ke GF (2 ^ 4), invers dan beberapa penambahan dan multiplikasi dalam GF (2 ^ 4), dan konversi kembali. Bahkan jika kita melakukan invers dengan menggunakan tabel, kita telah mengurangi ukuran tabel dari 256 menjadi 16.

Detail implementasi

Untuk membangun representasi GF (4) kita dapat memilih antara tiga polinomial untuk mengurangi x^4:

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

Perbedaan yang paling penting adalah dalam penerapan multiplikasi. Untuk salah satu dari tiga (yang sesuai dengan poly3, 9, f) berikut ini akan berfungsi:

// 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;
}

Namun, jika kita memilih, poly = 3kita dapat menangani luapan jauh lebih efisien karena memiliki struktur yang bagus: tidak ada aliran berlipat ganda, karena kedua input sama-sama kubik dan x^6 = x^2 (x + 1)juga kubik. Selain itu kita dapat menyimpan perubahan b: karena kita membiarkan overflow bertahan, a0 x^2tidak memiliki bit set yang sesuai dengan xatau 1 dan jadi kita bisa menutupinya dengan -4 bukannya -1. Hasilnya adalah

// 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;
}

Kita masih perlu memilih nilai-nilai pdan quntuk representasi GF (2 ^ 8) daripada GF (2 ^ 4), tetapi kami tidak memiliki banyak kendala pada mereka. Satu hal yang penting adalah kita bisa mendapatkan fungsi linear dari bit representasi asli kita ke bit representasi kerja. Ini penting karena dua alasan: pertama, mudah untuk melakukan transformasi linier, sedangkan transformasi non-linear akan membutuhkan pengoptimalan yang setara dalam kesulitan untuk hanya mengoptimalkan seluruh kotak-S; kedua, karena kita bisa mendapatkan beberapa manfaat sampingan. Untuk rekap struktur:

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
}

Jika bit-bit xtersebut x7 x6 ... x0maka karena transformasi linear kita dapatkan a = f({x7}0000000 + 0{x6}000000 + ... + 0000000{x0}) = f({x7}0000000) + f(0{x6}000000) + ... + f(0000000{x0}). Kuadratkan dan kami dapatkan di a^2 = f({x7}0000000)^2 + f(0{x6}000000)^2 + ... + f(0000000{x0})^2mana persyaratan silang dibatalkan (karena dalam GF (2), 1 + 1 = 0). Jadi a^2dapat juga dihitung sebagai fungsi linear dari x. Kami dapat menambah transformasi linear maju untuk mendapatkan:

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

dan kami turun ke tiga perkalian dan satu tambahan. Kita juga dapat memperluas kode perkalian untuk melakukan dua perkalian Dinvsecara paralel. Jadi total biaya kami adalah transformasi linear ke depan, tambahan, dua perkalian, kebalikan dari GF (2 ^ 4), dan transformasi linear kembali. Kita dapat menggulirkan transformasi linear pasca-terbalik dari S-box dan mendapatkannya secara gratis.

Perhitungan koefisien untuk transformasi linear tidak terlalu menarik, dan optimasi mikro juga tidak untuk menyelamatkan topeng di sini dan perubahan di sana. Bagian menarik yang tersisa adalah optimalisasiinverse_GF16. Ada 2 ^ 64 fungsi yang berbeda dari 4 bit hingga 4 bit, jadi pengoptimalan langsung membutuhkan banyak memori dan waktu. Apa yang saya lakukan adalah mempertimbangkan 4 fungsi dari 4 bit hingga 1 bit, letakkan batas atas total biaya yang diizinkan untuk satu fungsi (dengan biaya maksimum 63 per fungsi, saya dapat menghitung semua ekspresi yang sesuai dalam waktu kurang dari satu menit), dan untuk setiap tuple fungsi lakukan eliminasi subekspresi umum. Setelah 25 menit berderak, saya menemukan bahwa invers terbaik dengan cap itu memiliki total biaya 133 (rata-rata 33,25 per bit output, yang tidak buruk mengingat bahwa ekspresi termurah untuk setiap bit individu adalah 35) .

Saya masih bereksperimen dengan pendekatan lain untuk meminimalkan inversi dalam GF (2 ^ 4), dan pendekatan yang membangun bottom-up daripada top-down telah menghasilkan peningkatan dari 133 menjadi 126.

Peter Taylor
sumber
Fantastis! Saya konfirmasi itu berfungsi! Sebuah detail:-8 & 1dapat dipangkas (. Esp jika xadalah unsigned char; CHAR_BITadalah 8 di codegolf).
fgrieu
@ fgrieu, poin bagus.
Peter Taylor
8

Nilai: 980 = 7 * 5 + 115 * 7 + 7 * 5 + 15 * 7, minimalisasi Boyar dan Peralta

Saya menemukan teknik minimalisasi logika kombinasional baru dengan aplikasi kriptologi oleh Joan Boyar dan René Peralta, yang (kecuali untuk formalisme C) melakukan apa yang diperlukan. Teknik yang digunakan untuk menurunkan persamaan mereka dipatenkan oleh Amerika Serikat. Saya baru saja melakukan terjemahan C langsung dari persamaan mereka , silakan ditautkan di sini .

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;}
fgrieu
sumber
Wow, benar-benar bekerja, dan sangat murah. Saat membongkar, itu memang 144 instruksi, tidak termasuk prolog, epilogie, dan instruksi pemindahan.
ugoren
5

Nilai: 10965

Ini menggunakan prinsip yang sama untuk membuka gulungan array pencarian. Mungkin membutuhkan gips tambahan.

Terima kasih ugoren untuk menunjukkan cara meningkatkan 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;
}
Peter Taylor
sumber
2
Untuk bilangan bulat c, (c|-c)>>31adalah 0 untuk 0 dan -1 jika tidak.
ugoren
@ugoren, dalam bahasa yang masuk akal, ya. Di C, pemindahan kanan tipe yang tidak ditandatangani tidak ditentukan.
Peter Taylor
1
Saya kira maksud Anda ditandatangani. Tetapi situs ini tidak benar-benar terkenal karena kepatuhan standar yang ketat. Juga, c >> 4sepertinya kau masuk dengan benar ke saya. Dan jika Anda benar-benar bersikeras - ((unsigned int)(c|-c))>>31adalah c?1:0.
ugoren
@ugoren, kau benar, maksudku ditandatangani. The c >>4bekerja dengan atau tanpa ekstensi tanda. Tapi tangkapan yang bagus untuk menggunakan shift yang tidak ditandatangani: akan diedit ketika saya sampai di rumah dan dapat menggunakan komputer yang tepat daripada telepon. Terima kasih.
Peter Taylor
3

Nilai: 9109, pendekatan aljabar

Saya akan meninggalkan pendekatan pencarian kalau-kalau ada yang bisa memperbaikinya secara drastis, tetapi ternyata pendekatan aljabar yang baik adalah mungkin. Implementasi ini menemukan invers multiplikasi dengan menggunakan algoritma Euclid . Saya telah menulisnya di Jawa tetapi pada prinsipnya bisa diporting ke C - Saya sudah berkomentar bagian yang akan berubah jika Anda ingin mengolahnya hanya menggunakan tipe 8-bit.

Terima kasih ugoren untuk menunjukkan cara mempersingkat is_nonzerocek dalam komentar atas jawaban saya yang lain.

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;
    }
}
Peter Taylor
sumber
2

Nilai: 256 * (7+ (8 * (7 + 7 + 7) - (2 + 2)) + 5 + 7 + 7) = 48640 (dengan asumsi loop tidak terbuka)

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

Penjelasan:

Pada dasarnya pencarian array diimplementasikan kembali menggunakan operator bitwise dan selalu memproses seluruh array. Kita mengulang array, dan xor xdengan masing-masing indeks, kemudian menggunakan operator bitwise untuk meniadakan hasil secara logis, jadi kita mendapatkan 255 kapan x=idan 0 sebaliknya. Kami bitwise-dan ini dengan nilai array, sehingga nilai yang dipilih tidak berubah dan yang lain menjadi 0. Kemudian kita mengambil bitwise-atau array ini, dengan demikian hanya mengeluarkan nilai yang dipilih.

Kedua 1 << joperasi akan hilang sebagai bagian dari membuka gulungan, menggantinya dengan kekuatan 2 dari 1 hingga 128.

histokrat
sumber
Sekarang untuk melihat apakah mungkin untuk benar-benar melakukan matematika menggunakan operator bitwise.
histokrat
Melihat melalui algoritma di sini , saya agak ragu akan mungkin untuk menerapkan inversi polinomial tanpa mengulang semua byte setidaknya sekali sebagai pengganti untuk beberapa langkah waktu polinomial. Jadi ini mungkin mengalahkan solusi "pintar". Saya menduga menyetel pencarian array waktu-konstan ini adalah jalan yang lebih menjanjikan.
histokrat
Bagus. Fungsi rj_sbox di aes.c di sini mungkin memberikan inspirasi (walaupun, seperti itu, tidak cocok dengan pertanyaan).
fgrieu
Dari mana -(2+2)perhitungan perhitungan Anda berasal? Sunting: ah, sebaris menciptakan a <<1dan a >>1.
Peter Taylor
0

Skor 1968 1692, menggunakan tabel pencarian

Catatan: Solusi ini tidak lulus kriteria, karena w >> b.

Menggunakan tabel pencarian, tetapi membaca 8 byte sekaligus.
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
}
ugoren
sumber
Saya tidak berpikir ini memenuhi definisi waktu yang konstan dalam pertanyaan, karena w>>btelah dihitung RHS darix
Peter Taylor
Ada beberapa pelanggaran: di w >> bmana btergantung pada input; juga x/8, x%8, dan *= (1-badi). Yang pertama kemungkinan akan berubah menjadi ketergantungan waktu pada CPU low-end. Namun, gagasan menggunakan variabel luas tentu memiliki potensi.
fgrieu
Saya tidak membaca instruksi dengan cukup hati-hati. Saya dapat memperbaiki sebagian besar masalah, tetapi w >> bsangat penting (saya perlu melihat apakah itu dapat diperbaiki tanpa menulis ulang semuanya.
ugoren
0

Tabel lookup dan mask, skor = 256 * (5 * 7 + 1 * 5) = 10240

Menggunakan pencarian tabel dengan masker untuk memilih hanya hasil yang kita inginkan. Menggunakan fakta yang j|-jnegatif (ketika i! = X) atau nol (ketika saya == x). Pergeseran membuat topeng semua-satu atau semua-nol yang digunakan untuk memilih hanya entri yang kita inginkan.

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;
}
Keith Randall
sumber
Bukankah ini sama dengan jawaban kedua saya kecuali kurang dioptimalkan?
Peter Taylor
Tutup, kurasa. Saya menggunakan shift yang ditandatangani jadi saya tidak perlu melakukan -1 pada akhirnya.
Keith Randall