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:
Michael Munday 2016-04-15 16:56:37 -04:00
parent 03e216f30d
commit 9b6bf20a35
6 changed files with 87 additions and 76 deletions

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)
}

View 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])
}

View File

@ -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)
}
}

View File

@ -6,22 +6,15 @@
package aes
func encryptBlock(xk []uint32, dst, src []byte) {
encryptBlockGo(xk, dst, src)
}
import (
"crypto/cipher"
)
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
}
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)
}