github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/sm4/cipher_asm.go (about)

     1  //go:build amd64 || arm64
     2  // +build amd64 arm64
     3  
     4  package sm4
     5  
     6  import (
     7  	"crypto/cipher"
     8  
     9  	"github.com/hxx258456/ccgo/internal/subtle"
    10  	"golang.org/x/sys/cpu"
    11  )
    12  
    13  var supportSM4 = cpu.ARM64.HasSM4
    14  var supportsAES = cpu.X86.HasAES || cpu.ARM64.HasAES
    15  var supportsGFMUL = cpu.X86.HasPCLMULQDQ || cpu.ARM64.HasPMULL
    16  var useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2
    17  
    18  //goland:noinspection GoSnakeCaseUsage
    19  const (
    20  	INST_AES int = iota
    21  	INST_SM4
    22  )
    23  
    24  //go:noescape
    25  //goland:noinspection GoUnusedParameter
    26  func encryptBlocksAsm(xk *uint32, dst, src []byte, inst int)
    27  
    28  //go:noescape
    29  //goland:noinspection GoUnusedParameter
    30  func encryptBlockAsm(xk *uint32, dst, src *byte, inst int)
    31  
    32  //go:noescape
    33  //goland:noinspection GoUnusedParameter
    34  func expandKeyAsm(key *byte, ck, enc, dec *uint32, inst int)
    35  
    36  type sm4CipherAsm struct {
    37  	sm4Cipher
    38  	batchBlocks int
    39  	blocksSize  int
    40  }
    41  
    42  type sm4CipherNI struct {
    43  	sm4Cipher
    44  }
    45  
    46  func newCipherNI(key []byte) (cipher.Block, error) {
    47  	c := &sm4CipherNI{sm4Cipher{make([]uint32, rounds), make([]uint32, rounds)}}
    48  	expandKeyAsm(&key[0], &ck[0], &c.enc[0], &c.dec[0], INST_SM4)
    49  	if supportsGFMUL {
    50  		return &sm4CipherNIGCM{c}, nil
    51  	}
    52  	return c, nil
    53  }
    54  
    55  func (c *sm4CipherNI) Encrypt(dst, src []byte) {
    56  	if len(src) < BlockSize {
    57  		panic("sm4: input not full block")
    58  	}
    59  	if len(dst) < BlockSize {
    60  		panic("sm4: output not full block")
    61  	}
    62  	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
    63  		panic("sm4: invalid buffer overlap")
    64  	}
    65  	encryptBlockAsm(&c.enc[0], &dst[0], &src[0], INST_SM4)
    66  }
    67  
    68  func (c *sm4CipherNI) Decrypt(dst, src []byte) {
    69  	if len(src) < BlockSize {
    70  		panic("sm4: input not full block")
    71  	}
    72  	if len(dst) < BlockSize {
    73  		panic("sm4: output not full block")
    74  	}
    75  	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
    76  		panic("sm4: invalid buffer overlap")
    77  	}
    78  	encryptBlockAsm(&c.dec[0], &dst[0], &src[0], INST_SM4)
    79  }
    80  
    81  func newCipher(key []byte) (cipher.Block, error) {
    82  	if supportSM4 {
    83  		return newCipherNI(key)
    84  	}
    85  
    86  	if !supportsAES {
    87  		return newCipherGeneric(key)
    88  	}
    89  
    90  	blocks := 4
    91  	if useAVX2 {
    92  		blocks = 8
    93  	}
    94  	c := &sm4CipherAsm{sm4Cipher{make([]uint32, rounds), make([]uint32, rounds)}, blocks, blocks * BlockSize}
    95  	expandKeyAsm(&key[0], &ck[0], &c.enc[0], &c.dec[0], INST_AES)
    96  	if supportsGFMUL {
    97  		return &sm4CipherGCM{c}, nil
    98  	}
    99  	return c, nil
   100  }
   101  
   102  func (sm4c *sm4CipherAsm) BlockSize() int { return BlockSize }
   103  
   104  func (sm4c *sm4CipherAsm) Concurrency() int { return sm4c.batchBlocks }
   105  
   106  func (sm4c *sm4CipherAsm) Encrypt(dst, src []byte) {
   107  	if len(src) < BlockSize {
   108  		panic("sm4: input not full block")
   109  	}
   110  	if len(dst) < BlockSize {
   111  		panic("sm4: output not full block")
   112  	}
   113  	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
   114  		panic("sm4: invalid buffer overlap")
   115  	}
   116  	encryptBlockAsm(&sm4c.enc[0], &dst[0], &src[0], INST_AES)
   117  }
   118  
   119  func (sm4c *sm4CipherAsm) EncryptBlocks(dst, src []byte) {
   120  	if len(src) < sm4c.blocksSize {
   121  		panic("sm4: input not full blocks")
   122  	}
   123  	if len(dst) < sm4c.blocksSize {
   124  		panic("sm4: output not full blocks")
   125  	}
   126  	if subtle.InexactOverlap(dst[:sm4c.blocksSize], src[:sm4c.blocksSize]) {
   127  		panic("sm4: invalid buffer overlap")
   128  	}
   129  	encryptBlocksAsm(&sm4c.enc[0], dst, src, INST_AES)
   130  }
   131  
   132  func (sm4c *sm4CipherAsm) Decrypt(dst, src []byte) {
   133  	if len(src) < BlockSize {
   134  		panic("sm4: input not full block")
   135  	}
   136  	if len(dst) < BlockSize {
   137  		panic("sm4: output not full block")
   138  	}
   139  	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
   140  		panic("sm4: invalid buffer overlap")
   141  	}
   142  	encryptBlockAsm(&sm4c.dec[0], &dst[0], &src[0], INST_AES)
   143  }
   144  
   145  func (sm4c *sm4CipherAsm) DecryptBlocks(dst, src []byte) {
   146  	if len(src) < sm4c.blocksSize {
   147  		panic("sm4: input not full blocks")
   148  	}
   149  	if len(dst) < sm4c.blocksSize {
   150  		panic("sm4: output not full blocks")
   151  	}
   152  	if subtle.InexactOverlap(dst[:sm4c.blocksSize], src[:sm4c.blocksSize]) {
   153  		panic("sm4: invalid buffer overlap")
   154  	}
   155  	encryptBlocksAsm(&sm4c.dec[0], dst, src, INST_AES)
   156  }
   157  
   158  // expandKey is used by BenchmarkExpand to ensure that the asm implementation
   159  // of key expansion is used for the benchmark when it is available.
   160  func expandKey(key []byte, enc, dec []uint32) {
   161  	if supportSM4 {
   162  		expandKeyAsm(&key[0], &ck[0], &enc[0], &dec[0], INST_SM4)
   163  	} else if supportsAES {
   164  		expandKeyAsm(&key[0], &ck[0], &enc[0], &dec[0], INST_AES)
   165  	} else {
   166  		expandKeyGo(key, enc, dec)
   167  	}
   168  }