From: Rémy Oudompheng Date: Wed, 31 Jul 2013 20:06:48 +0000 (+0200) Subject: crypto/des: faster permutation. X-Git-Tag: go1.2rc2~863 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=441ef7978d262745fd275e29cede0522f88955d0;p=gostls13.git crypto/des: faster permutation. This patch introduces specialized functions for initial and final permutations, and precomputes the output of the third permutation on the S-box elements. benchmark old ns/op new ns/op delta BenchmarkEncrypt 3581 1226 -65.76% BenchmarkDecrypt 3590 1224 -65.91% benchmark old MB/s new MB/s speedup BenchmarkEncrypt 2.23 6.52 2.92x BenchmarkDecrypt 2.23 6.53 2.93x R=golang-dev, rsc CC=golang-dev https://golang.org/cl/12072045 --- diff --git a/src/pkg/crypto/des/block.go b/src/pkg/crypto/des/block.go index 805bb9f8cc..26355a22e7 100644 --- a/src/pkg/crypto/des/block.go +++ b/src/pkg/crypto/des/block.go @@ -10,7 +10,7 @@ import ( func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { b := binary.BigEndian.Uint64(src) - b = permuteBlock(b, initialPermutation[:]) + b = permuteInitialBlock(b) left, right := uint32(b>>32), uint32(b) var subkey uint64 @@ -25,7 +25,7 @@ func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { } // switch left & right and perform final permutation preOutput := (uint64(right) << 32) | uint64(left) - binary.BigEndian.PutUint64(dst, permuteBlock(preOutput, finalPermutation[:])) + binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput)) } // Encrypt one block from src into dst, using the subkeys. @@ -46,14 +46,18 @@ func feistel(right uint32, key uint64) (result uint32) { sBoxLocation := uint8(sBoxLocations>>42) & 0x3f sBoxLocations <<= 6 // row determined by 1st and 6th bit - row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4) // column is middle four bits + row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4) column := (sBoxLocation >> 1) & 0xf - sBoxResult |= uint32(sBoxes[i][row][column]) << (4 * (7 - i)) + sBoxResult ^= feistelBox[i][16*row+column] } - return uint32(permuteBlock(uint64(sBoxResult), permutationFunction[:])) + return sBoxResult } +// feistelBox[s][16*i+j] contains the output of permutationFunction +// for sBoxes[s][i][j] << 4*(7-s) +var feistelBox [8][64]uint32 + // general purpose function to perform DES block permutations func permuteBlock(src uint64, permutation []uint8) (block uint64) { for position, n := range permutation { @@ -63,18 +67,127 @@ func permuteBlock(src uint64, permutation []uint8) (block uint64) { return } +func init() { + for s := range sBoxes { + for i := 0; i < 4; i++ { + for j := 0; j < 16; j++ { + f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s))) + f = permuteBlock(uint64(f), permutationFunction[:]) + feistelBox[s][16*i+j] = uint32(f) + } + } + } +} + // expandBlock expands an input block of 32 bits, // producing an output block of 48 bits. func expandBlock(src uint32) (block uint64) { + // rotate the 5 highest bits to the right. src = (src << 5) | (src >> 27) for i := 0; i < 8; i++ { block <<= 6 + // take the 6 bits on the right block |= uint64(src) & (1<<6 - 1) + // advance by 4 bits. src = (src << 4) | (src >> 28) } return } +// permuteInitialBlock is equivalent to the permutation defined +// by initialPermutation. +func permuteInitialBlock(block uint64) uint64 { + // block = b7 b6 b5 b4 b3 b2 b1 b0 (8 bytes) + b1 := block >> 48 + b2 := block << 48 + block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48 + + // block = b1 b0 b5 b4 b3 b2 b7 b6 + b1 = block >> 32 & 0xff00ff + b2 = (block & 0xff00ff00) + block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24 // exchange b0 b4 with b3 b7 + + // block is now b1 b3 b5 b7 b0 b2 b4 b7, the permutation: + // ... 8 + // ... 24 + // ... 40 + // ... 56 + // 7 6 5 4 3 2 1 0 + // 23 22 21 20 19 18 17 16 + // ... 32 + // ... 48 + + // exchange 4,5,6,7 with 32,33,34,35 etc. + b1 = block & 0x0f0f00000f0f0000 + b2 = block & 0x0000f0f00000f0f0 + block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12 + + // block is the permutation: + // + // [+8] [+40] + // + // 7 6 5 4 + // 23 22 21 20 + // 3 2 1 0 + // 19 18 17 16 [+32] + + // exchange 0,1,4,5 with 18,19,22,23 + b1 = block & 0x3300330033003300 + b2 = block & 0x00cc00cc00cc00cc + block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6 + + // block is the permutation: + // 15 14 + // 13 12 + // 11 10 + // 9 8 + // 7 6 + // 5 4 + // 3 2 + // 1 0 [+16] [+32] [+64] + + // exchange 0,2,4,6 with 9,11,13,15: + b1 = block & 0xaaaaaaaa55555555 + block ^= b1 ^ b1>>33 ^ b1<<33 + + // block is the permutation: + // 6 14 22 30 38 46 54 62 + // 4 12 20 28 36 44 52 60 + // 2 10 18 26 34 42 50 58 + // 0 8 16 24 32 40 48 56 + // 7 15 23 31 39 47 55 63 + // 5 13 21 29 37 45 53 61 + // 3 11 19 27 35 43 51 59 + // 1 9 17 25 33 41 49 57 + return block +} + +// permuteInitialBlock is equivalent to the permutation defined +// by finalPermutation. +func permuteFinalBlock(block uint64) uint64 { + // Perform the same bit exchanges as permuteInitialBlock + // but in reverse order. + b1 := block & 0xaaaaaaaa55555555 + block ^= b1 ^ b1>>33 ^ b1<<33 + + b1 = block & 0x3300330033003300 + b2 := block & 0x00cc00cc00cc00cc + block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6 + + b1 = block & 0x0f0f00000f0f0000 + b2 = block & 0x0000f0f00000f0f0 + block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12 + + b1 = block >> 32 & 0xff00ff + b2 = (block & 0xff00ff00) + block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24 + + b1 = block >> 48 + b2 = block << 48 + block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48 + return block +} + // creates 16 28-bit blocks rotated according // to the rotation schedule func ksRotate(in uint32) (out []uint32) { diff --git a/src/pkg/crypto/des/const.go b/src/pkg/crypto/des/const.go index 1b898dc275..2bd485ee80 100644 --- a/src/pkg/crypto/des/const.go +++ b/src/pkg/crypto/des/const.go @@ -32,6 +32,17 @@ var finalPermutation = [64]byte{ 31, 63, 23, 55, 15, 47, 7, 39, } +// Used to expand an input block of 32 bits, producing an output block of 48 +// bits. +var expansionFunction = [48]byte{ + 0, 31, 30, 29, 28, 27, 28, 27, + 26, 25, 24, 23, 24, 23, 22, 21, + 20, 19, 20, 19, 18, 17, 16, 15, + 16, 15, 14, 13, 12, 11, 12, 11, + 10, 9, 8, 7, 8, 7, 6, 5, + 4, 3, 4, 3, 2, 1, 0, 31, +} + // Yields a 32-bit output from a 32-bit input var permutationFunction = [32]byte{ 16, 25, 12, 11, 3, 20, 4, 15, diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go index a08cbabb25..2bd525afec 100644 --- a/src/pkg/crypto/des/des_test.go +++ b/src/pkg/crypto/des/des_test.go @@ -1504,6 +1504,39 @@ func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) { } } +func TestInitialPermute(t *testing.T) { + for i := uint(0); i < 64; i++ { + bit := uint64(1) << i + got := permuteInitialBlock(bit) + want := uint64(1) << finalPermutation[63-i] + if got != want { + t.Errorf("permute(%x) = %x, want %x", bit, got, want) + } + } +} + +func TestFinalPermute(t *testing.T) { + for i := uint(0); i < 64; i++ { + bit := uint64(1) << i + got := permuteFinalBlock(bit) + want := uint64(1) << initialPermutation[63-i] + if got != want { + t.Errorf("permute(%x) = %x, want %x", bit, got, want) + } + } +} + +func TestExpandBlock(t *testing.T) { + for i := uint(0); i < 32; i++ { + bit := uint32(1) << i + got := expandBlock(bit) + want := permuteBlock(uint64(bit), expansionFunction[:]) + if got != want { + t.Errorf("expand(%x) = %x, want %x", bit, got, want) + } + } +} + func BenchmarkEncrypt(b *testing.B) { tt := encryptDESTests[0] c, err := NewCipher(tt.key)