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 }