348 lines
12 KiB
HTML
348 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Computer Historyz - Progressive Klick-Decryption</title>
|
|
<style>
|
|
/* --- KERNFARBEN AUS DESIGN-DOKU: MASCHINEN-INSTANZEN --- */
|
|
:root {
|
|
--bg-deep: #05070a;
|
|
--bg-panel: #0b0e14;
|
|
|
|
--accent-cyan: #00f2ff;
|
|
--accent-green: #00ff73;
|
|
--accent-amber: #ffb700;
|
|
--accent-red: #ff3e3e;
|
|
|
|
--text-dim: #888899;
|
|
--font-mono: "JetBrains Mono", "Fira Code", "Courier Prime", monospace;
|
|
}
|
|
|
|
body {
|
|
background: var(--bg-deep);
|
|
color: var(--text-dim);
|
|
font-family: var(--font-mono);
|
|
margin: 20px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.panel {
|
|
background: var(--bg-panel);
|
|
border: 1px solid var(--text-dim);
|
|
backdrop-filter: blur(4px);
|
|
padding: 15px;
|
|
margin-bottom: 25px;
|
|
box-shadow: 0 0 6px rgba(0, 242, 255, 0.1);
|
|
}
|
|
|
|
.panel-title {
|
|
color: var(--accent-green);
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
margin-top: 0;
|
|
margin-bottom: 15px;
|
|
border-bottom: 1px dashed var(--text-dim);
|
|
padding-bottom: 5px;
|
|
}
|
|
|
|
input, textarea, button {
|
|
background: #07090e;
|
|
border: 1px solid var(--text-dim);
|
|
color: var(--accent-green);
|
|
padding: 8px;
|
|
font-family: var(--font-mono);
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
input:focus, textarea:focus {
|
|
outline: none;
|
|
border-color: var(--accent-cyan);
|
|
box-shadow: 0 0 6px var(--accent-cyan);
|
|
}
|
|
|
|
button {
|
|
cursor: pointer;
|
|
font-weight: bold;
|
|
background: #0c161a;
|
|
border-color: var(--accent-cyan);
|
|
color: var(--accent-cyan);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
button:hover {
|
|
background: var(--accent-cyan);
|
|
color: var(--bg-deep);
|
|
box-shadow: 0 0 6px var(--accent-cyan);
|
|
}
|
|
|
|
/* --- CRYPTO BLÖCKE SYSTEM --- */
|
|
.lock-chain {
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
background: #020407;
|
|
padding: 12px;
|
|
border-left: 3px solid var(--text-dim);
|
|
margin: 15px 0;
|
|
color: #555566;
|
|
transition: all 0.3s ease;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/* Zustand: Block wartet darauf, geklickt zu werden */
|
|
.lock-chain.is-active {
|
|
border-left: 3px solid var(--accent-amber);
|
|
color: var(--accent-cyan);
|
|
cursor: pointer;
|
|
opacity: 1;
|
|
box-shadow: 0 0 6px rgba(255, 183, 0, 0.2);
|
|
animation: pulse-active 2s infinite ease-in-out;
|
|
}
|
|
|
|
/* Zustand: Erfolgreich decodiert */
|
|
.lock-chain.is-decrypted {
|
|
border-left: 3px solid var(--accent-green);
|
|
color: var(--accent-green);
|
|
background: transparent;
|
|
white-space: normal;
|
|
opacity: 1;
|
|
animation: none;
|
|
cursor: default;
|
|
}
|
|
|
|
/* Zustand: Fehler bei der Entschlüsselung */
|
|
.lock-chain.error {
|
|
border-left: 3px solid var(--accent-red);
|
|
color: var(--accent-red);
|
|
opacity: 1;
|
|
animation: blink-error 1s infinite;
|
|
}
|
|
|
|
@keyframes pulse-active {
|
|
0% { background: #020407; }
|
|
50% { background: #0a121c; }
|
|
100% { background: #020407; }
|
|
}
|
|
|
|
@keyframes blink-error {
|
|
50% { opacity: 0.4; }
|
|
}
|
|
|
|
.control-panel {
|
|
max-width: 600px;
|
|
}
|
|
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 2px 6px;
|
|
font-size: 0.85em;
|
|
background: #111;
|
|
border: 1px solid var(--text-dim);
|
|
margin-bottom: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="panel control-panel">
|
|
<div class="panel-title">Decryption Control Deck</div>
|
|
<label for="master-pass">Master-Passwort (Key 0):</label>
|
|
<input type="password" id="master-pass" value="OortscheWolke2448">
|
|
<button onclick="initializeCryptoChain()">Krypto-Kette initialisieren</button>
|
|
<div id="deck-status" class="status-badge" style="color: var(--accent-amber);">Status: Warte auf Initialisierung...</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<div class="panel-title">Dokument-Struktur (Instanzen-Protokoll)</div>
|
|
|
|
<h3>Knotenpunkt-Datenstrom</h3>
|
|
<p><em>Dieser Textbereich liegt außerhalb der Kette und bleibt dauerhaft sichtbar.</em></p>
|
|
|
|
<div class="lock-chain" id="block-0" onclick="triggerBlockDecryption(0)">
|
|
4a21184a44f9deeb8db93ee5c56dfba2d28fbfb3970b880197d19e9eb10a
|
|
a011de36bdfc1103f6f14066094ef5ad0554ba739cae9dfc01991a0c0f99
|
|
8ee2954aef83fbdf9842a2acb119cb
|
|
</div>
|
|
|
|
<div class="lock-chain" id="block-1" onclick="triggerBlockDecryption(1)">
|
|
108b688d01a3579ab5a6c117b38d388f8d689b9d3ff02102554dfa45bbca
|
|
c0a7cc02ccff5d8ee9d110f01ccba1dd010ea4fbc11e22778da5a6c1deba
|
|
99a9cf588b
|
|
</div>
|
|
|
|
<div class="lock-chain" id="block-2" onclick="triggerBlockDecryption(2)">
|
|
92da21085ba1f43a99e7d9b9d62da3ffde4bb8df115dd0ca31bdaef9d877
|
|
bbad84ff4da92da74e99a4c11100bb17dca5ffca188e7d22cb54fb77ac90
|
|
d22bb488de1a56daff8e9b
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<div class="panel-title">Ketten-Generator (Text-zu-Klick-Block)</div>
|
|
<textarea id="gen-input" rows="4">Das Ur-Ding der Erbauer war die Energie.
|
|
Die Hauptmaschinen sind monumentale Datenbanken.
|
|
Das Nina-Protokoll verhindert den Ressourcen-Kollaps.</textarea>
|
|
<button onclick="generateNewChain()">Neue Krypto-Kette generieren</button>
|
|
<div id="gen-output" style="white-space: pre; font-family: var(--font-mono); color: var(--accent-cyan); margin-top: 10px; font-size: 0.9em;"></div>
|
|
</div>
|
|
|
|
<script>
|
|
// --- STATE MACHINE FÜR DIE KLICK-KETTE ---
|
|
let currentActiveIndex = -1;
|
|
let nextKeyString = "";
|
|
|
|
// --- UTILITIES (Native Web Crypto) ---
|
|
function bufToHex(buf) {
|
|
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
|
|
function hexToBuf(hex) {
|
|
const bytes = new Uint8Array(hex.length / 2);
|
|
for (let i = 0; i < hex.length; i += 2) {
|
|
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
}
|
|
return bytes.buffer;
|
|
}
|
|
|
|
async function deriveKey(passwordString) {
|
|
const encoder = new TextEncoder();
|
|
const data = encoder.encode(passwordString);
|
|
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
return await crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);
|
|
}
|
|
|
|
// --- INTERAKTIVE LOGIK ---
|
|
|
|
// Schritt 1: Master-Passwort laden und Block 0 scharf schalten
|
|
function initializeCryptoChain() {
|
|
const masterPassword = document.getElementById('master-pass').value;
|
|
if (!masterPassword) {
|
|
updateStatus("FEHLER: Passwort fehlt", "var(--accent-red)");
|
|
return;
|
|
}
|
|
|
|
nextKeyString = masterPassword;
|
|
currentActiveIndex = 0;
|
|
|
|
// Alle UI-Klassen zurücksetzen
|
|
const blocks = document.querySelectorAll('.lock-chain');
|
|
blocks.forEach((block, idx) => {
|
|
block.classList.remove('is-active', 'is-decrypted', 'error');
|
|
// Wenn es der erste Block ist, aktiv schalten
|
|
if(idx === 0) {
|
|
block.classList.add('is-active');
|
|
}
|
|
});
|
|
|
|
updateStatus("Kette bereit. Klicke auf Block 1...", "var(--accent-cyan)");
|
|
}
|
|
|
|
// Schritt 2: Klick-Handler für die einzelnen Blöcke
|
|
async function triggerBlockDecryption(blockIndex) {
|
|
// Ignoriere Klicks, wenn der Block noch nicht an der Reihe ist
|
|
if (blockIndex !== currentActiveIndex) return;
|
|
|
|
const block = document.getElementById(`block-${blockIndex}`);
|
|
let rawContent = block.innerText.replace(/\r?\n|\r/g, "");
|
|
|
|
// Format-Sicherheitscheck (\x02 STX und \x03 ETX)
|
|
if (!rawContent.startsWith('\x02') || !rawContent.endsWith('\x03')) {
|
|
markError(block, "Formatierungsfehler (Steuerzeichen fehlen)");
|
|
return;
|
|
}
|
|
|
|
// Payload-Filterung
|
|
const payload = rawContent.substring(1, rawContent.length - 1).replace(/\s/g, "");
|
|
const ivHex = payload.substring(0, 24);
|
|
const cipherHex = payload.substring(24);
|
|
|
|
try {
|
|
const iv = hexToBuf(ivHex);
|
|
const cipherData = hexToBuf(cipherHex);
|
|
const key = await deriveKey(nextKeyString);
|
|
|
|
const decryptedBuf = await crypto.subtle.decrypt(
|
|
{ name: 'AES-GCM', iv: iv },
|
|
key,
|
|
cipherData
|
|
);
|
|
|
|
const plaintext = new TextDecoder().decode(decryptedBuf);
|
|
|
|
// DOM-Austausch unter Beibehaltung der Struktur
|
|
block.innerHTML = plaintext;
|
|
block.classList.remove('is-active', 'error');
|
|
block.classList.add('is-decrypted');
|
|
|
|
// HIER PASSIERT DIE FORTLAUFENDE SCHLÜSSEL-ÜBERGABE:
|
|
nextKeyString = plaintext;
|
|
|
|
// Nächsten Block in der Kette freischalten
|
|
currentActiveIndex++;
|
|
const nextBlock = document.getElementById(`block-${currentActiveIndex}`);
|
|
|
|
if (nextBlock) {
|
|
nextBlock.classList.add('is-active');
|
|
updateStatus(`Block ${blockIndex + 1} gelöst. Nächster Block freigegeben!`, "var(--accent-cyan)");
|
|
} else {
|
|
updateStatus("System vollständig decodiert. Kette beendet.", "var(--accent-green)");
|
|
}
|
|
|
|
} catch (err) {
|
|
markError(block, `Kettenschluss fehlgeschlagen. Falscher Key.`);
|
|
}
|
|
}
|
|
|
|
function markError(element, message) {
|
|
element.classList.remove('is-active');
|
|
element.classList.add('error');
|
|
element.innerText = `[CRITICAL CODE CORRUPTION: ${message}]`;
|
|
updateStatus("Kette dauerhaft unterbrochen.", "var(--accent-red)");
|
|
currentActiveIndex = -1; // Blockiert weitere Klicks
|
|
}
|
|
|
|
function updateStatus(text, color) {
|
|
const badge = document.getElementById('deck-status');
|
|
badge.innerText = `Status: ${text}`;
|
|
badge.style.color = color;
|
|
badge.style.borderColor = color;
|
|
}
|
|
|
|
// --- CHIP-GENERATOR FÜR DEINE TEXTE ---
|
|
async function generateNewChain() {
|
|
const masterPassword = document.getElementById('master-pass').value;
|
|
const lines = document.getElementById('gen-input').value.split('\n').filter(l => l.trim() !== "");
|
|
let currentKeyString = masterPassword;
|
|
let htmlOutput = "";
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const plaintext = lines[i];
|
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
const key = await deriveKey(currentKeyString);
|
|
|
|
const encryptedBuf = await crypto.subtle.encrypt(
|
|
{ name: 'AES-GCM', iv: iv },
|
|
key,
|
|
new TextEncoder().encode(plaintext)
|
|
);
|
|
|
|
const fullPayloadHex = bufToHex(iv) + bufToHex(encryptedBuf);
|
|
|
|
let formattedHex = "";
|
|
for (let j = 0; j < fullPayloadHex.length; j += 60) {
|
|
formattedHex += "\n" + fullPayloadHex.substring(j, j + 60);
|
|
}
|
|
|
|
// Generiert direkt die korrekten Klick-IDs und Handler für deinen Quelltext
|
|
htmlOutput += `<div class="lock-chain" id="block-${i}" onclick="triggerBlockDecryption(${i})">${formattedHex}\n</div>\n\n`;
|
|
|
|
currentKeyString = plaintext;
|
|
}
|
|
|
|
document.getElementById('gen-output').innerHTML = htmlOutput;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |