package signature import "strings" type SignatureProvider string const ( SignatureProviderUnknown SignatureProvider = "unknown" SignatureProviderClaude SignatureProvider = "claude" SignatureProviderGemini SignatureProvider = "gemini" SignatureProviderGeminiBypass SignatureProvider = "gemini_bypass" SignatureProviderGPT SignatureProvider = "gpt" // SignatureProviderKimi is identified by fixed signature size rather than by // an envelope. See kimi_validation.go for the empirical basis and its limits. SignatureProviderKimi SignatureProvider = "kimi" // SignatureProviderGrok is a target-only family. DetectSignatureProvider never // returns it: xAI emits no envelope, no version byte and no fixed length, and // its ciphertext is statistically indistinguishable from uniform random bytes, // so any positive claim would also capture every other opaque blob. Grok // handling is provenance-first - establish the target from the model or route, // then use InspectGrokEncryptedContent as a replay-safety shape check. SignatureProviderGrok SignatureProvider = "grok" ) type SignatureBlockKind string const ( SignatureBlockKindUnknown SignatureBlockKind = "unknown" SignatureBlockKindClaudeThinking SignatureBlockKind = "claude_thinking" SignatureBlockKindGeminiModelPart SignatureBlockKind = "gemini_model_part" SignatureBlockKindGeminiFunctionCall SignatureBlockKind = "gemini_function_call" SignatureBlockKindGPTReasoning SignatureBlockKind = "gpt_reasoning" ) type SignatureCompatibilityAction string const ( SignatureActionPreserve SignatureCompatibilityAction = "preserve" SignatureActionDropBlock SignatureCompatibilityAction = "drop_block" SignatureActionDropSignature SignatureCompatibilityAction = "drop_signature" SignatureActionReplaceWithGeminiBypass SignatureCompatibilityAction = "replace_with_gemini_bypass" SignatureActionNoCompatibleReplacement SignatureCompatibilityAction = "no_compatible_replacement" ) type SignatureCompatibilityDecision struct { TargetProvider SignatureProvider DetectedProvider SignatureProvider BlockKind SignatureBlockKind Compatible bool Action SignatureCompatibilityAction ReplacementSignature string NormalizedSignature string Reason string } // SignatureProviderFromModelName maps common model names to the provider family // whose signed history can be safely replayed for that model. func SignatureProviderFromModelName(modelName string) SignatureProvider { lower := strings.ToLower(strings.TrimSpace(modelName)) switch { case strings.Contains(lower, "claude"): return SignatureProviderClaude case strings.Contains(lower, "gemini"): return SignatureProviderGemini case strings.Contains(lower, "gpt"), strings.Contains(lower, "openai"), strings.Contains(lower, "codex"), strings.HasPrefix(lower, "o1"), strings.HasPrefix(lower, "o3"), strings.HasPrefix(lower, "o4"): return SignatureProviderGPT case strings.Contains(lower, "kimi"), strings.Contains(lower, "moonshot"), strings.HasPrefix(lower, "k2"), strings.HasPrefix(lower, "k3"): return SignatureProviderKimi case strings.Contains(lower, "grok"): return SignatureProviderGrok default: return SignatureProviderUnknown } } // selfDescribingSignatureFirstChars are the base64 first characters that a // self-describing provider envelope can produce. A base64 first character is // exactly the first payload byte shifted right by two, so a single character // comparison rules out every known envelope without decoding anything: // // 'C' -> 0x08..0x0b : Claude CAIS (0x08) // 'E' -> 0x10..0x13 : Claude single-layer (0x12), Gemini protobuf_field_2 (0x12) // 'R' -> 0x44..0x47 : Claude double-layer R (0x45, inner 'E') // 'g' -> 0x80..0x83 : GPT Fernet reasoning (0x80) // // Gemini's ascii_uuid envelope is deliberately absent. Its first byte is the // first hex character of the UUID, which spreads over 'M', 'N', 'O', 'Y' and 'Z' // depending on the value, and it is never a replay-safe envelope: it resolves to // SignatureProviderUnknown whether or not it reaches the validators, and Gemini // model parts recover it through the bypass sentinel keyed on block kind. Listing // one of its five possible characters would only look like coverage. // // Any provider added here must also be validated in // DetectSignatureProviderForBlock, otherwise its signatures would fall through // to the residual class. TestSelfDescribingSignatureFirstChars_CoversEveryKnownEnvelope // fails when a replay-safe envelope is missing from this set. const selfDescribingSignatureFirstChars = "CERg" // base64AlphabetSet builds a byte lookup table for the alphanumeric base64 core // plus the alphabet-specific characters in extra. Signature charset validation // runs over multi-kilobyte payloads, and a comparison chain over base64 text // mispredicts on nearly every byte because the characters are effectively random; // a single table load is branch-free and measures about an order of magnitude // faster on the observed corpora. func base64AlphabetSet(extra string) [256]bool { var set [256]bool for c := byte('A'); c <= 'Z'; c++ { set[c] = true } for c := byte('a'); c <= 'z'; c++ { set[c] = true } for c := byte('0'); c <= '9'; c++ { set[c] = true } for i := 0; i < len(extra); i++ { set[extra[i]] = true } return set } // maybeSelfDescribingSignatureEnvelope reports whether rawSignature can possibly // be a self-describing provider envelope. It is a structural pre-filter, not a // classifier: a false result is conclusive, a true result only narrows the // candidate set. Opaque ciphertext that carries no envelope (xAI/Grok // encrypted_content) is uniformly distributed over the byte space, so this // rejects roughly 92% of it with one comparison and no allocation. func maybeSelfDescribingSignatureEnvelope(rawSignature string) bool { if rawSignature == "" { return false } return strings.IndexByte(selfDescribingSignatureFirstChars, rawSignature[0]) >= 0 } // DetectSignatureProvider classifies the provider family that can replay // rawSignature. It intentionally uses Claude strict validation before Gemini // detection because Gemini 3 signatures also decode from an E-prefixed base64 // string and can look Claude-like under shallow prefix checks. func DetectSignatureProvider(rawSignature string) SignatureProvider { return DetectSignatureProviderForBlock(rawSignature, SignatureBlockKindUnknown) } // DetectSignatureProviderForBlock classifies rawSignature with block-kind // context. UUID-shaped payloads are deliberately not classified as replay-safe // provider signatures; callers targeting Gemini should replace them with the // bypass sentinel. func DetectSignatureProviderForBlock(rawSignature string, blockKind SignatureBlockKind) SignatureProvider { sig := strings.TrimSpace(rawSignature) if sig == "" { return SignatureProviderUnknown } if prefixedProvider, unprefixed, ok := SplitSignatureProviderPrefix(sig); ok { switch prefixedProvider { case SignatureProviderGemini: if IsGeminiThoughtSignatureBypass(unprefixed) { return SignatureProviderGeminiBypass } if isRecognizedGeminiProviderSignature(unprefixed, blockKind) { return SignatureProviderGemini } case SignatureProviderClaude: if IsValidClaudeThinkingSignature(unprefixed, ClaudeSignatureValidationOptions{Strict: true}) || IsValidClaudeCAISSignature(unprefixed) { return SignatureProviderClaude } case SignatureProviderGPT: if IsValidGPTReasoningSignature(unprefixed) { return SignatureProviderGPT } } return SignatureProviderUnknown } if strings.Contains(sig, "#") { return SignatureProviderUnknown } // The bypass sentinel is a plain literal rather than an envelope, so it must // be matched before the structural pre-filter below rejects it. if IsGeminiThoughtSignatureBypass(sig) { return SignatureProviderGeminiBypass } // Probes run from the strongest marker to the weakest: // 1. GPT carries the literal "gAAAA" prefix, which pins both the version // byte and the high timestamp bytes. // 2. Claude CAIS carries marker 0x08 plus a literal "claude-" model text. // 3. Claude single/double-layer carries marker 0x12 plus the same literal. // 4. Gemini validates wire shape only and has no literal to anchor on, so // it is the weakest judge and goes last. // // This ordering is defense in depth rather than a correctness requirement: // Claude envelopes carry extra top-level fields beyond the container, which // fails the single-record shape Gemini requires, so the two families stay // separable in either order. TestGeminiEnvelopeNeverClaimsClaudeSignatures // pins that invariant so a looser Gemini envelope check cannot make the // order silently start mattering. // // The envelope pre-filter gates only the envelope probes. A blob that cannot // be an envelope skips straight to the size probe below rather than returning // early, because Kimi's uniformly distributed base64 starts with one of // "CERg" about 6% of the time and would otherwise be dropped by whichever // side of the gate it happened to land on. if maybeSelfDescribingSignatureEnvelope(sig) { if IsValidGPTReasoningSignature(sig) { return SignatureProviderGPT } if IsValidClaudeCAISSignature(sig) { return SignatureProviderClaude } if IsValidClaudeThinkingSignature(sig, ClaudeSignatureValidationOptions{Strict: true}) { return SignatureProviderClaude } if isRecognizedGeminiProviderSignature(sig, blockKind) { return SignatureProviderGemini } } // Kimi carries no envelope, so it can only be claimed once every // self-describing probe above has declined. Ordering it last means a length // coincidence can never capture another provider's signature, and a future // drift in Kimi's sizes costs Kimi its own identification rather than // corrupting a neighbouring family. if IsValidKimiThinkingSignature(sig) { return SignatureProviderKimi } return SignatureProviderUnknown } func IsSignatureCompatibleWithProvider(targetProvider SignatureProvider, rawSignature string) bool { decision := DecideSignatureCompatibility(targetProvider, rawSignature, SignatureBlockKindUnknown) return decision.Compatible } // DecideSignatureCompatibility returns the safe handling policy for replaying a // signed block into targetProvider. func DecideSignatureCompatibility(targetProvider SignatureProvider, rawSignature string, blockKind SignatureBlockKind) SignatureCompatibilityDecision { return DecideSignatureCompatibilityForModel(targetProvider, "", rawSignature, blockKind) } // DecideSignatureCompatibilityForModel returns the safe handling policy for replaying a // signed block into targetProvider for targetModel. func DecideSignatureCompatibilityForModel(targetProvider SignatureProvider, targetModel string, rawSignature string, blockKind SignatureBlockKind) SignatureCompatibilityDecision { targetProvider = normalizeSignatureTargetProvider(targetProvider) if blockKind == "" { blockKind = SignatureBlockKindUnknown } detected := DetectSignatureProviderForBlock(rawSignature, blockKind) decision := SignatureCompatibilityDecision{ TargetProvider: targetProvider, DetectedProvider: detected, BlockKind: blockKind, } if signatureProviderMatchesTarget(targetProvider, detected) { decision.Compatible = true decision.Action = SignatureActionPreserve decision.NormalizedSignature = normalizeCompatibleSignatureForProvider(targetProvider, rawSignature, blockKind) decision.Reason = claudeCompatibleSignatureReason(targetProvider, rawSignature, targetModel) return decision } decision.Compatible = false switch targetProvider { case SignatureProviderGemini: if blockKind == SignatureBlockKindGeminiFunctionCall || blockKind == SignatureBlockKindGeminiModelPart || blockKind == SignatureBlockKindUnknown { decision.Action = SignatureActionReplaceWithGeminiBypass decision.ReplacementSignature = GeminiSkipThoughtSignatureValidator decision.Reason = "Gemini can bypass synthetic or incompatible model-part signatures with the documented sentinel" return decision } decision.Action = SignatureActionDropBlock decision.Reason = "signature is not compatible with Gemini and this block is not a bypass-safe Gemini model part" case SignatureProviderClaude: decision.Action = SignatureActionDropBlock decision.Reason = "Claude has no cross-provider bypass sentinel for thinking blocks" case SignatureProviderGPT: decision.Action = SignatureActionDropBlock decision.Reason = "GPT reasoning encrypted_content cannot be synthesized from another provider signature" case SignatureProviderKimi: // Kimi is the only target that can keep the reasoning text when the // signature does not match. Its Messages endpoint never reads the field // back: a mutated, truncated, non-base64 or absent signature all return // 200, because reasoning continuity there travels in OpenAI-style // reasoning_content instead. Dropping the block would discard recoverable // thinking text for no upstream benefit, so drop only the signature. decision.Action = SignatureActionDropSignature decision.Reason = "Kimi does not validate replayed thinking signatures, so the block survives without one" case SignatureProviderGrok: // xAI decrypts encrypted_content and rejects the request with 400 // "Could not decrypt" when the blob is foreign or mutated, so a // non-matching value has to leave with the block. decision.Action = SignatureActionDropBlock decision.Reason = "xAI verifies encrypted_content on replay and rejects foreign or mutated blobs" default: decision.Action = SignatureActionNoCompatibleReplacement decision.Reason = "unknown target provider" } return decision } func SplitSignatureProviderPrefix(rawSignature string) (SignatureProvider, string, bool) { prefix, rest, ok := strings.Cut(strings.TrimSpace(rawSignature), "#") if !ok { return SignatureProviderUnknown, rawSignature, false } provider := SignatureProviderFromCachePrefix(prefix) if provider == SignatureProviderUnknown { return SignatureProviderUnknown, rawSignature, false } return provider, strings.TrimSpace(rest), true } // SignatureProviderFromCachePrefix maps this repo's explicit provider-prefix // envelope to a provider family. This is intentionally stricter than // SignatureProviderFromModelName so arbitrary model names such as // "claude-cache#..." cannot be mistaken for trusted provider provenance. func SignatureProviderFromCachePrefix(prefix string) SignatureProvider { switch strings.ToLower(strings.TrimSpace(prefix)) { case "claude", "anthropic", "cais", "claude-cais", "claude_cais", "ccmax", "claude-code-max", "claude_code_max": return SignatureProviderClaude case "gemini", "google": return SignatureProviderGemini case "openai", "gpt", "codex": return SignatureProviderGPT default: return SignatureProviderUnknown } } // SignaturePayloadWithoutProviderPrefix strips this repo's provider cache prefix // when present. The returned string is the value that should be replayed to an // upstream provider. func SignaturePayloadWithoutProviderPrefix(rawSignature string) string { if _, unprefixed, ok := SplitSignatureProviderPrefix(rawSignature); ok { return unprefixed } return strings.TrimSpace(rawSignature) } // CompatibleSignatureForProvider returns a replayable provider-native signature // for targetProvider. It strips this repo's provider prefix and normalizes // Claude signatures to the format expected by the target when possible. func CompatibleSignatureForProvider(targetProvider SignatureProvider, rawSignature string) (string, bool) { return CompatibleSignatureForProviderBlock(targetProvider, rawSignature, SignatureBlockKindUnknown) } // CompatibleSignatureForProviderBlock returns a replayable provider-native // signature for targetProvider when the source block kind is known. func CompatibleSignatureForProviderBlock(targetProvider SignatureProvider, rawSignature string, blockKind SignatureBlockKind) (string, bool) { decision := DecideSignatureCompatibility(targetProvider, rawSignature, blockKind) if !decision.Compatible || decision.NormalizedSignature == "" { return "", false } return decision.NormalizedSignature, true } // CompatibleAntigravityClaudeThinkingSignature returns the double-layer R-form // required by Antigravity Claude replay. It only accepts signatures that are // strictly identifiable as Claude, so Gemini E-prefixed envelopes cannot slip // through the looser Antigravity bypass normalization path. func CompatibleAntigravityClaudeThinkingSignature(rawSignature string) (string, bool) { if DetectSignatureProviderForBlock(rawSignature, SignatureBlockKindClaudeThinking) != SignatureProviderClaude { return "", false } normalized, err := NormalizeClaudeThinkingSignature( SignaturePayloadWithoutProviderPrefix(rawSignature), ClaudeSignatureValidationOptions{Strict: true}, ) if err != nil { return "", false } return normalized, true } // claudeCompatibleSignatureReason explains why a matching signature is // replayable. Claude CAIS signatures carry the issuing model inside the payload, // so the embedded model and the target model are both reported to make signature // decisions traceable in debug logs. func claudeCompatibleSignatureReason(targetProvider SignatureProvider, rawSignature, targetModel string) string { const genericReason = "signature provider matches target provider" if targetProvider != SignatureProviderClaude { return genericReason } info, err := InspectClaudeCAISSignature(SignaturePayloadWithoutProviderPrefix(rawSignature)) if err != nil { return genericReason } reason := "valid Claude CAIS signature with embedded model " + info.ModelText + " is compatible with any Claude target" if trimmedModel := strings.TrimSpace(targetModel); trimmedModel != "" { reason += ", including target model " + trimmedModel } return reason } func normalizeSignatureTargetProvider(provider SignatureProvider) SignatureProvider { switch provider { case SignatureProviderGeminiBypass: return SignatureProviderGemini default: return provider } } func signatureProviderMatchesTarget(target, detected SignatureProvider) bool { switch target { case SignatureProviderGemini: return detected == SignatureProviderGemini || detected == SignatureProviderGeminiBypass case SignatureProviderClaude: return detected == SignatureProviderClaude case SignatureProviderGPT: return detected == SignatureProviderGPT case SignatureProviderKimi: return detected == SignatureProviderKimi default: // SignatureProviderGrok is deliberately absent. Detection never yields it, // so a Grok target must decide replay safety from provenance plus // InspectGrokEncryptedContent rather than from a detected-provider match. return false } } func normalizeCompatibleSignatureForProvider(targetProvider SignatureProvider, rawSignature string, blockKind SignatureBlockKind) string { payload := SignaturePayloadWithoutProviderPrefix(rawSignature) switch normalizeSignatureTargetProvider(targetProvider) { case SignatureProviderClaude: if IsValidClaudeCAISSignature(payload) { return payload } normalized, err := NormalizeClaudeProviderNativeThinkingSignature(payload) if err != nil { return "" } return normalized case SignatureProviderGemini: if IsGeminiThoughtSignatureBypass(payload) { return payload } if isRecognizedGeminiProviderSignature(payload, blockKind) { return payload } case SignatureProviderGPT: if IsValidGPTReasoningSignature(payload) { return payload } case SignatureProviderKimi: if IsValidKimiThinkingSignature(payload) { return payload } } return "" } func isRecognizedGeminiProviderSignature(rawSignature string, blockKind SignatureBlockKind) bool { if IsValidClaudeCAISSignature(rawSignature) { return false } if IsValidGeminiThoughtSignature(rawSignature, GeminiThoughtSignatureValidationOptions{RequireKnownEnvelope: true}) { return true } return false }