XTLS/Xray-core · error

bad shared secret length: %d

Error message

bad shared secret length: %d

What it means

The decrypted shared secret is not exactly 16 bytes. The Minecraft protocol fixes the AES shared secret at 128 bits (16 bytes); after successful RSA decryption any other length means the client encrypted something that is not a conforming secret. This is a hard protocol violation, not a transient fault.

Source

Thrown at transport/internet/finalmask/xmc/server.go:194

		if err != nil {
			return fmt.Errorf("read encrypt response: %w", err)
		}

		if pkt.packetID != 0x01 {
			return fmt.Errorf("bad encrypt response packet id")
		}

		err = pkt.readFields(&encryptedSharedSecret, &encryptedVerifyToken)
		if err != nil {
			return fmt.Errorf("read encrypt response: %w", err)
		}

		sharedSecret, err = rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedSharedSecret)
		if err != nil {
			return fmt.Errorf("decrypt shared secret: %w", err)
		}
		if len(sharedSecret) != 16 {
			return fmt.Errorf("bad shared secret length: %d", len(sharedSecret))
		}

		decryptedVerifyToken, err = rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedVerifyToken)
		if err != nil {
			return fmt.Errorf("decrypt verify token: %w", err)
		}

		if len(decryptedVerifyToken) < 4 || !bytes.Equal(verifyToken, decryptedVerifyToken[:4]) {
			return fmt.Errorf("verify token mismatch")
		}

		c.reader, err = newCryptoReader(c.reader, sharedSecret)
		if err != nil {
			return fmt.Errorf("new crypto reader: %w", err)
		}

		c.writer, err = newCryptoWriter(c.writer, sharedSecret)
		if err != nil {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Reject and disconnect: this is the correct server behavior; no retry is defined by the protocol.
  2. If you control the client, make it generate the secret with aes.NewCipher-compatible 16-byte keys (crypto/rand.Read(make([]byte, 16))).
  3. Log the observed length and client address to spot systematic client-side bugs versus one-off junk.

Example fix

// before
if len(sharedSecret) != 16 {
    return fmt.Errorf("bad shared secret length: %d", len(sharedSecret))
}

// after: same check, but send a proper disconnect before dropping
if len(sharedSecret) != 16 {
    writeDisconnectPacket(c.writer, `{"text":"Bad shared secret length"}`)
    return fmt.Errorf("bad shared secret length: %d", len(sharedSecret))
}
Defensive patterns

Strategy: validation

Validate before calling

if len(sharedSecret) != 16 {
    // protocol violation; disconnect, never negotiate
    return fmt.Errorf("bad shared secret length: %d", len(sharedSecret))
}

Type guard

func isValidSharedSecret(s []byte) bool { return len(s) == 16 }

Prevention

When it happens

Trigger: A client that encrypts a secret of the wrong size (e.g. 24 or 32 bytes for AES-192/256), a custom client that concatenated secret+extra data, or an attacker whose random ciphertext happened to decrypt to a different plaintext length.

Common situations: Modified or exotic Minecraft client forks using non-standard key sizes; test harnesses generating arbitrary plaintexts; protocol-version mismatch where the secret layout changed.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/1cc0224ebbf8583c. Report an issue: GitHub.