gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/sm4/sm4ni_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  //go:noescape
    14  //goland:noinspection GoUnusedParameter
    15  func gcmSm4niEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, rk []uint32)
    16  
    17  //go:noescape
    18  //goland:noinspection GoUnusedParameter
    19  func gcmSm4niDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, rk []uint32)
    20  
    21  // sm4CipherNIGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
    22  // will use the optimised implementation in this file when possible. Instances
    23  // of this type only exist when hasGCMAsm and hasSM4 returns true.
    24  type sm4CipherNIGCM struct {
    25  	*sm4CipherNI
    26  }
    27  
    28  // Assert that sm4CipherNIGCM implements the gcmAble interface.
    29  var _ gcmAble = (*sm4CipherNIGCM)(nil)
    30  
    31  type gcmNI struct {
    32  	cipher            *sm4CipherNI
    33  	nonceSize         int
    34  	tagSize           int
    35  	bytesProductTable [256]byte
    36  }
    37  
    38  func (g *gcmNI) NonceSize() int {
    39  	return g.nonceSize
    40  }
    41  
    42  func (g *gcmNI) Overhead() int {
    43  	return g.tagSize
    44  }
    45  
    46  // NewGCM returns the SM4 cipher wrapped in Galois Counter Mode. This is only
    47  // called by crypto/cipher.NewGCM via the gcmAble interface.
    48  func (c *sm4CipherNIGCM) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
    49  	g := &gcmNI{}
    50  	g.cipher = c.sm4CipherNI
    51  	g.nonceSize = nonceSize
    52  	g.tagSize = tagSize
    53  	gcmSm4Init(&g.bytesProductTable, g.cipher.enc, INST_SM4)
    54  	return g, nil
    55  }
    56  
    57  // Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
    58  // details.
    59  func (g *gcmNI) Seal(dst, nonce, plaintext, data []byte) []byte {
    60  	if len(nonce) != g.nonceSize {
    61  		panic("cipher: incorrect nonce length given to GCM")
    62  	}
    63  	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
    64  		panic("cipher: message too large for GCM")
    65  	}
    66  
    67  	var counter, tagMask [gcmBlockSize]byte
    68  
    69  	if len(nonce) == gcmStandardNonceSize {
    70  		// Init counter to nonce||1
    71  		copy(counter[:], nonce)
    72  		counter[gcmBlockSize-1] = 1
    73  	} else {
    74  		// Otherwise counter = GHASH(nonce)
    75  		gcmSm4Data(&g.bytesProductTable, nonce, &counter)
    76  		gcmSm4Finish(&g.bytesProductTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
    77  	}
    78  
    79  	g.cipher.Encrypt(tagMask[:], counter[:])
    80  
    81  	var tagOut [gcmTagSize]byte
    82  	gcmSm4Data(&g.bytesProductTable, data, &tagOut)
    83  
    84  	ret, out := subtle.SliceForAppend(dst, len(plaintext)+g.tagSize)
    85  	if subtle.InexactOverlap(out[:len(plaintext)], plaintext) {
    86  		panic("cipher: invalid buffer overlap")
    87  	}
    88  
    89  	if len(plaintext) > 0 {
    90  		gcmSm4niEnc(&g.bytesProductTable, out, plaintext, &counter, &tagOut, g.cipher.enc)
    91  	}
    92  	gcmSm4Finish(&g.bytesProductTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
    93  	copy(out[len(plaintext):], tagOut[:])
    94  
    95  	return ret
    96  }
    97  
    98  // Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
    99  // for details.
   100  func (g *gcmNI) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
   101  	if len(nonce) != g.nonceSize {
   102  		panic("cipher: incorrect nonce length given to GCM")
   103  	}
   104  	// Sanity check to prevent the authentication from always succeeding if an implementation
   105  	// leaves tagSize uninitialized, for example.
   106  	if g.tagSize < gcmMinimumTagSize {
   107  		panic("cipher: incorrect GCM tag size")
   108  	}
   109  
   110  	if len(ciphertext) < g.tagSize {
   111  		return nil, errOpen
   112  	}
   113  	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) {
   114  		return nil, errOpen
   115  	}
   116  
   117  	tag := ciphertext[len(ciphertext)-g.tagSize:]
   118  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
   119  
   120  	// See GCM spec, section 7.1.
   121  	var counter, tagMask [gcmBlockSize]byte
   122  
   123  	if len(nonce) == gcmStandardNonceSize {
   124  		// Init counter to nonce||1
   125  		copy(counter[:], nonce)
   126  		counter[gcmBlockSize-1] = 1
   127  	} else {
   128  		// Otherwise counter = GHASH(nonce)
   129  		gcmSm4Data(&g.bytesProductTable, nonce, &counter)
   130  		gcmSm4Finish(&g.bytesProductTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
   131  	}
   132  
   133  	g.cipher.Encrypt(tagMask[:], counter[:])
   134  
   135  	var expectedTag [gcmTagSize]byte
   136  	gcmSm4Data(&g.bytesProductTable, data, &expectedTag)
   137  
   138  	ret, out := subtle.SliceForAppend(dst, len(ciphertext))
   139  	if subtle.InexactOverlap(out, ciphertext) {
   140  		panic("cipher: invalid buffer overlap")
   141  	}
   142  	if len(ciphertext) > 0 {
   143  		gcmSm4niDec(&g.bytesProductTable, out, ciphertext, &counter, &expectedTag, g.cipher.enc)
   144  	}
   145  	gcmSm4Finish(&g.bytesProductTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
   146  
   147  	if goSubtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
   148  		for i := range out {
   149  			out[i] = 0
   150  		}
   151  		return nil, errOpen
   152  	}
   153  	return ret, nil
   154  }