mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-04 00:02:05 +00:00
72 lines
1.8 KiB
Go
72 lines
1.8 KiB
Go
package cipher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/infrastructure/encryptedkeys"
|
|
"github.com/warmbly/warmbly/internal/observability/errs"
|
|
)
|
|
|
|
type Cipher struct {
|
|
plainDEK []byte
|
|
}
|
|
|
|
func (s *cipherService) Cipher(ctx context.Context, orgID uuid.UUID) (*Cipher, error) {
|
|
// Cache hit: reuse the decrypted DEK. Any miss or cache error (redis.Nil
|
|
// on first use of an org's key) falls through to the KMS path — a cache
|
|
// problem must never block crypto.
|
|
if key, err := s.getDecryptedKey(ctx, orgID); err == nil && len(key) > 0 {
|
|
return &Cipher{plainDEK: key}, nil
|
|
}
|
|
|
|
key, err := s.loadOrCreateDEK(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := s.saveDecryptedKey(ctx, orgID, key); err != nil {
|
|
errs.CaptureException(err)
|
|
}
|
|
|
|
return &Cipher{
|
|
plainDEK: key,
|
|
}, nil
|
|
}
|
|
|
|
// loadOrCreateDEK returns the organization's stored DEK, creating it on first
|
|
// use. The stored key always wins: a concurrent first use adopts it.
|
|
func (s *cipherService) loadOrCreateDEK(ctx context.Context, orgID uuid.UUID) ([]byte, error) {
|
|
encDEKB64, err := s.encryptedKeys.Get(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if encDEKB64 != "" {
|
|
return s.kms.GetDecryptedKey(ctx, encDEKB64)
|
|
}
|
|
|
|
key, encryptedDEK, err := s.kms.GenerateDataKey(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = s.encryptedKeys.Put(ctx, orgID, encryptedDEK)
|
|
if err == nil {
|
|
return key, nil
|
|
}
|
|
if !errors.Is(err, encryptedkeys.ErrAlreadyExists) {
|
|
return nil, err
|
|
}
|
|
|
|
// Another caller stored this organization's DEK first; ours is discarded unused.
|
|
encDEKB64, err = s.encryptedKeys.Get(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if encDEKB64 == "" {
|
|
return nil, fmt.Errorf("cipher: dek for organization %s reported present but not readable", orgID)
|
|
}
|
|
return s.kms.GetDecryptedKey(ctx, encDEKB64)
|
|
}
|