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