gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/gmtls/ticket.go (about) 1 // Copyright (c) 2022 zhaochun 2 // gmgo 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/zhaochuninhefei/gmgo/sm3" 25 "gitee.com/zhaochuninhefei/gmgo/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 // golang原来的实现是用aes加密,sha256散列,国密改造改为 sm4 + sm3 130 func (c *Conn) encryptTicket(state []byte) ([]byte, error) { 131 if len(c.ticketKeys) == 0 { 132 return nil, errors.New("gmtls: internal error: session ticket keys unavailable") 133 } 134 // encrypted : ticketKeyName(16) + iv(16) + state对称加密结果 + 散列(32) 135 // encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size) 136 encrypted := make([]byte, ticketKeyNameLen+sm4.BlockSize+len(state)+sm3.Size) 137 // 前16个字节放ticketKeyName 138 keyName := encrypted[:ticketKeyNameLen] 139 // 16~32 放iv 140 iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+sm4.BlockSize] 141 // 最后32个字节放mac认证码 142 macBytes := encrypted[len(encrypted)-sm3.Size:] 143 // 生成随机字节数组填入iv 144 if _, err := io.ReadFull(c.config.rand(), iv); err != nil { 145 return nil, err 146 } 147 // 当前连接的ticketKeys在前面读取ClientHello之后的处理中已经初始化。 148 // 这里拿到第一个ticketKey。 149 key := c.ticketKeys[0] 150 // 填入keyname 151 copy(keyName, key.keyName[:]) 152 block, err := sm4.NewCipher(key.sm4Key[:]) 153 if err != nil { 154 return nil, errors.New("gmtls: failed to create cipher while encrypting ticket: " + err.Error()) 155 } 156 // encrypted的 32 ~ 倒数32 填入state对称加密结果 157 cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+sm4.BlockSize:], state) 158 // 使用sm3作为mac认证码函数 159 mac := hmac.New(sm3.New, key.hmacKey[:]) 160 // 写入 encrypted 前三部分内容: ticketKeyName(16) + iv(16) + state对称加密结果 161 mac.Write(encrypted[:len(encrypted)-sm3.Size]) 162 // 生成认证码填入macBytes 163 mac.Sum(macBytes[:0]) 164 165 return encrypted, nil 166 } 167 168 // 会话票据解密 169 // golang原来的实现是用aes加密,sha256散列,国密改造改为 sm4 + sm3 170 func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) { 171 if len(encrypted) < ticketKeyNameLen+sm4.BlockSize+sm3.Size { 172 return nil, false 173 } 174 // 获取keyname 175 keyName := encrypted[:ticketKeyNameLen] 176 // 获取iv 177 iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+sm4.BlockSize] 178 // 获取认证码 179 macBytes := encrypted[len(encrypted)-sm3.Size:] 180 // 获取秘文 181 ciphertext := encrypted[ticketKeyNameLen+sm4.BlockSize : len(encrypted)-sm3.Size] 182 // 根据keyname获取key 183 keyIndex := -1 184 for i, candidateKey := range c.ticketKeys { 185 if bytes.Equal(keyName, candidateKey.keyName[:]) { 186 keyIndex = i 187 break 188 } 189 } 190 if keyIndex == -1 { 191 return nil, false 192 } 193 key := &c.ticketKeys[keyIndex] 194 // 重新生成认证码 195 mac := hmac.New(sm3.New, key.hmacKey[:]) 196 mac.Write(encrypted[:len(encrypted)-sm3.Size]) 197 expected := mac.Sum(nil) 198 // 比较认证码 199 if subtle.ConstantTimeCompare(macBytes, expected) != 1 { 200 return nil, false 201 } 202 // 对称解密 203 block, err := sm4.NewCipher(key.sm4Key[:]) 204 if err != nil { 205 return nil, false 206 } 207 plaintext = make([]byte, len(ciphertext)) 208 cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) 209 210 return plaintext, keyIndex > 0 211 }