Add the first concrete engine implementation: a CA (PKI) engine that generates
a self-signed root CA at mount time, issues scoped intermediate CAs ("issuers"),
and signs leaf certificates using configurable profiles (server, client, peer).
Engine framework updates:
- Add CallerInfo struct for auth context in engine requests
- Add config parameter to Engine.Initialize for mount-time configuration
- Export Mount.Engine field; add GetEngine/GetMount on Registry
CA engine (internal/engine/ca/):
- Two-tier PKI: root CA → issuers → leaf certificates
- 10 operations: get-root, get-chain, get-issuer, create/delete/list issuers,
issue, get-cert, list-certs, renew
- Certificate profiles with user-overridable TTL, key usages, and key algorithm
- Private keys never stored in barrier; zeroized from memory on seal
- Supports ECDSA, RSA, and Ed25519 key types via goutils/certlib/certgen
Server routes:
- Wire up engine mount/request handlers (replace Phase 1 stubs)
- Add public PKI routes (/v1/pki/{mount}/ca, /ca/chain, /issuer/{name})
for unauthenticated TLS trust bootstrapping
Also includes: ARCHITECTURE.md, deploy config updates, operational tooling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
127 lines
3.8 KiB
Go
127 lines
3.8 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"git.wntrmute.dev/kyle/metacrypt/internal/barrier"
|
|
)
|
|
|
|
// mockEngine implements Engine for testing.
|
|
type mockEngine struct {
|
|
engineType EngineType
|
|
initialized bool
|
|
unsealed bool
|
|
}
|
|
|
|
func (m *mockEngine) Type() EngineType { return m.engineType }
|
|
func (m *mockEngine) Initialize(_ context.Context, _ barrier.Barrier, _ string, _ map[string]interface{}) error {
|
|
m.initialized = true
|
|
return nil
|
|
}
|
|
func (m *mockEngine) Unseal(_ context.Context, _ barrier.Barrier, _ string) error {
|
|
m.unsealed = true
|
|
return nil
|
|
}
|
|
func (m *mockEngine) Seal() error { m.unsealed = false; return nil }
|
|
func (m *mockEngine) HandleRequest(_ context.Context, _ *Request) (*Response, error) {
|
|
return &Response{Data: map[string]interface{}{"ok": true}}, nil
|
|
}
|
|
|
|
type mockBarrier struct{}
|
|
|
|
func (m *mockBarrier) Unseal(_ []byte) error { return nil }
|
|
func (m *mockBarrier) Seal() error { return nil }
|
|
func (m *mockBarrier) IsSealed() bool { return false }
|
|
func (m *mockBarrier) Get(_ context.Context, _ string) ([]byte, error) { return nil, barrier.ErrNotFound }
|
|
func (m *mockBarrier) Put(_ context.Context, _ string, _ []byte) error { return nil }
|
|
func (m *mockBarrier) Delete(_ context.Context, _ string) error { return nil }
|
|
func (m *mockBarrier) List(_ context.Context, _ string) ([]string, error) { return nil, nil }
|
|
|
|
func TestRegistryMountUnmount(t *testing.T) {
|
|
reg := NewRegistry(&mockBarrier{})
|
|
reg.RegisterFactory(EngineTypeTransit, func() Engine {
|
|
return &mockEngine{engineType: EngineTypeTransit}
|
|
})
|
|
|
|
ctx := context.Background()
|
|
if err := reg.Mount(ctx, "default", EngineTypeTransit, nil); err != nil {
|
|
t.Fatalf("Mount: %v", err)
|
|
}
|
|
|
|
mounts := reg.ListMounts()
|
|
if len(mounts) != 1 {
|
|
t.Fatalf("ListMounts: got %d, want 1", len(mounts))
|
|
}
|
|
if mounts[0].Name != "default" {
|
|
t.Errorf("mount name: got %q, want %q", mounts[0].Name, "default")
|
|
}
|
|
|
|
// Duplicate mount should fail.
|
|
if err := reg.Mount(ctx, "default", EngineTypeTransit, nil); err != ErrMountExists {
|
|
t.Fatalf("expected ErrMountExists, got: %v", err)
|
|
}
|
|
|
|
if err := reg.Unmount("default"); err != nil {
|
|
t.Fatalf("Unmount: %v", err)
|
|
}
|
|
|
|
mounts = reg.ListMounts()
|
|
if len(mounts) != 0 {
|
|
t.Fatalf("after unmount: got %d mounts", len(mounts))
|
|
}
|
|
}
|
|
|
|
func TestRegistryUnmountNotFound(t *testing.T) {
|
|
reg := NewRegistry(&mockBarrier{})
|
|
if err := reg.Unmount("nonexistent"); err != ErrMountNotFound {
|
|
t.Fatalf("expected ErrMountNotFound, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRegistryUnknownType(t *testing.T) {
|
|
reg := NewRegistry(&mockBarrier{})
|
|
err := reg.Mount(context.Background(), "test", EngineTypeTransit, nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown engine type")
|
|
}
|
|
}
|
|
|
|
func TestRegistryHandleRequest(t *testing.T) {
|
|
reg := NewRegistry(&mockBarrier{})
|
|
reg.RegisterFactory(EngineTypeTransit, func() Engine {
|
|
return &mockEngine{engineType: EngineTypeTransit}
|
|
})
|
|
|
|
ctx := context.Background()
|
|
reg.Mount(ctx, "test", EngineTypeTransit, nil)
|
|
|
|
resp, err := reg.HandleRequest(ctx, "test", &Request{Operation: "encrypt"})
|
|
if err != nil {
|
|
t.Fatalf("HandleRequest: %v", err)
|
|
}
|
|
if resp.Data["ok"] != true {
|
|
t.Error("expected ok=true in response")
|
|
}
|
|
|
|
_, err = reg.HandleRequest(ctx, "nonexistent", &Request{})
|
|
if err != ErrMountNotFound {
|
|
t.Fatalf("expected ErrMountNotFound, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRegistrySealAll(t *testing.T) {
|
|
reg := NewRegistry(&mockBarrier{})
|
|
reg.RegisterFactory(EngineTypeTransit, func() Engine {
|
|
return &mockEngine{engineType: EngineTypeTransit}
|
|
})
|
|
|
|
ctx := context.Background()
|
|
reg.Mount(ctx, "eng1", EngineTypeTransit, nil)
|
|
reg.Mount(ctx, "eng2", EngineTypeTransit, nil)
|
|
|
|
if err := reg.SealAll(); err != nil {
|
|
t.Fatalf("SealAll: %v", err)
|
|
}
|
|
}
|