diff options
author | Russ Cox <rsc@golang.org> | 2017-08-03 11:08:27 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2017-08-26 00:52:20 +0000 |
commit | 33ebf73c45c43624c8ab7813b0ab601b6f236754 (patch) | |
tree | a6e348b2120a1e1e5a48a13c6e92c8df0d50aaa7 | |
parent | 065bb6eb3012d2dcdd8a58b7f772a1542af4520f (diff) | |
download | go-33ebf73c45c43624c8ab7813b0ab601b6f236754.tar.gz go-33ebf73c45c43624c8ab7813b0ab601b6f236754.zip |
[dev.boringcrypto.go1.8] crypto/aes: use BoringCrypto
Change-Id: If83fdeac31f65aba818bbc7edd2f215b16814021
Reviewed-on: https://go-review.googlesource.com/55476
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
Reviewed-on: https://go-review.googlesource.com/57937
-rw-r--r-- | src/crypto/aes/cipher.go | 4 | ||||
-rw-r--r-- | src/crypto/aes/cipher_amd64.go | 3 | ||||
-rw-r--r-- | src/crypto/internal/boring/aes.go | 295 | ||||
-rw-r--r-- | src/crypto/internal/boring/notboring.go | 7 | ||||
-rw-r--r-- | src/crypto/tls/cipher_suites.go | 6 |
5 files changed, 313 insertions, 2 deletions
diff --git a/src/crypto/aes/cipher.go b/src/crypto/aes/cipher.go index c5a8e91d00..fbd01a8f40 100644 --- a/src/crypto/aes/cipher.go +++ b/src/crypto/aes/cipher.go @@ -6,6 +6,7 @@ package aes import ( "crypto/cipher" + "crypto/internal/boring" "strconv" ) @@ -36,6 +37,9 @@ func NewCipher(key []byte) (cipher.Block, error) { case 16, 24, 32: break } + if boring.Enabled { + return boring.NewAESCipher(key) + } return newCipher(key) } diff --git a/src/crypto/aes/cipher_amd64.go b/src/crypto/aes/cipher_amd64.go index 43de3bdffd..fcdebabb33 100644 --- a/src/crypto/aes/cipher_amd64.go +++ b/src/crypto/aes/cipher_amd64.go @@ -6,6 +6,7 @@ package aes import ( "crypto/cipher" + "crypto/internal/boring" "crypto/internal/cipherhw" ) @@ -46,6 +47,7 @@ func newCipher(key []byte) (cipher.Block, error) { func (c *aesCipherAsm) BlockSize() int { return BlockSize } func (c *aesCipherAsm) Encrypt(dst, src []byte) { + boring.Unreachable() if len(src) < BlockSize { panic("crypto/aes: input not full block") } @@ -56,6 +58,7 @@ func (c *aesCipherAsm) Encrypt(dst, src []byte) { } func (c *aesCipherAsm) Decrypt(dst, src []byte) { + boring.Unreachable() if len(src) < BlockSize { panic("crypto/aes: input not full block") } diff --git a/src/crypto/internal/boring/aes.go b/src/crypto/internal/boring/aes.go new file mode 100644 index 0000000000..8b55564138 --- /dev/null +++ b/src/crypto/internal/boring/aes.go @@ -0,0 +1,295 @@ +// Copyright 2017 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 linux,amd64 +// +build !cmd_go_bootstrap + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "crypto/cipher" + "errors" + "runtime" + "strconv" + "unsafe" +) + +type aesKeySizeError int + +func (k aesKeySizeError) Error() string { + return "crypto/aes: invalid key size " + strconv.Itoa(int(k)) +} + +const aesBlockSize = 16 + +type aesCipher struct { + key []byte + enc C.GO_AES_KEY + dec C.GO_AES_KEY +} + +type extraModes interface { + // Copied out of crypto/aes/modes.go. + NewCBCEncrypter(iv []byte) cipher.BlockMode + NewCBCDecrypter(iv []byte) cipher.BlockMode + NewCTR(iv []byte) cipher.Stream + NewGCM(size int) (cipher.AEAD, error) +} + +var _ extraModes = (*aesCipher)(nil) + +func NewAESCipher(key []byte) (cipher.Block, error) { + c := &aesCipher{key: make([]byte, len(key))} + copy(c.key, key) + // Note: 0 is success, contradicting the usual BoringCrypto convention. + if C._goboringcrypto_AES_set_decrypt_key((*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.uint(8*len(c.key)), &c.dec) != 0 || + C._goboringcrypto_AES_set_encrypt_key((*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.uint(8*len(c.key)), &c.enc) != 0 { + return nil, aesKeySizeError(len(key)) + } + return c, nil +} + +func (c *aesCipher) BlockSize() int { return aesBlockSize } + +func (c *aesCipher) Encrypt(dst, src []byte) { + if len(src) < aesBlockSize { + panic("crypto/aes: input not full block") + } + if len(dst) < aesBlockSize { + panic("crypto/aes: output not full block") + } + C._goboringcrypto_AES_encrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + &c.enc) +} + +func (c *aesCipher) Decrypt(dst, src []byte) { + if len(src) < aesBlockSize { + panic("crypto/aes: input not full block") + } + if len(dst) < aesBlockSize { + panic("crypto/aes: output not full block") + } + C._goboringcrypto_AES_decrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + &c.dec) +} + +type aesCBC struct { + key *C.GO_AES_KEY + mode C.int + iv [aesBlockSize]byte +} + +func (x *aesCBC) BlockSize() int { return aesBlockSize } + +func (x *aesCBC) CryptBlocks(dst, src []byte) { + if len(src)%aesBlockSize != 0 { + panic("crypto/cipher: input not full blocks") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if len(src) > 0 { + C._goboringcrypto_AES_cbc_encrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + C.size_t(len(src)), x.key, + (*C.uint8_t)(unsafe.Pointer(&x.iv[0])), x.mode) + } +} + +func (x *aesCBC) SetIV(iv []byte) { + if len(iv) != aesBlockSize { + panic("cipher: incorrect length IV") + } + copy(x.iv[:], iv) +} + +func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { + x := &aesCBC{key: &c.enc, mode: C.GO_AES_ENCRYPT} + copy(x.iv[:], iv) + return x +} + +func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { + x := &aesCBC{key: &c.dec, mode: C.GO_AES_DECRYPT} + copy(x.iv[:], iv) + return x +} + +type aesCTR struct { + key *C.GO_AES_KEY + iv [aesBlockSize]byte + num C.uint + ecount_buf [16]C.uint8_t +} + +func (x *aesCTR) XORKeyStream(dst, src []byte) { + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if len(src) == 0 { + return + } + C._goboringcrypto_AES_ctr128_encrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + C.size_t(len(src)), x.key, (*C.uint8_t)(unsafe.Pointer(&x.iv[0])), + &x.ecount_buf[0], &x.num) +} + +func (c *aesCipher) NewCTR(iv []byte) cipher.Stream { + x := &aesCTR{key: &c.enc} + copy(x.iv[:], iv) + return x +} + +type aesGCM struct { + ctx C.GO_EVP_AEAD_CTX + aead *C.GO_EVP_AEAD +} + +const ( + gcmBlockSize = 16 + gcmTagSize = 16 + gcmStandardNonceSize = 12 +) + +type aesNonceSizeError int + +func (n aesNonceSizeError) Error() string { + return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n)) +} + +type noGCM struct { + cipher.Block +} + +func (c *aesCipher) NewGCM(nonceSize int) (cipher.AEAD, error) { + if nonceSize != gcmStandardNonceSize { + // Fall back to standard library for GCM with non-standard nonce size. + return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize) + } + + var aead *C.GO_EVP_AEAD + switch len(c.key) * 8 { + case 128: + aead = C._goboringcrypto_EVP_aead_aes_128_gcm() + case 256: + aead = C._goboringcrypto_EVP_aead_aes_256_gcm() + default: + // Fall back to standard library for GCM with non-standard key size. + return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize) + } + + g := &aesGCM{aead: aead} + if C._goboringcrypto_EVP_AEAD_CTX_init(&g.ctx, aead, (*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.size_t(len(c.key)), C.GO_EVP_AEAD_DEFAULT_TAG_LENGTH, nil) == 0 { + return nil, fail("EVP_AEAD_CTX_init") + } + runtime.SetFinalizer(g, (*aesGCM).finalize) + if g.NonceSize() != nonceSize { + panic("boringcrypto: internal confusion about nonce size") + } + if g.Overhead() != gcmTagSize { + panic("boringcrypto: internal confusion about tag size") + } + + return g, nil +} + +func (g *aesGCM) finalize() { + C._goboringcrypto_EVP_AEAD_CTX_cleanup(&g.ctx) +} + +func (g *aesGCM) NonceSize() int { + return int(C._goboringcrypto_EVP_AEAD_nonce_length(g.aead)) +} + +func (g *aesGCM) Overhead() int { + return int(C._goboringcrypto_EVP_AEAD_max_overhead(g.aead)) +} + +// base returns the address of the underlying array in b, +// being careful not to panic when b has zero length. +func base(b []byte) *C.uint8_t { + if len(b) == 0 { + return nil + } + return (*C.uint8_t)(unsafe.Pointer(&b[0])) +} + +func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + if len(nonce) != gcmStandardNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + if uint64(len(plaintext)) > ((1<<32)-2)*aesBlockSize || len(plaintext)+gcmTagSize < len(plaintext) { + panic("cipher: message too large for GCM") + } + if len(dst)+len(plaintext)+gcmTagSize < len(dst) { + panic("cipher: message too large for buffer") + } + + // Make room in dst to append plaintext+overhead. + n := len(dst) + for cap(dst) < n+len(plaintext)+gcmTagSize { + dst = append(dst[:cap(dst)], 0) + } + dst = dst[:n+len(plaintext)+gcmTagSize] + + var outLen C.size_t + ok := C._goboringcrypto_EVP_AEAD_CTX_seal( + &g.ctx, + (*C.uint8_t)(unsafe.Pointer(&dst[n])), &outLen, C.size_t(len(plaintext)+gcmTagSize), + base(nonce), C.size_t(len(nonce)), + base(plaintext), C.size_t(len(plaintext)), + base(additionalData), C.size_t(len(additionalData))) + if ok == 0 { + panic(fail("EVP_AEAD_CTX_seal")) + } + if outLen != C.size_t(len(plaintext)+gcmTagSize) { + panic("boringcrypto: internal confusion about GCM tag size") + } + return dst[:n+int(outLen)] +} + +var errOpen = errors.New("cipher: message authentication failed") + +func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(nonce) != gcmStandardNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + if len(ciphertext) < gcmTagSize { + return nil, errOpen + } + if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize { + return nil, errOpen + } + + // Make room in dst to append ciphertext without tag. + n := len(dst) + for cap(dst) < n+len(ciphertext)-gcmTagSize { + dst = append(dst[:cap(dst)], 0) + } + dst = dst[:n+len(ciphertext)-gcmTagSize] + + var outLen C.size_t + ok := C._goboringcrypto_EVP_AEAD_CTX_open( + &g.ctx, + base(dst[n:]), &outLen, C.size_t(len(ciphertext)-gcmTagSize), + base(nonce), C.size_t(len(nonce)), + base(ciphertext), C.size_t(len(ciphertext)), + base(additionalData), C.size_t(len(additionalData))) + if ok == 0 { + return nil, errOpen + } + if outLen != C.size_t(len(ciphertext)-gcmTagSize) { + panic("boringcrypto: internal confusion about GCM tag size") + } + return dst[:n+int(outLen)], nil +} diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go index 727247bc61..5cf12bc4b0 100644 --- a/src/crypto/internal/boring/notboring.go +++ b/src/crypto/internal/boring/notboring.go @@ -6,7 +6,10 @@ package boring -import "hash" +import ( + "crypto/cipher" + "hash" +) const available = false @@ -31,3 +34,5 @@ func NewSHA384() hash.Hash { panic("boringcrypto: not available") } func NewSHA512() hash.Hash { panic("boringcrypto: not available") } func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("boringcrypto: not available") } + +func NewAESCipher(key []byte) (cipher.Block, error) { panic("boringcrypto: not available") } diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 2aeaf668e4..d39c6d3b66 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -136,7 +136,11 @@ func macSHA1(version uint16, key []byte) macFunction { copy(mac.key, key) return mac } - return tls10MAC{hmac.New(newConstantTimeHash(sha1.New), key)} + h := sha1.New + if !boring.Enabled { + h = newConstantTimeHash(h) + } + return tls10MAC{hmac.New(h, key)} } // macSHA256 returns a SHA-256 based MAC. These are only supported in TLS 1.2 |