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/6555051tls13
@@ -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 { | ||||
@@ -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 | ||||
@@ -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 := | |||||
keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) | |||||
masterSecret := masterFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random) | |||||
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) | ||||
@@ -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 | ||||
@@ -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) | |||||
} |
@@ -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) | |||||
// For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3 | |||||
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 | |||||
} | |||||
} | |||||
c.handshakeComplete = true | |||||
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 { | if !ok { | ||||
return c.sendAlert(alertUnexpectedMessage) | |||||
return false, c.sendAlert(alertUnexpectedMessage) | |||||
} | } | ||||
vers, ok := mutualVersion(clientHello.vers) | |||||
c.vers, ok = mutualVersion(hs.clientHello.vers) | |||||
if !ok { | if !ok { | ||||
return c.sendAlert(alertProtocolVersion) | |||||
return false, c.sendAlert(alertProtocolVersion) | |||||
} | } | ||||
c.vers = vers | |||||
c.haveVers = true | c.haveVers = true | ||||
finishedHash := newFinishedHash(vers) | |||||
finishedHash.Write(clientHello.marshal()) | |||||
hs.finishedHash = newFinishedHash(c.vers) | |||||
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 | ||||
} | } | ||||
} | } | ||||
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 | |||||
} | |||||
} | |||||
} | |||||
hs.ellipticOk = supportedCurve && supportedPointFormat | |||||
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 { | |||||
return c.sendAlert(alertHandshakeFailure) | |||||
if !foundCompression { | |||||
return false, c.sendAlert(alertHandshakeFailure) | |||||
} | } | ||||
hello.vers = vers | |||||
hello.cipherSuite = suite.id | |||||
hs.hello.vers = c.vers | |||||
t := uint32(config.time().Unix()) | t := uint32(config.time().Unix()) | ||||
hello.random = make([]byte, 32) | |||||
hello.random[0] = byte(t >> 24) | |||||
hello.random[1] = byte(t >> 16) | |||||
hello.random[2] = byte(t >> 8) | |||||
hello.random[3] = byte(t) | |||||
_, err = io.ReadFull(config.rand(), hello.random[4:]) | |||||
hs.hello.random = make([]byte, 32) | |||||
hs.hello.random[0] = byte(t >> 24) | |||||
hs.hello.random[1] = byte(t >> 16) | |||||
hs.hello.random[2] = byte(t >> 8) | |||||
hs.hello.random[3] = byte(t) | |||||
_, err = io.ReadFull(config.rand(), hs.hello.random[4:]) | |||||
if err != nil { | if err != nil { | ||||
return c.sendAlert(alertInternalError) | |||||
return false, c.sendAlert(alertInternalError) | |||||
} | |||||
hs.hello.compressionMethod = compressionNone | |||||
if len(hs.clientHello.serverName) > 0 { | |||||
c.serverName = hs.clientHello.serverName | |||||
} | |||||
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 | |||||
} | |||||
} | } | ||||
hello.compressionMethod = compressionNone | |||||
if clientHello.nextProtoNeg { | |||||
hello.nextProtoNeg = true | |||||
hello.nextProtos = config.NextProtos | |||||
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 { | |||||
c.serverName = clientHello.serverName | |||||
cert = config.getCertificateForName(clientHello.serverName) | |||||
if len(hs.clientHello.serverName) > 0 { | |||||
cert = config.getCertificateForName(hs.clientHello.serverName) | |||||
} | } | ||||
if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { | |||||
hello.ocspStapling = true | |||||
if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { | |||||
hs.hello.ocspStapling = true | |||||
} | } | ||||
finishedHash.Write(hello.marshal()) | |||||
c.writeRecord(recordTypeHandshake, hello.marshal()) | |||||
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled | |||||
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() | |||||
skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello) | |||||
keyAgreement := hs.suite.ka() | |||||
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)) | |||||
for i, asn1Data := range certMsg.certificates { | |||||
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { | |||||
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 | |||||
pub, err = hs.processCertsFromClient(certMsg.certificates) | |||||
if err != nil { | |||||
return err | |||||
} | } | ||||
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 = finishedHash.serverSHA1.Sum(digest) | |||||
digest = hs.finishedHash.serverMD5.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) | |||||
return nil | |||||
} | |||||
func (hs *serverHandshakeState) establishKeys() error { | |||||
c := hs.c | |||||
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := | |||||
keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) | |||||
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 := suite.cipher(clientKey, clientIV, true /* for reading */) | |||||
clientHash := suite.mac(c.vers, clientMAC) | |||||
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 { | |||||
msg, err = c.readHandshake() | |||||
if hs.hello.nextProtoNeg { | |||||
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 | |||||
} | |||||
func (hs *serverHandshakeState) sendSessionTicket() error { | |||||
if !hs.hello.ticketSupported { | |||||
return nil | |||||
} | |||||
c := hs.c | |||||
m := new(newSessionTicketMsg) | |||||
var err error | |||||
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 | |||||
serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) | |||||
serverHash := suite.mac(c.vers, serverMAC) | |||||
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) | |||||
c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) | c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) | ||||
finished := new(finishedMsg) | finished := new(finishedMsg) | ||||
finished.verifyData = finishedHash.serverSum(masterSecret) | |||||
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) | |||||
hs.finishedHash.Write(finished.marshal()) | |||||
c.writeRecord(recordTypeHandshake, finished.marshal()) | c.writeRecord(recordTypeHandshake, finished.marshal()) | ||||
c.handshakeComplete = true | |||||
c.cipherSuite = suite.id | |||||
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 | ||||
} | } |
@@ -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, | |||||
0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9, | |||||
0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd, | |||||
0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d, | |||||
0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f, | |||||
0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39, | |||||
0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, | |||||
0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, | |||||
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, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, | |||||
0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74, | |||||
0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, | |||||
0x23, 0x00, 0x00, | |||||
0x16, 0x03, 0x01, 0x00, 0xed, 0x01, 0x00, 0x00, | |||||
0xe9, 0x03, 0x02, 0x50, 0x5a, 0x1c, 0x90, 0x2b, | |||||
0xc8, 0xf1, 0xd9, 0x4b, 0xd0, 0x18, 0x69, 0xed, | |||||
0x5a, 0xbd, 0x68, 0xf6, 0xf7, 0xe3, 0xf0, 0x6e, | |||||
0xd1, 0xcc, 0xf1, 0x2d, 0x94, 0xa4, 0x01, 0x63, | |||||
0x91, 0xbe, 0xd0, 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, | |||||
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, | |||||
0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b, | |||||
0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14, | |||||
0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66, | |||||
0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2, | |||||
0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a, | |||||
0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c, | |||||
0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77, | |||||
0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0, | |||||
0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53, | |||||
0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf, | |||||
0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c, | |||||
0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f, | |||||
0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6, | |||||
0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba, | |||||
0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05, | |||||
0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01, | |||||
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71, | |||||
0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc, | |||||
0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6, | |||||
0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5, | |||||
0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0, | |||||
0x7f, 0xc1, | |||||
0x82, 0x00, 0x80, 0x45, 0x6d, 0x68, 0x61, 0xb9, | |||||
0x1a, 0xe5, 0xeb, 0x67, 0x22, 0x3b, 0x87, 0x19, | |||||
0x52, 0x86, 0x31, 0x91, 0xee, 0xcd, 0x17, 0x75, | |||||
0xc6, 0x44, 0xaf, 0x23, 0xef, 0xd9, 0xfa, 0xd2, | |||||
0x0b, 0xa2, 0xbb, 0xbf, 0x8b, 0x4b, 0x34, 0x50, | |||||
0xf6, 0x2e, 0x05, 0x09, 0x7e, 0xbf, 0xb3, 0xa6, | |||||
0x10, 0xe3, 0xc3, 0x49, 0x55, 0xa8, 0xdf, 0x6c, | |||||
0xaa, 0xab, 0x11, 0x4c, 0x80, 0x0a, 0x45, 0xf8, | |||||
0x37, 0xbb, 0xd3, 0x18, 0x4e, 0xec, 0x51, 0xbf, | |||||
0x1a, 0xf6, 0x11, 0x1b, 0xcf, 0x2c, 0xaf, 0x5f, | |||||
0x0b, 0x52, 0x4e, 0x92, 0x0c, 0x7a, 0xb2, 0x5d, | |||||
0xe2, 0x1f, 0x83, 0xbe, 0xf5, 0xbf, 0x05, 0xbf, | |||||
0x99, 0xd6, 0x9c, 0x86, 0x47, 0x5e, 0xb4, 0xff, | |||||
0xe7, 0xac, 0xad, 0x1e, 0x3c, 0xaa, 0x91, 0x39, | |||||
0xca, 0xad, 0xc5, 0x54, 0x64, 0x7e, 0xc2, 0x8a, | |||||
0x48, 0xee, 0xb6, 0x4e, 0xf9, 0x33, 0x82, 0x52, | |||||
0xe8, 0xed, 0x48, 0x14, 0x03, 0x01, 0x00, 0x01, | |||||
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc1, 0x2f, | |||||
0x34, 0x03, 0x2a, 0xf2, 0xfd, 0x83, 0x69, 0x23, | |||||
0x8c, 0x9e, 0x66, 0x3b, 0xbb, 0xd1, 0xab, 0xbb, | |||||
0x51, 0x89, 0x27, 0x88, 0x0f, 0x08, 0x3e, 0x00, | |||||
0xdc, 0xc7, 0x47, 0x82, 0x13, 0x34, 0xec, 0xca, | |||||
0x68, 0x6a, | |||||
}, | |||||
{ | |||||
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, | |||||
0x01, 0x00, 0x24, 0xda, 0x61, 0x76, 0x9f, 0x7a, | |||||
0x8a, 0xd0, 0x5f, 0x9b, 0x3d, 0xa7, 0xd5, 0xdd, | |||||
0x95, 0x4b, 0xd4, 0x64, 0x2d, 0x2d, 0x6a, 0x98, | |||||
0x9e, 0xfe, 0x77, 0x76, 0xe3, 0x02, 0x05, 0x0c, | |||||
0xb2, 0xa6, 0x15, 0x82, 0x28, 0x25, 0xc5, 0x17, | |||||
0x03, 0x01, 0x00, 0x21, 0x4e, 0x66, 0x2d, 0x50, | |||||
0x00, 0xa2, 0x44, 0x4d, 0xee, 0x5f, 0x81, 0x67, | |||||
0x21, 0x5d, 0x94, 0xc0, 0xfb, 0xdc, 0xbd, 0xf6, | |||||
0xa8, 0x32, 0x8e, 0x2c, 0x22, 0x58, 0x37, 0xb6, | |||||
0xa3, 0x1e, 0xf8, 0xdd, 0x83, 0x15, 0x03, 0x01, | |||||
0x00, 0x16, 0x68, 0x3b, 0x3a, 0xd0, 0x1e, 0xc4, | |||||
0x5e, 0x97, 0x6a, 0x47, 0x38, 0xfe, 0x17, 0x8e, | |||||
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, | 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, | ||||
0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c, | |||||
0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a, | |||||
0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba, | |||||
0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00, | |||||
0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17, | |||||
0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7, | |||||
0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20, | |||||
0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30, | |||||
0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc, | |||||
0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01, | |||||
0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85, | |||||
0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91, | |||||
0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f, | |||||
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, | |||||
}, | }, | ||||
} | } | ||||
@@ -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 | |||||
// secret, given the lengths of the MAC key, cipher key and IV, as defined in | |||||
// RFC 2246, section 6.3. | |||||
func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { | |||||
// masterFromPreMasterSecret generates the master secret from the pre-master | |||||
// secret. See http://tools.ietf.org/html/rfc5246#section-8.1 | |||||
func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []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) | ||||
@@ -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 || | |||||
clientMACString != test.clientMAC || | |||||
if 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) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -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 | |||||
} |