Pumpkin-MC/Pumpkin · error · TelemetryVerificationError

Signature verification failed

Error message

Signature verification failed: {0}

What it means

TelemetryVerificationError::VerificationFailed(String), produced when the Ed25519 cryptographic signature does not verify against the given public key and message. The key and signature parsed fine, but the signature was not made by the matching secret key over the exact verified payload.

Solutions

  1. Ensure the client signs the exact byte string the server verifies — agree on a canonical payload (timestamp included)
  2. Confirm the verifying key corresponds to the client's current signing key (sync key rotation)
  3. Log which payload bytes were verified on both sides and diff them
  4. Check that proxies/gateways are not rewriting the request body or timestamp before verification

Example fix

// before
let msg = format!("{payload}{timestamp}"); // client order differs from server
// after
let msg = format!("{timestamp}{payload}"); // use one agreed canonical format on both sides
Defensive patterns

Strategy: try-catch

Try / catch

match telemetry::verify(&key, &sig, &msg, now) {
    Err(TelemetryVerificationError::VerificationFailed(m)) => {
        log::warn!("telemetry signature mismatch: {m}");
        // reject request / rotate keys if this spikes
    }
    Ok(()) => { /* proceed */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling telemetry::verify with a signature produced over different bytes than what the server verifies (e.g. different timestamp string, reordered fields, different serialization), a mismatched key/signature pair, or an attacker-tampered payload.

Common situations: Client and server canonicalize the payload differently (whitespace, field order, timestamp normalization); key rotation on the server without updating clients; MITM/replay attempts on telemetry endpoints.

Related errors


AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09). Data as JSON: /api/errors/d399679ab7b91982. Report an issue: GitHub.

Appendix: source

Thrown at crates/pumpkin/src/telemetry.rs:135

/// Errors that can occur during telemetry request signature verification.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum TelemetryVerificationError {
    #[error("Invalid timestamp string format")]
    InvalidTimestamp,
    #[error(
        "Timestamp drift exceeded: drift was {drift}s (max allowed is {MAX_CLOCK_DRIFT_SECS}s)"
    )]
    ClockDriftExceeded { drift: u64 },
    #[error("Invalid public key hex encoding")]
    InvalidPublicKeyHex,
    #[error("Invalid public key bytes: {0}")]
    InvalidPublicKey(String),
    #[error("Invalid signature hex encoding")]
    InvalidSignatureHex,
    #[error("Invalid signature bytes: {0}")]
    InvalidSignature(String),
    #[error("Signature verification failed: {0}")]
    VerificationFailed(String),
}

/// Verifies a signed telemetry request against an Ed25519 public key and timestamp.
///
/// Ensures clock drift between `current_time_secs` and `timestamp_str` does not exceed `±300` seconds.
pub fn verify_telemetry_request(
    pubkey_hex: &str,
    sig_hex: &str,
    timestamp_str: &str,
    body_bytes: &[u8],
    current_time_secs: u64,
) -> Result<(), TelemetryVerificationError> {
    let ts: u64 = timestamp_str
        .parse()
        .map_err(|_| TelemetryVerificationError::InvalidTimestamp)?;

    let drift = current_time_secs.abs_diff(ts);

View on GitHub (pinned to 8d4639e25a)