rename example go files for documentation

This commit is contained in:
Evan Jarrett
2025-11-01 10:29:11 -05:00
parent 2d720e4154
commit ff7bc131b2
2 changed files with 0 additions and 439 deletions
@@ -1,225 +0,0 @@
// Package main implements an OPA Gatekeeper External Data Provider for ATProto signature verification.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"time"
)
const (
// DefaultPort is the default HTTP port
DefaultPort = "8080"
// DefaultTrustPolicyPath is the default trust policy file path
DefaultTrustPolicyPath = "/config/trust-policy.yaml"
)
// Server is the HTTP server for the external data provider.
type Server struct {
verifier *Verifier
port string
httpServer *http.Server
}
// ProviderRequest is the request format from Gatekeeper.
type ProviderRequest struct {
Keys []string `json:"keys"`
Values []string `json:"values"`
}
// ProviderResponse is the response format to Gatekeeper.
type ProviderResponse struct {
SystemError string `json:"system_error,omitempty"`
Responses []map[string]interface{} `json:"responses"`
}
// VerificationResult holds the result of verifying a single image.
type VerificationResult struct {
Image string `json:"image"`
Verified bool `json:"verified"`
DID string `json:"did,omitempty"`
Handle string `json:"handle,omitempty"`
SignedAt time.Time `json:"signedAt,omitempty"`
CommitCID string `json:"commitCid,omitempty"`
Error string `json:"error,omitempty"`
}
// NewServer creates a new provider server.
func NewServer(verifier *Verifier, port string) *Server {
return &Server{
verifier: verifier,
port: port,
}
}
// Start starts the HTTP server.
func (s *Server) Start() error {
mux := http.NewServeMux()
// Provider endpoint (called by Gatekeeper)
mux.HandleFunc("/provide", s.handleProvide)
// Health check endpoints
mux.HandleFunc("/health", s.handleHealth)
mux.HandleFunc("/ready", s.handleReady)
// Metrics endpoint (Prometheus)
// TODO: Implement metrics
// mux.HandleFunc("/metrics", s.handleMetrics)
s.httpServer = &http.Server{
Addr: ":" + s.port,
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Printf("Starting ATProto signature verification provider on port %s", s.port)
return s.httpServer.ListenAndServe()
}
// Stop gracefully stops the HTTP server.
func (s *Server) Stop(ctx context.Context) error {
if s.httpServer != nil {
return s.httpServer.Shutdown(ctx)
}
return nil
}
// handleProvide handles the provider endpoint called by Gatekeeper.
func (s *Server) handleProvide(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
// Parse request
var req ProviderRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
log.Printf("ERROR: failed to parse request: %v", err)
http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest)
return
}
log.Printf("INFO: received verification request for %d images", len(req.Values))
// Verify each image
responses := make([]map[string]interface{}, 0, len(req.Values))
for _, image := range req.Values {
result := s.verifyImage(r.Context(), image)
responses = append(responses, structToMap(result))
}
// Send response
resp := ProviderResponse{
Responses: responses,
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("ERROR: failed to encode response: %v", err)
}
}
// verifyImage verifies a single image.
func (s *Server) verifyImage(ctx context.Context, image string) VerificationResult {
start := time.Now()
log.Printf("INFO: verifying image: %s", image)
// Call verifier
verified, metadata, err := s.verifier.Verify(ctx, image)
duration := time.Since(start)
if err != nil {
log.Printf("ERROR: verification failed for %s: %v (duration: %v)", image, err, duration)
return VerificationResult{
Image: image,
Verified: false,
Error: err.Error(),
}
}
if !verified {
log.Printf("WARN: image %s failed verification (duration: %v)", image, duration)
return VerificationResult{
Image: image,
Verified: false,
Error: "signature verification failed",
}
}
log.Printf("INFO: image %s verified successfully (DID: %s, duration: %v)",
image, metadata.DID, duration)
return VerificationResult{
Image: image,
Verified: true,
DID: metadata.DID,
Handle: metadata.Handle,
SignedAt: metadata.SignedAt,
CommitCID: metadata.CommitCID,
}
}
// handleHealth handles health check requests.
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
"version": "1.0.0",
})
}
// handleReady handles readiness check requests.
func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
// TODO: Check dependencies (DID resolver, PDS connectivity)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "ready",
})
}
// structToMap converts a struct to a map for JSON encoding.
func structToMap(v interface{}) map[string]interface{} {
data, _ := json.Marshal(v)
var m map[string]interface{}
json.Unmarshal(data, &m)
return m
}
func main() {
// Load configuration
port := os.Getenv("HTTP_PORT")
if port == "" {
port = DefaultPort
}
trustPolicyPath := os.Getenv("TRUST_POLICY_PATH")
if trustPolicyPath == "" {
trustPolicyPath = DefaultTrustPolicyPath
}
// Create verifier
verifier, err := NewVerifier(trustPolicyPath)
if err != nil {
log.Fatalf("FATAL: failed to create verifier: %v", err)
}
// Create server
server := NewServer(verifier, port)
// Start server
if err := server.Start(); err != nil && err != http.ErrServerClosed {
log.Fatalf("FATAL: server error: %v", err)
}
}
// TODO: Implement verifier.go with ATProto signature verification logic
// TODO: Implement resolver.go with DID resolution
// TODO: Implement crypto.go with K-256 signature verification
@@ -1,214 +0,0 @@
// Package atproto implements a Ratify verifier plugin for ATProto signatures.
package atproto
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/ratify-project/ratify/pkg/common"
"github.com/ratify-project/ratify/pkg/ocispecs"
"github.com/ratify-project/ratify/pkg/referrerstore"
"github.com/ratify-project/ratify/pkg/verifier"
)
const (
// VerifierName is the name of this verifier
VerifierName = "atproto"
// VerifierType is the type of this verifier
VerifierType = "atproto"
// ATProtoSignatureArtifactType is the OCI artifact type for ATProto signatures
ATProtoSignatureArtifactType = "application/vnd.atproto.signature.v1+json"
)
// ATProtoVerifier implements the Ratify ReferenceVerifier interface for ATProto signatures.
type ATProtoVerifier struct {
name string
config ATProtoConfig
resolver *Resolver
verifier *SignatureVerifier
trustStore *TrustStore
}
// ATProtoConfig holds configuration for the ATProto verifier.
type ATProtoConfig struct {
// TrustPolicyPath is the path to the trust policy YAML file
TrustPolicyPath string `json:"trustPolicyPath"`
// DIDResolverTimeout is the timeout for DID resolution
DIDResolverTimeout time.Duration `json:"didResolverTimeout"`
// PDSTimeout is the timeout for PDS XRPC calls
PDSTimeout time.Duration `json:"pdsTimeout"`
// CacheEnabled enables caching of DID documents and public keys
CacheEnabled bool `json:"cacheEnabled"`
// CacheTTL is the cache TTL for DID documents and public keys
CacheTTL time.Duration `json:"cacheTTL"`
}
// ATProtoSignature represents the ATProto signature metadata stored in the OCI artifact.
type ATProtoSignature struct {
Type string `json:"$type"`
Version string `json:"version"`
Subject struct {
Digest string `json:"digest"`
MediaType string `json:"mediaType"`
} `json:"subject"`
ATProto struct {
DID string `json:"did"`
Handle string `json:"handle"`
PDSEndpoint string `json:"pdsEndpoint"`
RecordURI string `json:"recordUri"`
CommitCID string `json:"commitCid"`
SignedAt time.Time `json:"signedAt"`
} `json:"atproto"`
Signature struct {
Algorithm string `json:"algorithm"`
KeyID string `json:"keyId"`
PublicKeyMultibase string `json:"publicKeyMultibase"`
} `json:"signature"`
}
// NewATProtoVerifier creates a new ATProto verifier instance.
func NewATProtoVerifier(name string, config ATProtoConfig) (*ATProtoVerifier, error) {
// Load trust policy
trustStore, err := LoadTrustStore(config.TrustPolicyPath)
if err != nil {
return nil, fmt.Errorf("failed to load trust policy: %w", err)
}
// Create resolver with caching
resolver := NewResolver(config.DIDResolverTimeout, config.CacheEnabled, config.CacheTTL)
// Create signature verifier
verifier := NewSignatureVerifier(config.PDSTimeout)
return &ATProtoVerifier{
name: name,
config: config,
resolver: resolver,
verifier: verifier,
trustStore: trustStore,
}, nil
}
// Name returns the name of this verifier.
func (v *ATProtoVerifier) Name() string {
return v.name
}
// Type returns the type of this verifier.
func (v *ATProtoVerifier) Type() string {
return VerifierType
}
// CanVerify returns true if this verifier can verify the given artifact type.
func (v *ATProtoVerifier) CanVerify(artifactType string) bool {
return artifactType == ATProtoSignatureArtifactType
}
// VerifyReference verifies an ATProto signature artifact.
func (v *ATProtoVerifier) VerifyReference(
ctx context.Context,
subjectRef common.Reference,
referenceDesc ocispecs.ReferenceDescriptor,
store referrerstore.ReferrerStore,
) (verifier.VerifierResult, error) {
// 1. Fetch signature blob from store
sigBlob, err := store.GetBlobContent(ctx, subjectRef, referenceDesc.Digest)
if err != nil {
return v.failureResult(fmt.Sprintf("failed to fetch signature blob: %v", err)), err
}
// 2. Parse ATProto signature metadata
var sigData ATProtoSignature
if err := json.Unmarshal(sigBlob, &sigData); err != nil {
return v.failureResult(fmt.Sprintf("failed to parse signature metadata: %v", err)), err
}
// Validate signature format
if err := v.validateSignature(&sigData); err != nil {
return v.failureResult(fmt.Sprintf("invalid signature format: %v", err)), err
}
// 3. Check trust policy first (fail fast if DID not trusted)
if !v.trustStore.IsTrusted(sigData.ATProto.DID, time.Now()) {
return v.failureResult(fmt.Sprintf("DID %s not in trusted list", sigData.ATProto.DID)),
fmt.Errorf("untrusted DID")
}
// 4. Resolve DID to public key
pubKey, err := v.resolver.ResolveDIDToPublicKey(ctx, sigData.ATProto.DID)
if err != nil {
return v.failureResult(fmt.Sprintf("failed to resolve DID: %v", err)), err
}
// 5. Fetch repository commit from PDS
commit, err := v.verifier.FetchCommit(ctx, sigData.ATProto.PDSEndpoint,
sigData.ATProto.DID, sigData.ATProto.CommitCID)
if err != nil {
return v.failureResult(fmt.Sprintf("failed to fetch commit: %v", err)), err
}
// 6. Verify K-256 signature
if err := v.verifier.VerifySignature(pubKey, commit); err != nil {
return v.failureResult(fmt.Sprintf("signature verification failed: %v", err)), err
}
// 7. Success - return detailed result
return verifier.VerifierResult{
IsSuccess: true,
Name: v.name,
Type: v.Type(),
Message: fmt.Sprintf("Successfully verified ATProto signature for DID %s", sigData.ATProto.DID),
Extensions: map[string]interface{}{
"did": sigData.ATProto.DID,
"handle": sigData.ATProto.Handle,
"signedAt": sigData.ATProto.SignedAt,
"commitCid": sigData.ATProto.CommitCID,
"pdsEndpoint": sigData.ATProto.PDSEndpoint,
},
}, nil
}
// validateSignature validates the signature metadata format.
func (v *ATProtoVerifier) validateSignature(sig *ATProtoSignature) error {
if sig.Type != "io.atcr.atproto.signature" {
return fmt.Errorf("invalid signature type: %s", sig.Type)
}
if sig.ATProto.DID == "" {
return fmt.Errorf("missing DID")
}
if sig.ATProto.PDSEndpoint == "" {
return fmt.Errorf("missing PDS endpoint")
}
if sig.ATProto.CommitCID == "" {
return fmt.Errorf("missing commit CID")
}
if sig.Signature.Algorithm != "ECDSA-K256-SHA256" {
return fmt.Errorf("unsupported signature algorithm: %s", sig.Signature.Algorithm)
}
return nil
}
// failureResult creates a failure result with the given message.
func (v *ATProtoVerifier) failureResult(message string) verifier.VerifierResult {
return verifier.VerifierResult{
IsSuccess: false,
Name: v.name,
Type: v.Type(),
Message: message,
Extensions: map[string]interface{}{
"error": message,
},
}
}
// TODO: Implement resolver.go with DID resolution logic
// TODO: Implement crypto.go with K-256 signature verification
// TODO: Implement config.go with trust policy loading