package tls
import (
)
type SessionState struct {
Extra [][]byte
EarlyData bool
version uint16
isClient bool
cipherSuite uint16
createdAt uint64
secret []byte
extMasterSecret bool
peerCertificates []*x509.Certificate
activeCertHandles []*activeCert
ocspResponse []byte
scts [][]byte
verifiedChains [][]*x509.Certificate
alpnProtocol string
useBy uint64
ageAdd uint32
ticket []byte
}
func ( *SessionState) () ([]byte, error) {
var cryptobyte.Builder
.AddUint16(.version)
if .isClient {
.AddUint8(2)
} else {
.AddUint8(1)
}
.AddUint16(.cipherSuite)
addUint64(&, .createdAt)
.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes(.secret)
})
.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
for , := range .Extra {
.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes()
})
}
})
if .extMasterSecret {
.AddUint8(1)
} else {
.AddUint8(0)
}
if .EarlyData {
.AddUint8(1)
} else {
.AddUint8(0)
}
marshalCertificate(&, Certificate{
Certificate: certificatesToBytesSlice(.peerCertificates),
OCSPStaple: .ocspResponse,
SignedCertificateTimestamps: .scts,
})
.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
for , := range .verifiedChains {
.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
if len() == 0 {
.SetError(errors.New("tls: internal error: empty verified chain"))
return
}
for , := range [1:] {
.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes(.Raw)
})
}
})
}
})
if .EarlyData {
.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes([]byte(.alpnProtocol))
})
}
if .isClient {
if .version >= VersionTLS13 {
addUint64(&, .useBy)
.AddUint32(.ageAdd)
}
}
return .Bytes()
}
func ( []*x509.Certificate) [][]byte {
:= make([][]byte, 0, len())
for , := range {
= append(, .Raw)
}
return
}
func ( []byte) (*SessionState, error) {
:= &SessionState{}
:= cryptobyte.String()
var , , uint8
var Certificate
var cryptobyte.String
if !.ReadUint16(&.version) ||
!.ReadUint8(&) ||
( != 1 && != 2) ||
!.ReadUint16(&.cipherSuite) ||
!readUint64(&, &.createdAt) ||
!readUint8LengthPrefixed(&, &.secret) ||
!.ReadUint24LengthPrefixed(&) ||
!.ReadUint8(&) ||
!.ReadUint8(&) ||
len(.secret) == 0 ||
!unmarshalCertificate(&, &) {
return nil, errors.New("tls: invalid session encoding")
}
for !.Empty() {
var []byte
if !readUint24LengthPrefixed(&, &) {
return nil, errors.New("tls: invalid session encoding")
}
.Extra = append(.Extra, )
}
switch {
case 0:
.extMasterSecret = false
case 1:
.extMasterSecret = true
default:
return nil, errors.New("tls: invalid session encoding")
}
switch {
case 0:
.EarlyData = false
case 1:
.EarlyData = true
default:
return nil, errors.New("tls: invalid session encoding")
}
for , := range .Certificate {
, := globalCertCache.newCert()
if != nil {
return nil,
}
.activeCertHandles = append(.activeCertHandles, )
.peerCertificates = append(.peerCertificates, .cert)
}
.ocspResponse = .OCSPStaple
.scts = .SignedCertificateTimestamps
var cryptobyte.String
if !.ReadUint24LengthPrefixed(&) {
return nil, errors.New("tls: invalid session encoding")
}
for !.Empty() {
var cryptobyte.String
if !.ReadUint24LengthPrefixed(&) {
return nil, errors.New("tls: invalid session encoding")
}
var []*x509.Certificate
if len(.peerCertificates) == 0 {
return nil, errors.New("tls: invalid session encoding")
}
= append(, .peerCertificates[0])
for !.Empty() {
var []byte
if !readUint24LengthPrefixed(&, &) {
return nil, errors.New("tls: invalid session encoding")
}
, := globalCertCache.newCert()
if != nil {
return nil,
}
.activeCertHandles = append(.activeCertHandles, )
= append(, .cert)
}
.verifiedChains = append(.verifiedChains, )
}
if .EarlyData {
var []byte
if !readUint8LengthPrefixed(&, &) {
return nil, errors.New("tls: invalid session encoding")
}
.alpnProtocol = string()
}
if := == 2; ! {
if !.Empty() {
return nil, errors.New("tls: invalid session encoding")
}
return , nil
}
.isClient = true
if len(.peerCertificates) == 0 {
return nil, errors.New("tls: no server certificates in client session")
}
if .version < VersionTLS13 {
if !.Empty() {
return nil, errors.New("tls: invalid session encoding")
}
return , nil
}
if !.ReadUint64(&.useBy) || !.ReadUint32(&.ageAdd) || !.Empty() {
return nil, errors.New("tls: invalid session encoding")
}
return , nil
}
func ( *Conn) () *SessionState {
return &SessionState{
version: .vers,
cipherSuite: .cipherSuite,
createdAt: uint64(.config.time().Unix()),
alpnProtocol: .clientProtocol,
peerCertificates: .peerCertificates,
activeCertHandles: .activeCertHandles,
ocspResponse: .ocspResponse,
scts: .scts,
isClient: .isClient,
extMasterSecret: .extMasterSecret,
verifiedChains: .verifiedChains,
}
}
func ( *Config) ( ConnectionState, *SessionState) ([]byte, error) {
:= .ticketKeys(nil)
, := .Bytes()
if != nil {
return nil,
}
return .encryptTicket(, )
}
func ( *Config) ( []byte, []ticketKey) ([]byte, error) {
if len() == 0 {
return nil, errors.New("tls: internal error: session ticket keys unavailable")
}
:= make([]byte, aes.BlockSize+len()+sha256.Size)
:= [:aes.BlockSize]
:= [aes.BlockSize : len()-sha256.Size]
:= [:len()-sha256.Size]
:= [len()-sha256.Size:]
if , := io.ReadFull(.rand(), ); != nil {
return nil,
}
:= [0]
, := aes.NewCipher(.aesKey[:])
if != nil {
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + .Error())
}
cipher.NewCTR(, ).XORKeyStream(, )
:= hmac.New(sha256.New, .hmacKey[:])
.Write()
.Sum([:0])
return , nil
}
func ( *Config) ( []byte, ConnectionState) (*SessionState, error) {
:= .ticketKeys(nil)
:= .decryptTicket(, )
if == nil {
return nil, nil
}
, := ParseSessionState()
if != nil {
return nil, nil
}
return , nil
}
func ( *Config) ( []byte, []ticketKey) []byte {
if len() < aes.BlockSize+sha256.Size {
return nil
}
:= [:aes.BlockSize]
:= [aes.BlockSize : len()-sha256.Size]
:= [:len()-sha256.Size]
:= [len()-sha256.Size:]
for , := range {
:= hmac.New(sha256.New, .hmacKey[:])
.Write()
:= .Sum(nil)
if subtle.ConstantTimeCompare(, ) != 1 {
continue
}
, := aes.NewCipher(.aesKey[:])
if != nil {
return nil
}
:= make([]byte, len())
cipher.NewCTR(, ).XORKeyStream(, )
return
}
return nil
}
type ClientSessionState struct {
session *SessionState
}
func ( *ClientSessionState) () ( []byte, *SessionState, error) {
if == nil || .session == nil {
return nil, nil, nil
}
return .session.ticket, .session, nil
}
func ( []byte, *SessionState) (*ClientSessionState, error) {
.ticket =
return &ClientSessionState{
session: ,
}, nil
}