mirror of
https://github.com/golang/go
synced 2024-11-19 02:14:43 -07:00
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: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
08bb7ccb75
commit
76d4274491
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user