{"record":{"id":"702deda04d9ddd4f","repo":"Pumpkin-MC/Pumpkin","slug":"failed-to-verify-ed25519-signature","errorCode":null,"errorMessage":"Failed to verify Ed25519 signature","messagePattern":"Failed to verify Ed25519 signature","errorType":"error_code","errorClass":"VineError","httpStatus":null,"severity":"critical","filePath":"crates/pumpkin/src/net/proxy/vine.rs","lineNumber":34,"sourceCode":"use tracing::debug;\n\nuse crate::net::{GameProfile, java::pending::PendingConnection};\n\npub const VINE_PLAYER_INFO_CHANNEL: &str = \"vine:player_info\";\npub const VINE_FORWARDING_VERSION: i32 = 1;\npub const MAX_TIMESTAMP_DRIFT_SECS: i64 = 30;\n\n#[derive(Error, Debug)]\npub enum VineError {\n    #[error(\"No response data received\")]\n    NoData,\n    #[error(\"Vine response data too short (minimum 89 bytes)\")]\n    DataTooShort,\n    #[error(\"No public key or secret configured for Vine proxy\")]\n    MissingKeyConfig,\n    #[error(\"Invalid Ed25519 public key\")]\n    InvalidPublicKey,\n    #[error(\"Failed to verify Ed25519 signature\")]\n    InvalidSignature,\n    #[error(\"Failed to read forward version\")]\n    FailedReadForwardVersion,\n    #[error(\"Unsupported forwarding version {0}. Expected {1}\")]\n    UnsupportedForwardVersion(i32, i32),\n    #[error(\"Vine timestamp expired or desynchronized: skew of {0}s exceeds limit of {1}s\")]\n    TimestampExpired(i64, i64),\n    #[error(\"Vine challenge nonce mismatch\")]\n    ChallengeMismatch,\n    #[error(\"Missing expected challenge from pending connection\")]\n    MissingChallenge,\n    #[error(\"Failed to read address\")]\n    FailedReadAddress,\n    #[error(\"Failed to parse address\")]\n    FailedParseAddress,\n    #[error(\"Failed to read game profile name\")]\n    FailedReadProfileName,\n    #[error(\"Failed to read game profile UUID\")]","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin/src/net/proxy/vine.rs#L16-L52","documentation":"VineError::InvalidSignature is returned when the Ed25519 signature over the Vine forwarding payload does not verify against the configured public key (vine.rs:165-167). This means the response was not signed by the holder of the matching key or the payload was altered in transit. The server rejects the login to prevent spoofed player identities/IPs.","triggerScenarios":"verifying_key.verify(payload, &signature) fails in receive_vine_plugin_response: the proxy signed with a different key than the server configured, the payload bytes were modified, or a non-proxy client forged the packet.","commonSituations":"Key rotation happened on the proxy but the backend still has the old public key; the proxy and backend use different secrets that derive different keys (e.g. different secret strings, or one side uses hex seed vs. raw-string SHA-256 derivation); a man-in-the-middle or direct-connect client injected a fake response.","solutions":["Confirm the backend's public_key matches the proxy's current signing key and re-copy it after any key rotation","If using secret-based derivation, verify both sides use the identical secret string and the same derivation scheme","Ensure only the proxy can reach the backend port so third parties cannot inject forged responses","Update both proxy and server together when upgrading, so signing formats stay in sync"],"exampleFix":"// before\npublic_key = \"aaaaaaaa...\" # stale key from old proxy install\n// after\npublic_key = \"1fc8f9e2a4b7...\" # current public key shown by the proxy","handlingStrategy":"try-catch","validationCode":"// sanity-check pairing at startup: derive the verifying key and compare\n// against a key the proxy reports\nlet vk = get_verifying_key(&config)?;\nprintln!(\"expect proxy public key: {}\", hex::encode(vk.to_bytes()));","typeGuard":null,"tryCatchPattern":"match receive_vine_plugin_response(port, &config, response, challenge) {\n    Err(VineError::InvalidSignature) => {\n        tracing::warn!(\"Vine signature check failed: is the proxy's public key up to date?\");\n        disconnect(DisconnectReason::BadForwardingSignature);\n    }\n    result => result?,\n}","preventionTips":["Rotate the signing key on the proxy and backend together in one change window","Prefer the secret-based derivation on both sides with the identical secret string to avoid key mismatch","Restrict backend port access to the proxy host so attackers cannot inject forged responses","Alert on any InvalidSignature occurrence — a valid-protocol-but-bad-signature event is never benign"],"tags":["security","crypto","signature-verification","minecraft"],"backgroundTag":"signature-verification-failed","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}