Steuerkram/tests/36_CryptoTest.php
fds2610-bot 995b52fbd3 Feature: FinTS-Direktverbindung und vereinheitlichte Importquellen
- FinTS-Abruf für Comdirect/DKB/Sparkassen via nemiah/php-fints (4.1)
  inkl. photoTAN-Eingabe-Modal und Multi-Konto-Auswahl
- Neue Tabelle bank_sources (CSV/API/FinTS) ersetzt bank_import_formats
  und bank_connections, Backfill in Migration 068
- Symmetrische Verschlüsselung sensibler Felder (PayPal-Signature,
  FinTS-PIN) mit AES-256-GCM, APP_KEY in .env
- Routes /bank/fints* → /bank/direkt*; "+ Neue Bank…"-Dialog erlaubt
  freie API/FinTS-Anbieter
- Scripts/encrypt_bank_secrets.php als idempotenter Migrations-Helfer
- Neue Tests 36 (Crypto) und 37 (BankSource)
2026-05-22 07:17:00 +02:00

75 lines
3.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
use Steuerkram\Config;
use Steuerkram\Services\Crypto;
/**
* Test 36: Crypto – symmetrische Verschlüsselung für sensible Felder.
*/
class CryptoTest extends TestCase
{
protected string $testName = 'Test 36: Crypto – AES-256-GCM Roundtrip und Idempotenz';
public function run(): void
{
// Test-Key setzen und Config neu laden, damit Config::get('app_key') greift.
$oldKey = $_ENV['APP_KEY'] ?? null;
$_ENV['APP_KEY'] = str_repeat('a1', 32); // 64 Hex-Zeichen
Config::load();
try {
// 1. Roundtrip
$plain = 'streng-geheimes-passwort!';
$cipher = Crypto::encrypt($plain);
$this->assert($cipher !== $plain, 'Verschlüsselter Wert unterscheidet sich vom Klartext');
$this->assert(str_starts_with($cipher, 'enc:v1:'), 'Verschlüsselter Wert hat Magic-Prefix enc:v1:');
$this->assertEquals($plain, Crypto::decrypt($cipher), 'Entschlüsselung liefert Originaltext');
// 2. Leerer String
$this->assertEquals('', Crypto::encrypt(''), 'Leerer Eingabewert wird leer zurückgegeben (encrypt)');
$this->assertEquals('', Crypto::decrypt(''), 'Leerer Eingabewert wird leer zurückgegeben (decrypt)');
// 3. Idempotenz: bereits verschlüsselt → unverändert
$cipher2 = Crypto::encrypt($cipher);
$this->assertEquals($cipher, $cipher2, 'encrypt() auf bereits verschlüsselten Wert ist idempotent');
// 4. Migration: Klartext-Wert ohne Prefix → unverändert durch decrypt()
$this->assertEquals('roher-text', Crypto::decrypt('roher-text'), 'decrypt() reicht Klartext (ohne Prefix) unverändert durch');
// 5. Zwei Verschlüsselungen desselben Klartextes ergeben verschiedene Cipher (zufälliges IV)
$a = Crypto::encrypt('hallo');
$b = Crypto::encrypt('hallo');
$this->assert($a !== $b, 'Zwei encrypt()-Aufrufe ergeben verschiedene Cipher (IV ist zufällig)');
$this->assertEquals('hallo', Crypto::decrypt($a), 'Cipher A entschlüsselt korrekt');
$this->assertEquals('hallo', Crypto::decrypt($b), 'Cipher B entschlüsselt korrekt');
// 6. Falscher Key → Exception
$_ENV['APP_KEY'] = str_repeat('b2', 32);
Config::load();
$threw = false;
try {
Crypto::decrypt($cipher);
} catch (\RuntimeException) {
$threw = true;
}
$this->assert($threw, 'decrypt() mit falschem Schlüssel wirft RuntimeException');
// 7. isEncrypted-Helper
$_ENV['APP_KEY'] = str_repeat('a1', 32);
Config::load();
$this->assert(Crypto::isEncrypted($cipher), 'isEncrypted erkennt verschlüsselten Wert');
$this->assert(!Crypto::isEncrypted('plain'), 'isEncrypted erkennt Klartext nicht als verschlüsselt');
$this->assert(!Crypto::isEncrypted(''), 'isEncrypted ist false für leeren String');
} finally {
// Original-Key wiederherstellen
if ($oldKey === null) {
unset($_ENV['APP_KEY']);
} else {
$_ENV['APP_KEY'] = $oldKey;
}
Config::load();
}
}
}