Changeset 0.9.4 (#54)
This commit is contained in:
72
server/crypto/resolver.go
Normal file
72
server/crypto/resolver.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package crypto
|
||||
|
||||
// KeyResolver decrypts API keys using the appropriate tier:
|
||||
// - global/team → env-derived key
|
||||
// - personal → user's UEK from cache
|
||||
type KeyResolver struct {
|
||||
envKey []byte // derived from ENCRYPTION_KEY
|
||||
uekCache *UEKCache
|
||||
}
|
||||
|
||||
// NewKeyResolver creates a resolver. envKey may be nil if ENCRYPTION_KEY is
|
||||
// not set (fresh install with no providers — decryption will fail gracefully).
|
||||
func NewKeyResolver(envKey []byte, cache *UEKCache) *KeyResolver {
|
||||
return &KeyResolver{envKey: envKey, uekCache: cache}
|
||||
}
|
||||
|
||||
// Decrypt resolves an API key given its encrypted form, nonce, scope, and
|
||||
// the requesting user's ID (needed for personal-scope keys).
|
||||
func (r *KeyResolver) Decrypt(ciphertext, nonce []byte, keyScope, userID string) (string, error) {
|
||||
if len(ciphertext) == 0 {
|
||||
return "", nil // No key stored
|
||||
}
|
||||
|
||||
switch keyScope {
|
||||
case "global", "team":
|
||||
if r.envKey == nil {
|
||||
return "", ErrNoEncryptionKey
|
||||
}
|
||||
return Decrypt(ciphertext, nonce, r.envKey)
|
||||
|
||||
case "personal":
|
||||
uek, ok := r.uekCache.Load(userID)
|
||||
if !ok {
|
||||
return "", ErrVaultLocked
|
||||
}
|
||||
return Decrypt(ciphertext, nonce, uek)
|
||||
|
||||
default:
|
||||
// Fallback: try env key (covers pre-migration or unknown scopes)
|
||||
if r.envKey == nil {
|
||||
return "", ErrNoEncryptionKey
|
||||
}
|
||||
return Decrypt(ciphertext, nonce, r.envKey)
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptForScope encrypts an API key with the appropriate key for the scope.
|
||||
func (r *KeyResolver) EncryptForScope(plaintext, keyScope, userID string) ([]byte, []byte, error) {
|
||||
if plaintext == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
switch keyScope {
|
||||
case "personal":
|
||||
uek, ok := r.uekCache.Load(userID)
|
||||
if !ok {
|
||||
return nil, nil, ErrVaultLocked
|
||||
}
|
||||
return Encrypt(plaintext, uek)
|
||||
|
||||
default: // global, team
|
||||
if r.envKey == nil {
|
||||
return nil, nil, ErrNoEncryptionKey
|
||||
}
|
||||
return Encrypt(plaintext, r.envKey)
|
||||
}
|
||||
}
|
||||
|
||||
// HasEnvKey returns true if the platform encryption key is available.
|
||||
func (r *KeyResolver) HasEnvKey() bool {
|
||||
return r.envKey != nil
|
||||
}
|
||||
Reference in New Issue
Block a user