Pumpkin-MC/Pumpkin · critical · VineError
Vine challenge nonce mismatch
Error message
Vine challenge nonce mismatch
What it means
VineError::ChallengeMismatch is raised when the 16-byte nonce echoed back in the signed Vine payload does not equal the challenge the server generated for this specific login (vine.rs:196-203). The server stored the nonce in PendingConnection.vine_challenge when issuing the login plugin request; a mismatch means the response is stale, replayed, or forged, so the login is rejected.
Solutions
- Ensure the proxy answers each vine:player_info request with a fresh response signing the exact challenge received
- Update the proxy if it caches/reuses forwarding responses across logins
- Verify the backend matches responses to connections by message_id correctly and no packets are being cross-delivered
- Treat repeated mismatches from one source as an attack and block that source
Defensive patterns
Strategy: try-catch
Validate before calling
// before dispatching the response, confirm a challenge exists for this connection
fn challenge_present(conn: &PendingConnection) -> bool {
conn.vine_challenge.is_some()
} Try / catch
match receive_vine_plugin_response(port, &config, response, Some(expected)) {
Err(VineError::ChallengeMismatch) => {
tracing::warn!("Vine challenge nonce mismatch — possible replay or proxy bug; dropping login");
disconnect(DisconnectReason::ForwardingChallenge);
}
result => result?,
} Prevention
- Only allow the proxy host to reach the backend so attackers cannot replay payloads
- Ensure the proxy signs the exact challenge received per request and never reuses responses
- Keep the per-connection nonce stored until the response arrives, then clear it exactly once
- Treat repeated mismatches from one IP as an attack signal and rate-limit/block it
When it happens
Trigger: challenge != expected_challenge in receive_vine_plugin_response: the proxy responded to a different (earlier) login plugin request, an attacker replayed an old signed payload, or the response was routed to the wrong pending connection (message_id collision/mixup).
Common situations: Concurrent logins through a buggy proxy mixing up responses between connections; replay attempts against the backend; the proxy caching or reusing responses instead of answering each challenge freshly.
Related errors
- Vine timestamp expired or desynchronized: skew of
- JWT chain validation failed
- Cannot accept self-signed token. Authentication is enforced…
- Failed to verify Ed25519 signature
- The validated username is invalid
AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09).
Data as JSON: /api/errors/abc46f86c5edc815.
Report an issue: GitHub.
Appendix: source
Thrown at crates/pumpkin/src/net/proxy/vine.rs:42
#[derive(Error, Debug)]
pub enum VineError {
#[error("No response data received")]
NoData,
#[error("Vine response data too short (minimum 89 bytes)")]
DataTooShort,
#[error("No public key or secret configured for Vine proxy")]
MissingKeyConfig,
#[error("Invalid Ed25519 public key")]
InvalidPublicKey,
#[error("Failed to verify Ed25519 signature")]
InvalidSignature,
#[error("Failed to read forward version")]
FailedReadForwardVersion,
#[error("Unsupported forwarding version {0}. Expected {1}")]
UnsupportedForwardVersion(i32, i32),
#[error("Vine timestamp expired or desynchronized: skew of {0}s exceeds limit of {1}s")]
TimestampExpired(i64, i64),
#[error("Vine challenge nonce mismatch")]
ChallengeMismatch,
#[error("Missing expected challenge from pending connection")]
MissingChallenge,
#[error("Failed to read address")]
FailedReadAddress,
#[error("Failed to parse address")]
FailedParseAddress,
#[error("Failed to read game profile name")]
FailedReadProfileName,
#[error("Failed to read game profile UUID")]
FailedReadProfileUUID,
#[error("Failed to read game profile properties")]
FailedReadProfileProperties,
}
/// Initiates Vine modern forwarding handshake by sending a `CLoginPluginRequest`
/// with a unique 16-byte challenge nonce to protect against replay attacks.
pub async fn vine_login(connection: &mut PendingConnection) {View on GitHub (pinned to 8d4639e25a)