Skip to content

Commit

Permalink
crypto/tls: implement TLS 1.3 client handshake (base)
Browse files Browse the repository at this point in the history
Implement a basic TLS 1.3 client handshake, only enabled if explicitly
requested with MaxVersion.

This CL intentionally leaves for future CLs:
  - PSK modes and resumption
  - client authentication
  - post-handshake messages
  - downgrade protection
  - KeyLogWriter support

Updates #9671

Change-Id: Ieb6130fb6f25aea4f0d39e3a2448dfc942e1de7a
Reviewed-on: https://go-review.googlesource.com/c/146559
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
  • Loading branch information
FiloSottile committed Nov 2, 2018
1 parent ed74f78 commit 2c3ff7b
Show file tree
Hide file tree
Showing 25 changed files with 2,059 additions and 347 deletions.
4 changes: 4 additions & 0 deletions alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
alertInappropriateFallback alert = 86
alertUserCanceled alert = 90
alertNoRenegotiation alert = 100
alertMissingExtension alert = 109
alertUnsupportedExtension alert = 110
alertNoApplicationProtocol alert = 120
)

Expand Down Expand Up @@ -65,6 +67,8 @@ var alertText = map[alert]string{
alertInappropriateFallback: "inappropriate fallback",
alertUserCanceled: "user canceled",
alertNoRenegotiation: "no renegotiation",
alertMissingExtension: "missing extension",
alertUnsupportedExtension: "unsupported extension",
alertNoApplicationProtocol: "no application protocol",
}

Expand Down
28 changes: 27 additions & 1 deletion auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"encoding/asn1"
"errors"
"fmt"
"hash"
"io"
)

// pickSignatureAlgorithm selects a signature algorithm that is compatible with
Expand Down Expand Up @@ -43,7 +45,7 @@ func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []S
if !isSupportedSignatureAlgorithm(sigAlg, ourSigAlgs) {
continue
}
hashAlg, err := lookupTLSHash(sigAlg)
hashAlg, err := hashFromSignatureScheme(sigAlg)
if err != nil {
panic("tls: supported signature algorithm has an unknown hash function")
}
Expand Down Expand Up @@ -105,3 +107,27 @@ func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc c
}
return nil
}

const (
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
)

var signaturePadding = []byte{
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
}

// writeSignedMessage writes the content to be signed by certificate keys in TLS
// 1.3 to sigHash. See RFC 8446, Section 4.4.3.
func writeSignedMessage(sigHash io.Writer, context string, transcript hash.Hash) {
sigHash.Write(signaturePadding)
io.WriteString(sigHash, context)
sigHash.Write(transcript.Sum(nil))
}
14 changes: 14 additions & 0 deletions cipher_suites.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,20 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
return nil
}

func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
for _, id := range have {
if id == want {
for _, suite := range cipherSuitesTLS13 {
if suite.id == want {
return suite
}
}
return nil
}
}
return nil
}

// A list of cipher suite IDs that are, or have been, implemented by this
// package.
//
Expand Down
32 changes: 20 additions & 12 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,8 @@ const (

// Certificate types (for certificateRequestMsg)
const (
certTypeRSASign = 1 // A certificate containing an RSA key
certTypeDSSSign = 2 // A certificate containing a DSA key
certTypeRSAFixedDH = 3 // A certificate containing a static DH key
certTypeDSSFixedDH = 4 // A certificate containing a static DH key

// See RFC 4492 sections 3 and 5.5.
certTypeECDSASign = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
certTypeRSAFixedECDH = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.

// Rest of these are reserved by the TLS spec
certTypeRSASign = 1
certTypeECDSASign = 64 // RFC 4492, Section 5.5
)

// Signature algorithms (for internal signaling use). Starting at 16 to avoid overlap with
Expand Down Expand Up @@ -188,6 +179,15 @@ var supportedSignatureAlgorithms = []SignatureScheme{
ECDSAWithSHA1,
}

// helloRetryRequestRandom is set as the Random value of a ServerHello
// to signal that the message is actually a HelloRetryRequest.
var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3.
0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
}

// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
Version uint16 // TLS version used by the connection (e.g. VersionTLS12)
Expand Down Expand Up @@ -356,6 +356,8 @@ type CertificateRequestInfo struct {
// handshake and application data flow is not permitted so renegotiation can
// only be used with protocols that synchronise with the renegotiation, such as
// HTTPS.
//
// Renegotiation is not defined in TLS 1.3.
type RenegotiationSupport int

const (
Expand Down Expand Up @@ -530,7 +532,8 @@ type Config struct {

// CurvePreferences contains the elliptic curves that will be used in
// an ECDHE handshake, in preference order. If empty, the default will
// be used.
// be used. The client will use the first preference as the type for
// its key share in TLS 1.3. This may change in the future.
CurvePreferences []CurveID

// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
Expand Down Expand Up @@ -716,6 +719,7 @@ func (c *Config) cipherSuites() []uint16 {
}

var supportedVersions = []uint16{
VersionTLS13,
VersionTLS12,
VersionTLS11,
VersionTLS10,
Expand All @@ -735,6 +739,10 @@ func (c *Config) supportedVersions(isClient bool) []uint16 {
if isClient && v < VersionTLS10 {
continue
}
// TLS 1.3 is only supported if explicitly requested while in development.
if v == VersionTLS13 && (!isClient || c == nil || c.MaxVersion != VersionTLS13) {
continue
}
versions = append(versions, v)
}
return versions
Expand Down
19 changes: 17 additions & 2 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ type halfConn struct {

nextCipher interface{} // next encryption state
nextMac macFunction // next MAC algorithm

trafficSecret []byte // current TLS 1.3 traffic secret
}

func (hc *halfConn) setErrorLocked(err error) error {
Expand All @@ -172,7 +174,7 @@ func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac ma
// changeCipherSpec changes the encryption and MAC states
// to the ones previously passed to prepareCipherSpec.
func (hc *halfConn) changeCipherSpec() error {
if hc.nextCipher == nil {
if hc.nextCipher == nil || hc.version == VersionTLS13 {
return alertInternalError
}
hc.cipher = hc.nextCipher
Expand All @@ -185,6 +187,15 @@ func (hc *halfConn) changeCipherSpec() error {
return nil
}

func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) {
hc.trafficSecret = secret
key, iv := suite.trafficKey(secret)
hc.cipher = suite.aead(key, iv)
for i := range hc.seq {
hc.seq[i] = 0
}
}

// incSeq increments the sequence number.
func (hc *halfConn) incSeq() {
for i := 7; i >= 0; i-- {
Expand Down Expand Up @@ -1110,6 +1121,10 @@ func (c *Conn) Write(b []byte) (int, error) {

// handleRenegotiation processes a HelloRequest handshake message.
func (c *Conn) handleRenegotiation() error {
if c.vers == VersionTLS13 {
return errors.New("tls: internal error: unexpected renegotiation")
}

msg, err := c.readHandshake()
if err != nil {
return err
Expand Down Expand Up @@ -1299,7 +1314,7 @@ func (c *Conn) Handshake() error {
}

if c.handshakeErr == nil && !c.handshakeComplete() {
panic("handshake should have had a result.")
panic("tls: internal error: handshake should have had a result")
}

return c.handshakeErr
Expand Down
Loading

0 comments on commit 2c3ff7b

Please sign in to comment.