0
0
mirror of https://github.com/XTLS/REALITY.git synced 2025-08-22 22:48:36 +00:00
XTLS_REALITY/tls13/tls13.go
yuhan6665 4db1386622 crypto/internal/fips/tls13: implement TLS 1.3 KDF
The new implementation encodes the key schedule into the type system,
which is actually nicer than what we had before.

For #69536

Change-Id: Iddab62c2aae40bc2425a155443576bb9b7aafe03
Reviewed-on: https://go-review.googlesource.com/c/go/+/626836
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Commit-Queue: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
2025-05-04 14:17:23 -04:00

178 lines
6.5 KiB
Go

// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446,
// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7.
package tls13
import (
"github.com/xtls/reality/byteorder"
"github.com/xtls/reality/fips140"
"github.com/xtls/reality/hkdf"
)
// We don't set the service indicator in this package but we delegate that to
// the underlying functions because the TLS 1.3 KDF does not have a standard of
// its own.
// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
func ExpandLabel[H fips140.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte {
if len("tls13 ")+len(label) > 255 || len(context) > 255 {
// It should be impossible for this to panic: labels are fixed strings,
// and context is either a fixed-length computed hash, or parsed from a
// field which has the same length limitation.
//
// Another reasonable approach might be to return a randomized slice if
// we encounter an error, which would break the connection, but avoid
// panicking. This would perhaps be safer but significantly more
// confusing to users.
panic("tls13: label or context too long")
}
hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context))
hkdfLabel = byteorder.BEAppendUint16(hkdfLabel, uint16(length))
hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label)))
hkdfLabel = append(hkdfLabel, "tls13 "...)
hkdfLabel = append(hkdfLabel, label...)
hkdfLabel = append(hkdfLabel, byte(len(context)))
hkdfLabel = append(hkdfLabel, context...)
return hkdf.Expand(hash, secret, string(hkdfLabel), length)
}
func extract[H fips140.Hash](hash func() H, newSecret, currentSecret []byte) []byte {
if newSecret == nil {
newSecret = make([]byte, hash().Size())
}
return hkdf.Extract(hash, newSecret, currentSecret)
}
func deriveSecret[H fips140.Hash](hash func() H, secret []byte, label string, transcript fips140.Hash) []byte {
if transcript == nil {
transcript = hash()
}
return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size())
}
const (
resumptionBinderLabel = "res binder"
clientEarlyTrafficLabel = "c e traffic"
clientHandshakeTrafficLabel = "c hs traffic"
serverHandshakeTrafficLabel = "s hs traffic"
clientApplicationTrafficLabel = "c ap traffic"
serverApplicationTrafficLabel = "s ap traffic"
earlyExporterLabel = "e exp master"
exporterLabel = "exp master"
resumptionLabel = "res master"
)
type EarlySecret struct {
secret []byte
hash func() fips140.Hash
}
func NewEarlySecret[H fips140.Hash](hash func() H, psk []byte) *EarlySecret {
return &EarlySecret{
secret: extract(hash, psk, nil),
hash: func() fips140.Hash { return hash() },
}
}
func (s *EarlySecret) ResumptionBinderKey() []byte {
return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil)
}
// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the
// early secret and the transcript up to the ClientHello.
func (s *EarlySecret) ClientEarlyTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript)
}
type HandshakeSecret struct {
secret []byte
hash func() fips140.Hash
}
func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret {
derived := deriveSecret(s.hash, s.secret, "derived", nil)
return &HandshakeSecret{
secret: extract(s.hash, sharedSecret, derived),
hash: s.hash,
}
}
// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from
// the handshake secret and the transcript up to the ServerHello.
func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript)
}
// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from
// the handshake secret and the transcript up to the ServerHello.
func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript)
}
type MasterSecret struct {
secret []byte
hash func() fips140.Hash
}
func (s *HandshakeSecret) MasterSecret() *MasterSecret {
derived := deriveSecret(s.hash, s.secret, "derived", nil)
return &MasterSecret{
secret: extract(s.hash, nil, derived),
hash: s.hash,
}
}
// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0
// from the master secret and the transcript up to the server Finished.
func (s *MasterSecret) ClientApplicationTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript)
}
// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0
// from the master secret and the transcript up to the server Finished.
func (s *MasterSecret) ServerApplicationTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript)
}
// ResumptionMasterSecret derives the resumption_master_secret from the master secret
// and the transcript up to the client Finished.
func (s *MasterSecret) ResumptionMasterSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, resumptionLabel, transcript)
}
type ExporterMasterSecret struct {
secret []byte
hash func() fips140.Hash
}
// ExporterMasterSecret derives the exporter_master_secret from the master secret
// and the transcript up to the server Finished.
func (s *MasterSecret) ExporterMasterSecret(transcript fips140.Hash) *ExporterMasterSecret {
return &ExporterMasterSecret{
secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript),
hash: s.hash,
}
}
// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret
// and the transcript up to the ClientHello.
func (s *EarlySecret) EarlyExporterMasterSecret(transcript fips140.Hash) *ExporterMasterSecret {
return &ExporterMasterSecret{
secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript),
hash: s.hash,
}
}
func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte {
secret := deriveSecret(s.hash, s.secret, label, nil)
h := s.hash()
h.Write(context)
return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length)
}
func TestingOnlyExporterSecret(s *ExporterMasterSecret) []byte {
return s.secret
}