mirror of
https://github.com/golang/go
synced 2024-10-04 17:11:21 -06:00
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 <munday@ca.ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
03e216f30d
commit
9b6bf20a35
@ -45,7 +45,7 @@ var errOpen = errors.New("cipher: message authentication failed")
|
|||||||
// will use the optimised implementation in this file when possible. Instances
|
// will use the optimised implementation in this file when possible. Instances
|
||||||
// of this type only exist when hasGCMAsm returns true.
|
// of this type only exist when hasGCMAsm returns true.
|
||||||
type aesCipherGCM struct {
|
type aesCipherGCM struct {
|
||||||
aesCipher
|
aesCipherAsm
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
|
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
|
||||||
|
@ -380,6 +380,6 @@ func BenchmarkExpand(b *testing.B) {
|
|||||||
c := &aesCipher{make([]uint32, n), make([]uint32, n)}
|
c := &aesCipher{make([]uint32, n), make([]uint32, n)}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
expandKey(tt.key, c.enc, c.dec)
|
expandKeyGo(tt.key, c.enc, c.dec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,15 @@ func NewCipher(key []byte) (cipher.Block, error) {
|
|||||||
case 16, 24, 32:
|
case 16, 24, 32:
|
||||||
break
|
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)}
|
c := aesCipher{make([]uint32, n), make([]uint32, n)}
|
||||||
expandKey(key, c.enc, c.dec)
|
expandKeyGo(key, c.enc, c.dec)
|
||||||
|
|
||||||
if hasGCMAsm() {
|
|
||||||
return &aesCipherGCM{c}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &c, nil
|
return &c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ func (c *aesCipher) Encrypt(dst, src []byte) {
|
|||||||
if len(dst) < BlockSize {
|
if len(dst) < BlockSize {
|
||||||
panic("crypto/aes: output not full block")
|
panic("crypto/aes: output not full block")
|
||||||
}
|
}
|
||||||
encryptBlock(c.enc, dst, src)
|
encryptBlockGo(c.enc, dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *aesCipher) Decrypt(dst, src []byte) {
|
func (c *aesCipher) Decrypt(dst, src []byte) {
|
||||||
@ -67,5 +67,5 @@ func (c *aesCipher) Decrypt(dst, src []byte) {
|
|||||||
if len(dst) < BlockSize {
|
if len(dst) < BlockSize {
|
||||||
panic("crypto/aes: output not full block")
|
panic("crypto/aes: output not full block")
|
||||||
}
|
}
|
||||||
decryptBlock(c.dec, dst, src)
|
decryptBlockGo(c.dec, dst, src)
|
||||||
}
|
}
|
||||||
|
66
src/crypto/aes/cipher_amd64.go
Normal file
66
src/crypto/aes/cipher_amd64.go
Normal file
@ -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])
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,22 +6,15 @@
|
|||||||
|
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
func encryptBlock(xk []uint32, dst, src []byte) {
|
import (
|
||||||
encryptBlockGo(xk, dst, src)
|
"crypto/cipher"
|
||||||
}
|
)
|
||||||
|
|
||||||
func decryptBlock(xk []uint32, dst, src []byte) {
|
// newCipher calls the newCipherGeneric function
|
||||||
decryptBlockGo(xk, dst, src)
|
// directly. Platforms with hardware accelerated
|
||||||
}
|
// implementations of AES should implement their
|
||||||
|
// own version of newCipher (which may then call
|
||||||
func expandKey(key []byte, enc, dec []uint32) {
|
// newCipherGeneric if needed).
|
||||||
expandKeyGo(key, enc, dec)
|
func newCipher(key []byte) (cipher.Block, error) {
|
||||||
}
|
return newCipherGeneric(key)
|
||||||
|
|
||||||
func hasGCMAsm() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type aesCipherGCM struct {
|
|
||||||
aesCipher
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user