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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func setupSeal(t *testing.T) (*Manager, func()) {
|
||||
t.Fatalf("migrate: %v", err)
|
||||
}
|
||||
b := barrier.NewAESGCMBarrier(database)
|
||||
mgr := NewManager(database, b, slog.Default())
|
||||
mgr := NewManager(database, b, nil, slog.Default())
|
||||
return mgr, func() { _ = database.Close() }
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestSealCheckInitializedPersists(t *testing.T) {
|
||||
database, _ := db.Open(dbPath)
|
||||
_ = db.Migrate(database)
|
||||
b := barrier.NewAESGCMBarrier(database)
|
||||
mgr := NewManager(database, b, slog.Default())
|
||||
mgr := NewManager(database, b, nil, slog.Default())
|
||||
_ = mgr.CheckInitialized()
|
||||
params := crypto.Argon2Params{Time: 1, Memory: 64 * 1024, Threads: 1}
|
||||
_ = mgr.Initialize(context.Background(), []byte("password"), params)
|
||||
@@ -113,7 +113,7 @@ func TestSealCheckInitializedPersists(t *testing.T) {
|
||||
database2, _ := db.Open(dbPath)
|
||||
defer func() { _ = database2.Close() }()
|
||||
b2 := barrier.NewAESGCMBarrier(database2)
|
||||
mgr2 := NewManager(database2, b2, slog.Default())
|
||||
mgr2 := NewManager(database2, b2, nil, slog.Default())
|
||||
_ = mgr2.CheckInitialized()
|
||||
if mgr2.State() != StateSealed {
|
||||
t.Fatalf("state after reopen: got %v, want Sealed", mgr2.State())
|
||||
|
||||
Reference in New Issue
Block a user