github.com/emmansun/gmsm@v0.29.1/sm4/ctr_cipher_asm.go (about)

     1  //go:build (amd64 || arm64 || ppc64 || ppc64le) && !purego
     2  
     3  package sm4
     4  
     5  import (
     6  	"crypto/cipher"
     7  
     8  	"github.com/emmansun/gmsm/internal/alias"
     9  	"github.com/emmansun/gmsm/internal/subtle"
    10  )
    11  
    12  // Assert that sm4CipherAsm implements the ctrAble interface.
    13  var _ ctrAble = (*sm4CipherAsm)(nil)
    14  
    15  type ctr struct {
    16  	b       *sm4CipherAsm
    17  	ctr     []byte
    18  	out     []byte
    19  	outUsed int
    20  }
    21  
    22  const streamBufferSize = 512
    23  
    24  // NewCTR returns a Stream which encrypts/decrypts using the SM4 block
    25  // cipher in counter mode. The length of iv must be the same as BlockSize.
    26  func (c *sm4CipherAsm) NewCTR(iv []byte) cipher.Stream {
    27  	if len(iv) != BlockSize {
    28  		panic("cipher.NewCTR: IV length must equal block size")
    29  	}
    30  	bufSize := streamBufferSize
    31  	if bufSize < BlockSize {
    32  		bufSize = BlockSize
    33  	}
    34  	s := &ctr{
    35  		b:       c,
    36  		ctr:     make([]byte, c.blocksSize),
    37  		out:     make([]byte, 0, bufSize),
    38  		outUsed: 0,
    39  	}
    40  	copy(s.ctr, iv)
    41  	for i := 1; i < c.batchBlocks; i++ {
    42  		s.genCtr(i * BlockSize)
    43  	}
    44  	return s
    45  
    46  }
    47  
    48  func (x *ctr) genCtr(start int) {
    49  	if start >= BlockSize {
    50  		copy(x.ctr[start:], x.ctr[start-BlockSize:start])
    51  	} else {
    52  		copy(x.ctr[0:], x.ctr[len(x.ctr)-BlockSize:])
    53  	}
    54  	// Increment counter
    55  	buffer := x.ctr[start : start+BlockSize]
    56  	for i := BlockSize - 1; i >= 0; i-- {
    57  		buffer[i]++
    58  		if buffer[i] != 0 {
    59  			break
    60  		}
    61  	}
    62  }
    63  
    64  func (x *ctr) refill() {
    65  	remain := len(x.out) - x.outUsed
    66  	copy(x.out, x.out[x.outUsed:])
    67  	x.out = x.out[:cap(x.out)]
    68  	for remain <= len(x.out)-x.b.blocksSize {
    69  		encryptBlocksAsm(&x.b.enc[0], x.out[remain:], x.ctr, INST_AES)
    70  
    71  		remain += x.b.blocksSize
    72  
    73  		// Generate complelte [x.b.batchBlocks] counters
    74  		for i := 0; i < x.b.batchBlocks; i++ {
    75  			x.genCtr(i * BlockSize)
    76  		}
    77  	}
    78  	x.out = x.out[:remain]
    79  	x.outUsed = 0
    80  }
    81  
    82  func (x *ctr) XORKeyStream(dst, src []byte) {
    83  	if len(dst) < len(src) {
    84  		panic("cipher: output smaller than input")
    85  	}
    86  	if alias.InexactOverlap(dst[:len(src)], src) {
    87  		panic("cipher: invalid buffer overlap")
    88  	}
    89  	for len(src) > 0 {
    90  		if x.outUsed >= len(x.out)-BlockSize {
    91  			x.refill()
    92  		}
    93  		n := subtle.XORBytes(dst, src, x.out[x.outUsed:])
    94  		dst = dst[n:]
    95  		src = src[n:]
    96  		x.outUsed += n
    97  	}
    98  }