Commit 9b6bf20a authored by Michael Munday's avatar Michael Munday

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: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 03e216f3
......@@ -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
......
......@@ -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)
}
}
......@@ -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)
}
// 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])
}
// 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
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)
}
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