crypto/tls: support session ticket resumption.

Session resumption saves a round trip and removes the need to perform
the public-key operations of a TLS handshake when both the client and
server support it (which is true of Firefox and Chrome, at least).

R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/6555051
This commit is contained in:
Adam Langley 2012-09-24 16:52:43 -04:00
parent d263b7d38c
commit 13d26a420a
10 changed files with 1101 additions and 214 deletions

View File

@ -41,6 +41,7 @@ const (
const ( const (
typeClientHello uint8 = 1 typeClientHello uint8 = 1
typeServerHello uint8 = 2 typeServerHello uint8 = 2
typeNewSessionTicket uint8 = 4
typeCertificate uint8 = 11 typeCertificate uint8 = 11
typeServerKeyExchange uint8 = 12 typeServerKeyExchange uint8 = 12
typeCertificateRequest uint8 = 13 typeCertificateRequest uint8 = 13
@ -63,6 +64,7 @@ var (
extensionStatusRequest uint16 = 5 extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10 extensionSupportedCurves uint16 = 10
extensionSupportedPoints uint16 = 11 extensionSupportedPoints uint16 = 11
extensionSessionTicket uint16 = 35
extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned
) )
@ -97,6 +99,7 @@ const (
// ConnectionState records basic TLS details about the connection. // ConnectionState records basic TLS details about the connection.
type ConnectionState struct { type ConnectionState struct {
HandshakeComplete bool HandshakeComplete bool
DidResume bool
CipherSuite uint16 CipherSuite uint16
NegotiatedProtocol string NegotiatedProtocol string
NegotiatedProtocolIsMutual bool NegotiatedProtocolIsMutual bool
@ -180,6 +183,22 @@ type Config struct {
// CipherSuites is a list of supported cipher suites. If CipherSuites // CipherSuites is a list of supported cipher suites. If CipherSuites
// is nil, TLS uses a list of suites supported by the implementation. // is nil, TLS uses a list of suites supported by the implementation.
CipherSuites []uint16 CipherSuites []uint16
// SessionTicketsDisabled may be set to true to disable session ticket
// (resumption) support.
SessionTicketsDisabled bool
// SessionTicketKey is used by TLS servers to provide session
// resumption. See RFC 5077. If zero, it will be filled with
// random data before the first server handshake.
//
// If multiple servers are terminating connections for the same host
// they should all have the same SessionTicketKey. If the
// SessionTicketKey leaks, previously recorded and future TLS
// connections using that key are compromised.
SessionTicketKey [32]byte
serverInitOnce sync.Once
} }
func (c *Config) rand() io.Reader { func (c *Config) rand() io.Reader {

View File

@ -31,6 +31,7 @@ type Conn struct {
haveVers bool // version has been negotiated haveVers bool // version has been negotiated
config *Config // configuration passed to constructor config *Config // configuration passed to constructor
handshakeComplete bool handshakeComplete bool
didResume bool // whether this connection was a session resumption
cipherSuite uint16 cipherSuite uint16
ocspResponse []byte // stapled OCSP response ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
@ -830,6 +831,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.HandshakeComplete = c.handshakeComplete state.HandshakeComplete = c.handshakeComplete
if c.handshakeComplete { if c.handshakeComplete {
state.NegotiatedProtocol = c.clientProtocol state.NegotiatedProtocol = c.clientProtocol
state.DidResume = c.didResume
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
state.CipherSuite = c.cipherSuite state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates state.PeerCertificates = c.peerCertificates

View File

@ -278,8 +278,9 @@ func (c *Conn) clientHandshake() error {
c.writeRecord(recordTypeHandshake, certVerify.marshal()) c.writeRecord(recordTypeHandshake, certVerify.marshal())
} }
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := masterSecret := masterFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random)
keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
clientHash := suite.mac(c.vers, clientMAC) clientHash := suite.mac(c.vers, clientMAC)

View File

@ -18,6 +18,8 @@ type clientHelloMsg struct {
ocspStapling bool ocspStapling bool
supportedCurves []uint16 supportedCurves []uint16
supportedPoints []uint8 supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
} }
func (m *clientHelloMsg) equal(i interface{}) bool { func (m *clientHelloMsg) equal(i interface{}) bool {
@ -36,7 +38,9 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.serverName == m1.serverName && m.serverName == m1.serverName &&
m.ocspStapling == m1.ocspStapling && m.ocspStapling == m1.ocspStapling &&
eqUint16s(m.supportedCurves, m1.supportedCurves) && eqUint16s(m.supportedCurves, m1.supportedCurves) &&
bytes.Equal(m.supportedPoints, m1.supportedPoints) bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
m.ticketSupported == m1.ticketSupported &&
bytes.Equal(m.sessionTicket, m1.sessionTicket)
} }
func (m *clientHelloMsg) marshal() []byte { func (m *clientHelloMsg) marshal() []byte {
@ -66,6 +70,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += 1 + len(m.supportedPoints) extensionsLength += 1 + len(m.supportedPoints)
numExtensions++ numExtensions++
} }
if m.ticketSupported {
extensionsLength += len(m.sessionTicket)
numExtensions++
}
if numExtensions > 0 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@ -180,6 +188,17 @@ func (m *clientHelloMsg) marshal() []byte {
z = z[1:] z = z[1:]
} }
} }
if m.ticketSupported {
// http://tools.ietf.org/html/rfc5077#section-3.2
z[0] = byte(extensionSessionTicket >> 8)
z[1] = byte(extensionSessionTicket)
l := len(m.sessionTicket)
z[2] = byte(l >> 8)
z[3] = byte(l)
z = z[4:]
copy(z, m.sessionTicket)
z = z[len(m.sessionTicket):]
}
m.raw = x m.raw = x
@ -311,6 +330,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
} }
m.supportedPoints = make([]uint8, l) m.supportedPoints = make([]uint8, l)
copy(m.supportedPoints, data[1:]) copy(m.supportedPoints, data[1:])
case extensionSessionTicket:
// http://tools.ietf.org/html/rfc5077#section-3.2
m.ticketSupported = true
m.sessionTicket = data[:length]
} }
data = data[length:] data = data[length:]
} }
@ -328,6 +351,7 @@ type serverHelloMsg struct {
nextProtoNeg bool nextProtoNeg bool
nextProtos []string nextProtos []string
ocspStapling bool ocspStapling bool
ticketSupported bool
} }
func (m *serverHelloMsg) equal(i interface{}) bool { func (m *serverHelloMsg) equal(i interface{}) bool {
@ -344,7 +368,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
m.compressionMethod == m1.compressionMethod && m.compressionMethod == m1.compressionMethod &&
m.nextProtoNeg == m1.nextProtoNeg && m.nextProtoNeg == m1.nextProtoNeg &&
eqStrings(m.nextProtos, m1.nextProtos) && eqStrings(m.nextProtos, m1.nextProtos) &&
m.ocspStapling == m1.ocspStapling m.ocspStapling == m1.ocspStapling &&
m.ticketSupported == m1.ticketSupported
} }
func (m *serverHelloMsg) marshal() []byte { func (m *serverHelloMsg) marshal() []byte {
@ -368,6 +393,9 @@ func (m *serverHelloMsg) marshal() []byte {
if m.ocspStapling { if m.ocspStapling {
numExtensions++ numExtensions++
} }
if m.ticketSupported {
numExtensions++
}
if numExtensions > 0 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@ -416,6 +444,11 @@ func (m *serverHelloMsg) marshal() []byte {
z[1] = byte(extensionStatusRequest) z[1] = byte(extensionStatusRequest)
z = z[4:] z = z[4:]
} }
if m.ticketSupported {
z[0] = byte(extensionSessionTicket >> 8)
z[1] = byte(extensionSessionTicket)
z = z[4:]
}
m.raw = x m.raw = x
@ -489,6 +522,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false return false
} }
m.ocspStapling = true m.ocspStapling = true
case extensionSessionTicket:
if length > 0 {
return false
}
m.ticketSupported = true
} }
data = data[length:] data = data[length:]
} }
@ -1030,6 +1068,65 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
return true return true
} }
type newSessionTicketMsg struct {
raw []byte
ticket []byte
}
func (m *newSessionTicketMsg) equal(i interface{}) bool {
m1, ok := i.(*newSessionTicketMsg)
if !ok {
return false
}
return bytes.Equal(m.raw, m1.raw) &&
bytes.Equal(m.ticket, m1.ticket)
}
func (m *newSessionTicketMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
}
// See http://tools.ietf.org/html/rfc5077#section-3.3
ticketLen := len(m.ticket)
length := 2 + 4 + ticketLen
x = make([]byte, 4+length)
x[0] = typeNewSessionTicket
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
x[8] = uint8(ticketLen >> 8)
x[9] = uint8(ticketLen)
copy(x[10:], m.ticket)
m.raw = x
return
}
func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
m.raw = data
if len(data) < 10 {
return false
}
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
if uint32(len(data))-4 != length {
return false
}
ticketLen := int(data[8])<<8 + int(data[9])
if len(data)-10 != ticketLen {
return false
}
m.ticket = data[10:]
return true
}
func eqUint16s(x, y []uint16) bool { func eqUint16s(x, y []uint16) bool {
if len(x) != len(y) { if len(x) != len(y) {
return false return false

View File

@ -22,6 +22,8 @@ var tests = []interface{}{
&certificateStatusMsg{}, &certificateStatusMsg{},
&clientKeyExchangeMsg{}, &clientKeyExchangeMsg{},
&nextProtoMsg{}, &nextProtoMsg{},
&newSessionTicketMsg{},
&sessionState{},
} }
type testMessage interface { type testMessage interface {
@ -207,3 +209,22 @@ func (*nextProtoMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.proto = randomString(rand.Intn(255), rand) m.proto = randomString(rand.Intn(255), rand)
return reflect.ValueOf(m) return reflect.ValueOf(m)
} }
func (*newSessionTicketMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m := &newSessionTicketMsg{}
m.ticket = randomBytes(rand.Intn(4), rand)
return reflect.ValueOf(m)
}
func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value {
s := &sessionState{}
s.vers = uint16(rand.Intn(10000))
s.cipherSuite = uint16(rand.Intn(10000))
s.masterSecret = randomBytes(rand.Intn(100), rand)
numCerts := rand.Intn(20)
s.certificates = make([][]byte, numCerts)
for i := 0; i < numCerts; i++ {
s.certificates[i] = randomBytes(rand.Intn(10)+1, rand)
}
return reflect.ValueOf(s)
}

View File

@ -13,31 +13,120 @@ import (
"io" "io"
) )
// serverHandshakeState contains details of a server handshake in progress.
// It's discarded once the handshake has completed.
type serverHandshakeState struct {
c *Conn
clientHello *clientHelloMsg
hello *serverHelloMsg
suite *cipherSuite
ellipticOk bool
sessionState *sessionState
finishedHash finishedHash
masterSecret []byte
certsFromClient [][]byte
}
// serverHandshake performs a TLS handshake as a server.
func (c *Conn) serverHandshake() error { func (c *Conn) serverHandshake() error {
config := c.config config := c.config
msg, err := c.readHandshake()
// If this is the first server handshake, we generate a random key to
// encrypt the tickets with.
config.serverInitOnce.Do(func() {
if config.SessionTicketsDisabled {
return
}
// If the key has already been set then we have nothing to do.
for _, b := range config.SessionTicketKey {
if b != 0 {
return
}
}
if _, err := io.ReadFull(config.rand(), config.SessionTicketKey[:]); err != nil {
config.SessionTicketsDisabled = true
}
})
hs := serverHandshakeState{
c: c,
}
isResume, err := hs.readClientHello()
if err != nil { if err != nil {
return err return err
} }
clientHello, ok := msg.(*clientHelloMsg)
if !ok { // For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3
return c.sendAlert(alertUnexpectedMessage) if isResume {
// The client has included a session ticket and so we do an abbreviated handshake.
if err := hs.doResumeHandshake(); err != nil {
return err
}
if err := hs.establishKeys(); err != nil {
return err
}
if err := hs.sendFinished(); err != nil {
return err
}
if err := hs.readFinished(); err != nil {
return err
}
c.didResume = true
} else {
// The client didn't include a session ticket, or it wasn't
// valid so we do a full handshake.
if err := hs.doFullHandshake(); err != nil {
return err
}
if err := hs.establishKeys(); err != nil {
return err
}
if err := hs.readFinished(); err != nil {
return err
}
if err := hs.sendSessionTicket(); err != nil {
return err
}
if err := hs.sendFinished(); err != nil {
return err
}
} }
vers, ok := mutualVersion(clientHello.vers) c.handshakeComplete = true
if !ok {
return c.sendAlert(alertProtocolVersion) return nil
}
// readClientHello reads a ClientHello message from the client and decides
// whether we will perform session resumption.
func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
config := hs.c.config
c := hs.c
msg, err := c.readHandshake()
if err != nil {
return false, err
}
var ok bool
hs.clientHello, ok = msg.(*clientHelloMsg)
if !ok {
return false, c.sendAlert(alertUnexpectedMessage)
}
c.vers, ok = mutualVersion(hs.clientHello.vers)
if !ok {
return false, c.sendAlert(alertProtocolVersion)
} }
c.vers = vers
c.haveVers = true c.haveVers = true
finishedHash := newFinishedHash(vers) hs.finishedHash = newFinishedHash(c.vers)
finishedHash.Write(clientHello.marshal()) hs.finishedHash.Write(hs.clientHello.marshal())
hello := new(serverHelloMsg) hs.hello = new(serverHelloMsg)
supportedCurve := false supportedCurve := false
Curves: Curves:
for _, curve := range clientHello.supportedCurves { for _, curve := range hs.clientHello.supportedCurves {
switch curve { switch curve {
case curveP256, curveP384, curveP521: case curveP256, curveP384, curveP521:
supportedCurve = true supportedCurve = true
@ -46,110 +135,173 @@ Curves:
} }
supportedPointFormat := false supportedPointFormat := false
for _, pointFormat := range clientHello.supportedPoints { for _, pointFormat := range hs.clientHello.supportedPoints {
if pointFormat == pointFormatUncompressed { if pointFormat == pointFormatUncompressed {
supportedPointFormat = true supportedPointFormat = true
break break
} }
} }
hs.ellipticOk = supportedCurve && supportedPointFormat
ellipticOk := supportedCurve && supportedPointFormat
var suite *cipherSuite
FindCipherSuite:
for _, id := range clientHello.cipherSuites {
for _, supported := range config.cipherSuites() {
if id == supported {
var candidate *cipherSuite
for _, s := range cipherSuites {
if s.id == id {
candidate = s
break
}
}
if candidate == nil {
continue
}
// Don't select a ciphersuite which we can't
// support for this client.
if candidate.elliptic && !ellipticOk {
continue
}
suite = candidate
break FindCipherSuite
}
}
}
foundCompression := false foundCompression := false
// We only support null compression, so check that the client offered it. // We only support null compression, so check that the client offered it.
for _, compression := range clientHello.compressionMethods { for _, compression := range hs.clientHello.compressionMethods {
if compression == compressionNone { if compression == compressionNone {
foundCompression = true foundCompression = true
break break
} }
} }
if suite == nil || !foundCompression { if !foundCompression {
return c.sendAlert(alertHandshakeFailure) return false, c.sendAlert(alertHandshakeFailure)
} }
hello.vers = vers hs.hello.vers = c.vers
hello.cipherSuite = suite.id
t := uint32(config.time().Unix()) t := uint32(config.time().Unix())
hello.random = make([]byte, 32) hs.hello.random = make([]byte, 32)
hello.random[0] = byte(t >> 24) hs.hello.random[0] = byte(t >> 24)
hello.random[1] = byte(t >> 16) hs.hello.random[1] = byte(t >> 16)
hello.random[2] = byte(t >> 8) hs.hello.random[2] = byte(t >> 8)
hello.random[3] = byte(t) hs.hello.random[3] = byte(t)
_, err = io.ReadFull(config.rand(), hello.random[4:]) _, err = io.ReadFull(config.rand(), hs.hello.random[4:])
if err != nil { if err != nil {
return c.sendAlert(alertInternalError) return false, c.sendAlert(alertInternalError)
} }
hello.compressionMethod = compressionNone hs.hello.compressionMethod = compressionNone
if clientHello.nextProtoNeg { if len(hs.clientHello.serverName) > 0 {
hello.nextProtoNeg = true c.serverName = hs.clientHello.serverName
hello.nextProtos = config.NextProtos
} }
if hs.clientHello.nextProtoNeg {
hs.hello.nextProtoNeg = true
hs.hello.nextProtos = config.NextProtos
}
if hs.checkForResumption() {
return true, nil
}
for _, id := range hs.clientHello.cipherSuites {
if hs.suite = c.tryCipherSuite(id, hs.ellipticOk); hs.suite != nil {
break
}
}
if hs.suite == nil {
return false, c.sendAlert(alertHandshakeFailure)
}
return false, nil
}
// checkForResumption returns true if we should perform resumption on this connection.
func (hs *serverHandshakeState) checkForResumption() bool {
c := hs.c
var ok bool
if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
return false
}
if hs.sessionState.vers > hs.clientHello.vers {
return false
}
if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
return false
}
cipherSuiteOk := false
// Check that the client is still offering the ciphersuite in the session.
for _, id := range hs.clientHello.cipherSuites {
if id == hs.sessionState.cipherSuite {
cipherSuiteOk = true
break
}
}
if !cipherSuiteOk {
return false
}
// Check that we also support the ciphersuite from the session.
hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, hs.ellipticOk)
if hs.suite == nil {
return false
}
sessionHasClientCerts := len(hs.sessionState.certificates) != 0
needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert
if needClientCerts && !sessionHasClientCerts {
return false
}
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
return false
}
return true
}
func (hs *serverHandshakeState) doResumeHandshake() error {
c := hs.c
hs.hello.cipherSuite = hs.suite.id
// We echo the client's session ID in the ServerHello to let it know
// that we're doing a resumption.
hs.hello.sessionId = hs.clientHello.sessionId
hs.finishedHash.Write(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
if len(hs.sessionState.certificates) > 0 {
if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil {
return err
}
}
hs.masterSecret = hs.sessionState.masterSecret
return nil
}
func (hs *serverHandshakeState) doFullHandshake() error {
config := hs.c.config
c := hs.c
if len(config.Certificates) == 0 { if len(config.Certificates) == 0 {
return c.sendAlert(alertInternalError) return c.sendAlert(alertInternalError)
} }
cert := &config.Certificates[0] cert := &config.Certificates[0]
if len(clientHello.serverName) > 0 { if len(hs.clientHello.serverName) > 0 {
c.serverName = clientHello.serverName cert = config.getCertificateForName(hs.clientHello.serverName)
cert = config.getCertificateForName(clientHello.serverName)
} }
if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
hello.ocspStapling = true hs.hello.ocspStapling = true
} }
finishedHash.Write(hello.marshal()) hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
c.writeRecord(recordTypeHandshake, hello.marshal()) hs.hello.cipherSuite = hs.suite.id
hs.finishedHash.Write(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg) certMsg := new(certificateMsg)
certMsg.certificates = cert.Certificate certMsg.certificates = cert.Certificate
finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
if hello.ocspStapling { if hs.hello.ocspStapling {
certStatus := new(certificateStatusMsg) certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP certStatus.statusType = statusTypeOCSP
certStatus.response = cert.OCSPStaple certStatus.response = cert.OCSPStaple
finishedHash.Write(certStatus.marshal()) hs.finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal())
} }
keyAgreement := suite.ka() keyAgreement := hs.suite.ka()
skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello) skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
} }
if skx != nil { if skx != nil {
finishedHash.Write(skx.marshal()) hs.finishedHash.Write(skx.marshal())
c.writeRecord(recordTypeHandshake, skx.marshal()) c.writeRecord(recordTypeHandshake, skx.marshal())
} }
@ -166,28 +318,29 @@ FindCipherSuite:
if config.ClientCAs != nil { if config.ClientCAs != nil {
certReq.certificateAuthorities = config.ClientCAs.Subjects() certReq.certificateAuthorities = config.ClientCAs.Subjects()
} }
finishedHash.Write(certReq.marshal()) hs.finishedHash.Write(certReq.marshal())
c.writeRecord(recordTypeHandshake, certReq.marshal()) c.writeRecord(recordTypeHandshake, certReq.marshal())
} }
helloDone := new(serverHelloDoneMsg) helloDone := new(serverHelloDoneMsg)
finishedHash.Write(helloDone.marshal()) hs.finishedHash.Write(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal())
var pub *rsa.PublicKey // public key for client auth, if any var pub *rsa.PublicKey // public key for client auth, if any
msg, err = c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
return err return err
} }
var ok bool
// If we requested a client certificate, then the client must send a // If we requested a client certificate, then the client must send a
// certificate message, even if it's empty. // certificate message, even if it's empty.
if config.ClientAuth >= RequestClientCert { if config.ClientAuth >= RequestClientCert {
if certMsg, ok = msg.(*certificateMsg); !ok { if certMsg, ok = msg.(*certificateMsg); !ok {
return c.sendAlert(alertHandshakeFailure) return c.sendAlert(alertHandshakeFailure)
} }
finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())
if len(certMsg.certificates) == 0 { if len(certMsg.certificates) == 0 {
// The client didn't actually send a certificate // The client didn't actually send a certificate
@ -198,55 +351,9 @@ FindCipherSuite:
} }
} }
certs := make([]*x509.Certificate, len(certMsg.certificates)) pub, err = hs.processCertsFromClient(certMsg.certificates)
for i, asn1Data := range certMsg.certificates { if err != nil {
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { return err
c.sendAlert(alertBadCertificate)
return errors.New("tls: failed to parse client certificate: " + err.Error())
}
}
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
opts := x509.VerifyOptions{
Roots: c.config.ClientCAs,
CurrentTime: c.config.time(),
Intermediates: x509.NewCertPool(),
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
chains, err := certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate)
return errors.New("tls: failed to verify client's certificate: " + err.Error())
}
ok := false
for _, ku := range certs[0].ExtKeyUsage {
if ku == x509.ExtKeyUsageClientAuth {
ok = true
break
}
}
if !ok {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
}
c.verifiedChains = chains
}
if len(certs) > 0 {
if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok {
return c.sendAlert(alertUnsupportedCertificate)
}
c.peerCertificates = certs
} }
msg, err = c.readHandshake() msg, err = c.readHandshake()
@ -260,7 +367,7 @@ FindCipherSuite:
if !ok { if !ok {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
finishedHash.Write(ckx.marshal()) hs.finishedHash.Write(ckx.marshal())
// If we received a client cert in response to our certificate request message, // If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the // the client will send us a certificateVerifyMsg immediately after the
@ -279,15 +386,15 @@ FindCipherSuite:
} }
digest := make([]byte, 0, 36) digest := make([]byte, 0, 36)
digest = finishedHash.serverMD5.Sum(digest) digest = hs.finishedHash.serverMD5.Sum(digest)
digest = finishedHash.serverSHA1.Sum(digest) digest = hs.finishedHash.serverSHA1.Sum(digest)
err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature)
if err != nil { if err != nil {
c.sendAlert(alertBadCertificate) c.sendAlert(alertBadCertificate)
return errors.New("could not validate signature of connection nonces: " + err.Error()) return errors.New("could not validate signature of connection nonces: " + err.Error())
} }
finishedHash.Write(certVerify.marshal()) hs.finishedHash.Write(certVerify.marshal())
} }
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers)
@ -295,20 +402,38 @@ FindCipherSuite:
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, preMasterSecret, hs.clientHello.random, hs.hello.random)
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := return nil
keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) }
clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) func (hs *serverHandshakeState) establishKeys() error {
clientHash := suite.mac(c.vers, clientMAC) c := hs.c
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */)
clientHash := hs.suite.mac(c.vers, clientMAC)
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
serverHash := hs.suite.mac(c.vers, serverMAC)
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
return nil
}
func (hs *serverHandshakeState) readFinished() error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec) c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil { if err := c.error(); err != nil {
return err return err
} }
if hello.nextProtoNeg { if hs.hello.nextProtoNeg {
msg, err = c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
return err return err
} }
@ -316,11 +441,11 @@ FindCipherSuite:
if !ok { if !ok {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
finishedHash.Write(nextProto.marshal()) hs.finishedHash.Write(nextProto.marshal())
c.clientProtocol = nextProto.proto c.clientProtocol = nextProto.proto
} }
msg, err = c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
return err return err
} }
@ -329,25 +454,142 @@ FindCipherSuite:
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
verify := finishedHash.clientSum(masterSecret) verify := hs.finishedHash.clientSum(hs.masterSecret)
if len(verify) != len(clientFinished.verifyData) || if len(verify) != len(clientFinished.verifyData) ||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
return c.sendAlert(alertHandshakeFailure) return c.sendAlert(alertHandshakeFailure)
} }
finishedHash.Write(clientFinished.marshal()) hs.finishedHash.Write(clientFinished.marshal())
return nil
}
serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) func (hs *serverHandshakeState) sendSessionTicket() error {
serverHash := suite.mac(c.vers, serverMAC) if !hs.hello.ticketSupported {
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) return nil
c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) }
finished := new(finishedMsg) c := hs.c
finished.verifyData = finishedHash.serverSum(masterSecret) m := new(newSessionTicketMsg)
c.writeRecord(recordTypeHandshake, finished.marshal())
c.handshakeComplete = true var err error
c.cipherSuite = suite.id state := sessionState{
vers: c.vers,
cipherSuite: hs.suite.id,
masterSecret: hs.masterSecret,
certificates: hs.certsFromClient,
}
m.ticket, err = c.encryptTicket(&state)
if err != nil {
return err
}
hs.finishedHash.Write(m.marshal())
c.writeRecord(recordTypeHandshake, m.marshal())
return nil
}
func (hs *serverHandshakeState) sendFinished() error {
c := hs.c
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
hs.finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
c.cipherSuite = hs.suite.id
return nil
}
// processCertsFromClient takes a chain of client certificates either from a
// Certificates message or from a sessionState and verifies them. It returns
// the public key of the leaf certificate.
func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) {
c := hs.c
hs.certsFromClient = certificates
certs := make([]*x509.Certificate, len(certificates))
var err error
for i, asn1Data := range certificates {
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
c.sendAlert(alertBadCertificate)
return nil, errors.New("tls: failed to parse client certificate: " + err.Error())
}
}
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
opts := x509.VerifyOptions{
Roots: c.config.ClientCAs,
CurrentTime: c.config.time(),
Intermediates: x509.NewCertPool(),
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
chains, err := certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate)
return nil, errors.New("tls: failed to verify client's certificate: " + err.Error())
}
ok := false
for _, ku := range certs[0].ExtKeyUsage {
if ku == x509.ExtKeyUsageClientAuth {
ok = true
break
}
}
if !ok {
c.sendAlert(alertHandshakeFailure)
return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
}
c.verifiedChains = chains
}
if len(certs) > 0 {
pub, ok := certs[0].PublicKey.(*rsa.PublicKey)
if !ok {
return nil, c.sendAlert(alertUnsupportedCertificate)
}
c.peerCertificates = certs
return pub, nil
}
return nil, nil
}
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
// is acceptable to use.
func (c *Conn) tryCipherSuite(id uint16, ellipticOk bool) *cipherSuite {
for _, supported := range c.config.cipherSuites() {
if id == supported {
var candidate *cipherSuite
for _, s := range cipherSuites {
if s.id == id {
candidate = s
break
}
}
if candidate == nil {
continue
}
// Don't select a ciphersuite which we can't
// support for this client.
if candidate.elliptic && !ellipticOk {
continue
}
return candidate
}
}
return nil return nil
} }

View File

@ -83,12 +83,20 @@ func TestRejectBadProtocolVersion(t *testing.T) {
} }
func TestNoSuiteOverlap(t *testing.T) { func TestNoSuiteOverlap(t *testing.T) {
clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{0xff00}, []uint8{0}, false, "", false, nil, nil} clientHello := &clientHelloMsg{
vers: 0x0301,
cipherSuites: []uint16{0xff00},
compressionMethods: []uint8{0},
}
testClientHelloFailure(t, clientHello, alertHandshakeFailure) testClientHelloFailure(t, clientHello, alertHandshakeFailure)
} }
func TestNoCompressionOverlap(t *testing.T) { func TestNoCompressionOverlap(t *testing.T) {
clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}, false, "", false, nil, nil} clientHello := &clientHelloMsg{
vers: 0x0301,
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
compressionMethods: []uint8{0xff},
}
testClientHelloFailure(t, clientHello, alertHandshakeFailure) testClientHelloFailure(t, clientHello, alertHandshakeFailure)
} }
@ -188,6 +196,11 @@ func TestHandshakeServerSNI(t *testing.T) {
testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil) testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil)
} }
func TestResumption(t *testing.T) {
testServerScript(t, "IssueTicket", issueSessionTicketTest, testConfig, nil)
testServerScript(t, "Resume", serverResumeTest, testConfig, nil)
}
type clientauthTest struct { type clientauthTest struct {
name string name string
clientauth ClientAuthType clientauth ClientAuthType
@ -1039,21 +1052,37 @@ var sslv3ServerScript = [][]byte{
var selectCertificateBySNIScript = [][]byte{ var selectCertificateBySNIScript = [][]byte{
{ {
0x16, 0x03, 0x01, 0x00, 0x6e, 0x01, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0xed, 0x01, 0x00, 0x00,
0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9, 0xe9, 0x03, 0x02, 0x50, 0x5a, 0x1c, 0x90, 0x2b,
0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd, 0xc8, 0xf1, 0xd9, 0x4b, 0xd0, 0x18, 0x69, 0xed,
0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d, 0x5a, 0xbd, 0x68, 0xf6, 0xf7, 0xe3, 0xf0, 0x6e,
0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f, 0xd1, 0xcc, 0xf1, 0x2d, 0x94, 0xa4, 0x01, 0x63,
0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39, 0x91, 0xbe, 0xd0, 0x00, 0x00, 0x66, 0xc0, 0x14,
0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, 0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39,
0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f,
0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12,
0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16,
0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74, 0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99,
0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04,
0x23, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11,
0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05,
0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09,
0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06,
0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00,
0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74, 0x65, 0x73,
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x0b, 0x00,
0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00,
0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00,
0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00,
0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00,
0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00,
0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00,
0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x0f, 0x00,
0x01, 0x01,
}, },
{ {
0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
@ -1131,45 +1160,323 @@ var selectCertificateBySNIScript = [][]byte{
}, },
{ {
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
0x82, 0x00, 0x80, 0x70, 0x1d, 0x34, 0x75, 0xa2, 0x82, 0x00, 0x80, 0x45, 0x6d, 0x68, 0x61, 0xb9,
0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b, 0x1a, 0xe5, 0xeb, 0x67, 0x22, 0x3b, 0x87, 0x19,
0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14, 0x52, 0x86, 0x31, 0x91, 0xee, 0xcd, 0x17, 0x75,
0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66, 0xc6, 0x44, 0xaf, 0x23, 0xef, 0xd9, 0xfa, 0xd2,
0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2, 0x0b, 0xa2, 0xbb, 0xbf, 0x8b, 0x4b, 0x34, 0x50,
0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a, 0xf6, 0x2e, 0x05, 0x09, 0x7e, 0xbf, 0xb3, 0xa6,
0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c, 0x10, 0xe3, 0xc3, 0x49, 0x55, 0xa8, 0xdf, 0x6c,
0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77, 0xaa, 0xab, 0x11, 0x4c, 0x80, 0x0a, 0x45, 0xf8,
0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0, 0x37, 0xbb, 0xd3, 0x18, 0x4e, 0xec, 0x51, 0xbf,
0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53, 0x1a, 0xf6, 0x11, 0x1b, 0xcf, 0x2c, 0xaf, 0x5f,
0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf, 0x0b, 0x52, 0x4e, 0x92, 0x0c, 0x7a, 0xb2, 0x5d,
0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c, 0xe2, 0x1f, 0x83, 0xbe, 0xf5, 0xbf, 0x05, 0xbf,
0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f, 0x99, 0xd6, 0x9c, 0x86, 0x47, 0x5e, 0xb4, 0xff,
0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6, 0xe7, 0xac, 0xad, 0x1e, 0x3c, 0xaa, 0x91, 0x39,
0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba, 0xca, 0xad, 0xc5, 0x54, 0x64, 0x7e, 0xc2, 0x8a,
0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05, 0x48, 0xee, 0xb6, 0x4e, 0xf9, 0x33, 0x82, 0x52,
0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01, 0xe8, 0xed, 0x48, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71, 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc1, 0x2f,
0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc, 0x34, 0x03, 0x2a, 0xf2, 0xfd, 0x83, 0x69, 0x23,
0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6, 0x8c, 0x9e, 0x66, 0x3b, 0xbb, 0xd1, 0xab, 0xbb,
0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5, 0x51, 0x89, 0x27, 0x88, 0x0f, 0x08, 0x3e, 0x00,
0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0, 0xdc, 0xc7, 0x47, 0x82, 0x13, 0x34, 0xec, 0xca,
0x7f, 0xc1, 0x68, 0x6a,
}, },
{ {
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c, 0x01, 0x00, 0x24, 0xda, 0x61, 0x76, 0x9f, 0x7a,
0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a, 0x8a, 0xd0, 0x5f, 0x9b, 0x3d, 0xa7, 0xd5, 0xdd,
0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba, 0x95, 0x4b, 0xd4, 0x64, 0x2d, 0x2d, 0x6a, 0x98,
0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00, 0x9e, 0xfe, 0x77, 0x76, 0xe3, 0x02, 0x05, 0x0c,
0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17, 0xb2, 0xa6, 0x15, 0x82, 0x28, 0x25, 0xc5, 0x17,
0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7, 0x03, 0x01, 0x00, 0x21, 0x4e, 0x66, 0x2d, 0x50,
0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20, 0x00, 0xa2, 0x44, 0x4d, 0xee, 0x5f, 0x81, 0x67,
0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30, 0x21, 0x5d, 0x94, 0xc0, 0xfb, 0xdc, 0xbd, 0xf6,
0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc, 0xa8, 0x32, 0x8e, 0x2c, 0x22, 0x58, 0x37, 0xb6,
0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01, 0xa3, 0x1e, 0xf8, 0xdd, 0x83, 0x15, 0x03, 0x01,
0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85, 0x00, 0x16, 0x68, 0x3b, 0x3a, 0xd0, 0x1e, 0xc4,
0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91, 0x5e, 0x97, 0x6a, 0x47, 0x38, 0xfe, 0x17, 0x8e,
0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f, 0xc0, 0xb6, 0x4a, 0x94, 0x00, 0xb5, 0x91, 0xbf,
},
}
var issueSessionTicketTest = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0xdd, 0x01, 0x00, 0x00,
0xd9, 0x03, 0x02, 0x50, 0x5a, 0x32, 0xb6, 0x36,
0x0e, 0x94, 0x63, 0x57, 0x93, 0xd7, 0x1e, 0xb2,
0xa7, 0xd3, 0x20, 0x24, 0x30, 0x3f, 0x46, 0xf9,
0xfe, 0x22, 0x02, 0xa1, 0xff, 0x57, 0xf8, 0x8f,
0x95, 0x4c, 0xdd, 0x00, 0x00, 0x66, 0xc0, 0x14,
0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39,
0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f,
0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12,
0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16,
0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99,
0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04,
0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11,
0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05,
0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09,
0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06,
0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x00,
0x49, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00,
0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00,
0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00,
0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00,
0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00,
0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00,
0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00,
0x11, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00,
0x01, 0x01,
},
{
0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
0x00,
},
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
0x82, 0x00, 0x80, 0x92, 0x3f, 0xcc, 0x4d, 0x2f,
0xb2, 0x12, 0xc4, 0xf5, 0x72, 0xf3, 0x5a, 0x3c,
0x5a, 0xbb, 0x99, 0x89, 0xe6, 0x21, 0x0f, 0xdf,
0xf3, 0xa3, 0xd0, 0xce, 0x76, 0x55, 0xfd, 0xec,
0x38, 0x80, 0xf0, 0x46, 0x0b, 0xfa, 0x61, 0x7c,
0xc2, 0xb5, 0xe2, 0x89, 0x7b, 0xeb, 0xcf, 0x3e,
0x97, 0xab, 0x72, 0xf6, 0xfd, 0xcf, 0x10, 0x82,
0x3a, 0x05, 0x55, 0x7c, 0x2d, 0x7f, 0x44, 0x38,
0x9d, 0xeb, 0xa4, 0x7e, 0x53, 0x35, 0xda, 0xe0,
0x7c, 0x24, 0x66, 0x42, 0x5d, 0x85, 0xcf, 0xa6,
0x98, 0x81, 0xec, 0x42, 0x94, 0x4e, 0x25, 0xb1,
0x64, 0xac, 0x89, 0x98, 0x74, 0xd2, 0xeb, 0x51,
0x5a, 0xb3, 0xbd, 0x14, 0xf6, 0xc6, 0xec, 0x0b,
0xdd, 0x8b, 0x89, 0xdc, 0xde, 0xf3, 0xd6, 0x62,
0xee, 0xe3, 0xcf, 0xf5, 0x39, 0x23, 0x46, 0x4f,
0xb8, 0xef, 0x14, 0x39, 0x06, 0x36, 0xad, 0x84,
0x42, 0xb9, 0xd7, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xa1, 0xf0,
0x68, 0xf5, 0x29, 0x7e, 0x78, 0xaa, 0xbd, 0x59,
0xdc, 0x32, 0xab, 0x8e, 0x25, 0x54, 0x64, 0x9e,
0x2b, 0x08, 0xf9, 0xb8, 0xe3, 0x89, 0x09, 0xa4,
0xfd, 0x05, 0x78, 0x59, 0xcb, 0x33, 0xfc, 0x66,
0xb5, 0x73,
},
{
0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
0xe8, 0x4b, 0xd1, 0xef, 0xba, 0xfc, 0x00, 0xd4,
0x2f, 0xf5, 0x6f, 0xba, 0xdc, 0xb7, 0xd7, 0x87,
0x59, 0x58, 0x05, 0x06, 0x36, 0x8f, 0x47, 0xc7,
0x9e, 0x4c, 0xf8, 0xb5, 0xd7, 0x55, 0x84, 0x64,
0x0b, 0x4c, 0x0b, 0xad, 0x8d, 0x9b, 0x79, 0x4d,
0xd7, 0x61, 0xf7, 0x2b, 0x89, 0x46, 0x2b, 0x52,
0x1a, 0x3f, 0x51, 0x58, 0xce, 0x59, 0x23, 0xef,
0x60, 0x55, 0x07, 0xc0, 0x46, 0x97, 0xad, 0x0a,
0xe3, 0x55, 0x10, 0x06, 0xff, 0x57, 0x0c, 0xb1,
0x49, 0xac, 0x80, 0xc6, 0xc3, 0x95, 0x5f, 0x12,
0xe2, 0xe5, 0xaa, 0x9f, 0x78, 0xc2, 0x20, 0x14,
0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
0x00, 0x24, 0x47, 0x51, 0xf1, 0x13, 0xc8, 0xa6,
0xd2, 0x2c, 0xad, 0x35, 0xff, 0x53, 0xe2, 0x72,
0x01, 0xcb, 0x33, 0xcd, 0xf4, 0xa0, 0x9c, 0x03,
0x47, 0xfe, 0xcd, 0xc1, 0x46, 0x8d, 0x41, 0x5e,
0x54, 0xf7, 0xc3, 0x85, 0x2b, 0x2f, 0x17, 0x03,
0x01, 0x00, 0x21, 0xf4, 0xbf, 0x94, 0x3e, 0x93,
0x0b, 0x1b, 0x75, 0x3a, 0xd9, 0xd0, 0x57, 0x75,
0xf3, 0xa7, 0x82, 0xc9, 0x6b, 0x9e, 0x43, 0x98,
0x44, 0x9e, 0x9f, 0xad, 0x03, 0xa8, 0xb9, 0xa3,
0x0a, 0xd1, 0xc4, 0xb4, 0x15, 0x03, 0x01, 0x00,
0x16, 0xee, 0x57, 0xbd, 0xd3, 0xb7, 0x20, 0x29,
0xd1, 0x24, 0xe2, 0xdc, 0x24, 0xc3, 0x73, 0x86,
0x81, 0x8e, 0x40, 0xc3, 0x6e, 0x99, 0x9e,
},
}
var serverResumeTest = [][]byte{
{
0x16, 0x03, 0x01, 0x01, 0x65, 0x01, 0x00, 0x01,
0x61, 0x03, 0x01, 0x50, 0x5a, 0x32, 0xe2, 0xde,
0x19, 0x5c, 0xb6, 0x51, 0x87, 0xa4, 0x30, 0x2e,
0x95, 0x26, 0xd6, 0xed, 0xbf, 0xbf, 0x24, 0xbb,
0xd1, 0x1a, 0x29, 0x9f, 0x37, 0xfd, 0xfb, 0xae,
0xc2, 0xba, 0x2b, 0x20, 0xb5, 0x7a, 0x00, 0x96,
0x92, 0x51, 0xfc, 0x41, 0x16, 0x29, 0xc0, 0x54,
0x5e, 0xa7, 0xa9, 0x1f, 0xf8, 0xbf, 0x79, 0xfa,
0x49, 0x5a, 0x15, 0x28, 0x72, 0x9a, 0x59, 0xf9,
0x9b, 0xc4, 0x3a, 0xa8, 0x00, 0x66, 0xc0, 0x14,
0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39,
0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f,
0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12,
0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16,
0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99,
0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04,
0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11,
0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05,
0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09,
0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06,
0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x00,
0xb1, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00,
0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00,
0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00,
0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00,
0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00,
0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00,
0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00,
0x11, 0x00, 0x23, 0x00, 0x68, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe8, 0x4b,
0xd1, 0xef, 0xba, 0xfc, 0x00, 0xd4, 0x2f, 0xf5,
0x6f, 0xba, 0xdc, 0xb7, 0xd7, 0x87, 0x59, 0x58,
0x05, 0x06, 0x36, 0x8f, 0x47, 0xc7, 0x9e, 0x4c,
0xf8, 0xb5, 0xd7, 0x55, 0x84, 0x64, 0x0b, 0x4c,
0x0b, 0xad, 0x8d, 0x9b, 0x79, 0x4d, 0xd7, 0x61,
0xf7, 0x2b, 0x89, 0x46, 0x2b, 0x52, 0x1a, 0x3f,
0x51, 0x58, 0xce, 0x59, 0x23, 0xef, 0x60, 0x55,
0x07, 0xc0, 0x46, 0x97, 0xad, 0x0a, 0xe3, 0x55,
0x10, 0x06, 0xff, 0x57, 0x0c, 0xb1, 0x49, 0xac,
0x80, 0xc6, 0xc3, 0x95, 0x5f, 0x12, 0xe2, 0xe5,
0xaa, 0x9f, 0x78, 0xc2, 0x20, 0x00, 0x0f, 0x00,
0x01, 0x01,
},
{
0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00,
0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0xb5, 0x7a, 0x00, 0x96,
0x92, 0x51, 0xfc, 0x41, 0x16, 0x29, 0xc0, 0x54,
0x5e, 0xa7, 0xa9, 0x1f, 0xf8, 0xbf, 0x79, 0xfa,
0x49, 0x5a, 0x15, 0x28, 0x72, 0x9a, 0x59, 0xf9,
0x9b, 0xc4, 0x3a, 0xa8, 0x00, 0x05, 0x00, 0x14,
0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
0x00, 0x24, 0x2c, 0x86, 0xdd, 0x85, 0x21, 0xa7,
0xda, 0x25, 0xf5, 0x55, 0x62, 0x2d, 0x82, 0x6b,
0x9d, 0x67, 0x22, 0x28, 0xf4, 0x55, 0x33, 0xd0,
0x77, 0xc0, 0x9e, 0xb7, 0xf4, 0x96, 0x07, 0x8c,
0xf5, 0xea, 0x5b, 0x50, 0xa4, 0xb7,
},
{
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x24, 0x15, 0x14, 0x9c, 0x21, 0xdd,
0x47, 0x61, 0x52, 0xf9, 0x22, 0x15, 0x55, 0x3c,
0xbd, 0xd7, 0xff, 0xf9, 0xbd, 0x84, 0xec, 0x97,
0x2d, 0x4e, 0xa9, 0x6a, 0xb9, 0x9b, 0x96, 0xc6,
0x9e, 0x5c, 0x77, 0xa8, 0x5d, 0x7a, 0x08,
},
{
0x17, 0x03, 0x01, 0x00, 0x21, 0x04, 0xab, 0x0f,
0x7c, 0x54, 0x20, 0xab, 0x34, 0xa3, 0x73, 0x92,
0xc5, 0xaa, 0xdd, 0x5b, 0xf5, 0x0c, 0xe4, 0x4f,
0xf1, 0x93, 0x07, 0xe5, 0xe8, 0x72, 0xc2, 0x03,
0x60, 0xfa, 0x64, 0x01, 0x00, 0x25, 0x15, 0x03,
0x01, 0x00, 0x16, 0xc7, 0xd9, 0xff, 0x67, 0xfc,
0x7a, 0xac, 0x8a, 0xe6, 0x23, 0xfe, 0x32, 0xbf,
0x84, 0xe1, 0xe2, 0xf5, 0x6a, 0xc8, 0xda, 0x30,
0x8f,
}, },
} }

21
prf.go
View File

@ -106,10 +106,9 @@ var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished") var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished") var serverFinishedLabel = []byte("server finished")
// keysFromPreMasterSecret generates the connection keys from the pre master // masterFromPreMasterSecret generates the master secret from the pre-master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in // secret. See http://tools.ietf.org/html/rfc5246#section-8.1
// RFC 2246, section 6.3. func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte {
func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
prf := pRF10 prf := pRF10
if version == versionSSL30 { if version == versionSSL30 {
prf = pRF30 prf = pRF30
@ -118,9 +117,21 @@ func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serv
var seed [tlsRandomLength * 2]byte var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], clientRandom) copy(seed[0:len(clientRandom)], clientRandom)
copy(seed[len(clientRandom):], serverRandom) copy(seed[len(clientRandom):], serverRandom)
masterSecret = make([]byte, masterSecretLength) masterSecret := make([]byte, masterSecretLength)
prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
return masterSecret
}
// keysFromMasterSecret generates the connection keys from the master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3.
func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
prf := pRF10
if version == versionSSL30 {
prf = pRF30
}
var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], serverRandom) copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom) copy(seed[len(serverRandom):], clientRandom)

View File

@ -48,18 +48,23 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
in, _ := hex.DecodeString(test.preMasterSecret) in, _ := hex.DecodeString(test.preMasterSecret)
clientRandom, _ := hex.DecodeString(test.clientRandom) clientRandom, _ := hex.DecodeString(test.clientRandom)
serverRandom, _ := hex.DecodeString(test.serverRandom) serverRandom, _ := hex.DecodeString(test.serverRandom)
master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
masterString := hex.EncodeToString(master) masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom)
if s := hex.EncodeToString(masterSecret); s != test.masterSecret {
t.Errorf("#%d: bad master secret %s, want %s", s, test.masterSecret)
continue
}
clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromMasterSecret(test.version, masterSecret, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
clientMACString := hex.EncodeToString(clientMAC) clientMACString := hex.EncodeToString(clientMAC)
serverMACString := hex.EncodeToString(serverMAC) serverMACString := hex.EncodeToString(serverMAC)
clientKeyString := hex.EncodeToString(clientKey) clientKeyString := hex.EncodeToString(clientKey)
serverKeyString := hex.EncodeToString(serverKey) serverKeyString := hex.EncodeToString(serverKey)
if masterString != test.masterSecret || if clientMACString != test.clientMAC ||
clientMACString != test.clientMAC ||
serverMACString != test.serverMAC || serverMACString != test.serverMAC ||
clientKeyString != test.clientKey || clientKeyString != test.clientKey ||
serverKeyString != test.serverKey { serverKeyString != test.serverKey {
t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) t.Errorf("#%d: got: (%s, %s, %s, %s) want: (%s, %s, %s, %s)", i, clientMACString, serverMACString, clientKeyString, serverKeyString, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
} }
} }
} }

182
ticket.go Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2012 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 tls
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"errors"
"io"
)
// sessionState contains the information that is serialized into a session
// ticket in order to later resume a connection.
type sessionState struct {
vers uint16
cipherSuite uint16
masterSecret []byte
certificates [][]byte
}
func (s *sessionState) equal(i interface{}) bool {
s1, ok := i.(*sessionState)
if !ok {
return false
}
if s.vers != s1.vers ||
s.cipherSuite != s1.cipherSuite ||
!bytes.Equal(s.masterSecret, s1.masterSecret) {
return false
}
if len(s.certificates) != len(s1.certificates) {
return false
}
for i := range s.certificates {
if !bytes.Equal(s.certificates[i], s1.certificates[i]) {
return false
}
}
return true
}
func (s *sessionState) marshal() []byte {
length := 2 + 2 + 2 + len(s.masterSecret) + 2
for _, cert := range s.certificates {
length += 4 + len(cert)
}
ret := make([]byte, length)
x := ret
x[0] = byte(s.vers >> 8)
x[1] = byte(s.vers)
x[2] = byte(s.cipherSuite >> 8)
x[3] = byte(s.cipherSuite)
x[4] = byte(len(s.masterSecret) >> 8)
x[5] = byte(len(s.masterSecret))
x = x[6:]
copy(x, s.masterSecret)
x = x[len(s.masterSecret):]
x[0] = byte(len(s.certificates) >> 8)
x[1] = byte(len(s.certificates))
x = x[2:]
for _, cert := range s.certificates {
x[0] = byte(len(cert) >> 24)
x[1] = byte(len(cert) >> 16)
x[2] = byte(len(cert) >> 8)
x[3] = byte(len(cert))
copy(x[4:], cert)
x = x[4+len(cert):]
}
return ret
}
func (s *sessionState) unmarshal(data []byte) bool {
if len(data) < 8 {
return false
}
s.vers = uint16(data[0])<<8 | uint16(data[1])
s.cipherSuite = uint16(data[2])<<8 | uint16(data[3])
masterSecretLen := int(data[4])<<8 | int(data[5])
data = data[6:]
if len(data) < masterSecretLen {
return false
}
s.masterSecret = data[:masterSecretLen]
data = data[masterSecretLen:]
if len(data) < 2 {
return false
}
numCerts := int(data[0])<<8 | int(data[1])
data = data[2:]
s.certificates = make([][]byte, numCerts)
for i := range s.certificates {
if len(data) < 4 {
return false
}
certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
data = data[4:]
if certLen < 0 {
return false
}
if len(data) < certLen {
return false
}
s.certificates[i] = data[:certLen]
data = data[certLen:]
}
if len(data) > 0 {
return false
}
return true
}
func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
serialized := state.marshal()
encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
iv := encrypted[:aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
return nil, err
}
block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
if err != nil {
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
}
cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
mac.Sum(macBytes[:0])
return encrypted, nil
}
func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
if len(encrypted) < aes.BlockSize+sha256.Size {
return nil, false
}
iv := encrypted[:aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
expected := mac.Sum(nil)
if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
return nil, false
}
block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
if err != nil {
return nil, false
}
ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
plaintext := ciphertext
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
state := new(sessionState)
ok := state.unmarshal(plaintext)
return state, ok
}