#!/usr/bin/env bash set -uo pipefail # CASAN Plan-16 SEC-23 (23.10, MT-02) — per-tenant encryption at rest (local-key MVP). # # Sensitive state (audit / telemetry) is encrypted with a PER-TENANT key so tenant B # — or an admin of B — cannot read tenant A's plaintext on disk. The key lives under # the tenant partition (0600) and differs per tenant, so a ciphertext produced by A # cannot be decrypted with B's key. Production form uses Vault Transit (23.11, needs # infra); this is the offline form. # # Usage: # tenant-crypt.sh encrypt # tenant-crypt.sh decrypt # Env: CASAN_TENANT_ID (key is tenant-specific; prod requires it — tenant-store). # Exit: 0 ok · 2 crypto failure (e.g. wrong tenant key) · 3 tenant/key/openssl error · 64 usage. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TS="$SCRIPT_DIR/tenant-store.sh" CMD="${1:-}"; IN="${2:-}"; OUT="${3:-}" command -v openssl >/dev/null 2>&1 || { echo "TENANT_CRYPT_OPENSSL_UNAVAILABLE" >&2; exit 3; } [[ -n "$CMD" && -n "$IN" && -n "$OUT" ]] || { echo "usage: tenant-crypt.sh {encrypt|decrypt} " >&2; exit 64; } [[ -f "$IN" ]] || { echo "TENANT_CRYPT_INPUT_MISSING file=$IN" >&2; exit 3; } # Per-tenant key (created once, 0600). tenant-store fails closed on an invalid/missing # tenant in prod; in dev it resolves under the 'default' tenant. KEYFILE="$(bash "$TS" resolve keys/at-rest.key 2>/dev/null)" || { echo "TENANT_CRYPT_DENIED (tenant unresolved)" >&2; exit 3; } if [[ ! -f "$KEYFILE" ]]; then openssl rand -base64 48 > "$KEYFILE" 2>/dev/null || { echo "TENANT_CRYPT_KEYGEN_FAILED" >&2; exit 3; } chmod 600 "$KEYFILE" 2>/dev/null || true fi case "$CMD" in encrypt) if openssl enc -aes-256-cbc -pbkdf2 -salt -in "$IN" -out "$OUT" -pass "file:$KEYFILE" 2>/dev/null; then echo "TENANT_ENCRYPTED out=$OUT" exit 0 fi echo "TENANT_ENCRYPT_FAILED" >&2; exit 2 ;; decrypt) if openssl enc -d -aes-256-cbc -pbkdf2 -in "$IN" -out "$OUT" -pass "file:$KEYFILE" 2>/dev/null; then echo "TENANT_DECRYPTED out=$OUT" exit 0 fi rm -f "$OUT" 2>/dev/null || true echo "TENANT_DECRYPT_FAILED (wrong tenant key or corrupt ciphertext)" >&2; exit 2 ;; *) echo "Usage: tenant-crypt.sh {encrypt|decrypt} " >&2 exit 64 ;; esac