crypto/tls: initial TLS 1.3 client support

Advertise TLS 1.3 in supported_versions and send a key share for the
first preferred curve. Missing are HRR, certificate validation and
Encrypted Extensions processing (see TODO notes).

For simplicity only a single key share is remembered. This key share
should be updated with a HRR (when implemented).
This commit is contained in:
Peter Wu 2017-11-27 16:05:46 +00:00
parent 634f9a5858
commit 9e25a0a25d
2 changed files with 138 additions and 3 deletions

120
13.go
View File

@ -737,8 +737,122 @@ func (hs *serverHandshakeState) traceErr(err error) {
} }
func (hs *clientHandshakeState) doTLS13Handshake() error { func (hs *clientHandshakeState) doTLS13Handshake() error {
// TODO key exchange phase c := hs.c
// TODO server params phase hash := hashForSuite(hs.suite)
// TODO auth phase hashSize := hash.Size()
serverHello := hs.serverHello
// TODO check if keyshare is unacceptable, raise HRR.
clientKS := hs.hello.keyShares[0]
if serverHello.keyShare.group != clientKS.group {
c.sendAlert(alertIllegalParameter)
return errors.New("bad or missing key share from server")
}
// 0-RTT is not supported yet, so use an empty PSK.
hs.keySchedule.setSecret(nil)
ecdheSecret := deriveECDHESecret(serverHello.keyShare, hs.privateKey)
if ecdheSecret == nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: bad ECDHE server share")
}
// Calculate handshake secrets.
hs.keySchedule.setSecret(ecdheSecret)
clientCipher, clientHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeClient)
serverCipher, serverHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeServer)
if c.hand.Len() > 0 {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: unexpected data after Server Hello")
}
// Do not change the sender key yet, the server must authenticate first.
c.in.setCipher(c.vers, serverCipher)
// Calculate MAC key for Finished messages.
serverFinishedKey := hkdfExpandLabel(hash, serverHandshakeSecret, nil, "finished", hashSize)
clientFinishedKey := hkdfExpandLabel(hash, clientHandshakeSecret, nil, "finished", hashSize)
msg, err := c.readHandshake()
if err != nil {
return err
}
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(encryptedExtensions, msg)
}
hs.keySchedule.write(encryptedExtensions.marshal())
// TODO process encryptedExtensions
// PSKs are not supported, so receive Certificate message.
msg, err = c.readHandshake()
if err != nil {
return err
}
// TODO handle optional CertificateRequest
certMsg, ok := msg.(*certificateMsg13)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certMsg, msg)
}
hs.keySchedule.write(certMsg.marshal())
// TODO process Certificate
// Receive CertificateVerify message.
msg, err = c.readHandshake()
if err != nil {
return err
}
certVerifyMsg, ok := msg.(*certificateVerifyMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certVerifyMsg, msg)
}
// TODO process CertificateVerify
hs.keySchedule.write(certVerifyMsg.marshal())
// Receive Finished message.
msg, err = c.readHandshake()
if err != nil {
return err
}
serverFinished, ok := msg.(*finishedMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(serverFinished, msg)
}
// Validate server Finished hash.
expectedVerifyData := hmacOfSum(hash, hs.keySchedule.transcriptHash, serverFinishedKey)
if subtle.ConstantTimeCompare(expectedVerifyData, serverFinished.verifyData) != 1 {
c.sendAlert(alertDecryptError)
return errors.New("tls: server's Finished message is incorrect")
}
hs.keySchedule.write(serverFinished.marshal())
// Server has authenticated itself, change our cipher.
c.out.setCipher(c.vers, clientCipher)
// TODO optionally send a client cert
// Send Finished
verifyData := hmacOfSum(hash, hs.keySchedule.transcriptHash, clientFinishedKey)
clientFinished := &finishedMsg{
verifyData: verifyData,
}
if _, err := c.writeRecord(recordTypeHandshake, clientFinished.marshal()); err != nil {
return err
}
// Calculate application traffic secrets.
hs.keySchedule.setSecret(nil) // derive master secret
// TODO store initial traffic secret key for KeyUpdate
clientCipher, _ = hs.keySchedule.prepareCipher(secretApplicationClient)
serverCipher, _ = hs.keySchedule.prepareCipher(secretApplicationServer)
c.out.setCipher(c.vers, clientCipher)
if c.hand.Len() > 0 {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: unexpected data after handshake")
}
c.in.setCipher(c.vers, serverCipher)
return nil return nil
} }

View File

@ -33,6 +33,7 @@ type clientHandshakeState struct {
// TLS 1.3 fields // TLS 1.3 fields
keySchedule *keySchedule13 keySchedule *keySchedule13
privateKey []byte
} }
func makeClientHello(config *Config) (*clientHelloMsg, error) { func makeClientHello(config *Config) (*clientHelloMsg, error) {
@ -99,6 +100,13 @@ NextCipherSuite:
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms
} }
if hello.vers >= VersionTLS13 {
// Version preference is indicated via "supported_extensions",
// set legacy_version to TLS 1.2 for backwards compatibility.
hello.vers = VersionTLS12
hello.supportedVersions = config.getSupportedVersions()
}
return hello, nil return hello, nil
} }
@ -177,6 +185,19 @@ func (c *Conn) clientHandshake() error {
session: session, session: session,
} }
var clientKS keyShare
if c.config.maxVersion() >= VersionTLS13 {
// Create one keyshare for the first default curve. If it is not
// appropriate, the server should raise a HRR.
defaultGroup := c.config.curvePreferences()[0]
hs.privateKey, clientKS, err = c.config.generateKeyShare(defaultGroup)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
hello.keyShares = []keyShare{clientKS}
}
if err = hs.handshake(); err != nil { if err = hs.handshake(); err != nil {
return err return err
} }