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:
parent
aa68126eeb
commit
14202975e0
13
common.go
13
common.go
@ -90,6 +90,7 @@ const (
|
||||
extensionSignatureAlgorithms uint16 = 13
|
||||
extensionALPN uint16 = 16
|
||||
extensionSCT uint16 = 18
|
||||
extensionExtendedMasterSecret uint16 = 23
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionPreSharedKey uint16 = 41
|
||||
extensionEarlyData uint16 = 42
|
||||
@ -271,12 +272,8 @@ type ConnectionState struct {
|
||||
OCSPResponse []byte
|
||||
|
||||
// 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
|
||||
// resumed connections.
|
||||
//
|
||||
// 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.
|
||||
// Section 3). This value will be nil for TLS 1.3 connections and for
|
||||
// resumed connections that don't support Extended Master Secret (RFC 7627).
|
||||
TLSUnique []byte
|
||||
|
||||
// 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
|
||||
// the seed. If the connection was set to allow renegotiation via
|
||||
// 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) {
|
||||
return cs.ekm(label, context, length)
|
||||
}
|
||||
|
3
conn.go
3
conn.go
@ -50,6 +50,7 @@ type Conn struct {
|
||||
// connection so far. If renegotiation is disabled then this is either
|
||||
// zero or one.
|
||||
handshakes int
|
||||
extMasterSecret bool
|
||||
didResume bool // whether this connection was a session resumption
|
||||
cipherSuite uint16
|
||||
ocspResponse []byte // stapled OCSP response
|
||||
@ -1670,7 +1671,7 @@ func (c *Conn) connectionStateLocked() ConnectionState {
|
||||
state.VerifiedChains = c.verifiedChains
|
||||
state.SignedCertificateTimestamps = c.scts
|
||||
state.OCSPResponse = c.ocspResponse
|
||||
if !c.didResume && c.vers != VersionTLS13 {
|
||||
if (!c.didResume || c.extMasterSecret) && c.vers != VersionTLS13 {
|
||||
if c.clientFinishedIsFirst {
|
||||
state.TLSUnique = c.clientFinished[:]
|
||||
} else {
|
||||
|
@ -72,6 +72,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
||||
vers: clientHelloVersion,
|
||||
compressionMethods: []uint8{compressionNone},
|
||||
random: make([]byte, 32),
|
||||
extendedMasterSecret: true,
|
||||
ocspStapling: true,
|
||||
scts: true,
|
||||
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 {
|
||||
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()
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// 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
|
||||
hs.masterSecret = hs.session.secret
|
||||
c.extMasterSecret = hs.session.extMasterSecret
|
||||
c.peerCertificates = hs.session.peerCertificates
|
||||
c.activeCertHandles = hs.c.activeCertHandles
|
||||
c.verifiedChains = hs.session.verifiedChains
|
||||
|
@ -136,6 +136,7 @@ func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
|
||||
|
||||
if hs.serverHello.ocspStapling ||
|
||||
hs.serverHello.ticketSupported ||
|
||||
hs.serverHello.extendedMasterSecret ||
|
||||
hs.serverHello.secureRenegotiationSupported ||
|
||||
len(hs.serverHello.secureRenegotiation) != 0 ||
|
||||
len(hs.serverHello.alpnProtocol) != 0 ||
|
||||
|
@ -84,6 +84,7 @@ type clientHelloMsg struct {
|
||||
supportedSignatureAlgorithmsCert []SignatureScheme
|
||||
secureRenegotiationSupported bool
|
||||
secureRenegotiation []byte
|
||||
extendedMasterSecret bool
|
||||
alpnProtocols []string
|
||||
scts bool
|
||||
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 {
|
||||
// RFC 7301, Section 3.1
|
||||
exts.AddUint16(extensionALPN)
|
||||
@ -510,6 +516,9 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
||||
return false
|
||||
}
|
||||
m.secureRenegotiationSupported = true
|
||||
case extensionExtendedMasterSecret:
|
||||
// RFC 7627
|
||||
m.extendedMasterSecret = true
|
||||
case extensionALPN:
|
||||
// RFC 7301, Section 3.1
|
||||
var protoList cryptobyte.String
|
||||
@ -627,6 +636,7 @@ type serverHelloMsg struct {
|
||||
ticketSupported bool
|
||||
secureRenegotiationSupported bool
|
||||
secureRenegotiation []byte
|
||||
extendedMasterSecret bool
|
||||
alpnProtocol string
|
||||
scts [][]byte
|
||||
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 {
|
||||
exts.AddUint16(extensionALPN)
|
||||
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
|
||||
@ -802,6 +816,8 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
|
||||
return false
|
||||
}
|
||||
m.secureRenegotiationSupported = true
|
||||
case extensionExtendedMasterSecret:
|
||||
m.extendedMasterSecret = true
|
||||
case extensionALPN:
|
||||
var protoList cryptobyte.String
|
||||
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
|
||||
|
@ -214,6 +214,7 @@ func (hs *serverHandshakeState) processClientHello() error {
|
||||
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.compressionMethod = compressionNone
|
||||
if len(hs.clientHello.serverName) > 0 {
|
||||
@ -471,6 +472,17 @@ func (hs *serverHandshakeState) checkForResumption() error {
|
||||
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.suite = suite
|
||||
c.didResume = true
|
||||
@ -647,7 +659,14 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
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 {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
|
9
prf.go
9
prf.go
@ -80,6 +80,7 @@ const (
|
||||
)
|
||||
|
||||
var masterSecretLabel = []byte("master secret")
|
||||
var extendedMasterSecretLabel = []byte("extended master secret")
|
||||
var keyExpansionLabel = []byte("key expansion")
|
||||
var clientFinishedLabel = []byte("client finished")
|
||||
var serverFinishedLabel = []byte("server finished")
|
||||
@ -115,6 +116,14 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr
|
||||
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
|
||||
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
|
||||
// RFC 2246, Section 6.3.
|
||||
|
19
ticket.go
19
ticket.go
@ -34,6 +34,7 @@ type SessionState struct {
|
||||
// uint64 created_at;
|
||||
// opaque secret<1..2^8-1>;
|
||||
// opaque extra<0..2^24-1>;
|
||||
// uint8 ext_master_secret = { 0, 1 };
|
||||
// uint8 early_data = { 0, 1 };
|
||||
// CertificateEntry certificate_list<0..2^24-1>;
|
||||
// select (SessionState.early_data) {
|
||||
@ -81,6 +82,7 @@ type SessionState struct {
|
||||
// which the ticket was received on the client.
|
||||
createdAt uint64 // seconds since UNIX epoch
|
||||
secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
|
||||
extMasterSecret bool
|
||||
peerCertificates []*x509.Certificate
|
||||
activeCertHandles []*activeCert
|
||||
ocspResponse []byte
|
||||
@ -117,6 +119,11 @@ func (s *SessionState) Bytes() ([]byte, error) {
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(s.Extra)
|
||||
})
|
||||
if s.extMasterSecret {
|
||||
b.AddUint8(1)
|
||||
} else {
|
||||
b.AddUint8(0)
|
||||
}
|
||||
if s.EarlyData {
|
||||
b.AddUint8(1)
|
||||
} else {
|
||||
@ -173,7 +180,7 @@ func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
|
||||
func ParseSessionState(data []byte) (*SessionState, error) {
|
||||
ss := &SessionState{}
|
||||
s := cryptobyte.String(data)
|
||||
var typ, earlyData uint8
|
||||
var typ, extMasterSecret, earlyData uint8
|
||||
var cert Certificate
|
||||
if !s.ReadUint16(&ss.version) ||
|
||||
!s.ReadUint8(&typ) ||
|
||||
@ -182,11 +189,20 @@ func ParseSessionState(data []byte) (*SessionState, error) {
|
||||
!readUint64(&s, &ss.createdAt) ||
|
||||
!readUint8LengthPrefixed(&s, &ss.secret) ||
|
||||
!readUint24LengthPrefixed(&s, &ss.Extra) ||
|
||||
!s.ReadUint8(&extMasterSecret) ||
|
||||
!s.ReadUint8(&earlyData) ||
|
||||
len(ss.secret) == 0 ||
|
||||
!unmarshalCertificate(&s, &cert) {
|
||||
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 {
|
||||
case 0:
|
||||
ss.EarlyData = false
|
||||
@ -279,6 +295,7 @@ func (c *Conn) sessionState() (*SessionState, error) {
|
||||
ocspResponse: c.ocspResponse,
|
||||
scts: c.scts,
|
||||
isClient: c.isClient,
|
||||
extMasterSecret: c.extMasterSecret,
|
||||
verifiedChains: verifiedChains,
|
||||
}, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user