Commit 76d42744 authored by Mura Li's avatar Mura Li Committed by Russ Cox

crypto/des: improve the throughput of DES and 3DES

For detailed explanation of the adopted (Eric Young's) algorithm,
see http://ftp.nluug.nl/security/coast/libs/libdes/ALGORITHM

benchmark                   old ns/op     new ns/op     delta
BenchmarkEncrypt-16         649           164           -74.73%
BenchmarkDecrypt-16         546           156           -71.43%
BenchmarkTDESEncrypt-16     1651          385           -76.68%
BenchmarkTDESDecrypt-16     1645          378           -77.02%

benchmark                   old MB/s     new MB/s     speedup
BenchmarkEncrypt-16         12.31        48.76        3.96x
BenchmarkDecrypt-16         14.64        51.03        3.49x
BenchmarkTDESEncrypt-16     4.84         20.74        4.29x
BenchmarkTDESDecrypt-16     4.86         21.16        4.35x

Change-Id: Ic3e1fe3340419ec5a0e6379434911eb41e0246f6
Reviewed-on: https://go-review.googlesource.com/36490
Run-TryBot: Minux Ma <minux@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 08bb7ccb
...@@ -4,25 +4,29 @@ ...@@ -4,25 +4,29 @@
package des package des
import ( import "encoding/binary"
"encoding/binary"
)
func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
b := binary.BigEndian.Uint64(src) b := binary.BigEndian.Uint64(src)
b = permuteInitialBlock(b) b = permuteInitialBlock(b)
left, right := uint32(b>>32), uint32(b) left, right := uint32(b>>32), uint32(b)
var subkey uint64 left = (left << 1) | (left >> 31)
for i := 0; i < 16; i++ { right = (right << 1) | (right >> 31)
if decrypt { if decrypt {
subkey = subkeys[15-i] for i := 0; i < 8; i++ {
left, right = feistel(left, right, subkeys[15-2*i], subkeys[15-(2*i+1)])
}
} else { } else {
subkey = subkeys[i] for i := 0; i < 8; i++ {
left, right = feistel(left, right, subkeys[2*i], subkeys[2*i+1])
} }
left, right = right, left^feistel(right, subkey)
} }
left = (left << 31) | (left >> 1)
right = (right << 31) | (right >> 1)
// switch left & right and perform final permutation // switch left & right and perform final permutation
preOutput := (uint64(right) << 32) | uint64(left) preOutput := (uint64(right) << 32) | uint64(left)
binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput)) binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput))
...@@ -39,19 +43,34 @@ func decryptBlock(subkeys []uint64, dst, src []byte) { ...@@ -39,19 +43,34 @@ func decryptBlock(subkeys []uint64, dst, src []byte) {
} }
// DES Feistel function // DES Feistel function
func feistel(right uint32, key uint64) (result uint32) { func feistel(l, r uint32, k0, k1 uint64) (lout, rout uint32) {
sBoxLocations := key ^ expandBlock(right) var t uint32
var sBoxResult uint32
for i := uint8(0); i < 8; i++ { t = r ^ uint32(k0>>32)
sBoxLocation := uint8(sBoxLocations>>42) & 0x3f l ^= feistelBox[7][t&0x3f] ^
sBoxLocations <<= 6 feistelBox[5][(t>>8)&0x3f] ^
// row determined by 1st and 6th bit feistelBox[3][(t>>16)&0x3f] ^
// column is middle four bits feistelBox[1][(t>>24)&0x3f]
row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4)
column := (sBoxLocation >> 1) & 0xf t = ((r << 28) | (r >> 4)) ^ uint32(k0)
sBoxResult ^= feistelBox[i][16*row+column] l ^= feistelBox[6][(t)&0x3f] ^
} feistelBox[4][(t>>8)&0x3f] ^
return sBoxResult 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 // feistelBox[s][16*i+j] contains the output of permutationFunction
...@@ -73,25 +92,20 @@ func init() { ...@@ -73,25 +92,20 @@ func init() {
for j := 0; j < 16; j++ { for j := 0; j < 16; j++ {
f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s))) f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s)))
f = permuteBlock(f, permutationFunction[:]) 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 // permuteInitialBlock is equivalent to the permutation defined
...@@ -218,6 +232,24 @@ func (c *desCipher) generateSubkeys(keyBytes []byte) { ...@@ -218,6 +232,24 @@ func (c *desCipher) generateSubkeys(keyBytes []byte) {
// combine halves to form 56-bit input to PC2 // combine halves to form 56-bit input to PC2
pc2Input := uint64(leftRotations[i])<<28 | uint64(rightRotations[i]) pc2Input := uint64(leftRotations[i])<<28 | uint64(rightRotations[i])
// apply PC2 permutation to 7 byte input // 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
}
...@@ -6,6 +6,7 @@ package des ...@@ -6,6 +6,7 @@ package des
import ( import (
"crypto/cipher" "crypto/cipher"
"encoding/binary"
"strconv" "strconv"
) )
...@@ -61,13 +62,51 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) { ...@@ -61,13 +62,51 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) {
func (c *tripleDESCipher) BlockSize() int { return BlockSize } func (c *tripleDESCipher) BlockSize() int { return BlockSize }
func (c *tripleDESCipher) Encrypt(dst, src []byte) { func (c *tripleDESCipher) Encrypt(dst, src []byte) {
c.cipher1.Encrypt(dst, src) b := binary.BigEndian.Uint64(src)
c.cipher2.Decrypt(dst, dst) b = permuteInitialBlock(b)
c.cipher3.Encrypt(dst, dst) 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) { func (c *tripleDESCipher) Decrypt(dst, src []byte) {
c.cipher3.Decrypt(dst, src) b := binary.BigEndian.Uint64(src)
c.cipher2.Encrypt(dst, dst) b = permuteInitialBlock(b)
c.cipher1.Decrypt(dst, dst) 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))
} }
...@@ -1526,17 +1526,6 @@ func TestFinalPermute(t *testing.T) { ...@@ -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) { func BenchmarkEncrypt(b *testing.B) {
tt := encryptDESTests[0] tt := encryptDESTests[0]
c, err := NewCipher(tt.key) c, err := NewCipher(tt.key)
...@@ -1564,3 +1553,31 @@ func BenchmarkDecrypt(b *testing.B) { ...@@ -1564,3 +1553,31 @@ func BenchmarkDecrypt(b *testing.B) {
c.Decrypt(out, tt.out) 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)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment