diff --git a/src/crypto/des/block.go b/src/crypto/des/block.go index 99338d62a6e..21e6d4e82f6 100644 --- a/src/crypto/des/block.go +++ b/src/crypto/des/block.go @@ -4,25 +4,29 @@ package des -import ( - "encoding/binary" -) +import "encoding/binary" func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { b := binary.BigEndian.Uint64(src) b = permuteInitialBlock(b) left, right := uint32(b>>32), uint32(b) - var subkey uint64 - for i := 0; i < 16; i++ { - if decrypt { - subkey = subkeys[15-i] - } else { - subkey = subkeys[i] - } + left = (left << 1) | (left >> 31) + right = (right << 1) | (right >> 31) - left, right = right, left^feistel(right, subkey) + if decrypt { + for i := 0; i < 8; i++ { + left, right = feistel(left, right, subkeys[15-2*i], subkeys[15-(2*i+1)]) + } + } else { + for i := 0; i < 8; i++ { + left, right = feistel(left, right, subkeys[2*i], subkeys[2*i+1]) + } } + + left = (left << 31) | (left >> 1) + right = (right << 31) | (right >> 1) + // switch left & right and perform final permutation preOutput := (uint64(right) << 32) | uint64(left) binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput)) @@ -39,19 +43,34 @@ func decryptBlock(subkeys []uint64, dst, src []byte) { } // DES Feistel function -func feistel(right uint32, key uint64) (result uint32) { - sBoxLocations := key ^ expandBlock(right) - var sBoxResult uint32 - for i := uint8(0); i < 8; i++ { - sBoxLocation := uint8(sBoxLocations>>42) & 0x3f - sBoxLocations <<= 6 - // row determined by 1st and 6th bit - // column is middle four bits - row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4) - column := (sBoxLocation >> 1) & 0xf - sBoxResult ^= feistelBox[i][16*row+column] - } - return sBoxResult +func feistel(l, r uint32, k0, k1 uint64) (lout, rout uint32) { + var t uint32 + + t = r ^ uint32(k0>>32) + l ^= feistelBox[7][t&0x3f] ^ + feistelBox[5][(t>>8)&0x3f] ^ + feistelBox[3][(t>>16)&0x3f] ^ + feistelBox[1][(t>>24)&0x3f] + + t = ((r << 28) | (r >> 4)) ^ uint32(k0) + l ^= feistelBox[6][(t)&0x3f] ^ + feistelBox[4][(t>>8)&0x3f] ^ + feistelBox[2][(t>>16)&0x3f] ^ + feistelBox[0][(t>>24)&0x3f] + + t = l ^ uint32(k1>>32) + r ^= feistelBox[7][t&0x3f] ^ + feistelBox[5][(t>>8)&0x3f] ^ + feistelBox[3][(t>>16)&0x3f] ^ + feistelBox[1][(t>>24)&0x3f] + + t = ((l << 28) | (l >> 4)) ^ uint32(k1) + r ^= feistelBox[6][(t)&0x3f] ^ + feistelBox[4][(t>>8)&0x3f] ^ + feistelBox[2][(t>>16)&0x3f] ^ + feistelBox[0][(t>>24)&0x3f] + + return l, r } // feistelBox[s][16*i+j] contains the output of permutationFunction @@ -73,27 +92,22 @@ func init() { for j := 0; j < 16; j++ { f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s))) f = permuteBlock(f, permutationFunction[:]) - feistelBox[s][16*i+j] = uint32(f) + + // Row is determined by the 1st and 6th bit. + // Column is the middle four bits. + row := uint8(((i & 2) << 4) | i&1) + col := uint8(j << 1) + t := row | col + + // The rotation was performed in the feistel rounds, being factored out and now mixed into the feistelBox. + f = (f << 1) | (f >> 31) + + feistelBox[s][t] = 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 { @@ -218,6 +232,24 @@ func (c *desCipher) generateSubkeys(keyBytes []byte) { // combine halves to form 56-bit input to PC2 pc2Input := uint64(leftRotations[i])<<28 | uint64(rightRotations[i]) // apply PC2 permutation to 7 byte input - c.subkeys[i] = permuteBlock(pc2Input, permutedChoice2[:]) + c.subkeys[i] = unpack(permuteBlock(pc2Input, permutedChoice2[:])) } } + +// Expand 48-bit input to 64-bit, with each 6-bit block padded by extra two bits at the top. +// By doing so, we can have the input blocks (four bits each), and the key blocks (six bits each) well-aligned without +// extra shifts/rotations for alignments. +func unpack(x uint64) uint64 { + var result uint64 + + result = ((x>>(6*1))&0xff)<<(8*0) | + ((x>>(6*3))&0xff)<<(8*1) | + ((x>>(6*5))&0xff)<<(8*2) | + ((x>>(6*7))&0xff)<<(8*3) | + ((x>>(6*0))&0xff)<<(8*4) | + ((x>>(6*2))&0xff)<<(8*5) | + ((x>>(6*4))&0xff)<<(8*6) | + ((x>>(6*6))&0xff)<<(8*7) + + return result +} diff --git a/src/crypto/des/cipher.go b/src/crypto/des/cipher.go index 2f929ca7bed..46af5b0f022 100644 --- a/src/crypto/des/cipher.go +++ b/src/crypto/des/cipher.go @@ -6,6 +6,7 @@ package des import ( "crypto/cipher" + "encoding/binary" "strconv" ) @@ -61,13 +62,51 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) { func (c *tripleDESCipher) BlockSize() int { return BlockSize } func (c *tripleDESCipher) Encrypt(dst, src []byte) { - c.cipher1.Encrypt(dst, src) - c.cipher2.Decrypt(dst, dst) - c.cipher3.Encrypt(dst, dst) + b := binary.BigEndian.Uint64(src) + b = permuteInitialBlock(b) + left, right := uint32(b>>32), uint32(b) + + left = (left << 1) | (left >> 31) + right = (right << 1) | (right >> 31) + + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher1.subkeys[2*i], c.cipher1.subkeys[2*i+1]) + } + for i := 0; i < 8; i++ { + right, left = feistel(right, left, c.cipher2.subkeys[15-2*i], c.cipher2.subkeys[15-(2*i+1)]) + } + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher3.subkeys[2*i], c.cipher3.subkeys[2*i+1]) + } + + left = (left << 31) | (left >> 1) + right = (right << 31) | (right >> 1) + + preOutput := (uint64(right) << 32) | uint64(left) + binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput)) } func (c *tripleDESCipher) Decrypt(dst, src []byte) { - c.cipher3.Decrypt(dst, src) - c.cipher2.Encrypt(dst, dst) - c.cipher1.Decrypt(dst, dst) + b := binary.BigEndian.Uint64(src) + b = permuteInitialBlock(b) + left, right := uint32(b>>32), uint32(b) + + left = (left << 1) | (left >> 31) + right = (right << 1) | (right >> 31) + + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher3.subkeys[15-2*i], c.cipher3.subkeys[15-(2*i+1)]) + } + for i := 0; i < 8; i++ { + right, left = feistel(right, left, c.cipher2.subkeys[2*i], c.cipher2.subkeys[2*i+1]) + } + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher1.subkeys[15-2*i], c.cipher1.subkeys[15-(2*i+1)]) + } + + left = (left << 31) | (left >> 1) + right = (right << 31) | (right >> 1) + + preOutput := (uint64(right) << 32) | uint64(left) + binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput)) } diff --git a/src/crypto/des/des_test.go b/src/crypto/des/des_test.go index 2bd525afecc..690a49f5efb 100644 --- a/src/crypto/des/des_test.go +++ b/src/crypto/des/des_test.go @@ -1526,17 +1526,6 @@ func TestFinalPermute(t *testing.T) { } } -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) @@ -1564,3 +1553,31 @@ func BenchmarkDecrypt(b *testing.B) { c.Decrypt(out, tt.out) } } + +func BenchmarkTDESEncrypt(b *testing.B) { + tt := encryptTripleDESTests[0] + c, err := NewTripleDESCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.in)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Encrypt(out, tt.in) + } +} + +func BenchmarkTDESDecrypt(b *testing.B) { + tt := encryptTripleDESTests[0] + c, err := NewTripleDESCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.out)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Decrypt(out, tt.out) + } +}