0
0
mirror of https://github.com/XTLS/REALITY.git synced 2025-08-22 14:38:35 +00:00

crypto/tls: implement Extended Master Secret

All OpenSSL tests now test operation with EMS. To test a handshake
*without* EMS we need to pass -Options=-ExtendedMasterSecret which is
only available in OpenSSL 3.1, which breaks a number of other tests.

Updates #43922

Change-Id: Ib9ac79a1d03fab6bfba5fe9cd66689cff661cda7
Reviewed-on: https://go-review.googlesource.com/c/go/+/497376
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
yuhan6665 2023-10-14 09:15:54 -04:00
parent aa68126eeb
commit 14202975e0
8 changed files with 94 additions and 15 deletions

View File

@ -90,6 +90,7 @@ const (
extensionSignatureAlgorithms uint16 = 13 extensionSignatureAlgorithms uint16 = 13
extensionALPN uint16 = 16 extensionALPN uint16 = 16
extensionSCT uint16 = 18 extensionSCT uint16 = 18
extensionExtendedMasterSecret uint16 = 23
extensionSessionTicket uint16 = 35 extensionSessionTicket uint16 = 35
extensionPreSharedKey uint16 = 41 extensionPreSharedKey uint16 = 41
extensionEarlyData uint16 = 42 extensionEarlyData uint16 = 42
@ -271,12 +272,8 @@ type ConnectionState struct {
OCSPResponse []byte OCSPResponse []byte
// TLSUnique contains the "tls-unique" channel binding value (see RFC 5929, // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929,
// Section 3). This value will be nil for TLS 1.3 connections and for all // Section 3). This value will be nil for TLS 1.3 connections and for
// resumed connections. // resumed connections that don't support Extended Master Secret (RFC 7627).
//
// Deprecated: there are conditions in which this value 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.
TLSUnique []byte TLSUnique []byte
// ekm is a closure exposed via ExportKeyingMaterial. // ekm is a closure exposed via ExportKeyingMaterial.
@ -287,6 +284,10 @@ type ConnectionState struct {
// slice as defined in RFC 5705. If context is nil, it is not used as part of // 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 // the seed. If the connection was set to allow renegotiation via
// Config.Renegotiation, this function will return an error. // Config.Renegotiation, 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.
func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
return cs.ekm(label, context, length) return cs.ekm(label, context, length)
} }

View File

@ -50,6 +50,7 @@ type Conn struct {
// connection so far. If renegotiation is disabled then this is either // connection so far. If renegotiation is disabled then this is either
// zero or one. // zero or one.
handshakes int handshakes int
extMasterSecret bool
didResume bool // whether this connection was a session resumption didResume bool // whether this connection was a session resumption
cipherSuite uint16 cipherSuite uint16
ocspResponse []byte // stapled OCSP response ocspResponse []byte // stapled OCSP response
@ -1670,7 +1671,7 @@ func (c *Conn) connectionStateLocked() ConnectionState {
state.VerifiedChains = c.verifiedChains state.VerifiedChains = c.verifiedChains
state.SignedCertificateTimestamps = c.scts state.SignedCertificateTimestamps = c.scts
state.OCSPResponse = c.ocspResponse state.OCSPResponse = c.ocspResponse
if !c.didResume && c.vers != VersionTLS13 { if (!c.didResume || c.extMasterSecret) && c.vers != VersionTLS13 {
if c.clientFinishedIsFirst { if c.clientFinishedIsFirst {
state.TLSUnique = c.clientFinished[:] state.TLSUnique = c.clientFinished[:]
} else { } else {

View File

@ -72,6 +72,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
vers: clientHelloVersion, vers: clientHelloVersion,
compressionMethods: []uint8{compressionNone}, compressionMethods: []uint8{compressionNone},
random: make([]byte, 32), random: make([]byte, 32),
extendedMasterSecret: true,
ocspStapling: true, ocspStapling: true,
scts: true, scts: true,
serverName: hostnameInSNI(config.ServerName), serverName: hostnameInSNI(config.ServerName),
@ -645,6 +646,19 @@ func (hs *clientHandshakeState) doFullHandshake() error {
} }
} }
if hs.serverHello.extendedMasterSecret {
c.extMasterSecret = true
hs.masterSecret = extMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
hs.finishedHash.Sum())
} else {
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
hs.hello.random, hs.serverHello.random)
}
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + err.Error())
}
if chainToSend != nil && len(chainToSend.Certificate) > 0 { if chainToSend != nil && len(chainToSend.Certificate) > 0 {
certVerify := &certificateVerifyMsg{} certVerify := &certificateVerifyMsg{}
@ -692,12 +706,6 @@ func (hs *clientHandshakeState) doFullHandshake() error {
} }
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + err.Error())
}
hs.finishedHash.discardHandshakeBuffer() hs.finishedHash.discardHandshakeBuffer()
return nil return nil
@ -784,8 +792,15 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, errors.New("tls: server resumed a session with a different cipher suite") return false, errors.New("tls: server resumed a session with a different cipher suite")
} }
// RFC 7627, Section 5.3
if hs.session.extMasterSecret != hs.serverHello.extendedMasterSecret {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different EMS extension")
}
// Restore master secret and certificates from previous state // Restore master secret and certificates from previous state
hs.masterSecret = hs.session.secret hs.masterSecret = hs.session.secret
c.extMasterSecret = hs.session.extMasterSecret
c.peerCertificates = hs.session.peerCertificates c.peerCertificates = hs.session.peerCertificates
c.activeCertHandles = hs.c.activeCertHandles c.activeCertHandles = hs.c.activeCertHandles
c.verifiedChains = hs.session.verifiedChains c.verifiedChains = hs.session.verifiedChains

View File

@ -136,6 +136,7 @@ func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
if hs.serverHello.ocspStapling || if hs.serverHello.ocspStapling ||
hs.serverHello.ticketSupported || hs.serverHello.ticketSupported ||
hs.serverHello.extendedMasterSecret ||
hs.serverHello.secureRenegotiationSupported || hs.serverHello.secureRenegotiationSupported ||
len(hs.serverHello.secureRenegotiation) != 0 || len(hs.serverHello.secureRenegotiation) != 0 ||
len(hs.serverHello.alpnProtocol) != 0 || len(hs.serverHello.alpnProtocol) != 0 ||

View File

@ -84,6 +84,7 @@ type clientHelloMsg struct {
supportedSignatureAlgorithmsCert []SignatureScheme supportedSignatureAlgorithmsCert []SignatureScheme
secureRenegotiationSupported bool secureRenegotiationSupported bool
secureRenegotiation []byte secureRenegotiation []byte
extendedMasterSecret bool
alpnProtocols []string alpnProtocols []string
scts bool scts bool
supportedVersions []uint16 supportedVersions []uint16
@ -181,6 +182,11 @@ func (m *clientHelloMsg) marshal() ([]byte, error) {
}) })
}) })
} }
if m.extendedMasterSecret {
// RFC 7627
exts.AddUint16(extensionExtendedMasterSecret)
exts.AddUint16(0) // empty extension_data
}
if len(m.alpnProtocols) > 0 { if len(m.alpnProtocols) > 0 {
// RFC 7301, Section 3.1 // RFC 7301, Section 3.1
exts.AddUint16(extensionALPN) exts.AddUint16(extensionALPN)
@ -510,6 +516,9 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
return false return false
} }
m.secureRenegotiationSupported = true m.secureRenegotiationSupported = true
case extensionExtendedMasterSecret:
// RFC 7627
m.extendedMasterSecret = true
case extensionALPN: case extensionALPN:
// RFC 7301, Section 3.1 // RFC 7301, Section 3.1
var protoList cryptobyte.String var protoList cryptobyte.String
@ -627,6 +636,7 @@ type serverHelloMsg struct {
ticketSupported bool ticketSupported bool
secureRenegotiationSupported bool secureRenegotiationSupported bool
secureRenegotiation []byte secureRenegotiation []byte
extendedMasterSecret bool
alpnProtocol string alpnProtocol string
scts [][]byte scts [][]byte
supportedVersion uint16 supportedVersion uint16
@ -662,6 +672,10 @@ func (m *serverHelloMsg) marshal() ([]byte, error) {
}) })
}) })
} }
if m.extendedMasterSecret {
exts.AddUint16(extensionExtendedMasterSecret)
exts.AddUint16(0) // empty extension_data
}
if len(m.alpnProtocol) > 0 { if len(m.alpnProtocol) > 0 {
exts.AddUint16(extensionALPN) exts.AddUint16(extensionALPN)
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
@ -802,6 +816,8 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false return false
} }
m.secureRenegotiationSupported = true m.secureRenegotiationSupported = true
case extensionExtendedMasterSecret:
m.extendedMasterSecret = true
case extensionALPN: case extensionALPN:
var protoList cryptobyte.String var protoList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {

View File

@ -214,6 +214,7 @@ func (hs *serverHandshakeState) processClientHello() error {
return errors.New("tls: initial handshake had non-empty renegotiation extension") return errors.New("tls: initial handshake had non-empty renegotiation extension")
} }
hs.hello.extendedMasterSecret = hs.clientHello.extendedMasterSecret
hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
hs.hello.compressionMethod = compressionNone hs.hello.compressionMethod = compressionNone
if len(hs.clientHello.serverName) > 0 { if len(hs.clientHello.serverName) > 0 {
@ -471,6 +472,17 @@ func (hs *serverHandshakeState) checkForResumption() error {
return nil return nil
} }
// RFC 7627, Section 5.3
if !sessionState.extMasterSecret && hs.clientHello.extendedMasterSecret {
return nil
}
if sessionState.extMasterSecret && !hs.clientHello.extendedMasterSecret {
// Aborting is somewhat harsh, but it's a MUST and it would indicate a
// weird downgrade in client capabilities.
return errors.New("tls: session supported extended_master_secret but client does not")
}
c.extMasterSecret = sessionState.extMasterSecret
hs.sessionState = sessionState hs.sessionState = sessionState
hs.suite = suite hs.suite = suite
c.didResume = true c.didResume = true
@ -647,7 +659,14 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) if hs.hello.extendedMasterSecret {
c.extMasterSecret = true
hs.masterSecret = extMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
hs.finishedHash.Sum())
} else {
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
hs.clientHello.random, hs.hello.random)
}
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil { if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err

9
prf.go
View File

@ -80,6 +80,7 @@ const (
) )
var masterSecretLabel = []byte("master secret") var masterSecretLabel = []byte("master secret")
var extendedMasterSecretLabel = []byte("extended master secret")
var keyExpansionLabel = []byte("key expansion") var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished") var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished") var serverFinishedLabel = []byte("server finished")
@ -115,6 +116,14 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr
return masterSecret return masterSecret
} }
// extMasterFromPreMasterSecret generates the extended master secret from the
// pre-master secret. See RFC 7627.
func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte {
masterSecret := make([]byte, masterSecretLength)
prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, transcript)
return masterSecret
}
// keysFromMasterSecret generates the connection keys from the master // keysFromMasterSecret generates the connection keys from the master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in // secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, Section 6.3. // RFC 2246, Section 6.3.

View File

@ -34,6 +34,7 @@ type SessionState struct {
// uint64 created_at; // uint64 created_at;
// opaque secret<1..2^8-1>; // opaque secret<1..2^8-1>;
// opaque extra<0..2^24-1>; // opaque extra<0..2^24-1>;
// uint8 ext_master_secret = { 0, 1 };
// uint8 early_data = { 0, 1 }; // uint8 early_data = { 0, 1 };
// CertificateEntry certificate_list<0..2^24-1>; // CertificateEntry certificate_list<0..2^24-1>;
// select (SessionState.early_data) { // select (SessionState.early_data) {
@ -81,6 +82,7 @@ type SessionState struct {
// which the ticket was received on the client. // which the ticket was received on the client.
createdAt uint64 // seconds since UNIX epoch createdAt uint64 // seconds since UNIX epoch
secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3 secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
extMasterSecret bool
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
activeCertHandles []*activeCert activeCertHandles []*activeCert
ocspResponse []byte ocspResponse []byte
@ -117,6 +119,11 @@ func (s *SessionState) Bytes() ([]byte, error) {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(s.Extra) b.AddBytes(s.Extra)
}) })
if s.extMasterSecret {
b.AddUint8(1)
} else {
b.AddUint8(0)
}
if s.EarlyData { if s.EarlyData {
b.AddUint8(1) b.AddUint8(1)
} else { } else {
@ -173,7 +180,7 @@ func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
func ParseSessionState(data []byte) (*SessionState, error) { func ParseSessionState(data []byte) (*SessionState, error) {
ss := &SessionState{} ss := &SessionState{}
s := cryptobyte.String(data) s := cryptobyte.String(data)
var typ, earlyData uint8 var typ, extMasterSecret, earlyData uint8
var cert Certificate var cert Certificate
if !s.ReadUint16(&ss.version) || if !s.ReadUint16(&ss.version) ||
!s.ReadUint8(&typ) || !s.ReadUint8(&typ) ||
@ -182,11 +189,20 @@ func ParseSessionState(data []byte) (*SessionState, error) {
!readUint64(&s, &ss.createdAt) || !readUint64(&s, &ss.createdAt) ||
!readUint8LengthPrefixed(&s, &ss.secret) || !readUint8LengthPrefixed(&s, &ss.secret) ||
!readUint24LengthPrefixed(&s, &ss.Extra) || !readUint24LengthPrefixed(&s, &ss.Extra) ||
!s.ReadUint8(&extMasterSecret) ||
!s.ReadUint8(&earlyData) || !s.ReadUint8(&earlyData) ||
len(ss.secret) == 0 || len(ss.secret) == 0 ||
!unmarshalCertificate(&s, &cert) { !unmarshalCertificate(&s, &cert) {
return nil, errors.New("tls: invalid session encoding") return nil, errors.New("tls: invalid session encoding")
} }
switch extMasterSecret {
case 0:
ss.extMasterSecret = false
case 1:
ss.extMasterSecret = true
default:
return nil, errors.New("tls: invalid session encoding")
}
switch earlyData { switch earlyData {
case 0: case 0:
ss.EarlyData = false ss.EarlyData = false
@ -279,6 +295,7 @@ func (c *Conn) sessionState() (*SessionState, error) {
ocspResponse: c.ocspResponse, ocspResponse: c.ocspResponse,
scts: c.scts, scts: c.scts,
isClient: c.isClient, isClient: c.isClient,
extMasterSecret: c.extMasterSecret,
verifiedChains: verifiedChains, verifiedChains: verifiedChains,
}, nil }, nil
} }