* Re: [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ @ 2007-06-11 7:53 linux 2007-06-11 19:17 ` Benjamin Gilbert 0 siblings, 1 reply; 8+ messages in thread From: linux @ 2007-06-11 7:53 UTC (permalink / raw) To: bgilbert, linux-kernel; +Cc: linux +#define F3(x,y,z) \ + movl x, TMP2; \ + andl y, TMP2; \ + movl x, TMP; \ + orl y, TMP; \ + andl z, TMP; \ + orl TMP2, TMP *Sigh*. You don't need TMP2 to compute the majority function. You're implementing it as (x & y) | ((x | y) & z). Look at the rephrasing in lib/sha1.c: #define f3(x,y,z) ((x & y) + (z & (x ^ y))) /* majority */ By changing the second OR to x^y, you ensure that the two halves of the first disjunciton are distinct, so you can replace the OR with XOR, or better yet, +. Then you can just do two adds to e. That is, write: /* Bitwise select: x ? y : z, which is (z ^ (x & (y ^ z))) */ #define F1(x,y,z,dest) \ movl z, TMP; \ xorl y, TMP; \ andl x, TMP; \ xorl z, TMP; \ addl TMP, dest /* Three-way XOR (x ^ y ^ z) */ #define F2(x,y,z,dest) \ movl z, TMP; \ xorl x, TMP; \ xorl y, TMP; \ addl TMP, dest /* Majority: (x^y)|(y&z)|(z&x) = (x & z) + ((x ^ z) & y) #define F3(x,y,z,dest) \ movl z, TMP; \ andl x, TMP; \ addl TMP, dest; \ movl z, TMP; \ xorl x, TMP; \ andl y, TMP; \ addl TMP, dest Since y is the most recently computed result (it's rotated in the previous round), I arranged the code to delay its use as late as possible. Now you have one more register to play with. I thought I had some good sha1 asm code lying around, but I can't seem to find it. (I have some excellent PowerPC asm if anyone wants it.) Here's a basic implementation question: SHA-1 is made up of 80 rounds, 20 of each of 4 types. There are 5 working variables, a through e. The basic round is: t = F(b, c, d) + K + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; where W[] is the input array. W[0..15] are the input words, and W[16..79] are computed by a sort of LFSR from W[0..15]. Each group of 20 rounds has a different F() and K. This is the smallest way to write the function, but all the register shuffling makes for a bit of a speed penalty. A faster way is to unroll 5 iterations and do: e += F(b, c, d) + K + rol32(a, 5) + W[i ]; b = rol32(b, 30); d += F(a, b, c) + K + rol32(e, 5) + W[i+1]; a = rol32(a, 30); c += F(e, a, b) + K + rol32(d, 5) + W[i+2]; e = rol32(e, 30); b += F(d, e, a) + K + rol32(c, 5) + W[i+3]; d = rol32(d, 30); a += F(c, d, e) + K + rol32(b, 5) + W[i+4]; c = rol32(c, 30); then loop over that 4 times each. This is somewhat larger, but still reasonably compact; only 20 of the 80 rounds are written out long-hand. Faster yet is to unroll all 80 rounds directly. But it also takes the most code space, and as we have learned, when your code is not the execution time hot spot, less cache is faster code. Is there a preferred implementation? Another implementation choice has to do with the computation of W[]. W[i] is a function of W[i-3], W[i-8], W[i-14] and W[i-16]. It is possible to keep a 16-word circular buffer with only the most recent 16 values of W[i%16] and compute each new word as it is needed. However, the offsets i%16 repeat every 16 rounds, which is an awkward fit with the 5-round repeating pattern of the main computation. One option is to compute all the W[] values in a pre-pass beforehand. Simple and small, but uses 320 bytes of data on the stack or wherever. An intermediate one is to keep a 20-word buffer, and compute 20 words at a time just before each of the 20 round groups. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ 2007-06-11 7:53 [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ linux @ 2007-06-11 19:17 ` Benjamin Gilbert 2007-06-12 5:05 ` linux 0 siblings, 1 reply; 8+ messages in thread From: Benjamin Gilbert @ 2007-06-11 19:17 UTC (permalink / raw) To: linux; +Cc: linux-kernel linux@horizon.com wrote: > /* Majority: (x^y)|(y&z)|(z&x) = (x & z) + ((x ^ z) & y) > #define F3(x,y,z,dest) \ > movl z, TMP; \ > andl x, TMP; \ > addl TMP, dest; \ > movl z, TMP; \ > xorl x, TMP; \ > andl y, TMP; \ > addl TMP, dest > > Since y is the most recently computed result (it's rotated in the > previous round), I arranged the code to delay its use as late as > possible. > > > Now you have one more register to play with. Okay, thanks. It doesn't actually give one more register except in the F3 rounds (TMP2 is normally used to hold the magic constants) but it's a good cleanup. > A faster way is to unroll 5 iterations and do: > e += F(b, c, d) + K + rol32(a, 5) + W[i ]; b = rol32(b, 30); > d += F(a, b, c) + K + rol32(e, 5) + W[i+1]; a = rol32(a, 30); > c += F(e, a, b) + K + rol32(d, 5) + W[i+2]; e = rol32(e, 30); > b += F(d, e, a) + K + rol32(c, 5) + W[i+3]; d = rol32(d, 30); > a += F(c, d, e) + K + rol32(b, 5) + W[i+4]; c = rol32(c, 30); > then loop over that 4 times each. This is somewhat larger, but > still reasonably compact; only 20 of the 80 rounds are written out > long-hand. I got this code from Nettle, originally, and I never looked at the SHA-1 round structure very closely. I'll give that approach a try. Thanks --Benjamin Gilbert ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ 2007-06-11 19:17 ` Benjamin Gilbert @ 2007-06-12 5:05 ` linux 2007-06-13 5:29 ` [PATCH] random: fix folding Matt Mackall 2007-06-13 5:50 ` [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ Matt Mackall 0 siblings, 2 replies; 8+ messages in thread From: linux @ 2007-06-12 5:05 UTC (permalink / raw) To: bgilbert, linux; +Cc: linux-kernel > I got this code from Nettle, originally, and I never looked at the SHA-1 > round structure very closely. I'll give that approach a try. Attached is some (tested, working, and public domain) assembly code for three different sha_transform implementations. Compared to C code, the timings to hash 10 MiB on a 600 MHz PIII are: One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 564819 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 391086 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 399134 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 345986 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 301152 us One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 558652 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 390980 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 407661 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 412434 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 266809 us One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 559053 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 396506 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 401661 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 349668 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 265861 us One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 556082 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 392967 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 406381 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 338959 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 274712 us Um.. some more runs, nice --19, that come out a bit more stable: One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 552971 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 388167 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 398721 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 337220 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 259790 us One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 551240 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 387812 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 398519 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 336903 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 260161 us One: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 551934 us Four: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 387639 us Two: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 398094 us Three: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 335860 us Five: e02e77e3 bb7af36d bbf79d4a 46956044 2aaea172 -- 259805 us This is hot-cache testing; I haven't got around to writing macro tricks that exapnd to a megabyte of object code. The challenge is to purge not only the I- and D-caches, but also the branch predictor! The names are the order they were written in. "One" is the lib/sha1.c code (547 bytes with -Os). "Four" is a 5x unrolled C version (1106 bytes). "Two" is a space-optimized ASM version, 266 bytes long. "Three" is 5x unrolled, 722 bytes long. "Five" is a fully unrolled version, 3558 bytes long. (Further space savings are possible, but it doesn't seem worth it.) I have noticed that every caller of sha_transform in the kernel tree allocates the W[] array on the stack, so we might as well do that inside sha_transform. The point of passing in the buffer is to amortize the wiping afterwards, but see sha_stackwipe for ideas on how to do that. (It can even be done mostly portably in C, given a good guess about the C function's stack usage.) I also noticed a glaring BUG in the folding at the end of extract_buf at drivers/char/random.c:797. That should be: /* * In case the hash function has some recognizable * output pattern, we fold it in half. */ buf[0] ^= buf[4]; buf[1] ^= buf[3]; buf[2] ^= rol32(buf[2], 16); // <--- Bug was here memcpy(out, buf, EXTRACT_SIZE); memset(buf, 0, sizeof(buf)); if the code is to match the comment. === sha1asm.S === #define A %eax #define B %ebx #define C %ecx #define D %edx #define E %ebp #define I %esi #define T %edi # f1(x,y,z) = bitwise x ? y : z = (z ^ (x & (y ^ z))) #define F1(x,y,z,dest) \ movl z,T; \ xorl y,T; \ andl x,T; \ xorl z,T # f2(x,y,z) = x ^ y ^ z #define F2(x,y,z,dest) \ movl z,T; \ xorl x,T; \ xorl y,T # f3(x,y,z) = majority(x,y,z) = ((x & z) + (y & (x ^ z))) #define F3(x,y,z,dest) \ movl z,T; \ andl x,T; \ addl T,dest; \ movl z,T; \ xorl x,T; \ andl y,T #define K1 0x5A827999 /* Rounds 0-19: sqrt(2) * 2^30 */ #define K2 0x6ED9EBA1 /* Rounds 20-39: sqrt(3) * 2^30 */ #define K3 0x8F1BBCDC /* Rounds 40-59: sqrt(5) * 2^30 */ #define K4 0xCA62C1D6 /* Rounds 60-79: sqrt(10) * 2^30 */ # e += W[i] + K + f(b, c, d) + rol32(a, 5); b = rol32(b, 30); i++; #define ROUND(a,b,c,d,e,f,k) \ addl (%esp,I,4),e; \ incl I; \ f(b,c,d,e); \ leal k(T,e),e; \ movl a,T; \ roll $5,T; \ rorl $2,b; \ addl T,e .text .globl sha_transform3 .type sha_transform3, @function # void sha_transform3(__u32 digest[5], const char in[64]) sha_transform3: pushl %ebp pushl %edi pushl %esi pushl %ebx # Args start at 20(%esp) xorl I,I movl 24(%esp),B # B = in movl 20(%esp),T # T = digest subl $320,%esp 1: movl (B,I,4),A bswap A movl A,(%esp,I,4) incl I cmpl $16,I jne 1b 2: movl -64(%esp,I,4),A xorl -56(%esp,I,4),A xorl -32(%esp,I,4),A xorl -12(%esp,I,4),A roll $1,A movl A,(%esp,I,4) incl I cmpl $80,I jne 2b movl (T),A movl 4(T),B movl 8(T),C movl 12(T),D movl 16(T),E xorl I,I 3: ROUND(A,B,C,D,E,F1,K1) ROUND(E,A,B,C,D,F1,K1) ROUND(D,E,A,B,C,F1,K1) ROUND(C,D,E,A,B,F1,K1) ROUND(B,C,D,E,A,F1,K1) cmp $20,I jne 3b 4: ROUND(A,B,C,D,E,F2,K2) ROUND(E,A,B,C,D,F2,K2) ROUND(D,E,A,B,C,F2,K2) ROUND(C,D,E,A,B,F2,K2) ROUND(B,C,D,E,A,F2,K2) cmp $40,I jne 4b 5: ROUND(A,B,C,D,E,F3,K3) ROUND(E,A,B,C,D,F3,K3) ROUND(D,E,A,B,C,F3,K3) ROUND(C,D,E,A,B,F3,K3) ROUND(B,C,D,E,A,F3,K3) cmp $60,I jne 5b 6: ROUND(A,B,C,D,E,F2,K4) ROUND(E,A,B,C,D,F2,K4) ROUND(D,E,A,B,C,F2,K4) ROUND(C,D,E,A,B,F2,K4) ROUND(B,C,D,E,A,F2,K4) cmp $80,I jne 6b addl $320,%esp movl 20(%esp),T addl A,(T) addl B,4(T) addl C,8(T) addl D,12(T) addl E,16(T) popl %ebx popl %esi popl %edi popl %ebp ret .size sha_transform3, .-sha_transform3 # Size is 0x2D2 = 722 bytes # A smaller variant #define ROUND2(a,b,c,d,e,f,k) \ addl (%esp,I,4),e; \ incl I; \ f(b,c,d,e); \ leal k(T,e),T; \ movl d,e; \ movl c,d; \ movl b,c; \ rorl $2,c; \ movl a,b; \ roll $5,a; \ addl T,a .globl sha_transform2 .type sha_transform2, @function # void sha_transform2(__u32 digest[5], const char in[64]) sha_transform2: pushl %ebp pushl %edi pushl %esi pushl %ebx # Args start at 20(%esp) xorl I,I movl 24(%esp),B # B = in movl 20(%esp),T # T = digest subl $320,%esp 1: movl (B,I,4),A bswap A movl A,(%esp,I,4) incl I cmpl $16,I jne 1b 2: movl -64(%esp,I,4),A xorl -56(%esp,I,4),A xorl -32(%esp,I,4),A xorl -12(%esp,I,4),A roll $1,A movl A,(%esp,I,4) incl I cmpl $80,I jne 2b movl (T),A movl 4(T),B movl 8(T),C movl 12(T),D movl 16(T),E xorl I,I 3: ROUND2(A,B,C,D,E,F1,K1) cmp $20,I jne 3b 4: ROUND2(A,B,C,D,E,F2,K2) cmp $40,I jne 4b 5: ROUND2(A,B,C,D,E,F3,K3) cmp $60,I jne 5b 6: ROUND2(A,B,C,D,E,F2,K4) cmp $80,I jne 6b addl $320,%esp movl 20(%esp),T addl A,(T) addl B,4(T) addl C,8(T) addl D,12(T) addl E,16(T) popl %ebx popl %esi popl %edi popl %ebp ret .size sha_transform2, .-sha_transform2 # Size is 0x10A = 266 bytes # The three cases of the next input word... # Fetch big-endian (first 16 rounds) #define FETCH(i) \ movl 4*i(I),T; \ bswap T; \ movl T,4*i(%esp) # Calculate but don't store (last 3 rounds) #define CALCX(i) \ movl 4*(i&15)(%esp),T; \ xorl 4*((i+2)&15)(%esp),T; \ xorl 4*((i+8)&15)(%esp),T; \ xorl 4*((i+13)&15)(%esp),T; \ roll $1,T # Calculate and store on stack (middle 61 rounds) #define CALC(i) \ CALCX(i); \ movl T,4*(i&15)(%esp) # e += W[i] + K + f(b, c, d) + rol32(a, 5); b = rol32(b, 30); i++; #define ROUND5a(a,b,c,d,e,f,k) \ leal k(T,e),e; \ f(b,c,d,e); \ addl T,e; \ movl a,T; \ roll $5,T; \ rorl $2,b; \ addl T,e # A variant that assumes that k is stored in I #define ROUND5b(a,b,c,d,e,f) \ addl I,e; \ addl T,e; \ f(b,c,d,e); \ addl T,e; \ movl a,T; \ roll $5,T; \ rorl $2,b; \ addl T,e .globl sha_transform5 .type sha_transform5, @function # void sha_transform5(__u32 digest[5], const char in[64]) sha_transform5: pushl %ebp pushl %edi pushl %esi pushl %ebx # Args start at 20(%esp) movl 24(%esp),I # I = in movl 20(%esp),T # T = digest subl $64,%esp movl (T),A movl 4(T),B movl 8(T),C movl 12(T),D movl 16(T),E FETCH(0); ROUND5a(A,B,C,D,E,F1,K1) FETCH(1); ROUND5a(E,A,B,C,D,F1,K1) FETCH(2); ROUND5a(D,E,A,B,C,F1,K1) FETCH(3); ROUND5a(C,D,E,A,B,F1,K1) FETCH(4); ROUND5a(B,C,D,E,A,F1,K1) FETCH(5); ROUND5a(A,B,C,D,E,F1,K1) FETCH(6); ROUND5a(E,A,B,C,D,F1,K1) FETCH(7); ROUND5a(D,E,A,B,C,F1,K1) FETCH(8); ROUND5a(C,D,E,A,B,F1,K1) FETCH(9); ROUND5a(B,C,D,E,A,F1,K1) FETCH(10); ROUND5a(A,B,C,D,E,F1,K1) FETCH(11); ROUND5a(E,A,B,C,D,F1,K1) FETCH(12); ROUND5a(D,E,A,B,C,F1,K1) FETCH(13); ROUND5a(C,D,E,A,B,F1,K1) FETCH(14); ROUND5a(B,C,D,E,A,F1,K1) FETCH(15); movl $K1,I; ROUND5b(A,B,C,D,E,F1) CALC(16); ROUND5b(E,A,B,C,D,F1) CALC(17); ROUND5b(D,E,A,B,C,F1) CALC(18); ROUND5b(C,D,E,A,B,F1) CALC(19); ROUND5b(B,C,D,E,A,F1) movl $K2,I CALC(20); ROUND5b(A,B,C,D,E,F2) CALC(21); ROUND5b(E,A,B,C,D,F2) CALC(22); ROUND5b(D,E,A,B,C,F2) CALC(23); ROUND5b(C,D,E,A,B,F2) CALC(24); ROUND5b(B,C,D,E,A,F2) CALC(25); ROUND5b(A,B,C,D,E,F2) CALC(26); ROUND5b(E,A,B,C,D,F2) CALC(27); ROUND5b(D,E,A,B,C,F2) CALC(28); ROUND5b(C,D,E,A,B,F2) CALC(29); ROUND5b(B,C,D,E,A,F2) CALC(30); ROUND5b(A,B,C,D,E,F2) CALC(31); ROUND5b(E,A,B,C,D,F2) CALC(32); ROUND5b(D,E,A,B,C,F2) CALC(33); ROUND5b(C,D,E,A,B,F2) CALC(34); ROUND5b(B,C,D,E,A,F2) CALC(35); ROUND5b(A,B,C,D,E,F2) CALC(36); ROUND5b(E,A,B,C,D,F2) CALC(37); ROUND5b(D,E,A,B,C,F2) CALC(38); ROUND5b(C,D,E,A,B,F2) CALC(39); ROUND5b(B,C,D,E,A,F2) movl $K3,I CALC(40); ROUND5b(A,B,C,D,E,F3) CALC(41); ROUND5b(E,A,B,C,D,F3) CALC(42); ROUND5b(D,E,A,B,C,F3) CALC(43); ROUND5b(C,D,E,A,B,F3) CALC(44); ROUND5b(B,C,D,E,A,F3) CALC(45); ROUND5b(A,B,C,D,E,F3) CALC(46); ROUND5b(E,A,B,C,D,F3) CALC(47); ROUND5b(D,E,A,B,C,F3) CALC(48); ROUND5b(C,D,E,A,B,F3) CALC(49); ROUND5b(B,C,D,E,A,F3) CALC(50); ROUND5b(A,B,C,D,E,F3) CALC(51); ROUND5b(E,A,B,C,D,F3) CALC(52); ROUND5b(D,E,A,B,C,F3) CALC(53); ROUND5b(C,D,E,A,B,F3) CALC(54); ROUND5b(B,C,D,E,A,F3) CALC(55); ROUND5b(A,B,C,D,E,F3) CALC(56); ROUND5b(E,A,B,C,D,F3) CALC(57); ROUND5b(D,E,A,B,C,F3) CALC(58); ROUND5b(C,D,E,A,B,F3) CALC(59); ROUND5b(B,C,D,E,A,F3) movl $K4,I CALC(60); ROUND5b(A,B,C,D,E,F2) CALC(61); ROUND5b(E,A,B,C,D,F2) CALC(62); ROUND5b(D,E,A,B,C,F2) CALC(63); ROUND5b(C,D,E,A,B,F2) CALC(64); ROUND5b(B,C,D,E,A,F2) CALC(65); ROUND5b(A,B,C,D,E,F2) CALC(66); ROUND5b(E,A,B,C,D,F2) CALC(67); ROUND5b(D,E,A,B,C,F2) CALC(68); ROUND5b(C,D,E,A,B,F2) CALC(69); ROUND5b(B,C,D,E,A,F2) CALC(70); ROUND5b(A,B,C,D,E,F2) CALC(71); ROUND5b(E,A,B,C,D,F2) CALC(72); ROUND5b(D,E,A,B,C,F2) CALC(73); ROUND5b(C,D,E,A,B,F2) CALC(74); ROUND5b(B,C,D,E,A,F2) CALC(75); ROUND5b(A,B,C,D,E,F2) CALC(76); ROUND5b(E,A,B,C,D,F2) CALCX(77); ROUND5b(D,E,A,B,C,F2) CALCX(78); ROUND5b(C,D,E,A,B,F2) CALCX(79); ROUND5b(B,C,D,E,A,F2) addl $64,%esp movl 20(%esp),I #if 1 addl A,(I) addl B,4(I) addl C,8(I) addl D,12(I) addl E,16(I) #else movl A,(I) movl B,4(I) movl C,8(I) movl D,12(I) movl E,16(I) #endif popl %ebx popl %esi popl %edi popl %ebp ret .size sha_transform5, .-sha_transform5 # Size is 0xDE6 = 3558 bytes .globl sha_stackwipe .type sha_stackwipe, @function # void sha_stackwipe(void) # After one or more sha_transform calls, we have left the contents of W[] # on the stack, and from any 16 of those 80 words, the entire input # can be reconstructed. If the caller cares, this function obliterates # the relevant portion of the stack. # 2 words of argument + 4 woirds of saved registers + 80 words of W[] sha_stackwipe: xorl %eax,%eax movl $86,%ecx # Damn, I had hoped that loop; pushl %eax would work.. 1: decl %ecx pushl %eax jne 1b addl $4*86,%esp ret .size sha_stackwipe, .-sha_stackwipe ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] random: fix folding 2007-06-12 5:05 ` linux @ 2007-06-13 5:29 ` Matt Mackall 2007-06-13 5:45 ` linux 2007-06-13 5:50 ` [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ Matt Mackall 1 sibling, 1 reply; 8+ messages in thread From: Matt Mackall @ 2007-06-13 5:29 UTC (permalink / raw) To: linux, akpm; +Cc: bgilbert, linux-kernel, Theodore Tso On Tue, Jun 12, 2007 at 01:05:44AM -0400, linux@horizon.com wrote: > I also noticed a glaring BUG in the folding at the end of extract_buf at > drivers/char/random.c:797. That should be: > > /* > * In case the hash function has some recognizable > * output pattern, we fold it in half. > */ > > buf[0] ^= buf[4]; > buf[1] ^= buf[3]; > buf[2] ^= rol32(buf[2], 16); // <--- Bug was here > memcpy(out, buf, EXTRACT_SIZE); > memset(buf, 0, sizeof(buf)); > > if the code is to match the comment. Conveniently, the random.c maintainer is reading this thread. Good spotting, not sure how I bungled that. ---- random: fix output buffer folding (As reported by linux@horizon.com) Folding is done to minimize the theoretical possibility of systematic weakness in the particular bits of the SHA1 hash output. The result of this bug is that 16 out of 80 bits are un-folded. Without a major new vulnerability being found in SHA1, this is harmless, but still worth fixing. Signed-off-by: Matt Mackall <mpm@selenic.com> Index: mm/drivers/char/random.c =================================================================== --- mm.orig/drivers/char/random.c 2007-06-12 23:50:54.000000000 -0500 +++ mm/drivers/char/random.c 2007-06-12 23:51:51.000000000 -0500 @@ -794,7 +794,7 @@ static void extract_buf(struct entropy_s buf[0] ^= buf[3]; buf[1] ^= buf[4]; - buf[0] ^= rol32(buf[3], 16); + buf[2] ^= rol32(buf[2], 16); memcpy(out, buf, EXTRACT_SIZE); memset(buf, 0, sizeof(buf)); } -- Mathematics is the supreme nostalgia of our time. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] random: fix folding 2007-06-13 5:29 ` [PATCH] random: fix folding Matt Mackall @ 2007-06-13 5:45 ` linux 2007-06-13 6:08 ` Matt Mackall 0 siblings, 1 reply; 8+ messages in thread From: linux @ 2007-06-13 5:45 UTC (permalink / raw) To: akpm, linux, mpm; +Cc: bgilbert, linux-kernel, tytso > Folding is done to minimize the theoretical possibility of systematic > weakness in the particular bits of the SHA1 hash output. The result of > this bug is that 16 out of 80 bits are un-folded. Without a major new > vulnerability being found in SHA1, this is harmless, but still worth > fixing. Actually, even WITH a major new vulnerability found in SHA1, it's harmless. Sorry to put BUG in caps earlier; it actually doesn't warrant the sort of adjective I used. The purpose of the folding is to ensure that the feedback includes bits underivable from the output. Just outputting the first 80 bits and feeding back all 160 would achieve that effect; the folding is of pretty infinitesimal benefit. Note that last five rounds have as major outputs e, d, c, b, and a, in that order. Thus, the first words are the "most hashed" and the ones most worth using as output... which happens naturally with no folding. The folding is a submicroscopic bit of additional mixing. Frankly, the code size savings probably makes it worth deleting it. (That would also give you more flexibility to select the output/feedback ratio in whatever way you like.) ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] random: fix folding 2007-06-13 5:45 ` linux @ 2007-06-13 6:08 ` Matt Mackall 0 siblings, 0 replies; 8+ messages in thread From: Matt Mackall @ 2007-06-13 6:08 UTC (permalink / raw) To: linux; +Cc: akpm, bgilbert, linux-kernel, tytso On Wed, Jun 13, 2007 at 01:45:21AM -0400, linux@horizon.com wrote: > > Folding is done to minimize the theoretical possibility of systematic > > weakness in the particular bits of the SHA1 hash output. The result of > > this bug is that 16 out of 80 bits are un-folded. Without a major new > > vulnerability being found in SHA1, this is harmless, but still worth > > fixing. > > Actually, even WITH a major new vulnerability found in SHA1, it's > harmless. Sorry to put BUG in caps earlier; it actually doesn't warrant > the sort of adjective I used. The purpose of the folding is to ensure that > the feedback includes bits underivable from the output. Just outputting > the first 80 bits and feeding back all 160 would achieve that effect; > the folding is of pretty infinitesimal benefit. Well we're actually not feeding back those 160 bits. But we are hashing over the entire pool and feeding -that- back beforehand, which is equivalent. > Note that last five rounds have as major outputs e, d, c, b, and a, > in that order. Thus, the first words are the "most hashed" and > the ones most worth using as output... which happens naturally with > no folding. Indeed, though we actually want to keep random.c agnostic as to the type of hash actually used. > The folding is a submicroscopic bit of additional mixing. > Frankly, the code size savings probably makes it worth deleting it. > (That would also give you more flexibility to select the output/feedback > ratio in whatever way you like.) I'd tend to agree with you. Even if we did no folding at all in this last stage, we already do plenty of feedback. For every 80 bits extracted, we feed back at least.. 320. -- Mathematics is the supreme nostalgia of our time. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ 2007-06-12 5:05 ` linux 2007-06-13 5:29 ` [PATCH] random: fix folding Matt Mackall @ 2007-06-13 5:50 ` Matt Mackall 2007-06-13 6:46 ` linux 1 sibling, 1 reply; 8+ messages in thread From: Matt Mackall @ 2007-06-13 5:50 UTC (permalink / raw) To: linux; +Cc: bgilbert, linux-kernel On Tue, Jun 12, 2007 at 01:05:44AM -0400, linux@horizon.com wrote: > > I got this code from Nettle, originally, and I never looked at the SHA-1 > > round structure very closely. I'll give that approach a try. > > Attached is some (tested, working, and public domain) assembly code for > three different sha_transform implementations. Compared to C code, the > timings to hash 10 MiB on a 600 MHz PIII are: > > The names are the order they were written in. "One" is the lib/sha1.c > code (547 bytes with -Os). "Four" is a 5x unrolled C version (1106 bytes). I'd like to see your version four. -- Mathematics is the supreme nostalgia of our time. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ 2007-06-13 5:50 ` [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ Matt Mackall @ 2007-06-13 6:46 ` linux 0 siblings, 0 replies; 8+ messages in thread From: linux @ 2007-06-13 6:46 UTC (permalink / raw) To: linux, mpm; +Cc: bgilbert, linux-kernel >> The names are the order they were written in. "One" is the lib/sha1.c >> code (547 bytes with -Os). "Four" is a 5x unrolled C version (1106 bytes). > > I'd like to see your version four. Here's the test driver wrapped around the earlier assembly code. It's an ugly mess of copy & paste code, of course. I suspect it could be shrunk by allocating the W[] array locally, thereby freeing up a register. Size is -Os -fomit-frame-pointer. /* * SHA transform algorithm, originally taken from code written by * Peter Gutmann, and placed in the public domain. */ #include <stdint.h> #include <stdio.h> #define rol32(x, s) ((x)<<(s) | (x)>>(32-(s))) static inline uint32_t __attribute__((const)) be32_to_cpu(unsigned x) { asm("bswap %0" : "+r"(x)); return x; } /* The SHA f()-functions. */ #define f1(x,y,z) (z ^ (x & (y ^ z))) /* x ? y : z */ #define f2(x,y,z) (x ^ y ^ z) /* XOR */ #define f3(x,y,z) ((x & y) + (z & (x ^ y))) /* majority */ /* The SHA Mysterious Constants */ #define K1 0x5A827999L /* Rounds 0-19: sqrt(2) * 2^30 */ #define K2 0x6ED9EBA1L /* Rounds 20-39: sqrt(3) * 2^30 */ #define K3 0x8F1BBCDCL /* Rounds 40-59: sqrt(5) * 2^30 */ #define K4 0xCA62C1D6L /* Rounds 60-79: sqrt(10) * 2^30 */ /** * sha_transform - single block SHA1 transform * * @digest: 160 bit digest to update * @data: 512 bits of data to hash * @W: 80 words of workspace (see note) * * This function generates a SHA1 digest for a single 512-bit block. * Be warned, it does not handle padding and message digest, do not * confuse it with the full FIPS 180-1 digest algorithm for variable * length messages. * * Note: If the hash is security sensitive, the caller should be sure * to clear the workspace. This is left to the caller to avoid * unnecessary clears between chained hashing operations. */ void sha_transform(uint32_t digest[5], const char in[64], uint32_t W[80]) { register uint32_t a, b, c, d, e, t, i; for (i = 0; i < 16; i++) W[i] = be32_to_cpu(((const uint32_t *)in)[i]); for (i = 0; i < 64; i++) W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1); a = digest[0]; b = digest[1]; c = digest[2]; d = digest[3]; e = digest[4]; for (i = 0; i < 20; i++) { t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } for (; i < 40; i ++) { t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } for (; i < 60; i ++) { t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } for (; i < 80; i ++) { t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d; digest[4] += e; } #define ROUND(a,b,c,d,e,f,add) \ ( e += add + f(b,c,d), \ b = rol32(b, 30), \ e += rol32(a, 5) ) void sha_transform4(uint32_t digest[5], const char in[64], uint32_t W[80]) { register uint32_t a, b, c, d, e, i; for (i = 0; i < 16; i++) W[i] = be32_to_cpu(((const uint32_t *)in)[i]); for (i = 0; i < 64; i++) { a = W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i]; W[i+16] = rol32(a, 1); } a = digest[0]; b = digest[1]; c = digest[2]; d = digest[3]; e = digest[4]; for (i = 0; i < 20; i += 5) { ROUND(a,b,c,d,e,f1,W[i ]+K1); ROUND(e,a,b,c,d,f1,W[i+1]+K1); ROUND(d,e,a,b,c,f1,W[i+2]+K1); ROUND(c,d,e,a,b,f1,W[i+3]+K1); ROUND(b,c,d,e,a,f1,W[i+4]+K1); } for (; i < 40; i += 5) { ROUND(a,b,c,d,e,f2,W[i ]+K2); ROUND(e,a,b,c,d,f2,W[i+1]+K2); ROUND(d,e,a,b,c,f2,W[i+2]+K2); ROUND(c,d,e,a,b,f2,W[i+3]+K2); ROUND(b,c,d,e,a,f2,W[i+4]+K2); } for (; i < 60; i += 5) { ROUND(a,b,c,d,e,f3,W[i ]+K3); ROUND(e,a,b,c,d,f3,W[i+1]+K3); ROUND(d,e,a,b,c,f3,W[i+2]+K3); ROUND(c,d,e,a,b,f3,W[i+3]+K3); ROUND(b,c,d,e,a,f3,W[i+4]+K3); } for (; i < 80; i += 5) { ROUND(a,b,c,d,e,f2,W[i ]+K4); ROUND(e,a,b,c,d,f2,W[i+1]+K4); ROUND(d,e,a,b,c,f2,W[i+2]+K4); ROUND(c,d,e,a,b,f2,W[i+3]+K4); ROUND(b,c,d,e,a,f2,W[i+4]+K4); } digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d; digest[4] += e; } extern void sha_transform2(uint32_t digest[5], const char in[64]); extern void sha_transform3(uint32_t digest[5], const char in[64]); extern void sha_transform5(uint32_t digest[5], const char in[64]); extern void sha_stackwipe(void); void sha_init(uint32_t buf[5]) { buf[0] = 0x67452301; buf[1] = 0xefcdab89; buf[2] = 0x98badcfe; buf[3] = 0x10325476; buf[4] = 0xc3d2e1f0; } #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #if 1 void sha_stackwipe2(void) { uint32_t buf[90]; memset(buf, 0, sizeof buf); asm("" : : "r" (&buf)); /* Force the compiler to do the memset */ } #endif #define TEST_SIZE (10*1024*1024) int main(void) { uint32_t W[80]; uint32_t out[5]; char const text[64] = "Hello, world!\n"; char *buf; uint32_t *p; unsigned i; struct timeval start, stop; sha_init(out); sha_transform(out, text, W); printf(" One: %08x %08x %08x %08x %08x\n", out[0], out[1], out[2], out[3], out[4]); sha_init(out); sha_transform4(out, text, W); printf(" Four: %08x %08x %08x %08x %08x\n", out[0], out[1], out[2], out[3], out[4]); sha_init(out); sha_transform2(out, text); printf(" Two: %08x %08x %08x %08x %08x\n", out[0], out[1], out[2], out[3], out[4]); sha_init(out); sha_transform3(out, text); printf("Three: %08x %08x %08x %08x %08x\n", out[0], out[1], out[2], out[3], out[4]); sha_init(out); sha_transform5(out, text); printf(" Five: %08x %08x %08x %08x %08x\n", out[0], out[1], out[2], out[3], out[4]); sha_stackwipe(); #if 1 /* Set up a large buffer full of stuff */ buf = malloc(TEST_SIZE); p = (uint32_t *)buf; memcpy(p, W+80-16, 16*sizeof *p); for (i = 0; i < TEST_SIZE/sizeof *p - 16; i++) { uint32_t a = p[i+13] ^ p[i+8] ^ p[i+2] ^ p[i]; p[i+16] = rol32(a, 1); } sha_init(out); gettimeofday(&start, 0); for (i = 0; i < TEST_SIZE; i += 64) sha_transform(out, buf+i, W); gettimeofday(&stop, 0); printf(" One: %08x %08x %08x %08x %08x -- %lu us\n", out[0], out[1], out[2], out[3], out[4], 1000000*(stop.tv_sec-start.tv_sec)+stop.tv_usec-start.tv_usec); sha_init(out); gettimeofday(&start, 0); for (i = 0; i < TEST_SIZE; i += 64) sha_transform4(out, buf+i, W); gettimeofday(&stop, 0); printf(" Four: %08x %08x %08x %08x %08x -- %lu us\n", out[0], out[1], out[2], out[3], out[4], 1000000*(stop.tv_sec-start.tv_sec)+stop.tv_usec-start.tv_usec); sha_init(out); gettimeofday(&start, 0); for (i = 0; i < TEST_SIZE; i += 64) sha_transform2(out, buf+i); gettimeofday(&stop, 0); printf(" Two: %08x %08x %08x %08x %08x -- %lu us\n", out[0], out[1], out[2], out[3], out[4], 1000000*(stop.tv_sec-start.tv_sec)+stop.tv_usec-start.tv_usec); sha_init(out); gettimeofday(&start, 0); for (i = 0; i < TEST_SIZE; i += 64) sha_transform3(out, buf+i); gettimeofday(&stop, 0); printf("Three: %08x %08x %08x %08x %08x -- %lu us\n", out[0], out[1], out[2], out[3], out[4], 1000000*(stop.tv_sec-start.tv_sec)+stop.tv_usec-start.tv_usec); sha_init(out); gettimeofday(&start, 0); for (i = 0; i < TEST_SIZE; i += 64) sha_transform5(out, buf+i); gettimeofday(&stop, 0); printf(" Five: %08x %08x %08x %08x %08x -- %lu us\n", out[0], out[1], out[2], out[3], out[4], 1000000*(stop.tv_sec-start.tv_sec)+stop.tv_usec-start.tv_usec); sha_stackwipe(); #endif return 0; } ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2007-06-13 6:46 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2007-06-11 7:53 [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ linux 2007-06-11 19:17 ` Benjamin Gilbert 2007-06-12 5:05 ` linux 2007-06-13 5:29 ` [PATCH] random: fix folding Matt Mackall 2007-06-13 5:45 ` linux 2007-06-13 6:08 ` Matt Mackall 2007-06-13 5:50 ` [PATCH 2/3] [CRYPTO] Add optimized SHA-1 implementation for i486+ Matt Mackall 2007-06-13 6:46 ` linux
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®