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