package  tlsimport  (	"crypto/aes" 	"crypto/cipher" 	"crypto/hmac" 	"crypto/sha256" 	"crypto/subtle" 	"crypto/x509" 	"errors" 	"io" 	"golang.org/x/crypto/cryptobyte" )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  (s  *SessionState ) Bytes byte , error ) {	var  b  cryptobyte .Builder 	b .AddUint16 (s .version )	if  s .isClient  {		b .AddUint8 (2 ) 	} else  {		b .AddUint8 (1 ) 	}	b .AddUint16 (s .cipherSuite )	addUint64 (&b , s .createdAt )	b .AddUint8LengthPrefixed (func (b  *cryptobyte .Builder ) {		b .AddBytes (s .secret )	})	b .AddUint24LengthPrefixed (func (b  *cryptobyte .Builder ) {		for  _ , extra  := range  s .Extra  {			b .AddUint24LengthPrefixed (func (b  *cryptobyte .Builder ) {				b .AddBytes (extra )			})		}	})	if  s .extMasterSecret  {		b .AddUint8 (1 )	} else  {		b .AddUint8 (0 )	}	if  s .EarlyData  {		b .AddUint8 (1 )	} else  {		b .AddUint8 (0 )	}	marshalCertificate (&b , Certificate {		Certificate :                 certificatesToBytesSlice (s .peerCertificates ),		OCSPStaple :                  s .ocspResponse ,		SignedCertificateTimestamps : s .scts ,	})	b .AddUint24LengthPrefixed (func (b  *cryptobyte .Builder ) {		for  _ , chain  := range  s .verifiedChains  {			b .AddUint24LengthPrefixed (func (b  *cryptobyte .Builder ) {								if  len (chain ) == 0  {					b .SetError (errors .New ("tls: internal error: empty verified chain" ))					return 				}				for  _ , cert  := range  chain [1 :] {					b .AddUint24LengthPrefixed (func (b  *cryptobyte .Builder ) {						b .AddBytes (cert .Raw )					})				}			})		}	})	if  s .EarlyData  {		b .AddUint8LengthPrefixed (func (b  *cryptobyte .Builder ) {			b .AddBytes ([]byte (s .alpnProtocol ))		})	}	if  s .isClient  {		if  s .version  >= VersionTLS13  {			addUint64 (&b , s .useBy )			b .AddUint32 (s .ageAdd )		}	}	return  b .Bytes ()}func  certificatesToBytesSlice certs  []*x509 .Certificate ) [][]byte  {	s  := make ([][]byte , 0 , len (certs ))	for  _ , c  := range  certs  {		s  = append (s , c .Raw )	}	return  s }func  ParseSessionState data  []byte ) (*SessionState , error ) {	ss  := &SessionState {}	s  := cryptobyte .String (data )	var  typ , extMasterSecret , earlyData  uint8 	var  cert  Certificate 	var  extra  cryptobyte .String 	if  !s .ReadUint16 (&ss .version ) ||		!s .ReadUint8 (&typ ) ||		(typ  != 1  && typ  != 2 ) ||		!s .ReadUint16 (&ss .cipherSuite ) ||		!readUint64 (&s , &ss .createdAt ) ||		!readUint8LengthPrefixed (&s , &ss .secret ) ||		!s .ReadUint24LengthPrefixed (&extra ) ||		!s .ReadUint8 (&extMasterSecret ) ||		!s .ReadUint8 (&earlyData ) ||		len (ss .secret ) == 0  ||		!unmarshalCertificate (&s , &cert ) {		return  nil , errors .New ("tls: invalid session encoding" )	}	for  !extra .Empty () {		var  e  []byte 		if  !readUint24LengthPrefixed (&extra , &e ) {			return  nil , errors .New ("tls: invalid session encoding" )		}		ss .Extra  = append (ss .Extra , e )	}	switch  extMasterSecret  {	case  0 :		ss .extMasterSecret  = false 	case  1 :		ss .extMasterSecret  = true 	default :		return  nil , errors .New ("tls: invalid session encoding" )	}	switch  earlyData  {	case  0 :		ss .EarlyData  = false 	case  1 :		ss .EarlyData  = true 	default :		return  nil , errors .New ("tls: invalid session encoding" )	}	for  _ , cert  := range  cert .Certificate  {		c , err  := globalCertCache .newCert (cert )		if  err  != nil  {			return  nil , err 		}		ss .activeCertHandles  = append (ss .activeCertHandles , c )		ss .peerCertificates  = append (ss .peerCertificates , c .cert )	}	ss .ocspResponse  = cert .OCSPStaple 	ss .scts  = cert .SignedCertificateTimestamps 	var  chainList  cryptobyte .String 	if  !s .ReadUint24LengthPrefixed (&chainList ) {		return  nil , errors .New ("tls: invalid session encoding" )	}	for  !chainList .Empty () {		var  certList  cryptobyte .String 		if  !chainList .ReadUint24LengthPrefixed (&certList ) {			return  nil , errors .New ("tls: invalid session encoding" )		}		var  chain  []*x509 .Certificate 		if  len (ss .peerCertificates ) == 0  {			return  nil , errors .New ("tls: invalid session encoding" )		}		chain  = append (chain , ss .peerCertificates [0 ])		for  !certList .Empty () {			var  cert  []byte 			if  !readUint24LengthPrefixed (&certList , &cert ) {				return  nil , errors .New ("tls: invalid session encoding" )			}			c , err  := globalCertCache .newCert (cert )			if  err  != nil  {				return  nil , err 			}			ss .activeCertHandles  = append (ss .activeCertHandles , c )			chain  = append (chain , c .cert )		}		ss .verifiedChains  = append (ss .verifiedChains , chain )	}	if  ss .EarlyData  {		var  alpn  []byte 		if  !readUint8LengthPrefixed (&s , &alpn ) {			return  nil , errors .New ("tls: invalid session encoding" )		}		ss .alpnProtocol  = string (alpn )	}	if  isClient  := typ  == 2 ; !isClient  {		if  !s .Empty () {			return  nil , errors .New ("tls: invalid session encoding" )		}		return  ss , nil 	}	ss .isClient  = true 	if  len (ss .peerCertificates ) == 0  {		return  nil , errors .New ("tls: no server certificates in client session" )	}	if  ss .version  < VersionTLS13  {		if  !s .Empty () {			return  nil , errors .New ("tls: invalid session encoding" )		}		return  ss , nil 	}	if  !s .ReadUint64 (&ss .useBy ) || !s .ReadUint32 (&ss .ageAdd ) || !s .Empty () {		return  nil , errors .New ("tls: invalid session encoding" )	}	return  ss , nil }func  (c  *Conn ) sessionState SessionState  {	return  &SessionState {		version :           c .vers ,		cipherSuite :       c .cipherSuite ,		createdAt :         uint64 (c .config .time ().Unix ()),		alpnProtocol :      c .clientProtocol ,		peerCertificates :  c .peerCertificates ,		activeCertHandles : c .activeCertHandles ,		ocspResponse :      c .ocspResponse ,		scts :              c .scts ,		isClient :          c .isClient ,		extMasterSecret :   c .extMasterSecret ,		verifiedChains :    c .verifiedChains ,	}}func  (c  *Config ) EncryptTicket cs  ConnectionState , ss  *SessionState ) ([]byte , error ) {	ticketKeys  := c .ticketKeys (nil )	stateBytes , err  := ss .Bytes ()	if  err  != nil  {		return  nil , err 	}	return  c .encryptTicket (stateBytes , ticketKeys )}func  (c  *Config ) encryptTicket state  []byte , ticketKeys  []ticketKey ) ([]byte , error ) {	if  len (ticketKeys ) == 0  {		return  nil , errors .New ("tls: internal error: session ticket keys unavailable" )	}	encrypted  := make ([]byte , aes .BlockSize +len (state )+sha256 .Size )	iv  := encrypted [:aes .BlockSize ]	ciphertext  := encrypted [aes .BlockSize  : len (encrypted )-sha256 .Size ]	authenticated  := encrypted [:len (encrypted )-sha256 .Size ]	macBytes  := encrypted [len (encrypted )-sha256 .Size :]	if  _ , err  := io .ReadFull (c .rand (), iv ); err  != nil  {		return  nil , err 	}	key  := ticketKeys [0 ]	block , err  := aes .NewCipher (key .aesKey [:])	if  err  != nil  {		return  nil , errors .New ("tls: failed to create cipher while encrypting ticket: "  + err .Error())	}	cipher .NewCTR (block , iv ).XORKeyStream (ciphertext , state )	mac  := hmac .New (sha256 .New , key .hmacKey [:])	mac .Write (authenticated )	mac .Sum (macBytes [:0 ])	return  encrypted , nil }func  (c  *Config ) DecryptTicket identity  []byte , cs  ConnectionState ) (*SessionState , error ) {	ticketKeys  := c .ticketKeys (nil )	stateBytes  := c .decryptTicket (identity , ticketKeys )	if  stateBytes  == nil  {		return  nil , nil 	}	s , err  := ParseSessionState (stateBytes )	if  err  != nil  {		return  nil , nil  	}	return  s , nil }func  (c  *Config ) decryptTicket encrypted  []byte , ticketKeys  []ticketKey ) []byte  {	if  len (encrypted ) < aes .BlockSize +sha256 .Size  {		return  nil 	}	iv  := encrypted [:aes .BlockSize ]	ciphertext  := encrypted [aes .BlockSize  : len (encrypted )-sha256 .Size ]	authenticated  := encrypted [:len (encrypted )-sha256 .Size ]	macBytes  := encrypted [len (encrypted )-sha256 .Size :]	for  _ , key  := range  ticketKeys  {		mac  := hmac .New (sha256 .New , key .hmacKey [:])		mac .Write (authenticated )		expected  := mac .Sum (nil )		if  subtle .ConstantTimeCompare (macBytes , expected ) != 1  {			continue 		}		block , err  := aes .NewCipher (key .aesKey [:])		if  err  != nil  {			return  nil 		}		plaintext  := make ([]byte , len (ciphertext ))		cipher .NewCTR (block , iv ).XORKeyStream (plaintext , ciphertext )		return  plaintext 	}	return  nil }type  ClientSessionState  struct  {	session  *SessionState }func  (cs  *ClientSessionState ) ResumptionState ticket  []byte , state  *SessionState , err  error ) {	if  cs  == nil  || cs .session  == nil  {		return  nil , nil , nil 	}	return  cs .session .ticket , cs .session , nil }func  NewResumptionState ticket  []byte , state  *SessionState ) (*ClientSessionState , error ) {	state .ticket  = ticket 	return  &ClientSessionState {		session : state ,	}, nil } The pages are generated with Golds v0.7.6 . (GOOS=linux GOARCH=amd64)
Golds  is a Go 101  project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101  (reachable from the left QR code) to get the latest news of Golds .