Pumpkin-MC/Pumpkin · critical · VineError
Failed to verify Ed25519 signature
Error message
Failed to verify Ed25519 signature
What it means
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.
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
Example fix
// before public_key = "aaaaaaaa..." # stale key from old proxy install // after public_key = "1fc8f9e2a4b7..." # current public key shown by the proxy
Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check pairing at startup: derive the verifying key and compare
// against a key the proxy reports
let vk = get_verifying_key(&config)?;
println!("expect proxy public key: {}", hex::encode(vk.to_bytes())); Try / catch
match receive_vine_plugin_response(port, &config, response, challenge) {
Err(VineError::InvalidSignature) => {
tracing::warn!("Vine signature check failed: is the proxy's public key up to date?");
disconnect(DisconnectReason::BadForwardingSignature);
}
result => result?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- JWT chain validation failed
- Cannot accept self-signed token. Authentication is enforced…
- Invalid Ed25519 public key
- Vine timestamp expired or desynchronized: skew of
- Vine challenge nonce mismatch
AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09).
Data as JSON: /api/errors/702deda04d9ddd4f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/pumpkin/src/net/proxy/vine.rs:34
use tracing::debug;
use crate::net::{GameProfile, java::pending::PendingConnection};
pub const VINE_PLAYER_INFO_CHANNEL: &str = "vine:player_info";
pub const VINE_FORWARDING_VERSION: i32 = 1;
pub const MAX_TIMESTAMP_DRIFT_SECS: i64 = 30;
#[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")]View on GitHub (pinned to 8d4639e25a)