Add support for sending TLS 1.3 tickets in Go.
Also parse out the ticket lifetime which was previously ignored. BUG=75 Change-Id: I6ba92017bd4f1b31da55fd85d2af529fd592de11 Reviewed-on: https://boringssl-review.googlesource.com/8871 Reviewed-by: Nick Harper <nharper@chromium.org> Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
parent
4528e2b477
commit
58104889ad
@ -189,6 +189,13 @@ const (
|
||||
SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002
|
||||
)
|
||||
|
||||
// TicketFlags values (see draft-ietf-tls-tls13-14, section 4.4.1)
|
||||
const (
|
||||
ticketAllowEarlyData = 1
|
||||
ticketAllowDHEResumption = 2
|
||||
ticketAllowPSKResumption = 4
|
||||
)
|
||||
|
||||
// ConnectionState records basic TLS details about the connection.
|
||||
type ConnectionState struct {
|
||||
Version uint16 // TLS version used by the connection (e.g. VersionTLS12)
|
||||
|
@ -59,6 +59,7 @@ type Conn struct {
|
||||
|
||||
clientRandom, serverRandom [32]byte
|
||||
exporterSecret []byte
|
||||
resumptionSecret []byte
|
||||
|
||||
clientProtocol string
|
||||
clientProtocolFallback bool
|
||||
@ -1143,7 +1144,9 @@ func (c *Conn) readHandshake() (interface{}, error) {
|
||||
case typeHelloRetryRequest:
|
||||
m = new(helloRetryRequestMsg)
|
||||
case typeNewSessionTicket:
|
||||
m = new(newSessionTicketMsg)
|
||||
m = &newSessionTicketMsg{
|
||||
version: c.vers,
|
||||
}
|
||||
case typeEncryptedExtensions:
|
||||
m = new(encryptedExtensionsMsg)
|
||||
case typeCertificate:
|
||||
@ -1582,3 +1585,39 @@ func (c *Conn) noRenegotiationInfo() bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Conn) SendNewSessionTicket() error {
|
||||
if c.isClient || c.vers < VersionTLS13 {
|
||||
return errors.New("tls: cannot send post-handshake NewSessionTicket")
|
||||
}
|
||||
|
||||
var peerCertificatesRaw [][]byte
|
||||
for _, cert := range c.peerCertificates {
|
||||
peerCertificatesRaw = append(peerCertificatesRaw, cert.Raw)
|
||||
}
|
||||
state := sessionState{
|
||||
vers: c.vers,
|
||||
cipherSuite: c.cipherSuite.id,
|
||||
masterSecret: c.resumptionSecret,
|
||||
certificates: peerCertificatesRaw,
|
||||
}
|
||||
|
||||
// TODO(davidben): Allow configuring these values.
|
||||
m := &newSessionTicketMsg{
|
||||
version: c.vers,
|
||||
ticketLifetime: uint32(24 * time.Hour / time.Second),
|
||||
ticketFlags: ticketAllowDHEResumption | ticketAllowPSKResumption,
|
||||
}
|
||||
if !c.config.Bugs.SendEmptySessionTicket {
|
||||
var err error
|
||||
m.ticket, err = c.encryptTicket(&state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.out.Lock()
|
||||
defer c.out.Unlock()
|
||||
_, err := c.writeRecord(recordTypeHandshake, m.marshal())
|
||||
return err
|
||||
}
|
||||
|
@ -1716,49 +1716,74 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
|
||||
|
||||
type newSessionTicketMsg struct {
|
||||
raw []byte
|
||||
version uint16
|
||||
ticketLifetime uint32
|
||||
ticketFlags uint32
|
||||
ticketAgeAdd uint32
|
||||
ticket []byte
|
||||
}
|
||||
|
||||
func (m *newSessionTicketMsg) marshal() (x []byte) {
|
||||
func (m *newSessionTicketMsg) marshal() []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)
|
||||
ticketMsg := newByteBuilder()
|
||||
ticketMsg.addU8(typeNewSessionTicket)
|
||||
body := ticketMsg.addU24LengthPrefixed()
|
||||
body.addU32(m.ticketLifetime)
|
||||
if m.version >= VersionTLS13 {
|
||||
body.addU32(m.ticketFlags)
|
||||
body.addU32(m.ticketAgeAdd)
|
||||
// Send no extensions.
|
||||
//
|
||||
// TODO(davidben): Add an option to send a custom extension to
|
||||
// test we correctly ignore unknown ones.
|
||||
body.addU16(0)
|
||||
}
|
||||
ticket := body.addU16LengthPrefixed()
|
||||
ticket.addBytes(m.ticket)
|
||||
|
||||
m.raw = x
|
||||
|
||||
return
|
||||
m.raw = ticketMsg.finish()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
|
||||
m.raw = data
|
||||
|
||||
if len(data) < 8 {
|
||||
return false
|
||||
}
|
||||
m.ticketLifetime = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
|
||||
data = data[8:]
|
||||
|
||||
if m.version >= VersionTLS13 {
|
||||
if len(data) < 10 {
|
||||
return false
|
||||
}
|
||||
m.ticketFlags = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
||||
m.ticketAgeAdd = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
|
||||
extsLength := int(data[8])<<8 + int(data[9])
|
||||
data = data[10:]
|
||||
if len(data) < extsLength {
|
||||
return false
|
||||
}
|
||||
data = data[extsLength:]
|
||||
}
|
||||
|
||||
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
||||
if uint32(len(data))-4 != length {
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
ticketLen := int(data[0])<<8 + int(data[1])
|
||||
if len(data)-2 != ticketLen {
|
||||
return false
|
||||
}
|
||||
if m.version >= VersionTLS13 && ticketLen == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
ticketLen := int(data[8])<<8 + int(data[9])
|
||||
if len(data)-10 != ticketLen {
|
||||
return false
|
||||
}
|
||||
|
||||
m.ticket = data[10:]
|
||||
m.ticket = data[2:]
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -666,10 +666,19 @@ Curves:
|
||||
// Switch to application data keys on read.
|
||||
c.in.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite), c.vers)
|
||||
|
||||
// TODO(davidben): Derive and save the resumption master secret for receiving tickets.
|
||||
// TODO(davidben): Save the traffic secret for KeyUpdate.
|
||||
c.cipherSuite = hs.suite
|
||||
c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
|
||||
c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
|
||||
|
||||
// TODO(davidben): Allow configuring the number of tickets sent for
|
||||
// testing.
|
||||
if !c.config.SessionTicketsDisabled {
|
||||
ticketCount := 2
|
||||
for i := 0; i < ticketCount; i++ {
|
||||
c.SendNewSessionTicket()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user