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:
@@ -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 + "/"
|
||||
|
||||
Reference in New Issue
Block a user