Rijndael의 S-box 는 AES 암호화 및 암호 해독 에서 자주 사용되는 작업입니다 . 일반적으로 256 바이트 조회 테이블로 구현됩니다. 빠르지 만 코드에 256 바이트 조회 테이블을 열거해야 함을 의미합니다. 나는이 군중의 누군가가 기본 수학 구조를 감안할 때 적은 코드로 그것을 할 수 있다고 확신합니다.
Rijndael의 S-box를 구현하는 좋아하는 언어로 함수를 작성하십시오. 가장 짧은 코드가 승리합니다.
Rijndael의 S-box 는 AES 암호화 및 암호 해독 에서 자주 사용되는 작업입니다 . 일반적으로 256 바이트 조회 테이블로 구현됩니다. 빠르지 만 코드에 256 바이트 조회 테이블을 열거해야 함을 의미합니다. 나는이 군중의 누군가가 기본 수학 구조를 감안할 때 적은 코드로 그것을 할 수 있다고 확신합니다.
Rijndael의 S-box를 구현하는 좋아하는 언어로 함수를 작성하십시오. 가장 짧은 코드가 승리합니다.
답변:
{[[0 1{.283{1$2*.255>@*^}:r~^}255*].@?~)={257r}4*99]{^}*}:S;
이 코드는 S
바이트 단위로 이름 이 지정된 함수를 정의 하고 Rijndael S 상자를 적용합니다. (또한 r
몇 개의 문자를 저장하기 위해 명명 된 내부 도우미 함수를 사용합니다 .)
이 구현은 Thomas Pornin에서 제안한 대로 로그 테이블을 사용하여 GF (2 8 ) 역 을 계산합니다 . 몇 개의 문자를 저장하기 위해 각 입력 바이트에 대해 전체 로그 테이블이 다시 계산됩니다. 그럼에도 불구하고 GolfScript가 일반적으로 매우 느린 언어 임에도 불구 하고이 코드는 오래된 랩톱에서 바이트를 처리하는 데 약 10ms가 걸립니다. 로그 테이블을 미리 계산하면 (와 같이 ) 3 자 이상의 추가 비용을 들이지 않고 바이트 당 최대 약 0.5ms의 속도를냅니다.L
[0 1{.283{1$2*.255>@*^}:r~^}255*]:L;{[L?~)L={257r}4*99]{^}*}:S;
편의를 위해 S
위의 정의에 따라 함수를 호출하여 위키 백과 에서와 같이 S- 박스 전체를 16 진수로 계산하고 인쇄 하는 간단한 테스트 하네스가 있습니다 .
"0123456789abcdef"1/:h; 256, {S .16/h= \16%h= " "++ }% 16/ n*
(온라인 데모는 너무 많은 시간이 걸리지 않도록 로그 테이블을 미리 계산합니다. 그럼에도 불구하고 온라인 GolfScript 사이트는 때때로 시간 초과 될 수 있습니다. 이는 사이트의 알려진 문제이므로 일반적으로 다시로드하면 문제가 해결됩니다.)
로그 테이블 계산, 특히 도우미 함수부터 시작하겠습니다 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)로 함수 가 호출 되고 결과는 XOR입니다 원래 바이트 를 사용하여 Rijndael 유한 필드에서 3 ( 다항식으로 = x + 1)을 효과적으로 곱 합니다. 이 곱셈은 바이트 1부터 255 번 반복되며 결과 (초기 0 바이트 포함)는 L
다음과 같은 257 요소 배열로 수집됩니다 (중간 부분은 생략 됨).
[0 1 3 5 15 17 51 85 255 26 46 ... 180 199 82 246 1]
257 개의 요소가있는 이유는 앞에 0이 붙고 1이 두 번 발생하면이 배열에서 (0부터 시작) 인덱스를 찾아서 무시하고 살펴보면 주어진 바이트의 모듈러 역수를 찾을 수 있기 때문입니다. 같은 배열의 부정 인덱스의 바이트를 증가시킵니다. 다른 많은 프로그래밍 언어와 마찬가지로 GolfScript에서는 음수 배열 인덱스가 배열의 끝부터 거꾸로 계산됩니다. 실제로 L?~)L=
함수 시작 부분의 코드가하는 것과 정확히 같습니다 S
.
나머지 코드 r
는 축소 비트 마스크 257 = 2 8 + 1을 사용 하여 도우미 함수를 네 번 호출 하여 반전 된 입력 바이트의 비트 회전 복사본 4 개를 만듭니다. 이것들은 모두 상수 99 = 0x63과 함께 배열로 수집되며 XOR은 함께 최종 출력을 생성합니다.
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
첫 번째 바이트에 영향을 미치지 않기 때문에 를 뒤집을 필요는 없습니다 .
로그를 사용하여 유한 필드 GF (256)에서 역을 계산하지 않고 테이블을 생성 할 수 있습니다. 다음과 같이 표시됩니다 ( int
서명 된 byte
유형의 문제점을 피하기 위해 사용 하는 Java 코드 ).
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
int
유형은 자바 32 비트입니다; 더 높은 비트를 취소해야합니다.
{256:B,{0\2${@1$3$1&*^@2/@2*.B/283*^}8*;;1=},\+0=B)*:A.2*^4A*^8A*^128/A^99^B(&}:S;
글로벌 변수를 사용 A
하고 B
, 글로벌 변수와 함수를 생성한다 S
.
갈루아 역전은 무차별 적입니다. 나는 mul
반전 후 아핀 변환에 재사용 할 수 있는 별도의 함수를 실험 했지만 다른 오버 플로우 동작으로 인해 더 비싸다는 것이 밝혀졌습니다.
온라인 데모에서는 속도가 너무 느립니다. 테이블의 처음 두 행에서도 시간이 초과됩니다.
이 답변은 함수를 일정하게 유지하는 것에 대한 Paŭlo Ebermann의 의견 질문에 대한 것입니다. 이 코드는 청구서에 적합합니다.
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
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를 일반 매개 변수로 만들어 일부를 절약 할 수 있습니다
편집은 직접하지 ubyte
에 ubyte
, 배열 조회없이 아무 분기와 완전히 unrollable 루프를
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 'algo를 사용했습니다.