diff --git a/common.go b/common.go index 9078b63..78c762f 100644 --- a/common.go +++ b/common.go @@ -14,6 +14,7 @@ import ( "fmt" "io" "math/big" + "net" "strings" "sync" "time" @@ -238,6 +239,32 @@ type ClientHelloInfo struct { // is being used (see // http://tools.ietf.org/html/rfc4492#section-5.1.2). SupportedPoints []uint8 + + // SignatureSchemes lists the signature and hash schemes that the client + // is willing to verify. SignatureSchemes is set only if the Signature + // Algorithms Extension is being used (see + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1). + SignatureSchemes []uint16 + + // SupportedProtos lists the application protocols supported by the client. + // SupportedProtos is set only if the Application-Layer Protocol + // Negotiation Extension is being used (see + // https://tools.ietf.org/html/rfc7301#section-3.1). + // + // Servers can select a protocol by setting Config.NextProtos in a + // GetConfigForClient return value. + SupportedProtos []string + + // SupportedVersions lists the TLS versions supported by the client. + // For TLS versions less than 1.3, this is extrapolated from the max + // version advertised by the client, so values other than the greatest + // might be rejected if used. + SupportedVersions []uint16 + + // Conn is the underlying net.Conn for the connection. Do not read + // from, or write to, this connection; that will cause the TLS + // connection to fail. + Conn net.Conn } // RenegotiationSupport enumerates the different levels of support for TLS diff --git a/handshake_server.go b/handshake_server.go index 724ed71..0cccd65 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -19,19 +19,20 @@ import ( // serverHandshakeState contains details of a server handshake in progress. // It's discarded once the handshake has completed. type serverHandshakeState struct { - c *Conn - clientHello *clientHelloMsg - hello *serverHelloMsg - suite *cipherSuite - ellipticOk bool - ecdsaOk bool - rsaDecryptOk bool - rsaSignOk bool - sessionState *sessionState - finishedHash finishedHash - masterSecret []byte - certsFromClient [][]byte - cert *Certificate + c *Conn + clientHello *clientHelloMsg + hello *serverHelloMsg + suite *cipherSuite + ellipticOk bool + ecdsaOk bool + rsaDecryptOk bool + rsaSignOk bool + sessionState *sessionState + finishedHash finishedHash + masterSecret []byte + certsFromClient [][]byte + cert *Certificate + cachedClientHelloInfo *ClientHelloInfo } // serverHandshake performs a TLS handshake as a server. @@ -123,15 +124,8 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { return false, unexpectedMessageError(hs.clientHello, msg) } - clientHelloInfo := &ClientHelloInfo{ - CipherSuites: hs.clientHello.cipherSuites, - ServerName: hs.clientHello.serverName, - SupportedCurves: hs.clientHello.supportedCurves, - SupportedPoints: hs.clientHello.supportedPoints, - } - if c.config.GetConfigForClient != nil { - if newConfig, err := c.config.GetConfigForClient(clientHelloInfo); err != nil { + if newConfig, err := c.config.GetConfigForClient(hs.clientHelloInfo()); err != nil { c.sendAlert(alertInternalError) return false, err } else if newConfig != nil { @@ -223,7 +217,7 @@ Curves: } } - hs.cert, err = c.config.getCertificate(clientHelloInfo) + hs.cert, err = c.config.getCertificate(hs.clientHelloInfo()) if err != nil { c.sendAlert(alertInternalError) return false, err @@ -812,3 +806,37 @@ func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites } return false } + +// suppVersArray is the backing array of ClientHelloInfo.SupportedVersions +var suppVersArray = [...]uint16{VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30} + +func (hs *serverHandshakeState) clientHelloInfo() *ClientHelloInfo { + if hs.cachedClientHelloInfo != nil { + return hs.cachedClientHelloInfo + } + + var supportedVersions []uint16 + if hs.clientHello.vers > VersionTLS12 { + supportedVersions = suppVersArray[:] + } else if hs.clientHello.vers >= VersionSSL30 { + supportedVersions = suppVersArray[VersionTLS12-hs.clientHello.vers:] + } + + signatureSchemes := make([]uint16, 0, len(hs.clientHello.signatureAndHashes)) + for _, sah := range hs.clientHello.signatureAndHashes { + signatureSchemes = append(signatureSchemes, uint16(sah.hash)<<8+uint16(sah.signature)) + } + + hs.cachedClientHelloInfo = &ClientHelloInfo{ + CipherSuites: hs.clientHello.cipherSuites, + ServerName: hs.clientHello.serverName, + SupportedCurves: hs.clientHello.supportedCurves, + SupportedPoints: hs.clientHello.supportedPoints, + SignatureSchemes: signatureSchemes, + SupportedProtos: hs.clientHello.alpnProtocols, + SupportedVersions: supportedVersions, + Conn: hs.c.conn, + } + + return hs.cachedClientHelloInfo +}