gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/sm4/sm4_gcm_asm.go (about)

     1  //go:build amd64 || arm64
     2  // +build amd64 arm64
     3  
     4  package sm4
     5  
     6  import (
     7  	"crypto/cipher"
     8  	goSubtle "crypto/subtle"
     9  
    10  	"gitee.com/ks-custle/core-gm/internal/subtle"
    11  )
    12  
    13  // sm4CipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
    14  // will use the optimised implementation in this file when possible. Instances
    15  // of this type only exist when hasGCMAsm and hasAES returns true.
    16  type sm4CipherGCM struct {
    17  	*sm4CipherAsm
    18  }
    19  
    20  // Assert that sm4CipherGCM implements the gcmAble interface.
    21  var _ gcmAble = (*sm4CipherGCM)(nil)
    22  
    23  //go:noescape
    24  //goland:noinspection GoUnusedParameter
    25  func gcmSm4Init(productTable *[256]byte, rk []uint32, inst int)
    26  
    27  //go:noescape
    28  //goland:noinspection GoUnusedParameter
    29  func gcmSm4Enc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, rk []uint32)
    30  
    31  //go:noescape
    32  //goland:noinspection GoUnusedParameter
    33  func gcmSm4Dec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, rk []uint32)
    34  
    35  //go:noescape
    36  //goland:noinspection GoUnusedParameter
    37  func gcmSm4Data(productTable *[256]byte, data []byte, T *[16]byte)
    38  
    39  //go:noescape
    40  //goland:noinspection GoUnusedParameter
    41  func gcmSm4Finish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64)
    42  
    43  // gcmSm4InitInst is used for test
    44  func gcmSm4InitInst(productTable *[256]byte, rk []uint32) {
    45  	if supportSM4 {
    46  		gcmSm4Init(productTable, rk, INST_SM4)
    47  	} else {
    48  		gcmSm4Init(productTable, rk, INST_AES)
    49  	}
    50  }
    51  
    52  // gcmSm4EncInst is used for test
    53  func gcmSm4EncInst(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, rk []uint32) {
    54  	if supportSM4 {
    55  		gcmSm4niEnc(productTable, dst, src, ctr, T, rk)
    56  	} else {
    57  		gcmSm4Enc(productTable, dst, src, ctr, T, rk)
    58  	}
    59  }
    60  
    61  // gcmSm4DecInst is used for test
    62  func gcmSm4DecInst(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, rk []uint32) {
    63  	if supportSM4 {
    64  		gcmSm4niDec(productTable, dst, src, ctr, T, rk)
    65  	} else {
    66  		gcmSm4Dec(productTable, dst, src, ctr, T, rk)
    67  	}
    68  }
    69  
    70  type gcmAsm struct {
    71  	gcm
    72  	bytesProductTable [256]byte
    73  }
    74  
    75  // NewGCM returns the SM4 cipher wrapped in Galois Counter Mode. This is only
    76  // called by crypto/cipher.NewGCM via the gcmAble interface.
    77  func (c *sm4CipherGCM) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
    78  	// fmt.Println("sm4.NewGCM in sm4/sm4_gcm_asm.go")
    79  	g := &gcmAsm{}
    80  	g.cipher = c.sm4CipherAsm
    81  	g.nonceSize = nonceSize
    82  	g.tagSize = tagSize
    83  	gcmSm4Init(&g.bytesProductTable, g.cipher.enc, INST_AES)
    84  	return g, nil
    85  }
    86  
    87  func (g *gcmAsm) NonceSize() int {
    88  	return g.nonceSize
    89  }
    90  
    91  func (g *gcmAsm) Overhead() int {
    92  	return g.tagSize
    93  }
    94  
    95  // Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
    96  // details.
    97  func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
    98  	// fmt.Println("sm4.Seal in sm4/sm4_gcm_asm.go")
    99  	// fmt.Println("dst: %v, nonce: %v, plaintext: %v, data: %v", dst, nonce, plaintext, data)
   100  	if len(nonce) != g.nonceSize {
   101  		panic("cipher: incorrect nonce length given to GCM")
   102  	}
   103  	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
   104  		panic("cipher: message too large for GCM")
   105  	}
   106  
   107  	var counter, tagMask [gcmBlockSize]byte
   108  
   109  	if len(nonce) == gcmStandardNonceSize {
   110  		// Init counter to nonce||1
   111  		copy(counter[:], nonce)
   112  		counter[gcmBlockSize-1] = 1
   113  	} else {
   114  		// Otherwise counter = GHASH(nonce)
   115  		gcmSm4Data(&g.bytesProductTable, nonce, &counter)
   116  		gcmSm4Finish(&g.bytesProductTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
   117  	}
   118  
   119  	g.cipher.Encrypt(tagMask[:], counter[:])
   120  
   121  	var tagOut [gcmTagSize]byte
   122  	gcmSm4Data(&g.bytesProductTable, data, &tagOut)
   123  	// fmt.Println("tagOut 1 : %v", tagOut)
   124  	ret, out := subtle.SliceForAppend(dst, len(plaintext)+g.tagSize)
   125  	if subtle.InexactOverlap(out[:len(plaintext)], plaintext) {
   126  		panic("cipher: invalid buffer overlap")
   127  	}
   128  
   129  	if len(plaintext) > 0 {
   130  		gcmSm4Enc(&g.bytesProductTable, out, plaintext, &counter, &tagOut, g.cipher.enc)
   131  		// fmt.Println("tagOut 2 : %v", tagOut)
   132  	}
   133  	gcmSm4Finish(&g.bytesProductTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
   134  	// fmt.Println("tagOut 3 : %v", tagOut)
   135  	copy(out[len(plaintext):], tagOut[:])
   136  	// fmt.Println("ret: %v", ret)
   137  	return ret
   138  }
   139  
   140  // Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
   141  // for details.
   142  func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
   143  	// fmt.Println("sm4.Open in sm4/sm4_gcm_asm.go")
   144  	// fmt.Println("dst: %v, nonce: %v, ciphertext: %v, data: %v", dst, nonce, ciphertext, data)
   145  	if len(nonce) != g.nonceSize {
   146  		panic("cipher: incorrect nonce length given to GCM")
   147  	}
   148  	// Sanity check to prevent the authentication from always succeeding if an implementation
   149  	// leaves tagSize uninitialized, for example.
   150  	if g.tagSize < gcmMinimumTagSize {
   151  		panic("cipher: incorrect GCM tag size")
   152  	}
   153  
   154  	if len(ciphertext) < g.tagSize {
   155  		return nil, errOpen
   156  	}
   157  	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) {
   158  		return nil, errOpen
   159  	}
   160  
   161  	tag := ciphertext[len(ciphertext)-g.tagSize:]
   162  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
   163  
   164  	// See GCM spec, section 7.1.
   165  	var counter, tagMask [gcmBlockSize]byte
   166  
   167  	if len(nonce) == gcmStandardNonceSize {
   168  		// Init counter to nonce||1
   169  		copy(counter[:], nonce)
   170  		counter[gcmBlockSize-1] = 1
   171  	} else {
   172  		// Otherwise counter = GHASH(nonce)
   173  		gcmSm4Data(&g.bytesProductTable, nonce, &counter)
   174  		gcmSm4Finish(&g.bytesProductTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
   175  	}
   176  
   177  	g.cipher.Encrypt(tagMask[:], counter[:])
   178  
   179  	var expectedTag [gcmTagSize]byte
   180  	gcmSm4Data(&g.bytesProductTable, data, &expectedTag)
   181  	// fmt.Println("expectedTag 1 : %v", expectedTag)
   182  	// 目前gcmSm4Dec函数的入参dst与src在内存上必须各自独立,因此这里不能直接调用`subtle.SliceForAppend`函数,而是强制另外申请内存来生成ret和out两个切片。
   183  	ret, out := subtle.SliceForAppend(dst, len(ciphertext))
   184  	// total := len(dst) + len(ciphertext)
   185  	// ret := make([]byte, total)
   186  	// copy(ret, dst)
   187  	// out := ret[len(dst):]
   188  	if subtle.InexactOverlap(out, ciphertext) {
   189  		panic("cipher: invalid buffer overlap")
   190  	}
   191  	if len(ciphertext) > 0 {
   192  		// fmt.Println("ProductTable: %v, out: %v, ciphertext: %v, counter: %v, expectedTag: %v, g.cipher.enc: %v", g.bytesProductTable, out, ciphertext, &counter, expectedTag, g.cipher.enc)
   193  		gcmSm4Dec(&g.bytesProductTable, out, ciphertext, &counter, &expectedTag, g.cipher.enc)
   194  		// fmt.Println("expectedTag 2 : %v", expectedTag)
   195  	}
   196  	gcmSm4Finish(&g.bytesProductTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
   197  	// fmt.Println("expectedTag 3 : %v", expectedTag)
   198  	// fmt.Println("ret: %v", ret)
   199  	if goSubtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
   200  		for i := range out {
   201  			out[i] = 0
   202  		}
   203  		return nil, errOpen
   204  	}
   205  	return ret, nil
   206  }