{"record":{"id":"85d7c9d637d38759","repo":"Pumpkin-MC/Pumpkin","slug":"invalid-signature-hex-encoding","errorCode":null,"errorMessage":"Invalid signature hex encoding","messagePattern":"Invalid signature hex encoding","errorType":"error_code","errorClass":"TelemetryVerificationError","httpStatus":null,"severity":"error","filePath":"crates/pumpkin/src/telemetry.rs","lineNumber":131,"sourceCode":"    let pubkey_hex = hex::encode(signing_key.verifying_key().to_bytes());\n    let sig_hex = hex::encode(signature.to_bytes());\n    (pubkey_hex, sig_hex)\n}\n\n/// Errors that can occur during telemetry request signature verification.\n#[derive(Debug, thiserror::Error, PartialEq, Eq)]\npub enum TelemetryVerificationError {\n    #[error(\"Invalid timestamp string format\")]\n    InvalidTimestamp,\n    #[error(\n        \"Timestamp drift exceeded: drift was {drift}s (max allowed is {MAX_CLOCK_DRIFT_SECS}s)\"\n    )]\n    ClockDriftExceeded { drift: u64 },\n    #[error(\"Invalid public key hex encoding\")]\n    InvalidPublicKeyHex,\n    #[error(\"Invalid public key bytes: {0}\")]\n    InvalidPublicKey(String),\n    #[error(\"Invalid signature hex encoding\")]\n    InvalidSignatureHex,\n    #[error(\"Invalid signature bytes: {0}\")]\n    InvalidSignature(String),\n    #[error(\"Signature verification failed: {0}\")]\n    VerificationFailed(String),\n}\n\n/// Verifies a signed telemetry request against an Ed25519 public key and timestamp.\n///\n/// Ensures clock drift between `current_time_secs` and `timestamp_str` does not exceed `±300` seconds.\npub fn verify_telemetry_request(\n    pubkey_hex: &str,\n    sig_hex: &str,\n    timestamp_str: &str,\n    body_bytes: &[u8],\n    current_time_secs: u64,\n) -> Result<(), TelemetryVerificationError> {\n    let ts: u64 = timestamp_str","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin/src/telemetry.rs#L113-L149","documentation":"TelemetryVerificationError::InvalidSignatureHex, produced when the caller-supplied signature cannot be parsed as a hex string. The signature must be hex-encoded (128 hex chars for a 64-byte Ed25519 signature). Thrown before verification runs.","triggerScenarios":"Calling telemetry::verify with a signature string containing non-hex characters, odd length, base64 content, or extra whitespace/prefixes.","commonSituations":"Client sends the signature base64-encoded while the server expects hex; signature URL-decoded/mangled in transit; signature copied with surrounding quotes or a '0x' prefix.","solutions":["Encode the signature as pure hex (`hex::encode`) on the client side","Normalize the transport (header/query) so it does not alter the signature string","Strip whitespace, quotes, and prefixes from the signature before calling verify"],"exampleFix":"// before\nlet sig = base64::engine::general_purpose::STANDARD.encode(signature_bytes);\n// after\nlet sig = hex::encode(signature_bytes);","handlingStrategy":"validation","validationCode":"fn is_hex(s: &str) -> bool { !s.is_empty() && s.len() % 2 == 0 && s.bytes().all(|b| b.is_ascii_hexdigit()) }\nif !is_hex(sig.trim()) { return Err(\"signature must be pure hex\"); }","typeGuard":"fn valid_signature_hex(s: &str) -> bool {\n    let t = s.trim().trim_start_matches(\"0x\");\n    t.len() == 128 && t.bytes().all(|b| b.is_ascii_hexdigit())\n}","tryCatchPattern":"match telemetry::verify(&key, &sig, &msg, now) {\n    Err(TelemetryVerificationError::InvalidSignatureHex) => log::warn!(\"client sent non-hex signature; check client encoding\"),\n    Ok(()) => { /* proceed */ }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Use hex::encode consistently for signatures across client and server","Sanitize signature values received via HTTP headers/query (trim, strip prefixes)","Add an integration test signing and verifying an end-to-end request"],"tags":["rust","hex-decoding","signature","telemetry"],"backgroundTag":"invalid-argument-format","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}