diff options
Diffstat (limited to 'libgo/go/crypto/tls/common.go')
-rw-r--r-- | libgo/go/crypto/tls/common.go | 230 |
1 files changed, 218 insertions, 12 deletions
diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go index 9fc74200820..276d1761ea0 100644 --- a/libgo/go/crypto/tls/common.go +++ b/libgo/go/crypto/tls/common.go @@ -7,6 +7,7 @@ package tls import ( "container/list" "crypto" + "crypto/internal/cipherhw" "crypto/rand" "crypto/sha512" "crypto/x509" @@ -14,6 +15,7 @@ import ( "fmt" "io" "math/big" + "net" "strings" "sync" "time" @@ -95,6 +97,7 @@ const ( CurveP256 CurveID = 23 CurveP384 CurveID = 24 CurveP521 CurveID = 25 + X25519 CurveID = 29 ) // TLS Elliptic Curve Point Formats @@ -213,6 +216,25 @@ type ClientSessionCache interface { Put(sessionKey string, cs *ClientSessionState) } +// SignatureScheme identifies a signature algorithm supported by TLS. See +// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.3. +type SignatureScheme uint16 + +const ( + PKCS1WithSHA1 SignatureScheme = 0x0201 + PKCS1WithSHA256 SignatureScheme = 0x0401 + PKCS1WithSHA384 SignatureScheme = 0x0501 + PKCS1WithSHA512 SignatureScheme = 0x0601 + + PSSWithSHA256 SignatureScheme = 0x0804 + PSSWithSHA384 SignatureScheme = 0x0805 + PSSWithSHA512 SignatureScheme = 0x0806 + + ECDSAWithP256AndSHA256 SignatureScheme = 0x0403 + ECDSAWithP384AndSHA384 SignatureScheme = 0x0503 + ECDSAWithP521AndSHA512 SignatureScheme = 0x0603 +) + // ClientHelloInfo contains information from a ClientHello message in order to // guide certificate selection in the GetCertificate callback. type ClientHelloInfo struct { @@ -237,6 +259,47 @@ 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 []SignatureScheme + + // 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 +} + +// CertificateRequestInfo contains information from a server's +// CertificateRequest message, which is used to demand a certificate and proof +// of control from a client. +type CertificateRequestInfo struct { + // AcceptableCAs contains zero or more, DER-encoded, X.501 + // Distinguished Names. These are the names of root or intermediate CAs + // that the server wishes the returned certificate to be signed by. An + // empty slice indicates that the server has no preference. + AcceptableCAs [][]byte + + // SignatureSchemes lists the signature schemes that the server is + // willing to verify. + SignatureSchemes []SignatureScheme } // RenegotiationSupport enumerates the different levels of support for TLS @@ -281,10 +344,11 @@ type Config struct { // If Time is nil, TLS uses time.Now. Time func() time.Time - // Certificates contains one or more certificate chains - // to present to the other side of the connection. - // Server configurations must include at least one certificate - // or else set GetCertificate. + // Certificates contains one or more certificate chains to present to + // the other side of the connection. Server configurations must include + // at least one certificate or else set GetCertificate. Clients doing + // client-authentication may set either Certificates or + // GetClientCertificate. Certificates []Certificate // NameToCertificate maps from a certificate name to an element of @@ -302,7 +366,54 @@ type Config struct { // If GetCertificate is nil or returns nil, then the certificate is // retrieved from NameToCertificate. If NameToCertificate is nil, the // first element of Certificates will be used. - GetCertificate func(clientHello *ClientHelloInfo) (*Certificate, error) + GetCertificate func(*ClientHelloInfo) (*Certificate, error) + + // GetClientCertificate, if not nil, is called when a server requests a + // certificate from a client. If set, the contents of Certificates will + // be ignored. + // + // If GetClientCertificate returns an error, the handshake will be + // aborted and that error will be returned. Otherwise + // GetClientCertificate must return a non-nil Certificate. If + // Certificate.Certificate is empty then no certificate will be sent to + // the server. If this is unacceptable to the server then it may abort + // the handshake. + // + // GetClientCertificate may be called multiple times for the same + // connection if renegotiation occurs or if TLS 1.3 is in use. + GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error) + + // GetConfigForClient, if not nil, is called after a ClientHello is + // received from a client. It may return a non-nil Config in order to + // change the Config that will be used to handle this connection. If + // the returned Config is nil, the original Config will be used. The + // Config returned by this callback may not be subsequently modified. + // + // If GetConfigForClient is nil, the Config passed to Server() will be + // used for all connections. + // + // Uniquely for the fields in the returned Config, session ticket keys + // will be duplicated from the original Config if not set. + // Specifically, if SetSessionTicketKeys was called on the original + // config but not on the returned config then the ticket keys from the + // original config will be copied into the new config before use. + // Otherwise, if SessionTicketKey was set in the original config but + // not in the returned config then it will be copied into the returned + // config before use. If neither of those cases applies then the key + // material from the returned config will be used for session tickets. + GetConfigForClient func(*ClientHelloInfo) (*Config, error) + + // VerifyPeerCertificate, if not nil, is called after normal + // certificate verification by either a TLS client or server. It + // receives the raw ASN.1 certificates provided by the peer and also + // any verified chains that normal processing found. If it returns a + // non-nil error, the handshake is aborted and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. If normal verification is disabled by + // setting InsecureSkipVerify then this callback will be considered but + // the verifiedChains argument will always be nil. + VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error // RootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. @@ -387,15 +498,27 @@ type Config struct { // The default, none, is correct for the vast majority of applications. Renegotiation RenegotiationSupport + // KeyLogWriter optionally specifies a destination for TLS master secrets + // in NSS key log format that can be used to allow external programs + // such as Wireshark to decrypt TLS connections. + // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + // Use of KeyLogWriter compromises security and should only be + // used for debugging. + KeyLogWriter io.Writer + serverInitOnce sync.Once // guards calling (*Config).serverInit - // mutex protects sessionTicketKeys + // mutex protects sessionTicketKeys and originalConfig. mutex sync.RWMutex // sessionTicketKeys contains zero or more ticket keys. If the length // is zero, SessionTicketsDisabled must be true. The first key is used // for new tickets and any subsequent keys can be used to decrypt old // tickets. sessionTicketKeys []ticketKey + // originalConfig is set to the Config that was passed to Server if + // this Config is returned by a GetConfigForClient callback. It's used + // by serverInit in order to copy session ticket keys if needed. + originalConfig *Config } // ticketKeyNameLen is the number of bytes of identifier that is prepended to @@ -422,14 +545,26 @@ func ticketKeyFromBytes(b [32]byte) (key ticketKey) { return key } -// clone returns a copy of c. Only the exported fields are copied. -func (c *Config) clone() *Config { +// Clone returns a shallow clone of c. It is safe to clone a Config that is +// being used concurrently by a TLS client or server. +func (c *Config) Clone() *Config { + // Running serverInit ensures that it's safe to read + // SessionTicketsDisabled. + c.serverInitOnce.Do(c.serverInit) + + var sessionTicketKeys []ticketKey + c.mutex.RLock() + sessionTicketKeys = c.sessionTicketKeys + c.mutex.RUnlock() + return &Config{ Rand: c.Rand, Time: c.Time, Certificates: c.Certificates, NameToCertificate: c.NameToCertificate, GetCertificate: c.GetCertificate, + GetConfigForClient: c.GetConfigForClient, + VerifyPeerCertificate: c.VerifyPeerCertificate, RootCAs: c.RootCAs, NextProtos: c.NextProtos, ServerName: c.ServerName, @@ -446,14 +581,22 @@ func (c *Config) clone() *Config { CurvePreferences: c.CurvePreferences, DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, Renegotiation: c.Renegotiation, + KeyLogWriter: c.KeyLogWriter, + sessionTicketKeys: sessionTicketKeys, + // originalConfig is deliberately not duplicated. } } func (c *Config) serverInit() { - if c.SessionTicketsDisabled { + if c.SessionTicketsDisabled || len(c.ticketKeys()) != 0 { return } + var originalConfig *Config + c.mutex.Lock() + originalConfig, c.originalConfig = c.originalConfig, nil + c.mutex.Unlock() + alreadySet := false for _, b := range c.SessionTicketKey { if b != 0 { @@ -463,13 +606,21 @@ func (c *Config) serverInit() { } if !alreadySet { - if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil { + if originalConfig != nil { + copy(c.SessionTicketKey[:], originalConfig.SessionTicketKey[:]) + } else if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil { c.SessionTicketsDisabled = true return } } - c.sessionTicketKeys = []ticketKey{ticketKeyFromBytes(c.SessionTicketKey)} + if originalConfig != nil { + originalConfig.mutex.RLock() + c.sessionTicketKeys = originalConfig.sessionTicketKeys + originalConfig.mutex.RUnlock() + } else { + c.sessionTicketKeys = []ticketKey{ticketKeyFromBytes(c.SessionTicketKey)} + } } func (c *Config) ticketKeys() []ticketKey { @@ -539,7 +690,7 @@ func (c *Config) maxVersion() uint16 { return c.MaxVersion } -var defaultCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521} +var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521} func (c *Config) curvePreferences() []CurveID { if c == nil || len(c.CurvePreferences) == 0 { @@ -627,6 +778,26 @@ func (c *Config) BuildNameToCertificate() { } } +// writeKeyLog logs client random and master secret if logging was enabled by +// setting c.KeyLogWriter. +func (c *Config) writeKeyLog(clientRandom, masterSecret []byte) error { + if c.KeyLogWriter == nil { + return nil + } + + logLine := []byte(fmt.Sprintf("CLIENT_RANDOM %x %x\n", clientRandom, masterSecret)) + + writerMutex.Lock() + _, err := c.KeyLogWriter.Write(logLine) + writerMutex.Unlock() + + return err +} + +// writerMutex protects all KeyLogWriters globally. It is rarely enabled, +// and is only for debugging, so a global mutex saves space. +var writerMutex sync.Mutex + // A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte @@ -749,11 +920,46 @@ func defaultCipherSuites() []uint16 { } func initDefaultCipherSuites() { + var topCipherSuites []uint16 + if cipherhw.AESGCMSupport() { + // If AES-GCM hardware is provided then prioritise AES-GCM + // cipher suites. + topCipherSuites = []uint16{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + } + } else { + // Without AES-GCM hardware, we put the ChaCha20-Poly1305 + // cipher suites first. + topCipherSuites = []uint16{ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + } + } + varDefaultCipherSuites = make([]uint16, 0, len(cipherSuites)) + for _, topCipher := range topCipherSuites { + varDefaultCipherSuites = append(varDefaultCipherSuites, topCipher) + } + +NextCipherSuite: for _, suite := range cipherSuites { if suite.flags&suiteDefaultOff != 0 { continue } + for _, existing := range varDefaultCipherSuites { + if existing == suite.id { + continue NextCipherSuite + } + } varDefaultCipherSuites = append(varDefaultCipherSuites, suite.id) } } |