gmsm/cipher/bc.go

135 lines
3.3 KiB
Go
Raw Normal View History

// Block Chaining operation mode (BC mode) in Chinese national standard GB/T 17964-2021.
// See GB/T 17964-2021 Chapter 12.
package cipher
import (
_cipher "crypto/cipher"
"github.com/emmansun/gmsm/internal/subtle"
)
type bc struct {
b _cipher.Block
blockSize int
iv []byte
}
func newBC(b _cipher.Block, iv []byte) *bc {
c := &bc{
b: b,
blockSize: b.BlockSize(),
iv: make([]byte, b.BlockSize()),
}
copy(c.iv, iv)
return c
}
type bcEncrypter bc
// bcEncAble is an interface implemented by ciphers that have a specific
// optimized implementation of BC encryption.
// NewBCEncrypter will check for this interface and return the specific
// BlockMode if found.
type bcEncAble interface {
NewBCEncrypter(iv []byte) _cipher.BlockMode
}
// NewBCEncrypter returns a BlockMode which encrypts in block chaining
// mode, using the given Block. The length of iv must be the same as the
// Block's block size.
func NewBCEncrypter(b _cipher.Block, iv []byte) _cipher.BlockMode {
if len(iv) != b.BlockSize() {
panic("cipher.NewBCEncrypter: IV length must equal block size")
}
if bc, ok := b.(bcEncAble); ok {
return bc.NewBCEncrypter(iv)
}
return (*bcEncrypter)(newBC(b, iv))
}
func (x *bcEncrypter) BlockSize() int { return x.blockSize }
func (x *bcEncrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
iv := x.iv
for len(src) > 0 {
// Write the xor to dst, then encrypt in place.
subtle.XORBytes(dst[:x.blockSize], src[:x.blockSize], iv)
x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
subtle.XORBytes(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 *bcEncrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}
type bcDecrypter bc
// bcDecAble is an interface implemented by ciphers that have a specific
// optimized implementation of BC decryption.
// NewBCDecrypter will check for this interface and return the specific
// BlockMode if found.
type bcDecAble interface {
NewBCDecrypter(iv []byte) _cipher.BlockMode
}
// NewBCDecrypter returns a BlockMode which decrypts in 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 NewBCDecrypter(b _cipher.Block, iv []byte) _cipher.BlockMode {
if len(iv) != b.BlockSize() {
panic("cipher.NewBCDecrypter: IV length must equal block size")
}
if bc, ok := b.(bcDecAble); ok {
return bc.NewBCDecrypter(iv)
}
return (*bcDecrypter)(newBC(b, iv))
}
func (x *bcDecrypter) BlockSize() int { return x.blockSize }
func (x *bcDecrypter) CryptBlocks(dst, src []byte) {
validate(x.blockSize, dst, src)
if len(src) == 0 {
return
}
iv := x.iv
nextIV := make([]byte, x.blockSize)
for len(src) > 0 {
// Get F(i+1)
subtle.XORBytes(nextIV, iv, src[:x.blockSize])
// Get plaintext P(i)
x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize])
subtle.XORBytes(dst[:x.blockSize], dst[:x.blockSize], iv)
copy(iv, nextIV)
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
// Save the iv for the next CryptBlocks call.
copy(x.iv, iv)
}
func (x *bcDecrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}