diff --git a/common.go b/common.go index c64df94..a1f66c6 100644 --- a/common.go +++ b/common.go @@ -303,11 +303,13 @@ type ConnectionState struct { // ExportKeyingMaterial returns length bytes of exported key material in a new // slice as defined in RFC 5705. If context is nil, it is not used as part of // the seed. If the connection was set to allow renegotiation via -// Config.Renegotiation, this function will return an error. +// Config.Renegotiation, or if the connections supports neither TLS 1.3 nor +// Extended Master Secret, this function will return an error. // -// There are conditions in which the returned values might not be unique to a -// connection. See the Security Considerations sections of RFC 5705 and RFC 7627, -// and https://mitls.org/pages/attacks/3SHAKE#channelbindings. +// Exporting key material without Extended Master Secret or TLS 1.3 was disabled +// in Go 1.22 due to security issues (see the Security Considerations sections +// of RFC 5705 and RFC 7627), but can be re-enabled with the GODEBUG setting +// tlsunsafeekm=1. func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { return cs.ekm(label, context, length) } diff --git a/conn.go b/conn.go index 6668e32..1981d28 100644 --- a/conn.go +++ b/conn.go @@ -1684,7 +1684,15 @@ func (c *Conn) connectionStateLocked() ConnectionState { } } if c.config.Renegotiation != RenegotiateNever { - state.ekm = noExportedKeyingMaterial + state.ekm = noEKMBecauseRenegotiation + } else if c.vers != VersionTLS13 && !c.extMasterSecret { + state.ekm = func(label string, context []byte, length int) ([]byte, error) { + // if ekmgodebug.Value() == "1" { + // ekmgodebug.IncNonDefault() + // return c.ekm(label, context, length) + // } + return noEKMBecauseNoEMS(label, context, length) + } } else { state.ekm = c.ekm } diff --git a/prf.go b/prf.go index 648c64b..425216a 100644 --- a/prf.go +++ b/prf.go @@ -252,13 +252,20 @@ func (h *finishedHash) discardHandshakeBuffer() { h.buffer = nil } -// noExportedKeyingMaterial is used as a value of +// noEKMBecauseRenegotiation is used as a value of // ConnectionState.ekm when renegotiation is enabled and thus // we wish to fail all key-material export requests. -func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) { +func noEKMBecauseRenegotiation(label string, context []byte, length int) ([]byte, error) { return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled") } +// noEKMBecauseNoEMS is used as a value of ConnectionState.ekm when Extended +// Master Secret is not negotiated and thus we wish to fail all key-material +// export requests. +func noEKMBecauseNoEMS(label string, context []byte, length int) ([]byte, error) { + return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated; override with GODEBUG=tlsunsafeekm=1") +} + // ekmFromMasterSecret generates exported keying material as defined in RFC 5705. func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) { return func(label string, context []byte, length int) ([]byte, error) {