Changeset 0.9.4 (#54)

This commit is contained in:
2026-02-24 10:44:12 +00:00
parent 90021157e6
commit 5e416d3726
26 changed files with 1333 additions and 108 deletions

64
server/crypto/cache.go Normal file
View File

@@ -0,0 +1,64 @@
package crypto
import (
"sync"
)
// UEKCache holds decrypted User Encryption Keys in memory, keyed by user ID.
// Keys are loaded on login and evicted on logout / token expiry.
//
// This is the trust boundary: UEKs exist in plaintext only in server memory,
// never on disk or in the database.
type UEKCache struct {
mu sync.RWMutex
store map[string][]byte
}
// NewUEKCache creates a new empty cache.
func NewUEKCache() *UEKCache {
return &UEKCache{store: make(map[string][]byte)}
}
// Store caches a UEK for the given user ID.
func (c *UEKCache) Store(userID string, uek []byte) {
c.mu.Lock()
defer c.mu.Unlock()
// Copy to avoid caller mutation
key := make([]byte, len(uek))
copy(key, uek)
c.store[userID] = key
}
// Load retrieves the UEK for a user. Returns (uek, true) if present.
func (c *UEKCache) Load(userID string) ([]byte, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
uek, ok := c.store[userID]
if !ok {
return nil, false
}
// Return a copy
out := make([]byte, len(uek))
copy(out, uek)
return out, true
}
// Evict removes the UEK for a user (logout / session expiry).
func (c *UEKCache) Evict(userID string) {
c.mu.Lock()
defer c.mu.Unlock()
// Zero the memory before deleting
if uek, ok := c.store[userID]; ok {
for i := range uek {
uek[i] = 0
}
}
delete(c.store, userID)
}
// Size returns the number of cached UEKs (for diagnostics).
func (c *UEKCache) Size() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.store)
}