TestEmptyPlaintext and TestCryptBlocks were folded into cryptotest.
Change-Id: I6131ab8582eb0e6d3a1b24bab1147a145d9766ac
Reviewed-on: https://go-review.googlesource.com/c/go/+/624738
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
// Test Cipher Encrypt method against FIPS 197 examples.
func TestCipherEncrypt(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", testCipherEncrypt)
+}
+
+func testCipherEncrypt(t *testing.T) {
for i, tt := range encryptTests {
c, err := NewCipher(tt.key)
if err != nil {
// Test Cipher Decrypt against FIPS 197 examples.
func TestCipherDecrypt(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", testCipherDecrypt)
+}
+
+func testCipherDecrypt(t *testing.T) {
for i, tt := range encryptTests {
c, err := NewCipher(tt.key)
if err != nil {
// Test AES against the general cipher.Block interface tester
func TestAESBlock(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", testAESBlock)
+}
+
+func testAESBlock(t *testing.T) {
for _, keylen := range []int{128, 192, 256} {
t.Run(fmt.Sprintf("AES-%d", keylen), func(t *testing.T) {
cryptotest.TestBlock(t, keylen/8, NewCipher)
"bytes"
"crypto/aes"
"crypto/cipher"
+ "crypto/internal/cryptotest"
"testing"
)
}
func TestCBCEncrypterAES(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", testCBCEncrypterAES)
+}
+
+func testCBCEncrypterAES(t *testing.T) {
for _, test := range cbcAESTests {
c, err := aes.NewCipher(test.key)
if err != nil {
}
func TestCBCDecrypterAES(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", testCBCDecrypterAES)
+}
+
+func testCBCDecrypterAES(t *testing.T) {
for _, test := range cbcAESTests {
c, err := aes.NewCipher(test.key)
if err != nil {
// Test CBC Blockmode against the general cipher.BlockMode interface tester
func TestCBCBlockMode(t *testing.T) {
- for _, keylen := range []int{128, 192, 256} {
+ cryptotest.TestAllImplementations(t, "aes", func(t *testing.T) {
+ for _, keylen := range []int{128, 192, 256} {
+ t.Run(fmt.Sprintf("AES-%d", keylen), func(t *testing.T) {
+ rng := newRandReader(t)
- t.Run(fmt.Sprintf("AES-%d", keylen), func(t *testing.T) {
- rng := newRandReader(t)
+ key := make([]byte, keylen/8)
+ rng.Read(key)
- key := make([]byte, keylen/8)
- rng.Read(key)
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
- block, err := aes.NewCipher(key)
- if err != nil {
- panic(err)
- }
-
- cryptotest.TestBlockMode(t, block, cipher.NewCBCEncrypter, cipher.NewCBCDecrypter)
- })
- }
+ cryptotest.TestBlockMode(t, block, cipher.NewCBCEncrypter, cipher.NewCBCDecrypter)
+ })
+ }
+ })
t.Run("DES", func(t *testing.T) {
rng := newRandReader(t)
+++ /dev/null
-// Copyright 2013 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 cipher_test
-
-import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/des"
- "testing"
-)
-
-func TestCryptBlocks(t *testing.T) {
- buf := make([]byte, 16)
- block, _ := aes.NewCipher(buf)
-
- mode := cipher.NewCBCDecrypter(block, buf)
- mustPanic(t, "crypto/cipher: input not full blocks", func() { mode.CryptBlocks(buf, buf[:3]) })
- mustPanic(t, "crypto/cipher: output smaller than input", func() { mode.CryptBlocks(buf[:3], buf) })
-
- mode = cipher.NewCBCEncrypter(block, buf)
- mustPanic(t, "crypto/cipher: input not full blocks", func() { mode.CryptBlocks(buf, buf[:3]) })
- mustPanic(t, "crypto/cipher: output smaller than input", func() { mode.CryptBlocks(buf[:3], buf) })
-}
-
-func mustPanic(t *testing.T, msg string, f func()) {
- defer func() {
- err := recover()
- if err == nil {
- t.Errorf("function did not panic, wanted %q", msg)
- } else if err != msg {
- t.Errorf("got panic %v, wanted %q", err, msg)
- }
- }()
- f()
-}
-
-func TestEmptyPlaintext(t *testing.T) {
- var key [16]byte
- a, err := aes.NewCipher(key[:16])
- if err != nil {
- t.Fatal(err)
- }
- d, err := des.NewCipher(key[:8])
- if err != nil {
- t.Fatal(err)
- }
-
- s := 16
- pt := make([]byte, s)
- ct := make([]byte, s)
- for i := 0; i < 16; i++ {
- pt[i], ct[i] = byte(i), byte(i)
- }
-
- assertEqual := func(name string, got, want []byte) {
- if !bytes.Equal(got, want) {
- t.Fatalf("%s: got %v, want %v", name, got, want)
- }
- }
-
- for _, b := range []cipher.Block{a, d} {
- iv := make([]byte, b.BlockSize())
- cbce := cipher.NewCBCEncrypter(b, iv)
- cbce.CryptBlocks(ct, pt[:0])
- assertEqual("CBC encrypt", ct, pt)
-
- cbcd := cipher.NewCBCDecrypter(b, iv)
- cbcd.CryptBlocks(ct, pt[:0])
- assertEqual("CBC decrypt", ct, pt)
-
- cfbe := cipher.NewCFBEncrypter(b, iv)
- cfbe.XORKeyStream(ct, pt[:0])
- assertEqual("CFB encrypt", ct, pt)
-
- cfbd := cipher.NewCFBDecrypter(b, iv)
- cfbd.XORKeyStream(ct, pt[:0])
- assertEqual("CFB decrypt", ct, pt)
-
- ctr := cipher.NewCTR(b, iv)
- ctr.XORKeyStream(ct, pt[:0])
- assertEqual("CTR", ct, pt)
-
- ofb := cipher.NewOFB(b, iv)
- ofb.XORKeyStream(ct, pt[:0])
- assertEqual("OFB", ct, pt)
- }
-}
"crypto/aes"
"crypto/cipher"
"crypto/internal/boring"
+ "crypto/internal/cryptotest"
+ fipsaes "crypto/internal/fips/aes"
"encoding/hex"
"fmt"
"math/rand"
}
func TestCTR_AES(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", testCTR_AES)
+}
+
+func testCTR_AES(t *testing.T) {
for _, tt := range ctrAESTests {
test := tt.name
}
}
-// This wrapper type disables method NewCTR (interface ctrAble)
-// to force generic implementation.
-type nonCtrAble struct {
- impl cipher.Block
-}
-
-func (n *nonCtrAble) BlockSize() int {
- return n.impl.BlockSize()
-}
-
-func (n *nonCtrAble) Encrypt(dst, src []byte) {
- n.impl.Encrypt(dst, src)
-}
-
-func (n *nonCtrAble) Decrypt(dst, src []byte) {
- panic("must not be called")
-}
-
func makeTestingCiphers(aesBlock cipher.Block, iv []byte) (genericCtr, multiblockCtr cipher.Stream) {
- return cipher.NewCTR(&nonCtrAble{impl: aesBlock}, iv), cipher.NewCTR(aesBlock, iv)
+ return cipher.NewCTR(wrap(aesBlock), iv), cipher.NewCTR(aesBlock, iv)
}
func randBytes(t *testing.T, r *rand.Rand, count int) []byte {
if err != nil {
t.Fatal(err)
}
- if _, ok := aesBlock.(ctrAble); !ok {
- t.Skip("Skipping the test - multiblock implementation is not available")
- }
genericCtr, _ := makeTestingCiphers(aesBlock, iv)
plaintext := randBytes(t, r, Size)
if err != nil {
t.Fatal(err)
}
- if _, ok := aesBlock.(ctrAble); !ok {
- t.Skip("Skipping the test - multiblock implementation is not available")
- }
t.Run(fmt.Sprintf("iv=%s", hex.EncodeToString(iv)), func(t *testing.T) {
for _, offset := range []int{0, 1, 16, 1024} {
t.Skip("XORKeyStreamAt is not available in boring mode")
}
- type XORKeyStreamAtable interface {
- XORKeyStreamAt(dst, src []byte, offset uint64)
- }
-
r := rand.New(rand.NewSource(12345))
const Size = 32 * 1024 * 1024
plaintext := randBytes(t, r, Size)
if err != nil {
t.Fatal(err)
}
- if _, ok := aesBlock.(ctrAble); !ok {
- t.Skip("Skipping the test - multiblock implementation is not available")
- }
- genericCtr, multiblockCtr := makeTestingCiphers(aesBlock, iv)
- ctrAt, ok := multiblockCtr.(XORKeyStreamAtable)
- if !ok {
- t.Fatal("cipher is expected to have method XORKeyStreamAt")
- }
+ genericCtr, _ := makeTestingCiphers(aesBlock, iv)
+ ctrAt := fipsaes.NewCTR(aesBlock.(*fipsaes.Block), iv)
// Generate reference ciphertext.
genericCiphertext := make([]byte, Size)
func (b noopBlock) BlockSize() int { return int(b) }
func (noopBlock) Encrypt(dst, src []byte) { copy(dst, src) }
-func (noopBlock) Decrypt(dst, src []byte) { copy(dst, src) }
+func (noopBlock) Decrypt(dst, src []byte) { panic("unreachable") }
func inc(b []byte) {
for i := len(b) - 1; i >= 0; i++ {
}
func TestCTRStream(t *testing.T) {
+ cryptotest.TestAllImplementations(t, "aes", func(t *testing.T) {
+ for _, keylen := range []int{128, 192, 256} {
+ t.Run(fmt.Sprintf("AES-%d", keylen), func(t *testing.T) {
+ rng := newRandReader(t)
- for _, keylen := range []int{128, 192, 256} {
+ key := make([]byte, keylen/8)
+ rng.Read(key)
- t.Run(fmt.Sprintf("AES-%d", keylen), func(t *testing.T) {
- rng := newRandReader(t)
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
- key := make([]byte, keylen/8)
- rng.Read(key)
-
- block, err := aes.NewCipher(key)
- if err != nil {
- panic(err)
- }
-
- cryptotest.TestStreamFromBlock(t, block, cipher.NewCTR)
- })
- }
+ cryptotest.TestStreamFromBlock(t, block, cipher.NewCTR)
+ })
+ }
+ })
t.Run("DES", func(t *testing.T) {
rng := newRandReader(t)
+++ /dev/null
-// Copyright 2018 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 cipher
-
-// Export internal functions for testing.
-var NewCBCGenericEncrypter = newCBCGenericEncrypter
-var NewCBCGenericDecrypter = newCBCGenericDecrypter
c, _ := aes.NewCipher(ft.key)
cbcAsm := cipher.NewCBCEncrypter(c, commonIV)
- cbcGeneric := cipher.NewCBCGenericEncrypter(c, commonIV)
+ cbcGeneric := cipher.NewCBCEncrypter(wrap(c), commonIV)
if testing.Short() {
timeout = time.NewTimer(10 * time.Millisecond)
}
cbcAsm = cipher.NewCBCDecrypter(c, commonIV)
- cbcGeneric = cipher.NewCBCGenericDecrypter(c, commonIV)
+ cbcGeneric = cipher.NewCBCDecrypter(wrap(c), commonIV)
if testing.Short() {
timeout = time.NewTimer(10 * time.Millisecond)
"testing"
)
+var _ cipher.Block = (*wrapper)(nil)
+
+type wrapper struct {
+ block cipher.Block
+}
+
+func (w *wrapper) BlockSize() int { return w.block.BlockSize() }
+func (w *wrapper) Encrypt(dst, src []byte) { w.block.Encrypt(dst, src) }
+func (w *wrapper) Decrypt(dst, src []byte) { w.block.Decrypt(dst, src) }
+
+// wrap wraps the Block so that it does not type-asserts to *aes.Block.
+func wrap(b cipher.Block) cipher.Block {
+ return &wrapper{b}
+}
+
+func testAllImplementations(t *testing.T, f func(*testing.T, func([]byte) cipher.Block)) {
+ cryptotest.TestAllImplementations(t, "gcm", func(t *testing.T) {
+ f(t, func(b []byte) cipher.Block {
+ c, err := aes.NewCipher(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return c
+ })
+ })
+ t.Run("Fallback", func(t *testing.T) {
+ f(t, func(b []byte) cipher.Block {
+ c, err := aes.NewCipher(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return wrap(c)
+ })
+ })
+}
+
var aesGCMTests = []struct {
key, nonce, plaintext, ad, result string
}{
}
func TestAESGCM(t *testing.T) {
+ testAllImplementations(t, testAESGCM)
+}
+
+func testAESGCM(t *testing.T, newCipher func(key []byte) cipher.Block) {
for i, test := range aesGCMTests {
key, _ := hex.DecodeString(test.key)
- aes, err := aes.NewCipher(key)
- if err != nil {
- t.Fatal(err)
- }
+ aes := newCipher(key)
nonce, _ := hex.DecodeString(test.nonce)
plaintext, _ := hex.DecodeString(test.plaintext)
ad, _ := hex.DecodeString(test.ad)
tagSize := (len(test.result) - len(test.plaintext)) / 2
+ var err error
var aesgcm cipher.AEAD
switch {
// Handle non-standard tag sizes
}
func TestGCMInvalidTagSize(t *testing.T) {
- key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed")
+ testAllImplementations(t, testGCMInvalidTagSize)
+}
- aes, _ := aes.NewCipher(key)
+func testGCMInvalidTagSize(t *testing.T, newCipher func(key []byte) cipher.Block) {
+ key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed")
+ aes := newCipher(key)
for _, tagSize := range []int{0, 1, aes.BlockSize() + 1} {
aesgcm, err := cipher.NewGCMWithTagSize(aes, tagSize)
}
func TestTagFailureOverwrite(t *testing.T) {
+ testAllImplementations(t, testTagFailureOverwrite)
+}
+
+func testTagFailureOverwrite(t *testing.T, newCipher func(key []byte) cipher.Block) {
// The AESNI GCM code decrypts and authenticates concurrently and so
// overwrites the output buffer before checking the authentication tag.
// In order to be consistent across platforms, all implementations
nonce, _ := hex.DecodeString("54cc7dc2c37ec006bcc6d1db")
ciphertext, _ := hex.DecodeString("0e1bde206a07a9c2c1b65300f8c649972b4401346697138c7a4891ee59867d0c")
- aes, _ := aes.NewCipher(key)
+ aes := newCipher(key)
aesgcm, _ := cipher.NewGCM(aes)
dst := make([]byte, len(ciphertext)-16)
}
func TestGCMCounterWrap(t *testing.T) {
+ testAllImplementations(t, testGCMCounterWrap)
+}
+
+func testGCMCounterWrap(t *testing.T, newCipher func(key []byte) cipher.Block) {
// Test that the last 32-bits of the counter wrap correctly.
tests := []struct {
nonce, tag string
{"010ae3d486", "5405bb490b1f95d01e2ba735687154bc"}, // counter: e36c18e69406c49722808104fffffff8
{"01b1107a9d", "939a585f342e01e17844627492d44dbf"}, // counter: e6d56eaf9127912b6d62c6dcffffffff
}
- key, err := aes.NewCipher(make([]byte, 16))
- if err != nil {
- t.Fatal(err)
- }
+ key := newCipher(make([]byte, 16))
plaintext := make([]byte, 16*17+1)
for i, test := range tests {
nonce, _ := hex.DecodeString(test.nonce)
}
}
-var _ cipher.Block = (*wrapper)(nil)
-
-type wrapper struct {
- block cipher.Block
-}
-
-func (w *wrapper) BlockSize() int { return w.block.BlockSize() }
-func (w *wrapper) Encrypt(dst, src []byte) { w.block.Encrypt(dst, src) }
-func (w *wrapper) Decrypt(dst, src []byte) { w.block.Decrypt(dst, src) }
-
-// wrap wraps the Block interface so that it does not fulfill
-// any optimizing interfaces such as gcmAble.
-func wrap(b cipher.Block) cipher.Block {
- return &wrapper{b}
-}
-
func TestGCMAsm(t *testing.T) {
// Create a new pair of AEADs, one using the assembly implementation
// and one using the generic Go implementation.
// Test GCM against the general cipher.AEAD interface tester.
func TestGCMAEAD(t *testing.T) {
+ testAllImplementations(t, testGCMAEAD)
+}
+
+func testGCMAEAD(t *testing.T, newCipher func(key []byte) cipher.Block) {
minTagSize := 12
for _, keySize := range []int{128, 192, 256} {
key := make([]byte, keySize/8)
rng.Read(key)
- block, err := aes.NewCipher(key)
- if err != nil {
- panic(err)
- }
+ block := newCipher(key)
// Test GCM with the current AES block with the standard nonce and tag
// sizes.
// Test non-standard nonce sizes.
for _, nonceSize := range []int{1, 16, 100} {
t.Run(fmt.Sprintf("NonceSize-%d", nonceSize), func(t *testing.T) {
-
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithNonceSize(block, nonceSize) })
})
}
package cipher_test
import (
- "crypto/aes"
. "crypto/cipher"
"reflect"
"testing"
// TestNoExtraMethods makes sure we don't accidentally expose methods on the
// underlying implementations of modes.
func TestNoExtraMethods(t *testing.T) {
- b, _ := aes.NewCipher(make([]byte, 16))
+ testAllImplementations(t, testNoExtraMethods)
+}
+
+func testNoExtraMethods(t *testing.T, newBlock func([]byte) Block) {
+ b := newBlock(make([]byte, 16))
ctr := NewCTR(b, make([]byte, 16))
ctrExpected := []string{"XORKeyStream"}
mustPanic(t, "IV length must equal block size", func() { bm(b, iv) })
})
+ t.Run("EmptyInput", func(t *testing.T) {
+ rng := newRandReader(t)
+
+ src, dst := make([]byte, blockSize), make([]byte, blockSize)
+ rng.Read(dst)
+ before := bytes.Clone(dst)
+
+ bm(b, iv).CryptBlocks(dst, src[:0])
+ if !bytes.Equal(dst, before) {
+ t.Errorf("CryptBlocks modified dst on empty input; got %x, want %x", dst, before)
+ }
+ })
+
t.Run("AlterInput", func(t *testing.T) {
rng := newRandReader(t)
})
})
+ t.Run("EmptyInput", func(t *testing.T) {
+ rng := newRandReader(t)
+
+ src, dst := make([]byte, 100), make([]byte, 100)
+ rng.Read(dst)
+ before := bytes.Clone(dst)
+
+ ms().XORKeyStream(dst, src[:0])
+ if !bytes.Equal(dst, before) {
+ t.Errorf("XORKeyStream modified dst on empty input; got %s, want %s", truncateHex(dst), truncateHex(before))
+ }
+ })
+
t.Run("AlterInput", func(t *testing.T) {
rng := newRandReader(t)
src, dst, before := make([]byte, bufCap), make([]byte, bufCap), make([]byte, bufCap)
package aes
import (
+ "crypto/internal/impl"
"internal/cpu"
"internal/goarch"
+ "internal/godebug"
)
//go:noescape
var supportsAES = cpu.X86.HasAES && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3 ||
cpu.ARM64.HasAES || goarch.IsPpc64 == 1 || goarch.IsPpc64le == 1
+func init() {
+ if goarch.IsAmd64 == 1 {
+ impl.Register("aes", "AES-NI", &supportsAES)
+ }
+ if goarch.IsArm64 == 1 {
+ impl.Register("aes", "Armv8.0", &supportsAES)
+ }
+ if goarch.IsPpc64 == 1 || goarch.IsPpc64le == 1 {
+ // The POWER architecture doesn't have a way to turn off AES support
+ // at runtime with GODEBUG=cpu.something=off, so introduce a new GODEBUG
+ // knob for that. It's intentionally only checked at init() time, to
+ // avoid the performance overhead of checking it every time.
+ if godebug.New("#ppc64aes").Value() == "off" {
+ supportsAES = false
+ }
+ impl.Register("aes", "POWER8", &supportsAES)
+ }
+}
+
// checkGenericIsExpected is called by the variable-time implementation to make
// sure it is not used when hardware support is available. It shouldn't happen,
// but this way it's more evidently correct.
package aes
-import "internal/cpu"
+import (
+ "crypto/internal/impl"
+ "internal/cpu"
+)
type code int
var supportsAES = cpu.S390X.HasAES && cpu.S390X.HasAESCBC
+func init() {
+ // CP Assist for Cryptographic Functions (CPACF)
+ // https://www.ibm.com/docs/en/zos/3.1.0?topic=icsf-cp-assist-cryptographic-functions-cpacf
+ impl.Register("aes", "CPACF", &supportsAES)
+}
+
func checkGenericIsExpected() {
if supportsAES {
panic("crypto/aes: internal error: using generic implementation despite hardware support")
import (
"crypto/internal/fips/aes"
"crypto/internal/fips/subtle"
+ "crypto/internal/impl"
"internal/cpu"
+ "internal/goarch"
)
// The following functions are defined in gcm_*.s.
var supportsAESGCM = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3 ||
cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
+func init() {
+ if goarch.IsAmd64 == 1 {
+ impl.Register("gcm", "AES-NI", &supportsAESGCM)
+ }
+ if goarch.IsArm64 == 1 {
+ impl.Register("gcm", "Armv8.0", &supportsAESGCM)
+ }
+}
+
// checkGenericIsExpected is called by the variable-time implementation to make
// sure it is not used when hardware support is available. It shouldn't happen,
// but this way it's more evidently correct.
import (
"crypto/internal/fips/aes"
"crypto/internal/fips/subtle"
+ "crypto/internal/impl"
"internal/byteorder"
"internal/godebug"
"runtime"
// avoid the performance overhead of checking it every time.
var supportsAESGCM = godebug.New("#ppc64gcm").Value() == "off"
+func init() {
+ impl.Register("gcm", "POWER8", &supportsAESGCM)
+}
+
func checkGenericIsExpected() {
if supportsAESGCM {
panic("gcm: internal error: using generic implementation despite hardware support")
import (
"crypto/internal/fips/aes"
"crypto/internal/fips/subtle"
+ "crypto/internal/impl"
"internal/byteorder"
"internal/cpu"
)
var useGHASH = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
var useGCM = useGHASH && cpu.S390X.HasAESGCM
+func init() {
+ impl.Register("gcm", "CPACF/KIMD", &useGHASH)
+ impl.Register("gcm", "CPACF/KMA", &useGCM)
+}
+
func checkGenericIsExpected() {
if useGHASH || useGCM {
panic("gcm: internal error: using generic implementation despite hardware support")