SchemaPin Go Implementation
A Go implementation of the SchemaPin cryptographic protocol for ensuring the integrity and authenticity of tool schemas used by AI agents.
Overview
SchemaPin prevents "MCP Rug Pull" attacks through digital signatures and Trust-On-First-Use (TOFU) key pinning. This Go implementation provides 100% protocol compatibility with the Python and JavaScript versions while following idiomatic Go patterns.
Features
- Cryptographic Security: ECDSA P-256 digital signatures for schema integrity
- Trust-On-First-Use: Automatic key pinning with interactive user prompts
- Key Discovery: Automatic public key discovery via
.well-known/schemapin.json
- Key Revocation: Support for revoked key lists and security warnings
- CLI Tools: Professional command-line tools for key generation, signing, and verification
- Zero Dependencies: Pure Go implementation with single binary deployment
- Cross-Language Compatible: Full interoperability with Python and JavaScript implementations
- Interactive Pinning: User prompts for key decisions with domain policies
- Performance Optimized: Fast ECDSA operations with efficient key storage
Installation
From Source
git clone https://github.com/ThirdKeyAi/schemapin.git
cd schemapin/go
make install
Using Go Install
go install github.com/ThirdKeyAi/schemapin/go/cmd/...@latest
Binary Releases
Download pre-built binaries from the releases page.
Quick Start
1. Generate Keys
# Generate ECDSA P-256 key pair
schemapin-keygen --type ecdsa --developer "Your Company" --contact "security@yourcompany.com" --well-known
# Output:
# ✓ Generated private key: private_key.pem
# ✓ Generated public key: public_key.pem
# ✓ Generated .well-known response: well_known.json
2. Sign a Schema
# Sign a JSON schema file
schemapin-sign --key private_key.pem --schema schema.json --output signed_schema.json
# Or sign inline
echo '{"name": "test", "type": "object"}' | schemapin-sign --key private_key.pem
3. Verify a Schema
# Verify with automatic key discovery
schemapin-verify --schema signed_schema.json --domain example.com --tool-id my-tool
# Verify with explicit public key
schemapin-verify --schema signed_schema.json --public-key public_key.pem
# Output:
# ✅ Schema signature is VALID
# 🔑 Key pinned for future use
# 📋 Developer: Your Company
schemapin-keygen
Generate ECDSA key pairs and .well-known responses.
schemapin-keygen [OPTIONS]
Options:
--type string Key type (ecdsa) (default "ecdsa")
--developer string Developer name for .well-known response
--contact string Contact email for .well-known response
--output-dir string Output directory (default ".")
--private-key string Private key filename (default "private_key.pem")
--public-key string Public key filename (default "public_key.pem")
--well-known Generate .well-known/schemapin.json response
--revoked-keys string Comma-separated list of revoked key files
--schema-version string Schema version (default "1.1")
schemapin-sign
Sign JSON schemas with private keys.
schemapin-sign [OPTIONS]
Options:
--key string Private key file (required)
--schema string Schema file to sign (or use stdin)
--output string Output file (default stdout)
--format string Output format: json, compact (default "json")
schemapin-verify
Verify signed schemas with automatic key discovery.
schemapin-verify [OPTIONS]
Options:
--schema string Signed schema file (required)
--domain string Domain for key discovery
--tool-id string Tool identifier for key pinning
--public-key string Explicit public key file (skips discovery)
--db-path string Key pinning database path (default "~/.schemapin/keys.db")
--auto-pin Automatically pin keys on first use
--interactive Enable interactive key pinning prompts
--timeout duration Discovery timeout (default 10s)
API Documentation
Core Packages
ECDSA key management and signature operations.
// Generate key pair
keyManager := crypto.NewKeyManager()
privateKey, err := keyManager.GenerateKeypair()
// Export to PEM format
privateKeyPEM, err := keyManager.ExportPrivateKeyPEM(privateKey)
publicKeyPEM, err := keyManager.ExportPublicKeyPEM(&privateKey.PublicKey)
// Sign and verify
signatureManager := crypto.NewSignatureManager()
signature, err := signatureManager.SignHash(hash, privateKey)
valid := signatureManager.VerifySignature(hash, signature, &privateKey.PublicKey)
Schema canonicalization and hashing.
// Canonicalize and hash schema
core := core.NewSchemaPinCore()
canonical, err := core.CanonicalizeSchema(schema)
hash := core.HashCanonical(canonical)
// Combined operation
hash, err := core.CanonicalizeAndHash(schema)
High-level workflows for signing and verification.
// Signing workflow
signingWorkflow, err := utils.NewSchemaSigningWorkflow(privateKeyPEM)
signature, err := signingWorkflow.SignSchema(schema)
// Verification workflow
verificationWorkflow, err := utils.NewSchemaVerificationWorkflow(dbPath)
result, err := verificationWorkflow.VerifySchema(ctx, schema, signature, toolID, domain, autoPin)
Key pinning with BoltDB storage.
// Initialize key pinning
keyPinning, err := pinning.NewKeyPinning(dbPath, pinning.PinningModeInteractive, handler)
// Pin and verify keys
err = keyPinning.PinKey(toolID, publicKeyPEM, domain, developerName)
isPinned := keyPinning.IsKeyPinned(toolID)
pinnedKeys, err := keyPinning.ListPinnedKeys()
Interactive user prompts for key decisions.
// Console handler
handler := interactive.NewConsoleInteractiveHandler()
// Callback handler
handler := interactive.NewCallbackInteractiveHandler(
promptCallback,
displayCallback,
warningCallback,
)
// Prompt for key decisions
decision, err := handler.PromptUser(context)
Automatic public key discovery via .well-known endpoints.
// Initialize discovery
discovery := discovery.NewPublicKeyDiscovery()
// Discover keys
publicKeyPEM, err := discovery.GetPublicKeyPEM(ctx, domain)
developerInfo, err := discovery.GetDeveloperInfo(ctx, domain)
isValid, err := discovery.ValidateKeyNotRevoked(ctx, publicKeyPEM, domain)
Examples
Developer Workflow
See examples/developer/main.go
for a complete tool developer example:
cd examples/developer
go run main.go
This demonstrates:
- Key pair generation
- Schema creation and signing
- .well-known response generation
- File output for distribution
Client Verification
See examples/client/main.go
for client verification:
cd examples/client
go run main.go
This demonstrates:
- Loading signed schemas
- Key discovery simulation
- TOFU key pinning
- Signature verification
- Invalid signature detection
Interactive Pinning
See examples/interactive-demo/main.go
:
cd examples/interactive-demo
go run main.go
This demonstrates:
- Console interactive prompts
- Callback-based handlers
- Domain policies
- Key change scenarios
Cross-Language Compatibility
See examples/cross-language-demo/main.go
:
cd examples/cross-language-demo
go run main.go
This demonstrates:
- Verifying Python-generated signatures
- Verifying JavaScript-generated signatures
- Generating signatures for other languages
Project Structure
go/
├── cmd/ # CLI applications
│ ├── schemapin-keygen/ # Key generation tool
│ ├── schemapin-sign/ # Schema signing tool
│ └── schemapin-verify/ # Schema verification tool
├── pkg/ # Public API packages
│ ├── core/ # Schema canonicalization
│ ├── crypto/ # ECDSA operations
│ ├── discovery/ # .well-known discovery
│ ├── pinning/ # Key pinning with BoltDB
│ ├── interactive/ # User interaction
│ └── utils/ # High-level workflows
├── internal/ # Private packages
│ └── version/ # Version information
├── examples/ # Usage examples
│ ├── developer/ # Tool developer workflow
│ ├── client/ # Client verification
│ ├── interactive-demo/ # Interactive pinning
│ └── cross-language-demo/ # Cross-language compatibility
├── tests/ # Integration tests
└── docs/ # Additional documentation
Development
Prerequisites
- Go 1.19 or later
- Make (optional, for convenience)
Build
# Build all CLI tools
make build
# Build specific tool
go build -o bin/schemapin-keygen ./cmd/schemapin-keygen
Test
# Run all tests
make test
# Run with coverage
go test -v -race -coverprofile=coverage.out ./...
# Run integration tests
go test -v ./tests/
# Run specific package tests
go test -v ./pkg/crypto/
Lint
# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Run linter
make lint
# or
golangci-lint run
# Run benchmarks
go test -bench=. ./pkg/crypto/
go test -bench=. ./pkg/core/
# Example output:
# BenchmarkSignature-8 5000 234567 ns/op
# BenchmarkVerification-8 10000 123456 ns/op
Performance characteristics on modern hardware:
Operation |
Time |
Throughput |
Key Generation |
~2ms |
500 keys/sec |
Schema Signing |
~1ms |
1000 sigs/sec |
Signature Verification |
~0.5ms |
2000 verifs/sec |
Schema Canonicalization |
~0.1ms |
10000 schemas/sec |
Security Considerations
Key Storage
- Private keys are stored in PEM format with 0600 permissions
- Key pinning database uses BoltDB with file-level locking
- No keys are stored in memory longer than necessary
Cryptographic Details
- Algorithm: ECDSA with P-256 curve (secp256r1)
- Hash Function: SHA-256
- Signature Format: ASN.1 DER encoding for cross-language compatibility
- Key Format: PKCS#8 for private keys, PKIX for public keys
Trust Model
- TOFU (Trust On First Use): Keys are pinned on first encounter
- Domain Policies: Configure trust levels per domain
- Key Revocation: Support for revoked key lists in .well-known responses
- Interactive Prompts: User confirmation for key changes
Troubleshooting
Common Issues
"Key not found" errors
# Check if .well-known endpoint is accessible
curl https://yourdomain.com/.well-known/schemapin.json
# Verify domain in tool ID matches discovery domain
schemapin-verify --schema schema.json --domain yourdomain.com --tool-id yourdomain.com/tool
"Signature verification failed"
# Check schema format and canonicalization
schemapin-verify --schema schema.json --public-key public_key.pem --verbose
# Verify key format
openssl ec -in private_key.pem -text -noout
Database permission errors
# Check database directory permissions
ls -la ~/.schemapin/
# Reset database if corrupted
rm ~/.schemapin/keys.db
Debug Mode
Enable debug logging:
export SCHEMAPIN_DEBUG=1
schemapin-verify --schema schema.json --domain example.com
Cross-Language Issues
If signatures don't verify across languages:
-
Check schema canonicalization:
# Compare canonical forms
echo '{"b":2,"a":1}' | schemapin-sign --key key.pem --format compact
-
Verify key formats:
# Keys should be identical across implementations
diff go_public_key.pem python_public_key.pem
-
Test with known good signatures:
# Use cross-language demo
cd examples/cross-language-demo
go run main.go
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature-name
- Make changes and add tests
- Run tests:
make test
- Run linter:
make lint
- Commit changes:
git commit -m "Add feature"
- Push to branch:
git push origin feature-name
- Create a Pull Request
Code Style
- Follow standard Go conventions
- Use
gofmt
for formatting
- Add godoc comments for public APIs
- Include tests for new functionality
- Update documentation for user-facing changes
License
MIT License - see LICENSE file for details.
Support