Fix ECDH zeroization, add audit logging, and remediate high findings

- Fix #61: handleRotateKey and handleDeleteUser now zeroize stored
  privBytes instead of calling Bytes() (which returns a copy). New
  state populates privBytes; old references nil'd for GC.
- Add audit logging subsystem (internal/audit) with structured event
  recording for cryptographic operations.
- Add audit log engine spec (engines/auditlog.md).
- Add ValidateName checks across all engines for path traversal (#48).
- Update AUDIT.md: all High findings resolved (0 open).
- Add REMEDIATION.md with detailed remediation tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 14:04:39 -07:00
parent b33d1f99a0
commit 5c5d7e184e
24 changed files with 1699 additions and 72 deletions

View File

@@ -10,6 +10,7 @@ import (
"sync"
"time"
"git.wntrmute.dev/kyle/metacrypt/internal/audit"
"git.wntrmute.dev/kyle/metacrypt/internal/barrier"
"git.wntrmute.dev/kyle/metacrypt/internal/crypto"
)
@@ -54,6 +55,7 @@ type Manager struct {
lockoutUntil time.Time
db *sql.DB
barrier *barrier.AESGCMBarrier
audit *audit.Logger
logger *slog.Logger
mek []byte
state ServiceState
@@ -62,10 +64,11 @@ type Manager struct {
}
// NewManager creates a new seal manager.
func NewManager(db *sql.DB, b *barrier.AESGCMBarrier, logger *slog.Logger) *Manager {
func NewManager(db *sql.DB, b *barrier.AESGCMBarrier, auditLog *audit.Logger, logger *slog.Logger) *Manager {
return &Manager{
db: db,
barrier: b,
audit: auditLog,
logger: logger,
state: StateUninitialized,
}
@@ -223,6 +226,10 @@ func (m *Manager) Unseal(password []byte) error {
mek, err := crypto.Decrypt(kwk, encryptedMEK, nil)
if err != nil {
m.logger.Debug("unseal failed: invalid password")
m.audit.Log(context.Background(), audit.Event{
Caller: "operator", Operation: "unseal", Outcome: "denied",
Error: "invalid password",
})
return ErrInvalidPassword
}
@@ -235,6 +242,9 @@ func (m *Manager) Unseal(password []byte) error {
m.mek = mek
m.state = StateUnsealed
m.unsealAttempts = 0
m.audit.Log(context.Background(), audit.Event{
Caller: "operator", Operation: "unseal", Outcome: "success",
})
m.logger.Debug("unseal succeeded, barrier unsealed")
return nil
}
@@ -340,6 +350,9 @@ func (m *Manager) Seal() error {
}
_ = m.barrier.Seal()
m.state = StateSealed
m.audit.Log(context.Background(), audit.Event{
Caller: "operator", Operation: "seal", Outcome: "success",
})
m.logger.Debug("service sealed")
return nil
}