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  }