{"record":{"id":"6523e5bdfa6a0802","repo":"XTLS/Xray-core","slug":"bad-password","errorCode":null,"errorMessage":"bad password","messagePattern":"bad password","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/internet/finalmask/xmc/server.go","lineNumber":221,"sourceCode":"\t\t\treturn fmt.Errorf(\"verify token mismatch\")\n\t\t}\n\n\t\tc.reader, err = newCryptoReader(c.reader, sharedSecret)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"new crypto reader: %w\", err)\n\t\t}\n\n\t\tc.writer, err = newCryptoWriter(c.writer, sharedSecret)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"new crypto writer: %w\", err)\n\t\t}\n\n\t\t// verify password\n\t\treceivedPassword := decryptedVerifyToken[4:]\n\n\t\tif subtle.ConstantTimeCompare(receivedPassword, []byte(c.password)) != 1 {\n\t\t\twriteDisconnectPacket(c.writer, `{\"type\":\"translatable\",\"translate\":\"multiplayer.disconnect.authservers_down\"}`)\n\t\t\treturn fmt.Errorf(\"bad password\")\n\t\t}\n\t\tif !found {\n\t\t\tif err = writeDisconnectPacket(c.writer, `{\"text\":\"You are not white-listed on this server!\"}`); err != nil {\n\t\t\t\treturn fmt.Errorf(\"write unknown login profile disconnect: %w\", err)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"unknown login profile\")\n\t\t}\n\n\t\tloginName := String(profile.Username)\n\t\tpropertyCount := Varint(1)\n\t\tpropertyName := String(\"textures\")\n\t\ttexturesValue := String(profile.TexturesValue)\n\t\tsigned := Boolean(true)\n\t\ttexturesSignature := String(profile.TexturesSignature)\n\t\tif err = writePacket(c.writer, 0x02, &profile.UUID, &loginName, &propertyCount, &propertyName, &texturesValue, &signed, &texturesSignature); err != nil {\n\t\t\treturn fmt.Errorf(\"write login finished: %w\", err)\n\t\t}\n","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/finalmask/xmc/server.go#L203-L239","documentation":"Password authentication failed. This fork smuggles the password after the 4-byte verify token inside the encrypted verify-token field; after handshake success the server constant-time-compares decryptedVerifyToken[4:] against the configured c.password. On mismatch it writes a disconnect packet (mimicking Mojang's authservers_down message) and aborts the login.","triggerScenarios":"Client concatenated the wrong password (or none) after the 4 echo bytes: wrong password configured, password empty on one side but not the other, trailing whitespace/newline in either config, or a client that only sends the bare 4-byte token so the suffix comparison fails.","commonSituations":"Typo or stale password after rotation in the client config; client library not aware this fork appends the password to the token; encoding differences (UTF-8 vs escaped characters); empty-password defaults differing between client and server builds.","solutions":["Set the identical password byte-for-byte in both the server config and the client's verify-token payload; watch for trailing whitespace/newlines.","If you maintain the client, verify it sends verifyToken[0:4] || password exactly, with no length prefix or padding.","Compare lengths first in a log line (never log the password itself) — a 0-length received password means the client sends no suffix at all.","After changing the password, restart both ends; there is no re-negotiation path mid-handshake."],"exampleFix":"// client side, before: send only the echo token\ntoken := verifyToken // 4 bytes\n// after: append the password this server expects\ntoken := append(append([]byte{}, verifyToken...), []byte(password)...)\nencrypted, err := rsa.EncryptPKCS1v15(rand.Reader, serverPub, token)","handlingStrategy":"validation","validationCode":"// server startup: fail fast on empty/whitespace passwords\nif strings.TrimSpace(c.password) == \"\" && requirePassword {\n    return errors.New(\"xmc password must be configured\")\n}","typeGuard":null,"tryCatchPattern":"if subtle.ConstantTimeCompare(receivedPassword, []byte(c.password)) != 1 {\n    writeDisconnectPacket(c.writer, `{\"type\":\"translatable\",\"translate\":\"multiplayer.disconnect.authservers_down\"}`)\n    return errors.New(\"bad password\") // already the pattern; keep the disconnect before returning\n}","preventionTips":["Distribute the password to clients via the same mechanism/channel as the config to avoid drift after rotation.","Client: build the payload as verifyToken[0:4] || password with no separators or length prefixes.","Log lengths (not contents) of expected vs received passwords to debug encoding mismatches."],"tags":["authentication","password","handshake","config"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}