gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/gmtls/ticket.go (about) 1 // Copyright (c) 2022 zhaochun 2 // core-gm is licensed under Mulan PSL v2. 3 // You can use this software according to the terms and conditions of the Mulan PSL v2. 4 // You may obtain a copy of Mulan PSL v2 at: 5 // http://license.coscl.org.cn/MulanPSL2 6 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 7 // See the Mulan PSL v2 for more details. 8 9 /* 10 gmtls是基于`golang/go`的`tls`包实现的国密改造版本。 11 对应版权声明: thrid_licenses/github.com/golang/go/LICENSE 12 */ 13 14 package gmtls 15 16 import ( 17 "bytes" 18 "crypto/cipher" 19 "crypto/hmac" 20 "crypto/subtle" 21 "errors" 22 "io" 23 24 "gitee.com/ks-custle/core-gm/sm3" 25 "gitee.com/ks-custle/core-gm/sm4" 26 "golang.org/x/crypto/cryptobyte" 27 ) 28 29 // sessionState contains the information that is serialized into a session 30 // ticket in order to later resume a connection. 31 type sessionState struct { 32 vers uint16 33 cipherSuite uint16 34 createdAt uint64 35 masterSecret []byte // opaque master_secret<1..2^16-1>; 36 // struct { opaque certificate<1..2^24-1> } Certificate; 37 certificates [][]byte // Certificate certificate_list<0..2^24-1>; 38 39 // usedOldKey is true if the ticket from which this session came from 40 // was encrypted with an older key and thus should be refreshed. 41 usedOldKey bool 42 } 43 44 func (m *sessionState) marshal() []byte { 45 var b cryptobyte.Builder 46 b.AddUint16(m.vers) 47 b.AddUint16(m.cipherSuite) 48 addUint64(&b, m.createdAt) 49 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { 50 b.AddBytes(m.masterSecret) 51 }) 52 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { 53 for _, cert := range m.certificates { 54 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { 55 b.AddBytes(cert) 56 }) 57 } 58 }) 59 return b.BytesOrPanic() 60 } 61 62 func (m *sessionState) unmarshal(data []byte) bool { 63 *m = sessionState{usedOldKey: m.usedOldKey} 64 s := cryptobyte.String(data) 65 if ok := s.ReadUint16(&m.vers) && 66 s.ReadUint16(&m.cipherSuite) && 67 readUint64(&s, &m.createdAt) && 68 readUint16LengthPrefixed(&s, &m.masterSecret) && 69 len(m.masterSecret) != 0; !ok { 70 return false 71 } 72 var certList cryptobyte.String 73 if !s.ReadUint24LengthPrefixed(&certList) { 74 return false 75 } 76 for !certList.Empty() { 77 var cert []byte 78 if !readUint24LengthPrefixed(&certList, &cert) { 79 return false 80 } 81 m.certificates = append(m.certificates, cert) 82 } 83 return s.Empty() 84 } 85 86 // sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first 87 // version (revision = 0) doesn't carry any of the information needed for 0-RTT 88 // validation and the nonce is always empty. 89 type sessionStateTLS13 struct { 90 // uint8 version = 0x0304; 91 // uint8 revision = 0; 92 cipherSuite uint16 93 createdAt uint64 94 resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>; 95 certificate Certificate // CertificateEntry certificate_list<0..2^24-1>; 96 } 97 98 func (m *sessionStateTLS13) marshal() []byte { 99 var b cryptobyte.Builder 100 b.AddUint16(VersionTLS13) 101 b.AddUint8(0) // revision 102 b.AddUint16(m.cipherSuite) 103 addUint64(&b, m.createdAt) 104 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { 105 b.AddBytes(m.resumptionSecret) 106 }) 107 marshalCertificate(&b, m.certificate) 108 return b.BytesOrPanic() 109 } 110 111 func (m *sessionStateTLS13) unmarshal(data []byte) bool { 112 *m = sessionStateTLS13{} 113 s := cryptobyte.String(data) 114 var version uint16 115 var revision uint8 116 return s.ReadUint16(&version) && 117 version == VersionTLS13 && 118 s.ReadUint8(&revision) && 119 revision == 0 && 120 s.ReadUint16(&m.cipherSuite) && 121 readUint64(&s, &m.createdAt) && 122 readUint8LengthPrefixed(&s, &m.resumptionSecret) && 123 len(m.resumptionSecret) != 0 && 124 unmarshalCertificate(&s, &m.certificate) && 125 s.Empty() 126 } 127 128 // 会话票据加密 129 // 130 // golang原来的实现是用aes加密,sha256散列,国密改造改为 sm4 + sm3 131 func (c *Conn) encryptTicket(state []byte) ([]byte, error) { 132 if len(c.ticketKeys) == 0 { 133 return nil, errors.New("gmtls: internal error: session ticket keys unavailable") 134 } 135 // encrypted : ticketKeyName(16) + iv(16) + state对称加密结果 + 散列(32) 136 // encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size) 137 encrypted := make([]byte, ticketKeyNameLen+sm4.BlockSize+len(state)+sm3.Size) 138 // 前16个字节放ticketKeyName 139 keyName := encrypted[:ticketKeyNameLen] 140 // 16~32 放iv 141 iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+sm4.BlockSize] 142 // 最后32个字节放mac认证码 143 macBytes := encrypted[len(encrypted)-sm3.Size:] 144 // 生成随机字节数组填入iv 145 if _, err := io.ReadFull(c.config.rand(), iv); err != nil { 146 return nil, err 147 } 148 // 当前连接的ticketKeys在前面读取ClientHello之后的处理中已经初始化。 149 // 这里拿到第一个ticketKey。 150 key := c.ticketKeys[0] 151 // 填入keyname 152 copy(keyName, key.keyName[:]) 153 block, err := sm4.NewCipher(key.sm4Key[:]) 154 if err != nil { 155 return nil, errors.New("gmtls: failed to create cipher while encrypting ticket: " + err.Error()) 156 } 157 // encrypted的 32 ~ 倒数32 填入state对称加密结果 158 cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+sm4.BlockSize:], state) 159 // 使用sm3作为mac认证码函数 160 mac := hmac.New(sm3.New, key.hmacKey[:]) 161 // 写入 encrypted 前三部分内容: ticketKeyName(16) + iv(16) + state对称加密结果 162 mac.Write(encrypted[:len(encrypted)-sm3.Size]) 163 // 生成认证码填入macBytes 164 mac.Sum(macBytes[:0]) 165 166 return encrypted, nil 167 } 168 169 // 会话票据解密 170 // 171 // golang原来的实现是用aes加密,sha256散列,国密改造改为 sm4 + sm3 172 func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) { 173 if len(encrypted) < ticketKeyNameLen+sm4.BlockSize+sm3.Size { 174 return nil, false 175 } 176 // 获取keyname 177 keyName := encrypted[:ticketKeyNameLen] 178 // 获取iv 179 iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+sm4.BlockSize] 180 // 获取认证码 181 macBytes := encrypted[len(encrypted)-sm3.Size:] 182 // 获取秘文 183 ciphertext := encrypted[ticketKeyNameLen+sm4.BlockSize : len(encrypted)-sm3.Size] 184 // 根据keyname获取key 185 keyIndex := -1 186 for i, candidateKey := range c.ticketKeys { 187 if bytes.Equal(keyName, candidateKey.keyName[:]) { 188 keyIndex = i 189 break 190 } 191 } 192 if keyIndex == -1 { 193 return nil, false 194 } 195 key := &c.ticketKeys[keyIndex] 196 // 重新生成认证码 197 mac := hmac.New(sm3.New, key.hmacKey[:]) 198 mac.Write(encrypted[:len(encrypted)-sm3.Size]) 199 expected := mac.Sum(nil) 200 // 比较认证码 201 if subtle.ConstantTimeCompare(macBytes, expected) != 1 { 202 return nil, false 203 } 204 // 对称解密 205 block, err := sm4.NewCipher(key.sm4Key[:]) 206 if err != nil { 207 return nil, false 208 } 209 plaintext = make([]byte, len(ciphertext)) 210 cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) 211 212 return plaintext, keyIndex > 0 213 }