From: Michael Munday Date: Fri, 15 Apr 2016 20:56:37 +0000 (-0400) Subject: crypto/aes: de-couple asm and go implementations X-Git-Tag: go1.7beta1~606 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=9b6bf20a35130b2b0754f2ec54370e207e2f2d9b;p=gostls13.git crypto/aes: de-couple asm and go implementations There is currently only one assembly implementation of AES (amd64). While it is possible to fit other implementations to the same pattern it complicates the code. For example s390x does not use expanded keys, so having enc and dec in the aesCipher struct is confusing. By separating out the asm implementations we can more closely match the data structures to the underlying implementation. This also opens the door for AES implementations that support block cipher modes other than GCM (e.g. CTR and CBC). This commit changes BenchmarkExpandKey to test the go implementation of key expansion. It might be better to have some sort of 'initialisation' benchmark instead to cover the startup costs of the assembly implementations (which might be doing key expansion in a different way, or not at all). Change-Id: I094a7176b5bbe2177df73163a9c0b711a61c12d6 Reviewed-on: https://go-review.googlesource.com/22193 Run-TryBot: Michael Munday TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- diff --git a/src/crypto/aes/aes_gcm.go b/src/crypto/aes/aes_gcm.go index 1377578950..b55714d57a 100644 --- a/src/crypto/aes/aes_gcm.go +++ b/src/crypto/aes/aes_gcm.go @@ -45,7 +45,7 @@ var errOpen = errors.New("cipher: message authentication failed") // will use the optimised implementation in this file when possible. Instances // of this type only exist when hasGCMAsm returns true. type aesCipherGCM struct { - aesCipher + aesCipherAsm } // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only diff --git a/src/crypto/aes/aes_test.go b/src/crypto/aes/aes_test.go index 28144968fc..3cc390d4e2 100644 --- a/src/crypto/aes/aes_test.go +++ b/src/crypto/aes/aes_test.go @@ -380,6 +380,6 @@ func BenchmarkExpand(b *testing.B) { c := &aesCipher{make([]uint32, n), make([]uint32, n)} b.ResetTimer() for i := 0; i < b.N; i++ { - expandKey(tt.key, c.enc, c.dec) + expandKeyGo(tt.key, c.enc, c.dec) } } diff --git a/src/crypto/aes/cipher.go b/src/crypto/aes/cipher.go index 04d2be1283..c5a8e91d00 100644 --- a/src/crypto/aes/cipher.go +++ b/src/crypto/aes/cipher.go @@ -36,15 +36,15 @@ func NewCipher(key []byte) (cipher.Block, error) { case 16, 24, 32: break } + return newCipher(key) +} - n := k + 28 +// newCipherGeneric creates and returns a new cipher.Block +// implemented in pure Go. +func newCipherGeneric(key []byte) (cipher.Block, error) { + n := len(key) + 28 c := aesCipher{make([]uint32, n), make([]uint32, n)} - expandKey(key, c.enc, c.dec) - - if hasGCMAsm() { - return &aesCipherGCM{c}, nil - } - + expandKeyGo(key, c.enc, c.dec) return &c, nil } @@ -57,7 +57,7 @@ func (c *aesCipher) Encrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } - encryptBlock(c.enc, dst, src) + encryptBlockGo(c.enc, dst, src) } func (c *aesCipher) Decrypt(dst, src []byte) { @@ -67,5 +67,5 @@ func (c *aesCipher) Decrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } - decryptBlock(c.dec, dst, src) + decryptBlockGo(c.dec, dst, src) } diff --git a/src/crypto/aes/cipher_amd64.go b/src/crypto/aes/cipher_amd64.go new file mode 100644 index 0000000000..3b600c36f3 --- /dev/null +++ b/src/crypto/aes/cipher_amd64.go @@ -0,0 +1,66 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package aes + +import ( + "crypto/cipher" +) + +// defined in asm_amd64.s +func hasAsm() bool +func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) +func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) +func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32) + +type aesCipherAsm struct { + aesCipher +} + +var useAsm = hasAsm() + +func newCipher(key []byte) (cipher.Block, error) { + if !useAsm { + return newCipherGeneric(key) + } + n := len(key) + 28 + c := aesCipherAsm{aesCipher{make([]uint32, n), make([]uint32, n)}} + rounds := 10 + switch len(key) { + case 128 / 8: + rounds = 10 + case 192 / 8: + rounds = 12 + case 256 / 8: + rounds = 14 + } + expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0]) + if hasGCMAsm() { + return &aesCipherGCM{c}, nil + } + + return &c, nil +} + +func (c *aesCipherAsm) BlockSize() int { return BlockSize } + +func (c *aesCipherAsm) Encrypt(dst, src []byte) { + if len(src) < BlockSize { + panic("crypto/aes: input not full block") + } + if len(dst) < BlockSize { + panic("crypto/aes: output not full block") + } + encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0]) +} + +func (c *aesCipherAsm) Decrypt(dst, src []byte) { + if len(src) < BlockSize { + panic("crypto/aes: input not full block") + } + if len(dst) < BlockSize { + panic("crypto/aes: output not full block") + } + decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0]) +} diff --git a/src/crypto/aes/cipher_asm.go b/src/crypto/aes/cipher_asm.go deleted file mode 100644 index 964eaaa6f8..0000000000 --- a/src/crypto/aes/cipher_asm.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build amd64 - -package aes - -// defined in asm_$GOARCH.s -func hasAsm() bool -func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) -func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) -func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32) - -var useAsm = hasAsm() - -func encryptBlock(xk []uint32, dst, src []byte) { - if useAsm { - encryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0]) - } else { - encryptBlockGo(xk, dst, src) - } -} - -func decryptBlock(xk []uint32, dst, src []byte) { - if useAsm { - decryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0]) - } else { - decryptBlockGo(xk, dst, src) - } -} - -func expandKey(key []byte, enc, dec []uint32) { - if useAsm { - rounds := 10 - switch len(key) { - case 128 / 8: - rounds = 10 - case 192 / 8: - rounds = 12 - case 256 / 8: - rounds = 14 - } - expandKeyAsm(rounds, &key[0], &enc[0], &dec[0]) - } else { - expandKeyGo(key, enc, dec) - } -} diff --git a/src/crypto/aes/cipher_generic.go b/src/crypto/aes/cipher_generic.go index 32b2b3cc56..c5e02fe79b 100644 --- a/src/crypto/aes/cipher_generic.go +++ b/src/crypto/aes/cipher_generic.go @@ -6,22 +6,15 @@ package aes -func encryptBlock(xk []uint32, dst, src []byte) { - encryptBlockGo(xk, dst, src) -} - -func decryptBlock(xk []uint32, dst, src []byte) { - decryptBlockGo(xk, dst, src) -} - -func expandKey(key []byte, enc, dec []uint32) { - expandKeyGo(key, enc, dec) -} - -func hasGCMAsm() bool { - return false -} +import ( + "crypto/cipher" +) -type aesCipherGCM struct { - aesCipher +// newCipher calls the newCipherGeneric function +// directly. Platforms with hardware accelerated +// implementations of AES should implement their +// own version of newCipher (which may then call +// newCipherGeneric if needed). +func newCipher(key []byte) (cipher.Block, error) { + return newCipherGeneric(key) }