golang/go · error

tls: received malformed key_share extension

Error message

tls: received malformed key_share extension

What it means

Thrown during HelloRetryRequest processing when the server's key_share extension includes a server_share with a non-zero group. In a HelloRetryRequest, the server should use the selected_group field to request a different key share from the client — including an actual server share is malformed.

Source

Thrown at src/crypto/tls/handshake_client_tls13.go:306

		c.sendAlert(alertUnsupportedExtension)
		return errors.New("tls: unexpected encrypted client hello extension in serverHello")
	}

	// 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 {
		hello.cookie = hs.serverHello.cookie
	}

	if hs.serverHello.serverShare.group != 0 {
		c.sendAlert(alertDecodeError)
		return errors.New("tls: received malformed key_share extension")
	}

	// 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 {
		if !slices.Contains(hello.supportedCurves, curveID) {
			c.sendAlert(alertIllegalParameter)
			return errors.New("tls: server selected unsupported group")
		}
		if slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
			return ks.group == curveID
		}) {
			c.sendAlert(alertIllegalParameter)
			return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
		}
		ke, err := keyExchangeForCurveID(curveID)
		if err != nil {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Server should use the key_share selected_group field in HelloRetryRequest, not the server_share entry.
  2. Report as a server-side TLS 1.3 implementation bug.
  3. If the server cannot be fixed, restrict client to TLS 1.2.
  4. Verify with an alternative TLS client (e.g. openssl) to confirm it is a server issue.
Defensive patterns

Strategy: try-catch

Try / catch

conn, err := tls.Dial("tcp", addr, config)
if err != nil {
    if strings.Contains(err.Error(), "malformed key_share extension") {
        // Server bug in HRR key_share — fall back to TLS 1.2
        config.MaxVersion = tls.VersionTLS12
        conn, err = tls.Dial("tcp", addr, config)
    }
}

Prevention

When it happens

Trigger: Triggered when hs.serverHello.serverShare.group != 0 during HRR processing. The client sends alertDecodeError. The server incorrectly included a key share (its own public key) in the HRR instead of just selecting a group.

Common situations: Server bug: including a server key share in the HelloRetryRequest extension instead of using the selected_group field. Non-compliant TLS 1.3 server implementation that confuses the HRR key_share format with the regular ServerHello format.

Understand the failure class

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/35d69b40b3692f4d. Report an issue: GitHub.