tris: implement SSLKEYLOGFILE for TLS 1.3 server

This makes it easier to validate the handshake contents using Wireshark.
This commit is contained in:
Peter Wu 2017-09-18 16:50:43 +01:00
parent 6f580251ca
commit 3107d575a8
5 changed files with 36 additions and 9 deletions

27
13.go
View File

@ -45,12 +45,20 @@ type keySchedule13 struct {
transcriptHash hash.Hash // uses the cipher suite hash algo transcriptHash hash.Hash // uses the cipher suite hash algo
secret []byte // Current secret as used for Derive-Secret secret []byte // Current secret as used for Derive-Secret
handshakeCtx []byte // cached handshake context, invalidated on updates. handshakeCtx []byte // cached handshake context, invalidated on updates.
clientRandom []byte // Used for keylogging, nil if keylogging is disabled.
config *Config // Used for KeyLogWriter callback, nil if keylogging is disabled.
} }
func newKeySchedule13(suite *cipherSuite) *keySchedule13 { func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 {
if config.KeyLogWriter == nil {
clientRandom = nil
config = nil
}
return &keySchedule13{ return &keySchedule13{
suite: suite, suite: suite,
transcriptHash: hashForSuite(suite).New(), transcriptHash: hashForSuite(suite).New(),
clientRandom: clientRandom,
config: config,
} }
} }
@ -68,20 +76,25 @@ func (ks *keySchedule13) write(data []byte) {
ks.transcriptHash.Write(data) ks.transcriptHash.Write(data)
} }
func (ks *keySchedule13) getLabel(secretLabel secretLabel) (label string) { func (ks *keySchedule13) getLabel(secretLabel secretLabel) (label, keylogType string) {
switch secretLabel { switch secretLabel {
case secretResumptionPskBinder: case secretResumptionPskBinder:
label = "resumption psk binder key" label = "resumption psk binder key"
case secretEarlyClient: case secretEarlyClient:
label = "client early traffic secret" label = "client early traffic secret"
keylogType = "CLIENT_EARLY_TRAFFIC_SECRET"
case secretHandshakeClient: case secretHandshakeClient:
label = "client handshake traffic secret" label = "client handshake traffic secret"
keylogType = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
case secretHandshakeServer: case secretHandshakeServer:
label = "server handshake traffic secret" label = "server handshake traffic secret"
keylogType = "SERVER_HANDSHAKE_TRAFFIC_SECRET"
case secretApplicationClient: case secretApplicationClient:
label = "client application traffic secret" label = "client application traffic secret"
keylogType = "CLIENT_TRAFFIC_SECRET_0"
case secretApplicationServer: case secretApplicationServer:
label = "server application traffic secret" label = "server application traffic secret"
keylogType = "SERVER_TRAFFIC_SECRET_0"
case secretResumption: case secretResumption:
label = "resumption master secret" label = "resumption master secret"
} }
@ -90,12 +103,16 @@ func (ks *keySchedule13) getLabel(secretLabel secretLabel) (label string) {
// deriveSecret returns the secret derived from the handshake context and label. // deriveSecret returns the secret derived from the handshake context and label.
func (ks *keySchedule13) deriveSecret(secretLabel secretLabel) []byte { func (ks *keySchedule13) deriveSecret(secretLabel secretLabel) []byte {
label := ks.getLabel(secretLabel) label, keylogType := ks.getLabel(secretLabel)
if ks.handshakeCtx == nil { if ks.handshakeCtx == nil {
ks.handshakeCtx = ks.transcriptHash.Sum(nil) ks.handshakeCtx = ks.transcriptHash.Sum(nil)
} }
hash := hashForSuite(ks.suite) hash := hashForSuite(ks.suite)
return hkdfExpandLabel(hash, ks.secret, ks.handshakeCtx, label, hash.Size()) secret := hkdfExpandLabel(hash, ks.secret, ks.handshakeCtx, label, hash.Size())
if keylogType != "" && ks.config != nil {
ks.config.writeKeyLog(keylogType, ks.clientRandom, secret)
}
return secret
} }
func (ks *keySchedule13) prepareCipher(secretLabel secretLabel) (interface{}, []byte) { func (ks *keySchedule13) prepareCipher(secretLabel secretLabel) (interface{}, []byte) {
@ -146,7 +163,7 @@ CurvePreferenceLoop:
hash := hashForSuite(hs.suite) hash := hashForSuite(hs.suite)
hashSize := hash.Size() hashSize := hash.Size()
hs.keySchedule = newKeySchedule13(hs.suite) hs.keySchedule = newKeySchedule13(hs.suite, config, hs.clientHello.random)
// Check for PSK and update key schedule with new early secret key // Check for PSK and update key schedule with new early secret key
isResumed, pskAlert := hs.checkPSK() isResumed, pskAlert := hs.checkPSK()

View File

@ -4,6 +4,7 @@ import (
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -30,12 +31,21 @@ func startServer(addr string, rsa, offer0RTT, accept0RTT bool) {
if offer0RTT { if offer0RTT {
Max0RTTDataSize = 100 * 1024 Max0RTTDataSize = 100 * 1024
} }
var keyLogWriter io.Writer
if keyLogFile := os.Getenv("SSLKEYLOGFILE"); keyLogFile != "" {
keyLogWriter, err = os.OpenFile(keyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
log.Fatalf("Cannot open keylog file: %v", err)
}
log.Println("Enabled keylog")
}
s := &http.Server{ s := &http.Server{
Addr: addr, Addr: addr,
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert}, Certificates: []tls.Certificate{cert},
Max0RTTDataSize: Max0RTTDataSize, Max0RTTDataSize: Max0RTTDataSize,
Accept0RTTData: accept0RTT, Accept0RTTData: accept0RTT,
KeyLogWriter: keyLogWriter,
GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) {
// If we send the first flight too fast, NSS sends empty early data. // If we send the first flight too fast, NSS sends empty early data.
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)

View File

@ -907,12 +907,12 @@ func (c *Config) BuildNameToCertificate() {
// writeKeyLog logs client random and master secret if logging was enabled by // writeKeyLog logs client random and master secret if logging was enabled by
// setting c.KeyLogWriter. // setting c.KeyLogWriter.
func (c *Config) writeKeyLog(clientRandom, masterSecret []byte) error { func (c *Config) writeKeyLog(what string, clientRandom, masterSecret []byte) error {
if c.KeyLogWriter == nil { if c.KeyLogWriter == nil {
return nil return nil
} }
logLine := []byte(fmt.Sprintf("CLIENT_RANDOM %x %x\n", clientRandom, masterSecret)) logLine := []byte(fmt.Sprintf("%s %x %x\n", what, clientRandom, masterSecret))
writerMutex.Lock() writerMutex.Lock()
_, err := c.KeyLogWriter.Write(logLine) _, err := c.KeyLogWriter.Write(logLine)

View File

@ -469,7 +469,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
if err := c.config.writeKeyLog(hs.hello.random, hs.masterSecret); err != nil { if err := c.config.writeKeyLog("CLIENT_RANDOM", hs.hello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + err.Error()) return errors.New("tls: failed to write to key log: " + err.Error())
} }

View File

@ -573,7 +573,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
return err return err
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
if err := c.config.writeKeyLog(hs.clientHello.random, hs.masterSecret); err != nil { if err := c.config.writeKeyLog("CLIENT_RANDOM", hs.clientHello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
} }