Envelope-Encryption: DEK (32 Byte) frisch je Objekt, AES-256-GCM fuer Objektinhalt, DEK selbst mit Tenant-KEK verpackt (WrapDEK/UnwrapDEK). KEK kommt ausschliesslich ueber HTTPKEKProvider von Core API-10 (internal/kek.Handler.TenantKEKHandler-Vertrag, Service-Credential- authentifiziert), wird nie persistiert - jeder Seal/Open-Aufruf bezieht ihn frisch. RewrapDEK fuer KEK-Rotation ohne Neuverschluesselung der Objekte (nur der DEK-Wrapper wird neu verpackt, Chiffretext bleibt unveraendert). Auf 192.168.1.131 verifiziert: Round-Trip Encrypt/Decrypt, manipulierter Chiffretext UND falscher Schluessel werden beide ueber GCM-Auth-Tag abgelehnt, KEK-Rotation real getestet (RewrapDEK, altes Objekt danach mit neuem KEK weiterhin lesbar, alter KEK funktioniert nicht mehr). Befund dokumentiert: Cores internal/kek.Handler (Gegenstelle fuer den KEK-Bezug) ist noch in keinem cmd/*/main.go verdrahtet, dieselbe Fehlerklasse wie FDN-03/QA-05/AUD-06. HTTPKEKProvider daher gegen den dokumentierten Vertrag getestet, nicht gegen eine laufende Core-Instanz. Siehe dms/docs/FDN-09-PRUEFPROTOKOLL.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
81 lines
2.6 KiB
Go
81 lines
2.6 KiB
Go
package crypto
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
)
|
|
|
|
// KEKProvider liefert den aktuellen, entschluesselten Tenant-KEK
|
|
// (Akzeptanzkriterium 2: KEK kommt ausschliesslich von Core API-10).
|
|
// Schmale Schnittstelle, damit Tests einen Fake statt eines echten
|
|
// HTTP-Aufrufs einsetzen koennen.
|
|
type KEKProvider interface {
|
|
TenantKEK(ctx context.Context, tenantSlug string) ([]byte, error)
|
|
}
|
|
|
|
// tenantKEKResponse entspricht Core internal/kek.tenantKEKResponse
|
|
// (JSON-Vertrag: tenant_kek_base64) — dieselbe Struktur, hier gespiegelt,
|
|
// da DMS Cores internal/-Pakete nicht importieren kann.
|
|
type tenantKEKResponse struct {
|
|
TenantKEKBase64 string `json:"tenant_kek_base64"`
|
|
}
|
|
|
|
// HTTPKEKProvider bezieht den Tenant-KEK ueber Cores KEK-Handler
|
|
// (internal/kek.Handler.TenantKEKHandler, API-10), authentifiziert ueber
|
|
// dasselbe Service-Credential-Verfahren wie jeder andere Modul-Core-Aufruf
|
|
// (API-02) — identisches Muster wie internal/storage.HTTPUsageReporter aus
|
|
// FDN-03.
|
|
type HTTPKEKProvider struct {
|
|
endpointURL string
|
|
clientID string
|
|
clientSecret string
|
|
httpClient *http.Client
|
|
}
|
|
|
|
func NewHTTPKEKProvider(endpointURL, clientID, clientSecret string, httpClient *http.Client) *HTTPKEKProvider {
|
|
if httpClient == nil {
|
|
httpClient = http.DefaultClient
|
|
}
|
|
return &HTTPKEKProvider{endpointURL: endpointURL, clientID: clientID, clientSecret: clientSecret, httpClient: httpClient}
|
|
}
|
|
|
|
func (p *HTTPKEKProvider) TenantKEK(ctx context.Context, tenantSlug string) ([]byte, error) {
|
|
u, err := url.Parse(p.endpointURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("crypto: kek-endpunkt-url ungueltig: %w", err)
|
|
}
|
|
q := u.Query()
|
|
q.Set("tenant", tenantSlug)
|
|
u.RawQuery = q.Encode()
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("crypto: kek-anfrage aufbauen: %w", err)
|
|
}
|
|
req.Header.Set("X-Nexarch-Client-Id", p.clientID)
|
|
req.Header.Set("X-Nexarch-Client-Secret", p.clientSecret)
|
|
|
|
resp, err := p.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("crypto: kek-anfrage senden: %w", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("crypto: kek-bezug von core abgelehnt: status %d", resp.StatusCode)
|
|
}
|
|
|
|
var body tenantKEKResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
|
return nil, fmt.Errorf("crypto: kek-antwort dekodieren: %w", err)
|
|
}
|
|
kek, err := base64.StdEncoding.DecodeString(body.TenantKEKBase64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("crypto: kek base64-dekodieren: %w", err)
|
|
}
|
|
return kek, nil
|
|
}
|