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

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