hasRight('bericht', 'read')) bericht_ajax_fail('Permission denied', 403); $pageid = (int) ($_POST['pageid'] ?? $_GET['pageid'] ?? 0); if (!$pageid) bericht_ajax_fail('pageid fehlt'); $sqlr = $db->query("SELECT rowid, fk_bericht, source_path FROM ".$db->prefix()."bericht_page WHERE rowid = ".$pageid); if (!$sqlr || !($row = $db->fetch_object($sqlr))) bericht_ajax_fail('Seite nicht gefunden', 404); $full = bericht_resolve_data_path($row->source_path); if (!$full || !file_exists($full)) bericht_ajax_fail('Datei nicht gefunden', 404); $meta_path = $full.'.meta.json'; if (!file_exists($meta_path)) { bericht_ajax_ok(array( 'verified' => false, 'reason' => 'Keine Metadaten-Datei — diese Seite ist keine aufgewertete Unterschrift', )); } $meta = json_decode(file_get_contents($meta_path), true); if (!is_array($meta) || empty($meta['chain_hash_sha256'])) { bericht_ajax_ok(array( 'verified' => false, 'reason' => 'Metadaten ungültig oder kein Hash enthalten', )); } // Wir brauchen den Bericht $bericht = new Bericht($db); if ($bericht->fetch($row->fk_bericht) <= 0) bericht_ajax_fail('Bericht nicht gefunden', 404); // Die zum Signaturzeitpunkt existierenden Seiten: alle mit page_order < unserer. // Sort nach page_order wie beim Signieren. $prev_pages = BerichtPage::fetchAllForBericht($db, $row->fk_bericht); // Nur die Seiten die VOR der Signatur waren — wir nehmen die ersten N entsprechend meta.page_count_at_signing $expected_count = (int) ($meta['page_count_at_signing'] ?? 0); $to_hash = array_slice($prev_pages, 0, $expected_count); $current_count = 0; foreach ($prev_pages as $pp) { if ($pp->id == $row->rowid) break; $current_count++; } // Hash nachrechnen $hash_ctx = hash_init('sha256'); hash_update($hash_ctx, 'bericht:'.$bericht->id.'|ref:'.$bericht->ref); foreach ($to_hash as $pp) { $full_pp = bericht_resolve_data_path($pp->source_path); if ($full_pp && file_exists($full_pp)) { hash_update_file($hash_ctx, $full_pp); } hash_update($hash_ctx, '|order:'.$pp->page_order.'|note:'.($pp->note ?? '')); } $current_hash = hash_final($hash_ctx); $stored_hash = $meta['chain_hash_sha256']; $verified = hash_equals($stored_hash, $current_hash); $result = array( 'verified' => $verified, 'stored_hash' => $stored_hash, 'current_hash' => $current_hash, 'expected_page_count' => $expected_count, 'current_page_count' => $current_count, 'meta' => array( 'signer_name' => $meta['signer_name'] ?? '', 'signed_at' => $meta['signed_at'] ?? '', 'user_login' => $meta['user_login'] ?? '', 'kunde' => $meta['kunde'] ?? '', 'parent_ref' => $meta['parent_ref'] ?? '', 'gps_lat' => $meta['gps_lat'] ?? null, 'gps_lon' => $meta['gps_lon'] ?? null, 'remote_ip' => $meta['remote_ip'] ?? '', ), ); if (!$verified) { if ($current_count !== $expected_count) { $result['reason'] = "Seitenanzahl hat sich geändert ($expected_count → $current_count)"; } else { $result['reason'] = "Hash der Vorseiten stimmt nicht — eine Seite wurde nach der Unterschrift verändert"; } } bericht_ajax_ok($result);