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

  1. Ensure the proxy answers each vine:player_info request with a fresh response signing the exact challenge received
  2. Update the proxy if it caches/reuses forwarding responses across logins
  3. Verify the backend matches responses to connections by message_id correctly and no packets are being cross-delivered
  4. 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

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


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)