mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-01 19:17:36 +03:00
179 lines
6.5 KiB
Go
179 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 (
|
|
fips140 "hash"
|
|
|
|
"github.com/refraction-networking/utls/internal/byteorder"
|
|
"github.com/refraction-networking/utls/internal/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
|
|
}
|