Browse Source

tris: add SessionTicketSealer

tls13
Filippo Valsorda 7 years ago
committed by Peter Wu
parent
commit
7f449cbaa7
4 changed files with 49 additions and 7 deletions
  1. +24
    -6
      13.go
  2. +6
    -1
      common.go
  3. +17
    -0
      ticket.go
  4. +2
    -0
      tls_test.go

+ 24
- 6
13.go View File

@@ -191,7 +191,7 @@ func (hs *serverHandshakeState) readClientFinished13() error {
// happens, even if the Read call might do more work. // happens, even if the Read call might do more work.
c.confirmMutex.Unlock() c.confirmMutex.Unlock()


return hs.sendSessionTicket13()
return hs.sendSessionTicket13() // TODO: do in a goroutine
} }


func (hs *serverHandshakeState) sendCertificate13() error { func (hs *serverHandshakeState) sendCertificate13() error {
@@ -460,12 +460,20 @@ func (hs *serverHandshakeState) checkPSK() (earlySecret []byte, alert alert) {
hashSize := hash.Size() hashSize := hash.Size()
for i := range hs.clientHello.psks { for i := range hs.clientHello.psks {
sessionTicket := append([]uint8{}, hs.clientHello.psks[i].identity...) sessionTicket := append([]uint8{}, hs.clientHello.psks[i].identity...)
serializedTicket, _ := hs.c.decryptTicket(sessionTicket)
if serializedTicket == nil {
continue
if hs.c.config.SessionTicketSealer != nil {
var ok bool
sessionTicket, ok = hs.c.config.SessionTicketSealer.Unseal(hs.clientHelloInfo(), sessionTicket)
if !ok {
continue
}
} else {
sessionTicket, _ = hs.c.decryptTicket(sessionTicket)
if sessionTicket == nil {
continue
}
} }
s := &sessionState13{} s := &sessionState13{}
if s.unmarshal(serializedTicket) != alertSuccess {
if s.unmarshal(sessionTicket) != alertSuccess {
continue continue
} }
if s.vers != hs.c.vers { if s.vers != hs.c.vers {
@@ -565,11 +573,21 @@ func (hs *serverHandshakeState) sendSessionTicket13() error {
} }


for i := 0; i < numSessionTickets; i++ { for i := 0; i < numSessionTickets; i++ {
ticket, err := c.encryptTicket(sessionState.marshal())
ticket := sessionState.marshal()
var err error
if c.config.SessionTicketSealer != nil {
cs := c.ConnectionState()
ticket, err = c.config.SessionTicketSealer.Seal(&cs, ticket)
} else {
ticket, err = c.encryptTicket(ticket)
}
if err != nil { if err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
} }
if ticket == nil {
continue
}
ticketMsg := &newSessionTicketMsg13{ ticketMsg := &newSessionTicketMsg13{
lifetime: 24 * 3600, // TODO(filippo) lifetime: 24 * 3600, // TODO(filippo)
maxEarlyDataLength: c.config.Max0RTTDataSize, maxEarlyDataLength: c.config.Max0RTTDataSize,


+ 6
- 1
common.go View File

@@ -592,6 +592,10 @@ type Config struct {
// See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-2.3. // See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-2.3.
Accept0RTTData bool Accept0RTTData bool


// SessionTicketSealer, if not nil, is used to wrap and unwrap
// session tickets, instead of SessionTicketKey.
SessionTicketSealer SessionTicketSealer

serverInitOnce sync.Once // guards calling (*Config).serverInit serverInitOnce sync.Once // guards calling (*Config).serverInit


// mutex protects sessionTicketKeys. // mutex protects sessionTicketKeys.
@@ -668,6 +672,7 @@ func (c *Config) Clone() *Config {
KeyLogWriter: c.KeyLogWriter, KeyLogWriter: c.KeyLogWriter,
Accept0RTTData: c.Accept0RTTData, Accept0RTTData: c.Accept0RTTData,
Max0RTTDataSize: c.Max0RTTDataSize, Max0RTTDataSize: c.Max0RTTDataSize,
SessionTicketSealer: c.SessionTicketSealer,
sessionTicketKeys: sessionTicketKeys, sessionTicketKeys: sessionTicketKeys,
} }
} }
@@ -676,7 +681,7 @@ func (c *Config) Clone() *Config {
// returned by a GetConfigForClient callback then the argument should be the // returned by a GetConfigForClient callback then the argument should be the
// Config that was passed to Server, otherwise it should be nil. // Config that was passed to Server, otherwise it should be nil.
func (c *Config) serverInit(originalConfig *Config) { func (c *Config) serverInit(originalConfig *Config) {
if c.SessionTicketsDisabled || len(c.ticketKeys()) != 0 {
if c.SessionTicketsDisabled || len(c.ticketKeys()) != 0 || c.SessionTicketSealer != nil {
return return
} }




+ 17
- 0
ticket.go View File

@@ -15,6 +15,23 @@ import (
"io" "io"
) )


// A SessionTicketSealer provides a way to securely encapsulate
// session state for storage on the client. All methods are safe for
// concurrent use.
type SessionTicketSealer interface {
// Seal returns a session ticket value that can be later passed to Unseal
// to recover the content, usually by encrypting it. The ticket will be sent
// to the client to be stored, and will be sent back in plaintext, so it can
// be read and modified by an attacker.
Seal(cs *ConnectionState, content []byte) (ticket []byte, err error)

// Unseal returns a session ticket contents. The ticket can't be safely
// assumed to have been generated by Seal.
// If unable to unseal the ticket, the connection will proceed with a
// complete handshake.
Unseal(chi *ClientHelloInfo, ticket []byte) (content []byte, success bool)
}

// sessionState contains the information that is serialized into a session // sessionState contains the information that is serialized into a session
// ticket in order to later resume a connection. // ticket in order to later resume a connection.
type sessionState struct { type sessionState struct {


+ 2
- 0
tls_test.go View File

@@ -656,6 +656,8 @@ func TestCloneNonFuncFields(t *testing.T) {
f.Set(reflect.ValueOf(RenegotiateOnceAsClient)) f.Set(reflect.ValueOf(RenegotiateOnceAsClient))
case "Max0RTTDataSize": case "Max0RTTDataSize":
f.Set(reflect.ValueOf(uint32(0))) f.Set(reflect.ValueOf(uint32(0)))
case "SessionTicketSealer":
// TODO
default: default:
t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) t.Errorf("all fields must be accounted for, but saw unknown field %q", fn)
} }


Loading…
Cancel
Save