feat(signature): upgrade provider signature checks

This commit is contained in:
sususu98
2026-05-29 12:24:18 +08:00
parent 7d9980e8fa
commit 86cb9c150b
3 changed files with 91 additions and 3 deletions
+34
View File
@@ -226,6 +226,40 @@ func NormalizeClaudeThinkingSignature(rawSignature string, opts ...ClaudeSignatu
}
}
// NormalizeClaudeProviderNativeThinkingSignature strips any cache prefix,
// validates the signature, and returns the single-layer E-form expected by
// Claude-native providers.
func NormalizeClaudeProviderNativeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) (string, error) {
opt := claudeSignatureValidationOptions(opts)
sig := stripClaudeSignaturePrefix(rawSignature)
if sig == "" {
return "", fmt.Errorf("empty signature")
}
if len(sig) > MaxClaudeThinkingSignatureLen {
return "", fmt.Errorf("signature exceeds maximum length (%d bytes)", MaxClaudeThinkingSignatureLen)
}
switch sig[0] {
case 'E':
if err := validateClaudeSingleLayerSignature(sig, opt); err != nil {
return "", err
}
return sig, nil
case 'R':
if err := validateClaudeDoubleLayerSignature(sig, opt); err != nil {
return "", err
}
decoded, err := base64.StdEncoding.DecodeString(sig)
if err != nil {
return "", fmt.Errorf("invalid double-layer signature: base64 decode failed: %w", err)
}
return string(decoded), nil
default:
return "", fmt.Errorf("invalid signature: expected 'E' or 'R' prefix, got %q", string(sig[0]))
}
}
func validateClaudeDoubleLayerSignature(sig string, opt ClaudeSignatureValidationOptions) error {
decoded, err := base64.StdEncoding.DecodeString(sig)
if err != nil {
+19 -1
View File
@@ -229,6 +229,24 @@ func CompatibleSignatureForProviderBlock(targetProvider SignatureProvider, rawSi
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
}
func normalizeSignatureTargetProvider(provider SignatureProvider) SignatureProvider {
switch provider {
case SignatureProviderGeminiBypass:
@@ -255,7 +273,7 @@ func normalizeCompatibleSignatureForProvider(targetProvider SignatureProvider, r
payload := SignaturePayloadWithoutProviderPrefix(rawSignature)
switch normalizeSignatureTargetProvider(targetProvider) {
case SignatureProviderClaude:
normalized, err := NormalizeClaudeThinkingSignature(payload)
normalized, err := NormalizeClaudeProviderNativeThinkingSignature(payload)
if err != nil {
return ""
}
@@ -61,6 +61,42 @@ func TestDetectSignatureProvider_Gemini3EPrefixDoesNotLookClaude(t *testing.T) {
}
}
func TestCompatibleSignatureForProvider_ClaudeUsesProviderNativeEForm(t *testing.T) {
nativeSig := testClaudeThinkingSignature()
doubleEncoded := base64.StdEncoding.EncodeToString([]byte(nativeSig))
normalized, ok := CompatibleSignatureForProvider(SignatureProviderClaude, doubleEncoded)
if !ok {
t.Fatal("double-layer Claude signature should be compatible")
}
if normalized != nativeSig {
t.Fatalf("CompatibleSignatureForProvider(Claude) = %q, want provider-native %q", normalized, nativeSig)
}
}
func TestCompatibleAntigravityClaudeThinkingSignature_UsesDoubleLayerRForm(t *testing.T) {
nativeSig := testClaudeThinkingSignature()
expected := base64.StdEncoding.EncodeToString([]byte(nativeSig))
normalized, ok := CompatibleAntigravityClaudeThinkingSignature(nativeSig)
if !ok {
t.Fatal("Claude signature should be compatible with Antigravity Claude")
}
if normalized != expected {
t.Fatalf("CompatibleAntigravityClaudeThinkingSignature = %q, want %q", normalized, expected)
}
}
func TestCompatibleAntigravityClaudeThinkingSignature_RejectsGeminiEPrefix(t *testing.T) {
geminiSig := testGemini3ThoughtSignature([]byte{0x01, 0x0c, 0x39, 0xd6, 0xc7, 0x34})
if !strings.HasPrefix(geminiSig, "E") {
t.Fatalf("test signature should start with E, got %q", geminiSig[:1])
}
if normalized, ok := CompatibleAntigravityClaudeThinkingSignature(geminiSig); ok || normalized != "" {
t.Fatalf("Gemini E-prefix signature normalized=%q ok=%v, want rejected", normalized, ok)
}
}
func TestDetectSignatureProvider_DoesNotClassifyArbitraryBase64AsGemini(t *testing.T) {
opaque := testGeminiThoughtSignature([]byte{0x45, 0x12})
if got := DetectSignatureProvider(opaque); got != SignatureProviderUnknown {
@@ -172,9 +208,9 @@ func TestSanitizeClaudeMessagesSignaturesForModel_NormalizesSameProviderClaude(t
nativeSig := testClaudeThinkingSignature()
sig := "claude#" + nativeSig
input := []byte(`{"model":"claude-sonnet","messages":[{"role":"assistant","content":[{"type":"thinking","thinking":"keep","signature":"` + sig + `"},{"type":"text","text":"answer"}]}]}`)
expectedSig, err := NormalizeClaudeThinkingSignature(nativeSig)
expectedSig, err := NormalizeClaudeProviderNativeThinkingSignature(nativeSig)
if err != nil {
t.Fatalf("NormalizeClaudeThinkingSignature failed: %v", err)
t.Fatalf("NormalizeClaudeProviderNativeThinkingSignature failed: %v", err)
}
output, report := SanitizeClaudeMessagesSignaturesForModel(input, "claude-sonnet-4-5")