package crypto import ( "context" "fmt" "io" ) // Envelope ist das Ergebnis einer Seal-Operation: der Chiffretext- // Stream plus der mit dem Tenant-KEK verpackte DEK, der zusammen mit // dem Objekt persistiert werden muss (siehe mail/internal/encstorage). type Envelope struct { Ciphertext io.Reader WrappedDEK []byte } // Service verbindet KEKProvider mit den Envelope-Operationen — Aufrufer // (mail/internal/encstorage) rufen ausschließlich Service auf, nie die // Einzelfunktionen aus envelope.go direkt. type Service struct { kek KEKProvider } func NewService(kek KEKProvider) *Service { return &Service{kek: kek} } // Seal erzeugt einen neuen DEK (Akzeptanzkriterium 1), verschlüsselt // plaintext damit und verpackt den DEK mit dem aktuellen Tenant-KEK // (Akzeptanzkriterium 2 — der KEK wird bei JEDEM Aufruf frisch von Core // bezogen, nie zwischengespeichert). func (s *Service) Seal(ctx context.Context, tenantSlug string, plaintext io.Reader) (*Envelope, error) { dek, err := GenerateDEK() if err != nil { return nil, err } ciphertext, err := EncryptStream(dek, plaintext) if err != nil { return nil, err } kek, err := s.kek.TenantKEK(ctx, tenantSlug) if err != nil { return nil, fmt.Errorf("crypto: tenant-kek beziehen: %w", err) } wrappedDEK, err := WrapDEK(kek, dek) if err != nil { return nil, err } return &Envelope{Ciphertext: ciphertext, WrappedDEK: wrappedDEK}, nil } // Open entpackt den DEK mit dem aktuellen Tenant-KEK (Akzeptanzkriterium // 3: nur mit gültigem, mandantenbezogenem Schlüssel möglich — ein // falscher Tenant-Slug liefert entweder einen falschen KEK von Core // [dann schlägt UnwrapDEK fehl] oder Core verweigert den Zugriff direkt) // und entschlüsselt ciphertext damit. func (s *Service) Open(ctx context.Context, tenantSlug string, wrappedDEK []byte, ciphertext io.Reader) (io.Reader, error) { kek, err := s.kek.TenantKEK(ctx, tenantSlug) if err != nil { return nil, fmt.Errorf("crypto: tenant-kek beziehen: %w", err) } dek, err := UnwrapDEK(kek, wrappedDEK) if err != nil { return nil, err } return DecryptStream(dek, ciphertext) }