<?php
/**
 * login.php — Maximum security login
 * 
 * Security features:
 * 1. Bcrypt password hashing (cost 12)
 * 2. TOTP 2FA (time-based one-time password)
 * 3. Brute-force protection (5 attempts → 15min lockout, escalating)
 * 4. CSRF token on all forms
 * 5. Secure session settings (httponly, secure, samesite)
 * 6. Session fingerprinting (IP + User-Agent binding)
 * 7. Session expiry (2 hours idle, 8 hours max)
 * 8. Rate limiting per IP
 * 9. Timing-safe string comparison
 * 10. Security headers (CSP, X-Frame, X-Content-Type, etc.)
 * 11. Login audit log
 * 12. Account lockout after repeated failures
 */

// ===== SECURITY HEADERS =====
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: no-referrer');
header('Content-Security-Policy: default-src \'self\'; style-src \'self\' \'unsafe-inline\' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src \'self\' https://api.qrserver.com');
header('Permissions-Policy: camera=(), microphone=(), geolocation=()');

// ===== SECURE SESSION =====
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.use_strict_mode', 1);
ini_set('session.use_only_cookies', 1);
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
    ini_set('session.cookie_secure', 1);
}
session_start();

// Session expiry
$SESSION_IDLE_TIMEOUT = 7200;   // 2 hours idle
$SESSION_MAX_LIFETIME = 28800;  // 8 hours absolute max

if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > $SESSION_IDLE_TIMEOUT)) {
    session_unset(); session_destroy(); session_start();
}
if (isset($_SESSION['created_at']) && (time() - $_SESSION['created_at'] > $SESSION_MAX_LIFETIME)) {
    session_unset(); session_destroy(); session_start();
}
$_SESSION['last_activity'] = time();
if (!isset($_SESSION['created_at'])) $_SESSION['created_at'] = time();

// Regenerate session ID periodically (every 30 min)
if (!isset($_SESSION['regenerated']) || time() - $_SESSION['regenerated'] > 1800) {
    session_regenerate_id(true);
    $_SESSION['regenerated'] = time();
}

// ===== DB =====
$DB_HOST='localhost';$DB_NAME='deeldabr_eu261';$DB_USER='deeldabr_eu261';$DB_PASS='ASDjaS(DCWEFHjewke3we4f@#';
$pdo = new PDO("mysql:host=$DB_HOST;dbname=$DB_NAME;charset=utf8mb4", $DB_USER, $DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]);

// ===== TABLES =====
$pdo->exec("CREATE TABLE IF NOT EXISTS auth_config (
    id INT PRIMARY KEY DEFAULT 1,
    username VARCHAR(100) NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    totp_secret VARCHAR(64) NOT NULL,
    account_locked_until DATETIME NULL,
    total_failed INT DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

$pdo->exec("CREATE TABLE IF NOT EXISTS login_attempts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ip VARCHAR(45) NOT NULL,
    username_tried VARCHAR(100) DEFAULT '',
    success TINYINT(1) DEFAULT 0,
    step VARCHAR(20) DEFAULT 'password',
    user_agent TEXT,
    attempted_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_ip_time (ip, attempted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

// ===== CSRF =====
function generateCsrf() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}
function verifyCsrf($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
$csrf = generateCsrf();

// ===== TOTP FUNCTIONS =====
function base32_encode($data) {
    $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    $binary = '';
    foreach (str_split($data) as $char) $binary .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
    $binary = str_pad($binary, ceil(strlen($binary) / 5) * 5, '0');
    $result = '';
    foreach (str_split($binary, 5) as $chunk) $result .= $chars[bindec($chunk)];
    return $result;
}
function base32_decode($b32) {
    $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    $b32 = strtoupper(rtrim($b32, '='));
    $binary = '';
    foreach (str_split($b32) as $char) {
        $pos = strpos($chars, $char);
        if ($pos === false) continue;
        $binary .= str_pad(decbin($pos), 5, '0', STR_PAD_LEFT);
    }
    $result = '';
    foreach (str_split($binary, 8) as $byte) {
        if (strlen($byte) < 8) break;
        $result .= chr(bindec($byte));
    }
    return $result;
}
function generate_totp_secret() { return base32_encode(random_bytes(20)); }
function get_totp_code($secret, $timeSlice = null) {
    if ($timeSlice === null) $timeSlice = floor(time() / 30);
    $key = base32_decode($secret);
    $time = pack('N*', 0) . pack('N*', $timeSlice);
    $hash = hash_hmac('sha1', $time, $key, true);
    $offset = ord(substr($hash, -1)) & 0x0F;
    $code = (((ord($hash[$offset]) & 0x7F) << 24) | ((ord($hash[$offset+1]) & 0xFF) << 16) | ((ord($hash[$offset+2]) & 0xFF) << 8) | (ord($hash[$offset+3]) & 0xFF)) % 1000000;
    return str_pad($code, 6, '0', STR_PAD_LEFT);
}
function verify_totp($secret, $code) {
    for ($i = -1; $i <= 1; $i++) {
        if (hash_equals(get_totp_code($secret, floor(time() / 30) + $i), $code)) return true;
    }
    return false;
}

// ===== RATE LIMITING =====
$ip = $_SERVER['REMOTE_ADDR'];
$MAX_ATTEMPTS = 5;
$LOCKOUT_MINUTES = 15;

function getRecentAttempts($pdo, $ip, $minutes) {
    $st = $pdo->prepare("SELECT COUNT(*) as cnt FROM login_attempts WHERE ip=? AND success=0 AND attempted_at > DATE_SUB(NOW(), INTERVAL ? MINUTE)");
    $st->execute([$ip, $minutes]);
    return $st->fetch()['cnt'];
}
function logAttempt($pdo, $ip, $user, $success, $step='password') {
    $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $pdo->prepare("INSERT INTO login_attempts (ip,username_tried,success,step,user_agent) VALUES (?,?,?,?,?)")
        ->execute([$ip, $user, $success ? 1 : 0, $step, $ua]);
}
function clearAttempts($pdo, $ip) {
    $pdo->prepare("DELETE FROM login_attempts WHERE ip=? AND success=0")->execute([$ip]);
}

// Session fingerprint
function sessionFingerprint() {
    return hash('sha256', ($_SERVER['HTTP_USER_AGENT'] ?? '') . '|' . ($_SERVER['REMOTE_ADDR'] ?? ''));
}

// ===== LOGIC =====
$error = '';
$step = 'login';
$locked = false;
$cfg = $pdo->query("SELECT * FROM auth_config WHERE id=1")->fetch();

// Check account lockout
if ($cfg && $cfg['account_locked_until'] && strtotime($cfg['account_locked_until']) > time()) {
    $locked = true;
    $remaining = ceil((strtotime($cfg['account_locked_until']) - time()) / 60);
    $error = "Account locked. Try again in {$remaining} minutes.";
}

// Check IP rate limit
$recentFails = getRecentAttempts($pdo, $ip, $LOCKOUT_MINUTES);
if ($recentFails >= $MAX_ATTEMPTS) {
    $locked = true;
    $error = "Too many failed attempts from your IP. Wait {$LOCKOUT_MINUTES} minutes.";
}

// ===== LOGOUT =====
if (isset($_GET['logout'])) {
    session_unset();
    session_destroy();
    session_start();
    session_regenerate_id(true);
    header('Location: login.php');
    exit;
}

// ===== ALREADY AUTHENTICATED =====
if (isset($_SESSION['auth_ok']) && $_SESSION['auth_ok'] === true) {
    // Verify session fingerprint
    if (isset($_SESSION['fingerprint']) && $_SESSION['fingerprint'] !== sessionFingerprint()) {
        session_unset(); session_destroy(); session_start();
    } else {
        header('Location: dashboard.php');
        exit;
    }
}

if (isset($_SESSION['pass_ok']) && $_SESSION['pass_ok'] === true) {
    $step = 'totp';
}

if (!$cfg) {
    $step = 'setup';
}

// ===== SETUP =====
if (!$cfg && isset($_POST['setup'])) {
    if (!verifyCsrf($_POST['csrf'] ?? '')) { $error = 'Invalid request.'; }
    else {
        $user = trim($_POST['username'] ?? '');
        $pass = $_POST['password'] ?? '';
        $pass2 = $_POST['password2'] ?? '';
        if (!$user || !$pass) { $error = 'All fields required.'; }
        elseif (strlen($pass) < 10) { $error = 'Password must be at least 10 characters.'; }
        elseif ($pass !== $pass2) { $error = 'Passwords do not match.'; }
        elseif (!preg_match('/[A-Z]/', $pass) || !preg_match('/[a-z]/', $pass) || !preg_match('/[0-9]/', $pass)) {
            $error = 'Password needs uppercase, lowercase, and a number.';
        } else {
            $hash = password_hash($pass, PASSWORD_BCRYPT, ['cost' => 12]);
            $secret = generate_totp_secret();
            $pdo->prepare("INSERT INTO auth_config (id,username,password_hash,totp_secret) VALUES (1,?,?,?) ON DUPLICATE KEY UPDATE username=?,password_hash=?,totp_secret=?")
                ->execute([$user, $hash, $secret, $user, $hash, $secret]);
            $cfg = $pdo->query("SELECT * FROM auth_config WHERE id=1")->fetch();
            $_SESSION['setup_done'] = true;
            $_SESSION['setup_secret'] = $secret;
            $otpUri = "otpauth://totp/SkyClaim:" . urlencode($user) . "?secret={$secret}&issuer=SkyClaim";
            $_SESSION['setup_qr'] = "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=" . urlencode($otpUri);
        }
    }
}

// ===== LOGIN STEP 1 =====
if ($cfg && isset($_POST['login']) && !$locked) {
    if (!verifyCsrf($_POST['csrf'] ?? '')) { $error = 'Invalid request.'; }
    else {
        $user = trim($_POST['username'] ?? '');
        $pass = $_POST['password'] ?? '';
        // Timing-safe: always verify even with wrong username
        $validUser = hash_equals($cfg['username'], $user);
        $validPass = password_verify($pass, $cfg['password_hash']);
        if ($validUser && $validPass) {
            logAttempt($pdo, $ip, $user, false, 'password_ok');
            $_SESSION['pass_ok'] = true;
            $_SESSION['auth_user'] = $user;
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // Regenerate CSRF
            $csrf = $_SESSION['csrf_token'];
            $step = 'totp';
        } else {
            logAttempt($pdo, $ip, $user, false, 'password');
            // Increment total failed
            $pdo->exec("UPDATE auth_config SET total_failed = total_failed + 1 WHERE id=1");
            $tf = $pdo->query("SELECT total_failed FROM auth_config WHERE id=1")->fetchColumn();
            // Progressive lockout: 10+ fails → lock account 30 min
            if ($tf >= 10) {
                $pdo->exec("UPDATE auth_config SET account_locked_until = DATE_ADD(NOW(), INTERVAL 30 MINUTE) WHERE id=1");
                $error = 'Account locked for 30 minutes due to repeated failures.';
                $locked = true;
            } else {
                $remaining = $MAX_ATTEMPTS - getRecentAttempts($pdo, $ip, $LOCKOUT_MINUTES);
                if ($remaining <= 0) { $locked = true; $error = "IP locked. Wait {$LOCKOUT_MINUTES} minutes."; }
                else $error = "Invalid credentials. {$remaining} attempts remaining.";
            }
            // Deliberate delay to slow brute force
            usleep(random_int(500000, 1500000)); // 0.5-1.5 sec
        }
    }
}

// ===== LOGIN STEP 2: TOTP =====
if ($cfg && isset($_POST['verify_totp']) && !$locked) {
    if (!verifyCsrf($_POST['csrf'] ?? '')) { $error = 'Invalid request.'; }
    else {
        $code = preg_replace('/[^0-9]/', '', $_POST['totp_code'] ?? '');
        if (verify_totp($cfg['totp_secret'], $code)) {
            // Success!
            clearAttempts($pdo, $ip);
            $pdo->exec("UPDATE auth_config SET total_failed=0, account_locked_until=NULL WHERE id=1");
            logAttempt($pdo, $ip, $cfg['username'], true, 'totp');
            session_regenerate_id(true);
            $_SESSION['auth_ok'] = true;
            $_SESSION['pass_ok'] = false;
            $_SESSION['fingerprint'] = sessionFingerprint();
            $_SESSION['created_at'] = time();
            header('Location: dashboard.php');
            exit;
        } else {
            logAttempt($pdo, $ip, $cfg['username'], false, 'totp');
            $error = 'Invalid 2FA code.';
            $step = 'totp';
            usleep(random_int(300000, 800000));
        }
    }
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SkyClaim — Login</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root{--navy:#1B2A4A;--orange:#FF6B35;--orange-light:#FFF4EE;--blue:#3B82F6;--blue-light:#EFF6FF;--green:#22C55E;--red:#DC2626;--g50:#F9FAFB;--g100:#F3F4F6;--g200:#E5E7EB;--g300:#D1D5DB;--g400:#9CA3AF;--g500:#6B7280;--white:#FFFFFF}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--g50);font-family:'DM Sans',sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center}
.login-box{background:var(--white);border:1px solid var(--g200);border-radius:16px;box-shadow:0 8px 30px rgba(27,42,74,.08);padding:40px;width:100%;max-width:400px}
.logo{font-family:'Plus Jakarta Sans',sans-serif;font-size:24px;font-weight:800;color:var(--navy);text-align:center;margin-bottom:4px;display:flex;align-items:center;justify-content:center;gap:8px}
.logo span{color:var(--orange)}
.sub{text-align:center;color:var(--g400);font-size:13px;margin-bottom:28px}
.fg{margin-bottom:16px}
.fg label{display:block;font-size:12px;font-weight:600;color:var(--g500);margin-bottom:6px}
.fg input{width:100%;height:44px;border:1.5px solid var(--g200);border-radius:10px;padding:0 14px;font-size:14px;font-family:'DM Sans',sans-serif;color:var(--navy);outline:none;transition:border .15s}
.fg input:focus{border-color:var(--orange);box-shadow:0 0 0 3px rgba(255,107,53,.1)}
.btn{width:100%;height:46px;border:none;border-radius:10px;background:var(--orange);color:var(--white);font-size:15px;font-weight:700;font-family:'Plus Jakarta Sans',sans-serif;cursor:pointer;transition:background .15s;margin-top:8px}
.btn:hover{background:#E55A2B}
.btn:disabled{opacity:.5;cursor:not-allowed}
.err{background:#FEF2F2;color:var(--red);font-size:12px;font-weight:600;padding:10px 14px;border-radius:8px;margin-bottom:16px;border:1px solid rgba(220,38,38,.2)}
.info{background:var(--blue-light);color:var(--blue);font-size:12px;font-weight:600;padding:10px 14px;border-radius:8px;margin-bottom:16px;border:1px solid rgba(59,130,246,.2)}
.warn{background:var(--orange-light);color:var(--orange);font-size:12px;font-weight:600;padding:10px 14px;border-radius:8px;margin-bottom:16px;border:1px solid rgba(255,107,53,.2)}
.totp-input{text-align:center;font-size:24px;font-weight:800;letter-spacing:8px;font-family:'Plus Jakarta Sans',sans-serif}
.qr-wrap{text-align:center;margin:16px 0}
.qr-wrap img{border-radius:12px;border:1px solid var(--g200)}
.secret-box{background:var(--g50);border:1px solid var(--g200);border-radius:8px;padding:10px;text-align:center;font-family:monospace;font-size:14px;font-weight:700;color:var(--navy);margin:12px 0;letter-spacing:2px;word-break:break-all;user-select:all}
.lock-icon{text-align:center;margin-bottom:16px}
.lock-icon svg{color:var(--g300)}
.pw-rules{font-size:11px;color:var(--g400);margin-top:4px;line-height:1.6}
.pw-rules span{display:block}
.step-dots{display:flex;justify-content:center;gap:6px;margin-bottom:20px}
.step-dot{width:8px;height:8px;border-radius:50%;background:var(--g200)}
.step-dot.active{background:var(--orange);width:24px;border-radius:4px}
.step-dot.done{background:var(--green)}
</style>
</head>
<body>
<div class="login-box">
  <div class="logo">Sky<span>Claim</span></div>

<?php if ($step === 'setup'): ?>
  <?php if (isset($_SESSION['setup_done'])): ?>
    <div class="sub">Setup complete! Set up your 2FA authenticator.</div>
    <div class="qr-wrap">
      <img src="<?= htmlspecialchars($_SESSION['setup_qr']) ?>" width="200" height="200" alt="QR Code">
    </div>
    <p style="text-align:center;font-size:11px;color:var(--g400);margin-bottom:4px">Or enter this key manually:</p>
    <div class="secret-box"><?= htmlspecialchars($_SESSION['setup_secret']) ?></div>
    <div class="warn">Save this secret key somewhere safe. You need it if you lose access to your authenticator.</div>
    <?php if ($error): ?><div class="err"><?= htmlspecialchars($error) ?></div><?php endif; ?>
    <form method="POST">
      <input type="hidden" name="csrf" value="<?= $csrf ?>">
      <div class="fg">
        <label>Enter 6-digit code from authenticator</label>
        <input type="text" name="totp_code" class="totp-input" maxlength="6" pattern="\d{6}" placeholder="000000" required autofocus autocomplete="off">
      </div>
      <input type="hidden" name="verify_totp" value="1">
      <button type="submit" class="btn">Verify & Complete Setup</button>
    </form>
  <?php else: ?>
    <div class="sub">Create your admin account</div>
    <?php if ($error): ?><div class="err"><?= htmlspecialchars($error) ?></div><?php endif; ?>
    <form method="POST">
      <input type="hidden" name="csrf" value="<?= $csrf ?>">
      <div class="fg">
        <label>Username</label>
        <input type="text" name="username" required autofocus autocomplete="off">
      </div>
      <div class="fg">
        <label>Password</label>
        <input type="password" name="password" required minlength="10">
        <div class="pw-rules">
          <span>• Minimum 10 characters</span>
          <span>• At least 1 uppercase, 1 lowercase, 1 number</span>
        </div>
      </div>
      <div class="fg">
        <label>Confirm Password</label>
        <input type="password" name="password2" required>
      </div>
      <input type="hidden" name="setup" value="1">
      <button type="submit" class="btn">Create Account</button>
    </form>
  <?php endif; ?>

<?php elseif ($step === 'login'): ?>
  <div class="step-dots"><div class="step-dot active"></div><div class="step-dot"></div></div>
  <div class="sub">Sign in to your dashboard</div>
  <?php if ($error): ?><div class="err"><?= htmlspecialchars($error) ?></div><?php endif; ?>
  <div class="lock-icon"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg></div>
  <form method="POST">
    <input type="hidden" name="csrf" value="<?= $csrf ?>">
    <div class="fg">
      <label>Username</label>
      <input type="text" name="username" required autofocus autocomplete="off" <?= $locked ? 'disabled' : '' ?>>
    </div>
    <div class="fg">
      <label>Password</label>
      <input type="password" name="password" required <?= $locked ? 'disabled' : '' ?>>
    </div>
    <input type="hidden" name="login" value="1">
    <button type="submit" class="btn" <?= $locked ? 'disabled' : '' ?>>Continue</button>
  </form>

<?php elseif ($step === 'totp'): ?>
  <div class="step-dots"><div class="step-dot done"></div><div class="step-dot active"></div></div>
  <div class="sub">Two-factor authentication</div>
  <?php if ($error): ?><div class="err"><?= htmlspecialchars($error) ?></div><?php endif; ?>
  <div class="lock-icon"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="5" y="2" width="14" height="20" rx="2"/><circle cx="12" cy="14" r="2"/><line x1="12" y1="16" x2="12" y2="18"/></svg></div>
  <div class="info">Enter the 6-digit code from your authenticator app.</div>
  <form method="POST">
    <input type="hidden" name="csrf" value="<?= $csrf ?>">
    <div class="fg">
      <label>2FA Code</label>
      <input type="text" name="totp_code" class="totp-input" maxlength="6" pattern="\d{6}" placeholder="000000" required autofocus autocomplete="off" <?= $locked ? 'disabled' : '' ?>>
    </div>
    <input type="hidden" name="verify_totp" value="1">
    <button type="submit" class="btn" <?= $locked ? 'disabled' : '' ?>>Verify</button>
  </form>
  <form method="POST" style="margin-top:12px">
    <input type="hidden" name="csrf" value="<?= $csrf ?>">
    <button type="submit" name="cancel_totp" value="1" style="width:100%;padding:10px;border:1px solid var(--g200);border-radius:8px;background:var(--white);color:var(--g400);font-size:12px;cursor:pointer;font-family:'DM Sans',sans-serif">← Back to login</button>
  </form>
<?php endif; ?>

</div>
<script>
// Auto-focus and auto-submit TOTP after 6 digits
document.querySelectorAll('.totp-input').forEach(el=>{
  el.addEventListener('input',function(){
    this.value=this.value.replace(/[^0-9]/g,'');
    if(this.value.length===6)this.closest('form').submit();
  });
});
</script>
</body>
</html>
<?php
// Handle "Back to login" from TOTP step
if (isset($_POST['cancel_totp'])) {
    $_SESSION['pass_ok'] = false;
    header('Location: login.php');
    exit;
}
