github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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 "github.com/hxx258456/ccgo/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 // log.Print("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 // log.Print("sm4.Seal in sm4/sm4_gcm_asm.go") 99 // log.Print("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 // log.Print("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 // log.Print("tagOut 2 : %v", tagOut) 132 } 133 gcmSm4Finish(&g.bytesProductTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data))) 134 // log.Print("tagOut 3 : %v", tagOut) 135 copy(out[len(plaintext):], tagOut[:]) 136 // log.Print("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 // log.Print("sm4.Open in sm4/sm4_gcm_asm.go") 144 // log.Print("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 // log.Print("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 // log.Print("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 // log.Print("expectedTag 2 : %v", expectedTag) 195 } 196 gcmSm4Finish(&g.bytesProductTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data))) 197 // log.Print("expectedTag 3 : %v", expectedTag) 198 // log.Print("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 }