# barox transfer

Selbst gehostete Webanwendung für den **Ende-zu-Ende-verschlüsselten** Austausch
vertraulicher Daten zwischen barox-Mitarbeitern und Kunden (intern & extern).

- **Zero-Knowledge:** Sämtliche Ver-/Entschlüsselung passiert im Browser
  (libsodium/WebAssembly). Der Server speichert ausschließlich Chiffrate und
  für die jeweiligen Empfänger gewickelte Schlüssel – niemals Klartext,
  Dateinamen, Passphrasen oder entschlüsselte Schlüssel.
- **Interne Anmeldung:** Microsoft 365 / Azure AD (OIDC, Auth-Code-Flow + PKCE).
- **Externe Empfänger:** Abruf über einen unrätselbaren Link; das **Passwort**
  wird vom Absender separat (out-of-band, z. B. telefonisch) übermittelt und
  erreicht den Server nie.
- **Recovery:** Beim Schlüssel-Setup erhält jeder Nutzer einen einmaligen
  Wiederherstellungs-Code (zero-knowledge bleibt erhalten).
- **Große Dateien:** Gechunkte Streaming-Verschlüsselung
  (XChaCha20-Poly1305), resumierbarer Upload, Range-Download.

Keine schweren Frameworks, **kein Composer nötig** – eigener PSR-4-Autoloader,
eigener OIDC-Client (cURL) und eigener SMTP-Client.

---

## Architektur in Kürze

| Schicht        | Verfahren |
|----------------|-----------|
| Identität      | X25519-Schlüsselpaar pro Nutzer (`crypto_box`) |
| Passphrase-KDF | Argon2id (`crypto_pwhash`, MODERATE) |
| Key-Wrapping   | `crypto_secretbox` (privater Schlüssel mit Passphrase & Recovery-Code) |
| Empfänger-Wrap | intern: `crypto_box_seal` an Public Key · extern: Argon2id(Passwort) |
| Datei-Streaming| `crypto_secretstream_xchacha20poly1305` (chunkweise) |
| Metadaten      | Dateinamen/Nachricht mit dem File-Key verschlüsselt |

Ablauf siehe Quellcode-Kommentare in [public/assets/js/crypto.js](public/assets/js/crypto.js).

---

## Voraussetzungen

- PHP **8.2+** mit Extensions: `pdo_mysql`, `openssl`, `curl`, `mbstring`,
  `fileinfo` (Server-seitiges `sodium` wird **nicht** benötigt).
- MySQL 8 / MariaDB 10.4+.
- Ein Webserver (Nginx + PHP-FPM oder Apache) mit **HTTPS** (zwingend).
- Eine Azure-App-Registrierung (siehe unten).
- Ein SMTP-Postausgang (z. B. Microsoft 365).

---

## Installation (VPS)

```bash
# 1) Code nach /var/www/barox-transfer kopieren
# 2) Konfiguration anlegen
cp .env.example .env
# .env ausfüllen (DB, Azure, SMTP, APP_URL, STORAGE_PATH, APP_KEY)
php -r "echo 'APP_KEY=' . bin2hex(random_bytes(32)) . PHP_EOL;"   # Wert in .env eintragen

# 3) Speicherverzeichnis außerhalb des Webroots anlegen
sudo mkdir -p /var/lib/barox-transfer/storage
sudo chown -R www-data:www-data /var/lib/barox-transfer
sudo chmod 700 /var/lib/barox-transfer/storage

# 4) Datenbank/Schema einspielen
mysql -e "CREATE DATABASE barox_transfer CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
php bin/migrate.php

# 5) Webserver: Webroot = /var/www/barox-transfer/public
#    Nginx-Beispiel: deploy/nginx.conf.example   (Apache: public/.htaccess ist enthalten)
```

> **Wichtig:** Der Webroot ist ausschließlich `public/`. `.env`, `src/`,
> `storage/` dürfen niemals direkt erreichbar sein.

> **PHP-Limit:** `post_max_size` muss größer als `CHUNK_BYTES` sein (Default 4 MB).
> Beispiel in `php.ini`: `post_max_size = 8M`. Die Gesamtgröße wird über
> `MAX_TRANSFER_BYTES` gesteuert – einzelne Uploads erfolgen gechunkt, daher
> sind keine großen `upload_max_filesize`-Werte nötig.

### libsodium (Browser)

Die WebAssembly-Bibliothek liegt bereits unter
`public/assets/vendor/` (`libsodium-sumo.js` + `libsodium-wrappers.js`).
Falls sie aktualisiert werden soll:

```bash
cd public/assets/vendor
curl -fsSL -o libsodium-sumo.js \
  https://cdn.jsdelivr.net/npm/libsodium-sumo@0.7.15/dist/modules-sumo/libsodium-sumo.js
curl -fsSL -o libsodium-wrappers.js \
  https://cdn.jsdelivr.net/npm/libsodium-wrappers-sumo@0.7.15/dist/modules-sumo/libsodium-wrappers.js
```

### Azure-App-Registrierung (Microsoft 365)

1. Azure-Portal → *App-Registrierungen* → *Neue Registrierung*.
2. Plattform **Web**, Redirect-URI: `https://transfer.barox.de/auth/callback`.
3. Unter *Zertifikate & Geheimnisse* ein **Client-Secret** erstellen.
4. *API-Berechtigungen*: delegiert `openid`, `profile`, `email` (Standard).
5. Werte in `.env` eintragen: `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`,
   `AZURE_CLIENT_SECRET`, `AZURE_REDIRECT_URI`.
6. Optional `ALLOWED_EMAIL_DOMAINS` setzen, um die Anmeldung auf barox-Domains
   zu beschränken.

### Sprachen (Deutsch / Englisch)

Die Oberfläche ist zweisprachig. Die Sprache wird per Cookie gewählt
(Umschalter **DE/EN** oben rechts) bzw. aus dem Browser (`Accept-Language`)
abgeleitet; Standard ist Deutsch. Übersetzungen liegen in `lang/de.php` und
`lang/en.php` (Server-Templates **und** Frontend-Texte via eingebettetem
JSON-Block). Beim Erstellen eines **Transfers** oder einer **Einladung** lässt
sich die Sprache der **E-Mails** (und bei Einladungen der Upload-Seite) separat
wählen – unabhängig von der eigenen UI-Sprache.

### Administrator ernennen

Ein Nutzer muss sich zuerst einmal anmelden (damit er in der DB existiert),
danach:

```bash
php bin/make-admin.php benutzer@barox.ch        # zum Admin machen
php bin/make-admin.php benutzer@barox.ch user   # wieder zurückstufen
```

Admins sehen unter **/admin** alle Transfers, alle Einladungen und den freien
Speicherplatz – ausschließlich Metadaten, Inhalte bleiben Ende-zu-Ende-verschlüsselt.

### Cron (Aufräumen abgelaufener Transfers)

```cron
0 * * * * php /var/www/barox-transfer/bin/cleanup.php >> /var/log/barox-transfer-cleanup.log 2>&1
```

---

## Lokale Entwicklung

```bash
cp .env.example .env   # APP_ENV=development, lokale DB/Storage eintragen
php bin/migrate.php
php -S 127.0.0.1:8000 -t public public/index.php
```

In `development` wird HTTPS nicht erzwungen (Cookies dann ohne `Secure`).
Für den vollständigen Azure-Login wird dennoch eine erreichbare Redirect-URI
benötigt (z. B. via Tunnel).

---

## Benutzung

1. **Anmelden** mit Microsoft 365.
2. **Verschlüsselung einrichten:** Passphrase wählen, Wiederherstellungs-Code
   sicher verwahren.
3. **Senden:** Dateien wählen, Empfänger-E-Mails eingeben.
   - Bekannte interne Nutzer werden automatisch erkannt (Versiegelung an ihren
     Public Key) und per E-Mail benachrichtigt.
   - Unbekannte Adressen werden als externe Empfänger behandelt: die App erzeugt
     ein starkes Passwort. Den **Link** verschickt das System per E-Mail, das
     **Passwort** teilt der Absender out-of-band mit (Telefon/SMS).
4. **Empfangen:**
   - Intern: `/t/{uuid}` öffnen, Passphrase eingeben, herunterladen.
   - Extern: Link öffnen, Passwort eingeben, herunterladen.
5. **Kunden zum Hochladen einladen (eingehende Uploads):** Im Dashboard einen
   Einladungslink erstellen (optional direkt per E-Mail an den Kunden). Der Kunde
   öffnet `/u/{token}`, lädt Dateien hoch – diese werden im Browser verschlüsselt
   und an den **öffentlichen Schlüssel des Mitarbeiters versiegelt**
   (`crypto_box_seal`), sodass nur dieser sie entschlüsseln kann. Der Mitarbeiter
   sieht alle Einladungen samt Status und empfangenen Uploads im Dashboard und
   kann jede Einladung jederzeit widerrufen. Jeder Upload erscheint als Transfer
   unter `/t/{uuid}` und wird mit der Passphrase entschlüsselt.

---

## Sicherheits-Eigenschaften

- HTTPS erzwungen, HSTS, strenge CSP (`script-src 'self'`), `X-Frame-Options`,
  `nosniff`, sichere Cookies (`HttpOnly`/`Secure`/`SameSite`).
- CSRF-Schutz (Synchronizer-Token) für alle state-changing Requests.
- Rate-Limiting der externen Abruf-Endpunkte.
- Datei-Chiffrate außerhalb des Webroots; Auslieferung nur über PHP mit
  Zugriffsprüfung, Ablauf- und Download-Limits.
- Lückenloses Audit-Log ohne Geheimnisse.
- Server kann selbst bei vollständiger Kompromittierung keine Inhalte
  entschlüsseln (kein Schlüsselmaterial im Klartext vorhanden).

### Restrisiken / bewusste Entscheidungen

- Empfänger-**E-Mail-Adressen** und Transfer-Metadaten (Größen, Zeitpunkte) sind
  dem Server bekannt – nötig für Benachrichtigung und Zugriffssteuerung.
- Verliert ein Nutzer **Passphrase und** Recovery-Code, sind empfangene Daten
  unwiederbringlich verloren (Eigenschaft von Zero-Knowledge).
- Externe Sicherheit hängt an der Stärke des (app-generierten) Passworts und der
  Geheimhaltung des Links; beide werden out-of-band/per E-Mail getrennt verteilt.

---

## Projektstruktur

```
public/            Webroot (Front-Controller, Assets, libsodium)
src/Controller     HTTP-Endpunkte
src/Service        Auth, AzureOidc, Mailer, Storage, RateLimiter, AuditLogger
src/Repository     PDO-Datenzugriff
src/Middleware     SecurityHeaders, Csrf, AuthGuard
src/Support        Router, Request, Response, Config, Db, Session, Autoloader
templates/         Server-Views + E-Mail-Templates
db/                schema.sql + Migrationen
bin/               migrate.php, cleanup.php
deploy/            nginx.conf.example
```
