c := hs.c
// The first ClientHello gets double-hashed into the transcript upon a
- // HelloRetryRequest. See RFC 8446, Section 4.4.1.
+ // HelloRetryRequest. (The idea is that the server might offload transcript
+ // storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
chHash := hs.transcript.Sum(nil)
hs.transcript.Reset()
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
hs.transcript.Write(chHash)
hs.transcript.Write(hs.serverHello.marshal())
+ // The only HelloRetryRequest extensions we support are key_share and
+ // cookie, and clients must abort the handshake if the HRR would not result
+ // in any change in the ClientHello.
+ if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
+ }
+
+ if hs.serverHello.cookie != nil {
+ hs.hello.cookie = hs.serverHello.cookie
+ }
+
if hs.serverHello.serverShare.group != 0 {
c.sendAlert(alertDecodeError)
return errors.New("tls: received malformed key_share extension")
}
- curveID := hs.serverHello.selectedGroup
- if curveID == 0 {
- c.sendAlert(alertMissingExtension)
- return errors.New("tls: received HelloRetryRequest without selected group")
- }
- curveOK := false
- for _, id := range hs.hello.supportedCurves {
- if id == curveID {
- curveOK = true
- break
+ // If the server sent a key_share extension selecting a group, ensure it's
+ // a group we advertised but did not send a key share for, and send a key
+ // share for it this time.
+ if curveID := hs.serverHello.selectedGroup; curveID != 0 {
+ curveOK := false
+ for _, id := range hs.hello.supportedCurves {
+ if id == curveID {
+ curveOK = true
+ break
+ }
}
+ if !curveOK {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+ if hs.ecdheParams.CurveID() == curveID {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
+ }
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err := generateECDHEParameters(c.config.rand(), curveID)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.ecdheParams = params
+ hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
}
- if !curveOK {
- c.sendAlert(alertIllegalParameter)
- return errors.New("tls: server selected unsupported group")
- }
- if hs.ecdheParams.CurveID() == curveID {
- c.sendAlert(alertIllegalParameter)
- return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
- }
- if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
- c.sendAlert(alertInternalError)
- return errors.New("tls: CurvePreferences includes unsupported curve")
- }
- params, err := generateECDHEParameters(c.config.rand(), curveID)
- if err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
- hs.ecdheParams = params
- hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
-
- hs.hello.cookie = hs.serverHello.cookie
hs.hello.raw = nil
if len(hs.hello.pskIdentities) > 0 {