github.com/emmansun/gmsm@v0.29.1/cipher/bc.go (about)

     1  // Block Chaining operation mode (BC mode) in Chinese national standard GB/T 17964-2021.
     2  // See GB/T 17964-2021 Chapter 12.
     3  package cipher
     4  
     5  import (
     6  	_cipher "crypto/cipher"
     7  
     8  	"github.com/emmansun/gmsm/internal/subtle"
     9  )
    10  
    11  type bc struct {
    12  	b         _cipher.Block
    13  	blockSize int
    14  	iv        []byte
    15  }
    16  
    17  func newBC(b _cipher.Block, iv []byte) *bc {
    18  	c := &bc{
    19  		b:         b,
    20  		blockSize: b.BlockSize(),
    21  		iv:        make([]byte, b.BlockSize()),
    22  	}
    23  	copy(c.iv, iv)
    24  	return c
    25  }
    26  
    27  type bcEncrypter bc
    28  
    29  // bcEncAble is an interface implemented by ciphers that have a specific
    30  // optimized implementation of BC encryption.
    31  // NewBCEncrypter will check for this interface and return the specific
    32  // BlockMode if found.
    33  type bcEncAble interface {
    34  	NewBCEncrypter(iv []byte) _cipher.BlockMode
    35  }
    36  
    37  // NewBCEncrypter returns a BlockMode which encrypts in block chaining
    38  // mode, using the given Block. The length of iv must be the same as the
    39  // Block's block size.
    40  func NewBCEncrypter(b _cipher.Block, iv []byte) _cipher.BlockMode {
    41  	if len(iv) != b.BlockSize() {
    42  		panic("cipher.NewBCEncrypter: IV length must equal block size")
    43  	}
    44  	if bc, ok := b.(bcEncAble); ok {
    45  		return bc.NewBCEncrypter(iv)
    46  	}
    47  	return (*bcEncrypter)(newBC(b, iv))
    48  }
    49  
    50  func (x *bcEncrypter) BlockSize() int { return x.blockSize }
    51  
    52  func (x *bcEncrypter) CryptBlocks(dst, src []byte) {
    53  	validate(x.blockSize, dst, src)
    54  
    55  	iv := x.iv
    56  
    57  	for len(src) > 0 {
    58  		// Write the xor to dst, then encrypt in place.
    59  		subtle.XORBytes(dst[:x.blockSize], src[:x.blockSize], iv)
    60  		x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
    61  		subtle.XORBytes(iv, iv, dst[:x.blockSize])
    62  
    63  		src = src[x.blockSize:]
    64  		dst = dst[x.blockSize:]
    65  	}
    66  
    67  	// Save the iv for the next CryptBlocks call.
    68  	copy(x.iv, iv)
    69  }
    70  
    71  func (x *bcEncrypter) SetIV(iv []byte) {
    72  	if len(iv) != len(x.iv) {
    73  		panic("cipher: incorrect length IV")
    74  	}
    75  	copy(x.iv, iv)
    76  }
    77  
    78  type bcDecrypter bc
    79  
    80  // bcDecAble is an interface implemented by ciphers that have a specific
    81  // optimized implementation of BC decryption.
    82  // NewBCDecrypter will check for this interface and return the specific
    83  // BlockMode if found.
    84  type bcDecAble interface {
    85  	NewBCDecrypter(iv []byte) _cipher.BlockMode
    86  }
    87  
    88  // NewBCDecrypter returns a BlockMode which decrypts in block chaining
    89  // mode, using the given Block. The length of iv must be the same as the
    90  // Block's block size and must match the iv used to encrypt the data.
    91  func NewBCDecrypter(b _cipher.Block, iv []byte) _cipher.BlockMode {
    92  	if len(iv) != b.BlockSize() {
    93  		panic("cipher.NewBCDecrypter: IV length must equal block size")
    94  	}
    95  	if bc, ok := b.(bcDecAble); ok {
    96  		return bc.NewBCDecrypter(iv)
    97  	}
    98  	return (*bcDecrypter)(newBC(b, iv))
    99  }
   100  
   101  func (x *bcDecrypter) BlockSize() int { return x.blockSize }
   102  
   103  func (x *bcDecrypter) CryptBlocks(dst, src []byte) {
   104  	validate(x.blockSize, dst, src)
   105  
   106  	if len(src) == 0 {
   107  		return
   108  	}
   109  
   110  	iv := x.iv
   111  	nextIV := make([]byte, x.blockSize)
   112  
   113  	for len(src) > 0 {
   114  		// Get F(i+1)
   115  		subtle.XORBytes(nextIV, iv, src[:x.blockSize])
   116  		// Get plaintext P(i)
   117  		x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize])
   118  		subtle.XORBytes(dst[:x.blockSize], dst[:x.blockSize], iv)
   119  
   120  		copy(iv, nextIV)
   121  		src = src[x.blockSize:]
   122  		dst = dst[x.blockSize:]
   123  	}
   124  
   125  	// Save the iv for the next CryptBlocks call.
   126  	copy(x.iv, iv)
   127  }
   128  
   129  func (x *bcDecrypter) SetIV(iv []byte) {
   130  	if len(iv) != len(x.iv) {
   131  		panic("cipher: incorrect length IV")
   132  	}
   133  	copy(x.iv, iv)
   134  }