mirror of
https://github.com/golang/go
synced 2024-11-08 13:36:14 -07:00
2f8475648a
This commit adds the cbcEncAble and cbcDecAble interfaces that can be implemented by block ciphers that support an optimized implementation of CBC. This is similar to what is done for GCM with the gcmAble interface. The cbcEncAble, cbcDecAble and gcmAble interfaces all now have tests to ensure they are detected correctly in the cipher package. name old speed new speed delta AESCBCEncrypt1K 152MB/s ± 1% 1362MB/s ± 0% +795.59% (p=0.000 n=10+9) AESCBCDecrypt1K 143MB/s ± 1% 1362MB/s ± 0% +853.00% (p=0.000 n=10+9) Change-Id: I715f686ab3686b189a3dac02f86001178fa60580 Reviewed-on: https://go-review.googlesource.com/22523 Run-TryBot: Michael Munday <munday@ca.ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
156 lines
4.2 KiB
Go
156 lines
4.2 KiB
Go
// Copyright 2009 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.
|
|
|
|
// Cipher block chaining (CBC) mode.
|
|
|
|
// CBC provides confidentiality by xoring (chaining) each plaintext block
|
|
// with the previous ciphertext block before applying the block cipher.
|
|
|
|
// See NIST SP 800-38A, pp 10-11
|
|
|
|
package cipher
|
|
|
|
type cbc struct {
|
|
b Block
|
|
blockSize int
|
|
iv []byte
|
|
tmp []byte
|
|
}
|
|
|
|
func newCBC(b Block, iv []byte) *cbc {
|
|
return &cbc{
|
|
b: b,
|
|
blockSize: b.BlockSize(),
|
|
iv: dup(iv),
|
|
tmp: make([]byte, b.BlockSize()),
|
|
}
|
|
}
|
|
|
|
type cbcEncrypter cbc
|
|
|
|
// cbcEncAble is an interface implemented by ciphers that have a specific
|
|
// optimized implementation of CBC encryption, like crypto/aes.
|
|
// NewCBCEncrypter will check for this interface and return the specific
|
|
// BlockMode if found.
|
|
type cbcEncAble interface {
|
|
NewCBCEncrypter(iv []byte) BlockMode
|
|
}
|
|
|
|
// NewCBCEncrypter returns a BlockMode which encrypts in cipher block chaining
|
|
// mode, using the given Block. The length of iv must be the same as the
|
|
// Block's block size.
|
|
func NewCBCEncrypter(b Block, iv []byte) BlockMode {
|
|
if len(iv) != b.BlockSize() {
|
|
panic("cipher.NewCBCEncrypter: IV length must equal block size")
|
|
}
|
|
if cbc, ok := b.(cbcEncAble); ok {
|
|
return cbc.NewCBCEncrypter(iv)
|
|
}
|
|
return (*cbcEncrypter)(newCBC(b, iv))
|
|
}
|
|
|
|
func (x *cbcEncrypter) BlockSize() int { return x.blockSize }
|
|
|
|
func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {
|
|
if len(src)%x.blockSize != 0 {
|
|
panic("crypto/cipher: input not full blocks")
|
|
}
|
|
if len(dst) < len(src) {
|
|
panic("crypto/cipher: output smaller than input")
|
|
}
|
|
|
|
iv := x.iv
|
|
|
|
for len(src) > 0 {
|
|
// Write the xor to dst, then encrypt in place.
|
|
xorBytes(dst[:x.blockSize], src[:x.blockSize], iv)
|
|
x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
|
|
|
|
// Move to the next block with this block as the next iv.
|
|
iv = dst[:x.blockSize]
|
|
src = src[x.blockSize:]
|
|
dst = dst[x.blockSize:]
|
|
}
|
|
|
|
// Save the iv for the next CryptBlocks call.
|
|
copy(x.iv, iv)
|
|
}
|
|
|
|
func (x *cbcEncrypter) SetIV(iv []byte) {
|
|
if len(iv) != len(x.iv) {
|
|
panic("cipher: incorrect length IV")
|
|
}
|
|
copy(x.iv, iv)
|
|
}
|
|
|
|
type cbcDecrypter cbc
|
|
|
|
// cbcDecAble is an interface implemented by ciphers that have a specific
|
|
// optimized implementation of CBC decryption, like crypto/aes.
|
|
// NewCBCDecrypter will check for this interface and return the specific
|
|
// BlockMode if found.
|
|
type cbcDecAble interface {
|
|
NewCBCDecrypter(iv []byte) BlockMode
|
|
}
|
|
|
|
// NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining
|
|
// mode, using the given Block. The length of iv must be the same as the
|
|
// Block's block size and must match the iv used to encrypt the data.
|
|
func NewCBCDecrypter(b Block, iv []byte) BlockMode {
|
|
if len(iv) != b.BlockSize() {
|
|
panic("cipher.NewCBCDecrypter: IV length must equal block size")
|
|
}
|
|
if cbc, ok := b.(cbcDecAble); ok {
|
|
return cbc.NewCBCDecrypter(iv)
|
|
}
|
|
return (*cbcDecrypter)(newCBC(b, iv))
|
|
}
|
|
|
|
func (x *cbcDecrypter) BlockSize() int { return x.blockSize }
|
|
|
|
func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
|
|
if len(src)%x.blockSize != 0 {
|
|
panic("crypto/cipher: input not full blocks")
|
|
}
|
|
if len(dst) < len(src) {
|
|
panic("crypto/cipher: output smaller than input")
|
|
}
|
|
if len(src) == 0 {
|
|
return
|
|
}
|
|
|
|
// For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv).
|
|
// To avoid making a copy each time, we loop over the blocks BACKWARDS.
|
|
end := len(src)
|
|
start := end - x.blockSize
|
|
prev := start - x.blockSize
|
|
|
|
// Copy the last block of ciphertext in preparation as the new iv.
|
|
copy(x.tmp, src[start:end])
|
|
|
|
// Loop over all but the first block.
|
|
for start > 0 {
|
|
x.b.Decrypt(dst[start:end], src[start:end])
|
|
xorBytes(dst[start:end], dst[start:end], src[prev:start])
|
|
|
|
end = start
|
|
start = prev
|
|
prev -= x.blockSize
|
|
}
|
|
|
|
// The first block is special because it uses the saved iv.
|
|
x.b.Decrypt(dst[start:end], src[start:end])
|
|
xorBytes(dst[start:end], dst[start:end], x.iv)
|
|
|
|
// Set the new iv to the first block we copied earlier.
|
|
x.iv, x.tmp = x.tmp, x.iv
|
|
}
|
|
|
|
func (x *cbcDecrypter) SetIV(iv []byte) {
|
|
if len(iv) != len(x.iv) {
|
|
panic("cipher: incorrect length IV")
|
|
}
|
|
copy(x.iv, iv)
|
|
}
|