From 9e25a0a25d3ee76f9000a9b7caf0ca696ac05b32 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 27 Nov 2017 16:05:46 +0000 Subject: [PATCH] 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). --- 13.go | 120 ++++++++++++++++++++++++++++++++++++++++++-- handshake_client.go | 21 ++++++++ 2 files changed, 138 insertions(+), 3 deletions(-) diff --git a/13.go b/13.go index c5f48b1..73199a7 100644 --- a/13.go +++ b/13.go @@ -737,8 +737,122 @@ func (hs *serverHandshakeState) traceErr(err error) { } func (hs *clientHandshakeState) doTLS13Handshake() error { - // TODO key exchange phase - // TODO server params phase - // TODO auth phase + c := hs.c + hash := hashForSuite(hs.suite) + 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 } diff --git a/handshake_client.go b/handshake_client.go index 821a446..bb6e591 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -33,6 +33,7 @@ type clientHandshakeState struct { // TLS 1.3 fields keySchedule *keySchedule13 + privateKey []byte } func makeClientHello(config *Config) (*clientHelloMsg, error) { @@ -99,6 +100,13 @@ NextCipherSuite: 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 } @@ -177,6 +185,19 @@ func (c *Conn) clientHandshake() error { 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 { return err }