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