{"record":{"id":"f05dbb9eedd72cfb","repo":"XTLS/Xray-core","slug":"decrypt-verify-token-w","errorCode":null,"errorMessage":"decrypt verify token: %w","messagePattern":"decrypt verify token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/internet/finalmask/xmc/server.go","lineNumber":199,"sourceCode":"\t\t\treturn fmt.Errorf(\"bad encrypt response packet id\")\n\t\t}\n\n\t\terr = pkt.readFields(&encryptedSharedSecret, &encryptedVerifyToken)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"read encrypt response: %w\", err)\n\t\t}\n\n\t\tsharedSecret, err = rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedSharedSecret)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt shared secret: %w\", err)\n\t\t}\n\t\tif len(sharedSecret) != 16 {\n\t\t\treturn fmt.Errorf(\"bad shared secret length: %d\", len(sharedSecret))\n\t\t}\n\n\t\tdecryptedVerifyToken, err = rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedVerifyToken)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt verify token: %w\", err)\n\t\t}\n\n\t\tif len(decryptedVerifyToken) < 4 || !bytes.Equal(verifyToken, decryptedVerifyToken[:4]) {\n\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:]","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/finalmask/xmc/server.go#L181-L217","documentation":"RSA PKCS#1 v1.5 decryption of the encrypted verify token failed. The verify token is the 4 random bytes the server sent in its encryption request, echoed back encrypted with the server public key (in this fork it also carries the password suffix). Decryption failure means the ciphertext does not match the server's private key or is malformed.","triggerScenarios":"Client encrypted the token with a different public key than the one in this connection's encryption request; the encryptedVerifyToken field was truncated or empty from a framing bug; hostile random bytes. Note the shared secret decrypted fine at this point, so a failure here specifically implicates the second field or a partially-replayed handshake.","commonSituations":"Handshake replay where an attacker reuses an old shared-secret ciphertext but a fresh garbage token; client library bug serializing two RSA blocks; key rotation races.","solutions":["Compare len(encryptedVerifyToken) with the RSA modulus size — an off-by-framing truncation shows up immediately.","Confirm the client encrypts both fields with the exact public key bytes from the encryption request packet of THIS session.","Treat as a failed authentication: disconnect and count the event; repeated hits from one IP indicate probing.","If both this and error 961 fire together, suspect key mismatch; if only this fires, suspect field framing."],"exampleFix":"// before\ndecryptedVerifyToken, err = rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedVerifyToken)\nif err != nil {\n    return fmt.Errorf(\"decrypt verify token: %w\", err)\n}\n\n// after: add size context for triage\ndecryptedVerifyToken, err := rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedVerifyToken)\nif err != nil {\n    return fmt.Errorf(\"decrypt verify token (ct len %d): %w\", len(encryptedVerifyToken), err)\n}","handlingStrategy":"validation","validationCode":"if len(encryptedVerifyToken) != c.rsaPrivateKey.Size() {\n    return fmt.Errorf(\"verify token ciphertext has wrong size %d\", len(encryptedVerifyToken))\n}","typeGuard":null,"tryCatchPattern":"if _, err := rsa.DecryptPKCS1v15(rand.Reader, key, encToken); err != nil {\n    return errors.New(\"verify token rejected\") // generic message, no crypto detail\n}","preventionTips":["Client must encrypt token+password as one RSA block with the session's exact public key.","Check both ciphertext lengths before decrypting either; cheap and localizes failures.","Rate-limit repeated handshake failures per source address."],"tags":["crypto","rsa","handshake","authentication"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}