Triển khai S-box của Rijndael


15

S-box của Rijndael là một hoạt động thường được sử dụng trong mã hóa và giải mã AES . Nó thường được thực hiện dưới dạng bảng tra cứu 256 byte. Điều đó nhanh, nhưng có nghĩa là bạn cần liệt kê bảng tra cứu 256 byte trong mã của mình. Tôi cá là ai đó trong đám đông này có thể làm điều đó với ít mã hơn, dựa trên cấu trúc toán học cơ bản.

Viết một hàm bằng ngôn ngữ yêu thích của bạn thực hiện S-box của Rijndael. Mã ngắn nhất sẽ thắng.


1
Điểm thưởng (nâng cấp từ tôi) nếu hàm kết quả là thời gian không đổi (nghĩa là không có đường dẫn mã phụ thuộc dữ liệu hoặc truy cập mảng hoặc bất cứ điều gì ngôn ngữ của bạn hỗ trợ).
Paŭlo Ebermann

Truy cập mảng @ PaŭloEbermann là thời gian không đổi trong nhiều ngôn ngữ (đó là thêm giá trị (được chia tỷ lệ) vào một con trỏ và hủy bỏ nó, đây là lý do tại sao một bảng tra cứu rất nhanh)
ratchet freak

@ratchetfreak Truy cập mảng là O (1), nhưng thời gian truy cập thực tế phụ thuộc vào lần truy cập hoặc lỗi bộ nhớ cache, dẫn đến các cuộc tấn công kênh bên vào AES.
Paŭlo Ebermann

@ PaŭloEbermann, nhưng bạn có thể sử dụng mã ngắn hơn để điền vào bảng tra cứu, sau đó sẽ phù hợp với một trang bộ nhớ.
Peter Taylor

@ PaŭloEbermann và nếu bảng 256 độ dài được lưu trữ dọc theo mã (như enum được tạo tại thời điểm biên dịch), bạn gần như đảm bảo một lần truy cập bộ đệm
ratchet freak

Câu trả lời:


6

Ruby, 161 ký tự

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}

Để kiểm tra đầu ra, bạn có thể sử dụng đoạn mã sau để in dưới dạng bảng:

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

7

GolfScript, 60 ký tự

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

Mã này định nghĩa một hàm có tên Slà byte và áp dụng hộp S Rijndael cho nó. (Nó cũng sử dụng chức năng trợ giúp nội bộ có tên rđể lưu một vài ký tự.)

Việc triển khai này sử dụng bảng logarit để tính toán nghịch đảo của GF (2 8 ), theo đề xuất của Thomas Pornin . Để lưu một vài ký tự, toàn bộ bảng logarit được tính toán lại cho mỗi byte đầu vào; mặc dù vậy, và mặc dù GolfScript là một ngôn ngữ rất chậm nói chung, mã này chỉ mất khoảng 10 ms để xử lý một byte trên máy tính xách tay cũ của tôi. Tính toán trước bảng logarit (as L) tăng tốc lên tới khoảng 0,5 ms mỗi byte, với chi phí khiêm tốn của ba ký tự khác:

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

Để thuận tiện, đây là một khai thác thử nghiệm đơn giản gọi hàm S, như được định nghĩa ở trên, để tính toán và in ra toàn bộ hộp chữ S trong hex như trên Wikipedia :

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

Hãy thử mã này trực tuyến.

(Bản demo trực tuyến định trước bảng logarit để tránh mất quá nhiều thời gian. Mặc dù vậy, trang web GolfScript trực tuyến đôi khi có thể hết thời gian ngẫu nhiên; đây là sự cố đã biết với trang web và tải lại thường khắc phục nó.)

Giải trình:

Hãy bắt đầu với tính toán bảng logarit và cụ thể là với hàm trợ giúp r:

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

Hàm này có hai đầu vào trên ngăn xếp: một byte và bitmask giảm (hằng số nằm trong khoảng từ 256 đến 511). Nó nhân đôi byte đầu vào, nhân số sao chép lên 2 và, nếu kết quả vượt quá 255, XOR sẽ tạo bit với bitmask để đưa nó trở lại dưới 256.

Trong mã tạo bảng nhật ký, hàm rđược gọi với bitmask khử 283 = 0x11b (tương ứng với đa thức khử Rijndael GF (2 8 ) x 8 + x 4 + x 3 + x + 1) và kết quả là XORed với byte gốc, nhân nó một cách hiệu quả với 3 (= x + 1, dưới dạng đa thức) trong trường hữu hạn Rijndael. Phép nhân này được lặp lại 255 lần, bắt đầu từ byte 1 và kết quả (cộng với một byte số 0 ban đầu) được thu thập vào một mảng gồm 256 phần tử Ltrông như thế này (bỏ qua phần giữa):

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

Lý do tại sao có tới 256 phần tử là vì, với 0 được đặt trước và với 1 xảy ra hai lần, chúng ta có thể tìm thấy nghịch đảo mô-đun của bất kỳ byte đã cho nào chỉ bằng cách tra cứu chỉ số (dựa trên không) của nó trong mảng này, phủ nhận nó và tìm kiếm lên byte tại chỉ mục phủ định trong cùng một mảng. (Trong GolfScript, như trong nhiều ngôn ngữ lập trình khác, các chỉ mục mảng âm tính ngược từ cuối mảng.) Thật vậy, đây chính xác là những gì mã L?~)L=ở đầu hàm Sthực hiện.

Phần còn lại của mã gọi hàm trợ giúp rbốn lần với bitmask khử 256 = 2 8 + 1 để tạo bốn bản sao xoay bit của byte đầu vào đảo ngược. Tất cả đều được thu thập thành một mảng, cùng với hằng số 99 = 0x63 và XOR cùng nhau để tạo ra đầu ra cuối cùng.


7

x86-64 Mã máy - 23 22 20 19 byte

Sử dụng tập lệnh 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

Sử dụng các quy ước gọi Windows, lấy một byte và xuất ra một byte. Không cần thiết phải đảo ngược ShiftRowsvì nó không ảnh hưởng đến byte đầu tiên.


2
Lần đầu tiên, x86_64 kéo một toán học và có một nội dung cho điều đó.
moonheart08

6

Bảng có thể được tạo mà không cần tính toán nghịch đảo trong trường hữu hạn GF (256), bằng cách sử dụng logarit. Nó sẽ trông như thế này (mã Java, sử dụng intđể tránh các vấn đề với byteloại đã ký ):

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

Ý tưởng là 3 là một trình tạo nhân của GF (256) *. Bảng t[]là như vậy t[x]bằng 3 x ; kể từ 3 255 = 1, chúng ta nhận được 1 / (3 x ) = 3 255-x .


không nên 0x1B(một trong một chữ lục) thay vì0x11B
ratchet freak

@ratchetfreak: không, phải là 0x11B (tôi đã thử). Các intloại là 32-bit trong Java; Tôi phải hủy bỏ bit cao hơn.
Thomas Pornin

ah đã không nhận ra rằng
ratchet freak

Đó có phải là >>> thay vì >> trong dòng 4 không?
Joe Z.

@JoeZeng: cả hai sẽ hoạt động. Trong Java, '>>>' là "dịch chuyển không dấu", '>>' là "dịch chuyển có chữ ký". Chúng khác nhau bởi cách chúng xử lý bit dấu. Ở đây, các giá trị sẽ không bao giờ đủ rộng để bit dấu là khác không, vì vậy nó không tạo ra sự khác biệt thực sự.
Thomas Pornin

6

GolfScript (82 ký tự)

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

Sử dụng các biến toàn cục AB, và tạo ra hàm là biến toàn cục S.

Sự đảo ngược của Galois là vũ phu; Tôi đã thử nghiệm với một mulchức năng riêng biệt có thể được sử dụng lại cho phép biến đổi affine sau đảo ngược, nhưng hóa ra nó đắt hơn vì hành vi tràn khác nhau.

Điều này quá chậm đối với một bản demo trực tuyến - nó sẽ hết thời gian ngay cả trên hai hàng đầu tiên của bảng.


Của tôi nhanh hơn (và ngắn hơn;). Dù sao +1 cũng vậy.
Ilmari Karonen

4

Python, 176 ký tự

Câu trả lời này dành cho câu hỏi bình luận của Paŭlo Ebermann về việc làm cho hàm không đổi theo thời gian. Mã này phù hợp với hóa đơn.

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

Phép nhân là thời gian không đổi phụ thuộc vào nền tảng (ngay cả trên nền tảng 32 bit, ví dụ ARM Cortex M0). Xem câu hỏi liên quan
fgrieu 6/03/2015

1
@fgrieu Chắc chắn, nhưng đây là tất cả các phép nhân của hằng số, có thể dễ dàng thực hiện trong thời gian liên tục bằng cách sử dụng ca và bổ sung.
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;

}

điều này có thể tạo bảng tra cứu tại thời gian biên dịch, tôi có thể tiết kiệm một số bằng cách biến ubyte thành một tham số chung

chỉnh sửa trực tiếp ubyteđể ubytekhông cần tra cứu mảng, không phân nhánh và các vòng lặp hoàn toàn không thể kiểm soát

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 đã sử dụng thuật toán của @Thomas để tạo bảng tra cứu


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.