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

@@ -212,6 +212,9 @@ func (e *UserEngine) handleRegister(ctx context.Context, req *engine.Request) (*
}
username := req.CallerInfo.Username
if err := engine.ValidateName(username); err != nil {
return nil, fmt.Errorf("user: invalid username: %w", err)
}
e.mu.RLock()
if u, ok := e.users[username]; ok {
pubB64 := base64.StdEncoding.EncodeToString(u.pubKey.Bytes())
@@ -302,6 +305,9 @@ func (e *UserEngine) handleGetPublicKey(_ context.Context, req *engine.Request)
if username == "" {
return nil, fmt.Errorf("user: username is required")
}
if err := engine.ValidateName(username); err != nil {
return nil, err
}
e.mu.RLock()
defer e.mu.RUnlock()
@@ -657,14 +663,16 @@ func (e *UserEngine) handleRotateKey(ctx context.Context, req *engine.Request) (
return nil, fmt.Errorf("user: rotate key: %w", err)
}
// Zeroize old key.
oldRaw := oldState.privKey.Bytes()
crypto.Zeroize(oldRaw)
// Zeroize old key material and drop reference for GC.
crypto.Zeroize(oldState.privBytes)
oldState.privKey = nil
oldState.privBytes = nil
// Update in-memory state.
e.users[caller] = &userState{
privKey: priv,
pubKey: priv.PublicKey(),
privKey: priv,
privBytes: priv.Bytes(),
pubKey: priv.PublicKey(),
config: &UserKeyConfig{
Algorithm: e.config.KeyAlgorithm,
CreatedAt: time.Now().UTC(),
@@ -692,6 +700,9 @@ func (e *UserEngine) handleDeleteUser(ctx context.Context, req *engine.Request)
if username == "" {
return nil, fmt.Errorf("user: username is required")
}
if err := engine.ValidateName(username); err != nil {
return nil, err
}
e.mu.Lock()
defer e.mu.Unlock()
@@ -701,9 +712,10 @@ func (e *UserEngine) handleDeleteUser(ctx context.Context, req *engine.Request)
return nil, ErrUserNotFound
}
// Zeroize private key.
oldRaw := oldState.privKey.Bytes()
crypto.Zeroize(oldRaw)
// Zeroize private key material and drop reference for GC.
crypto.Zeroize(oldState.privBytes)
oldState.privKey = nil
oldState.privBytes = nil
// Delete from barrier.
prefix := e.mountPath + "users/" + username + "/"