github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/gmtls/key_schedule.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 /* 17 gmtls/key_schedule.go TLS1.3密钥调度相关函数,已补充国密SM2曲线相关处理。 18 */ 19 20 import ( 21 "crypto/elliptic" 22 "crypto/hmac" 23 "errors" 24 "fmt" 25 "hash" 26 "io" 27 "math/big" 28 29 "gitee.com/zhaochuninhefei/zcgolog/zclog" 30 "github.com/hxx258456/ccgo/sm2" 31 "golang.org/x/crypto/cryptobyte" 32 "golang.org/x/crypto/curve25519" 33 "golang.org/x/crypto/hkdf" 34 ) 35 36 // This file contains the functions necessary to compute the TLS 1.3 key 37 // schedule. See RFC 8446, Section 7. 38 39 const ( 40 resumptionBinderLabel = "res binder" 41 clientHandshakeTrafficLabel = "c hs traffic" 42 serverHandshakeTrafficLabel = "s hs traffic" 43 clientApplicationTrafficLabel = "c ap traffic" 44 serverApplicationTrafficLabel = "s ap traffic" 45 exporterLabel = "exp master" 46 resumptionLabel = "res master" 47 trafficUpdateLabel = "traffic upd" 48 ) 49 50 // expandLabel 密钥扩展方法,实现 HKDF-Expand-Label。 51 // - secret 基础密钥 52 // - label 标签 53 // - context 消息转录散列 54 // - length 散列长度 55 // 56 // expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. 57 func (cs *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte { 58 var hkdfLabel cryptobyte.Builder 59 hkdfLabel.AddUint16(uint16(length)) 60 hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { 61 b.AddBytes([]byte("tls13 ")) 62 b.AddBytes([]byte(label)) 63 }) 64 hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { 65 b.AddBytes(context) 66 }) 67 out := make([]byte, length) 68 // 使用 HKDF-Expand 将 HKDF-Extract 提取的伪随机密钥(主密钥)扩展为长度为length的字节数组 69 // c.hash.New 为对应HMAC函数的散列函数; 70 // secret 为 HKDF-Extract 提取的伪随机密钥(主密钥) 71 // hkdfLabel.BytesOrPanic() 为可选上下文和应用程序特定信息 72 n, err := hkdf.Expand(cs.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out) 73 if err != nil || n != length { 74 panic("gmtls: HKDF-Expand-Label invocation failed unexpectedly") 75 } 76 return out 77 } 78 79 // deriveSecret 密钥派生方法,实现 Derive-Secret。内部使用 HKDF-Expand。 80 // - secret 基础密钥 81 // - label 标签 82 // - transcript 转录散列函数 83 // 84 // deriveSecret implements Derive-Secret from RFC 8446, Section 7.1. 85 func (cs *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte { 86 if transcript == nil { 87 // transcript默认使用tls1.3密码套件的散列函数 88 transcript = cs.hash.New() 89 } 90 return cs.expandLabel(secret, label, transcript.Sum(nil), cs.hash.Size()) 91 } 92 93 // extract 密钥提炼方法,实现 HKDF-Extract。 94 // 95 // 该方法用于从预主密钥提取主密钥。 96 // 97 // extract implements HKDF-Extract with the cipher suite hash. 98 func (cs *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte { 99 if newSecret == nil { 100 newSecret = make([]byte, cs.hash.Size()) 101 } 102 // 使用 HKDF-Extract 提取一个新的伪随机密钥。 103 // c.hash.New 为对应HMAC函数的散列函数; 104 // newSecret 作为原始密钥; 105 // currentSecret 作为盐值。 106 return hkdf.Extract(cs.hash.New, newSecret, currentSecret) 107 } 108 109 // 根据当前的通信密钥派生一个新的通信密钥。 110 // nextTrafficSecret generates the next traffic secret, given the current one, 111 // according to RFC 8446, Section 7.2. 112 func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte { 113 return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size()) 114 } 115 116 // 根据通信密钥派生会话密钥与初始偏移量(对称加密用的key,iv) 117 // trafficKey generates traffic keys according to RFC 8446, Section 7.3. 118 func (cs *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) { 119 key = cs.expandLabel(trafficSecret, "key", nil, cs.keyLen) 120 iv = cs.expandLabel(trafficSecret, "iv", nil, aeadNonceLength) 121 return 122 } 123 124 // 生成Finished消息散列。 125 // finishedHash generates the Finished verify_data or PskBinderEntry according 126 // to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey 127 // selection. 128 func (cs *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte { 129 finishedKey := cs.expandLabel(baseKey, "finished", nil, cs.hash.Size()) 130 verifyData := hmac.New(cs.hash.New, finishedKey) 131 verifyData.Write(transcript.Sum(nil)) 132 return verifyData.Sum(nil) 133 } 134 135 // exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to 136 // RFC 8446, Section 7.5. 137 func (cs *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) { 138 expMasterSecret := cs.deriveSecret(masterSecret, exporterLabel, transcript) 139 return func(label string, context []byte, length int) ([]byte, error) { 140 secret := cs.deriveSecret(expMasterSecret, label, nil) 141 h := cs.hash.New() 142 h.Write(context) 143 return cs.expandLabel(secret, "exporter", h.Sum(nil), length), nil 144 } 145 } 146 147 // ECDHE接口 148 // 149 // Elliptic Curve Diffie-Hellman Ephemeral,基于椭圆曲线的,动态的,笛福赫尔曼算法。 150 // 151 // ecdheParameters implements Diffie-Hellman with either NIST curves or X25519, 152 // according to RFC 8446, Section 4.2.8.2. 153 type ecdheParameters interface { 154 // CurveID 曲线ID 155 CurveID() CurveID 156 // PublicKey 获取公钥 157 PublicKey() []byte 158 // SharedKey 计算共享密钥 : 己方私钥 * 对方公钥peerPublicKey 159 SharedKey(peerPublicKey []byte) []byte 160 } 161 162 // 基于给定的椭圆曲线ID,获取椭圆曲线并生成ecdhe参数,已支持SM2-P-256曲线。 163 // 164 // ecdhe : Elliptic Curve Diffie-Hellman Ephemeral, 临时的基于椭圆曲线的笛福赫尔曼密钥交换算法。 165 // ecdheParameters是一个接口,实际对象需要实现该接口的SharedKey等方法,其内部包含曲线ID与对应的公私钥。 166 func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) { 167 if curveID == X25519 { 168 privateKey := make([]byte, curve25519.ScalarSize) 169 if _, err := io.ReadFull(rand, privateKey); err != nil { 170 return nil, err 171 } 172 publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint) 173 if err != nil { 174 return nil, err 175 } 176 return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil 177 } 178 // 椭圆曲线获取,已支持p256sm2 179 curve, ok := curveForCurveID(curveID) 180 if !ok { 181 return nil, errors.New("gmtls: internal error: unsupported curve") 182 } 183 // 生成密钥交换算法参数 184 p := &nistParameters{curveID: curveID} 185 var err error 186 // 利用曲线生成公私钥 187 p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand) 188 if err != nil { 189 return nil, err 190 } 191 return p, nil 192 } 193 194 // 根据曲线ID获取对应曲线 195 func curveForCurveID(id CurveID) (elliptic.Curve, bool) { 196 switch id { 197 // 添加国密SM2曲线 198 case Curve256Sm2: 199 return sm2.P256Sm2(), true 200 case CurveP256: 201 return elliptic.P256(), true 202 case CurveP384: 203 return elliptic.P384(), true 204 case CurveP521: 205 return elliptic.P521(), true 206 default: 207 return nil, false 208 } 209 } 210 211 func CheckCurveNameById(id CurveID) (string, bool) { 212 switch id { 213 case Curve256Sm2: 214 return sm2.P256Sm2().Params().Name, true 215 case CurveP256: 216 return elliptic.P256().Params().Name, true 217 case CurveP384: 218 return elliptic.P384().Params().Name, true 219 case CurveP521: 220 return elliptic.P521().Params().Name, true 221 case X25519: 222 return "Curve25519", true 223 default: 224 return fmt.Sprintf("unknown CurveID: %d", id), false 225 } 226 } 227 228 //goland:noinspection GoUnusedExportedFunction 229 func CurveNameById(id CurveID) string { 230 switch id { 231 // 添加国密SM2曲线 232 case Curve256Sm2: 233 return sm2.P256Sm2().Params().Name 234 case CurveP256: 235 return elliptic.P256().Params().Name 236 case CurveP384: 237 return elliptic.P384().Params().Name 238 case CurveP521: 239 return elliptic.P521().Params().Name 240 case X25519: 241 return "Curve25519" 242 default: 243 return fmt.Sprintf("unknown CurveID: %d", id) 244 } 245 } 246 247 type nistParameters struct { 248 privateKey []byte 249 x, y *big.Int // public key 250 curveID CurveID 251 } 252 253 func (p *nistParameters) CurveID() CurveID { 254 return p.curveID 255 } 256 257 func (p *nistParameters) PublicKey() []byte { 258 curve, _ := curveForCurveID(p.curveID) 259 return elliptic.Marshal(curve, p.x, p.y) 260 } 261 262 func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte { 263 curve, _ := curveForCurveID(p.curveID) 264 // 将 peerPublicKey 的座标位置解析出来,同时验证该座标是否在曲线上。 265 // Unmarshal also checks whether the given point is on the curve. 266 x, y := elliptic.Unmarshal(curve, peerPublicKey) 267 if x == nil { 268 return nil 269 } 270 // peerPublicKey * 私钥 获取共享密钥 271 xShared, _ := curve.ScalarMult(x, y, p.privateKey) 272 sharedKey := make([]byte, (curve.Params().BitSize+7)/8) 273 zclog.Debugf("===== 使用曲线 %s 与对方公钥计算共享密钥", curve.Params().Name) 274 return xShared.FillBytes(sharedKey) 275 } 276 277 type x25519Parameters struct { 278 privateKey []byte 279 publicKey []byte 280 } 281 282 func (p *x25519Parameters) CurveID() CurveID { 283 return X25519 284 } 285 286 func (p *x25519Parameters) PublicKey() []byte { 287 return p.publicKey[:] 288 } 289 290 func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte { 291 sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey) 292 if err != nil { 293 return nil 294 } 295 return sharedKey 296 }