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
- Reject and disconnect: this is the correct server behavior; no retry is defined by the protocol.
- If you control the client, make it generate the secret with aes.NewCipher-compatible 16-byte keys (crypto/rand.Read(make([]byte, 16))).
- 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
- On the client, always generate the secret via make([]byte, 16) filled from crypto/rand.
- Never accept AES-192/256 secrets from forks; the protocol fixes the secret at 128 bits.
- Include the observed length in logs to catch client-side key-size bugs early.
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
- empty rsa public key
- new crypto reader: %w
- new crypto writer: %w
- verify token mismatch
- new crypto reader: %w
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/1cc0224ebbf8583c.
Report an issue: GitHub.