Enchantrix/pkg/keyserver/server.go
Claude 447f3ccaca
feat: add Keyserver Secure Environment (SE) for key isolation
Introduces an in-process keyserver that holds cryptographic key material
and exposes operations by opaque key ID — callers (including AI agents)
never see raw key bytes.

New packages:
- pkg/keystore: Trix-based encrypted key store with Argon2id master key
- pkg/keyserver: KeyServer interface, composite crypto ops, session/ACL,
  audit logging

New CLI commands:
- trix keystore init/import/generate/list/delete
- trix keyserver start, trix keyserver session create

Specification: RFC-0005-Keyserver-Secure-Environment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 21:30:31 +00:00

87 lines
2.2 KiB
Go

package keyserver
import (
"context"
"fmt"
"sync"
"github.com/Snider/Enchantrix/pkg/keystore"
)
// Server is an in-process KeyServer backed by a keystore.Store.
// It holds the store open and performs all crypto operations internally —
// callers only see key IDs and encrypted results.
type Server struct {
store *keystore.Store
mu sync.RWMutex
}
// NewServer creates a new keyserver backed by the given store.
func NewServer(store *keystore.Store) *Server {
return &Server{store: store}
}
// GenerateKey creates a new random key of the specified type.
func (s *Server) GenerateKey(ctx context.Context, keyType keystore.KeyType, label string) (string, error) {
s.mu.Lock()
defer s.mu.Unlock()
id, err := s.store.Generate(keyType, label)
if err != nil {
return "", fmt.Errorf("keyserver: generate key: %w", err)
}
return id, nil
}
// ImportPassword derives a key from a password and stores it.
func (s *Server) ImportPassword(ctx context.Context, password string, label string) (string, error) {
s.mu.Lock()
defer s.mu.Unlock()
id, err := s.store.Import(password, label)
if err != nil {
return "", fmt.Errorf("keyserver: import password: %w", err)
}
return id, nil
}
// DeleteKey removes a key from the store.
func (s *Server) DeleteKey(ctx context.Context, keyID string) error {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.store.Delete(keyID); err != nil {
return fmt.Errorf("keyserver: delete key: %w", err)
}
return nil
}
// ListKeys returns metadata for all keys (no key material).
func (s *Server) ListKeys(ctx context.Context) ([]keystore.Entry, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return s.store.List(), nil
}
// GetPublicKey returns the public component of an asymmetric key.
func (s *Server) GetPublicKey(ctx context.Context, keyID string) ([]byte, error) {
return s.getPublicKey(keyID)
}
// getKey retrieves the raw key material for internal use. Never exposed externally.
func (s *Server) getKey(keyID string) (*keystore.Entry, error) {
entry, err := s.store.Get(keyID)
if err != nil {
return nil, fmt.Errorf("keyserver: %w", err)
}
return entry, nil
}
// Store returns the underlying keystore for direct access (e.g. for Save).
func (s *Server) Store() *keystore.Store {
return s.store
}