| {% extends "base.html" %} |
| {% block title %}Dead Drop{% endblock %} |
| {% block page_title %}Secure Dead Drop{% endblock %} |
|
|
| {% block content %} |
| <div class="row justify-content-center mt-4"> |
| <div class="col-lg-8 fade-in-up"> |
| <div class="cyber-card p-5"> |
| <div class="text-center mb-4"> |
| <i class="bi bi-incognito fs-1 text-danger"></i> |
| <h3 class="mt-2">Self-Destructing Messages</h3> |
| <p class="text-muted"> |
| Create a one-time link. The message is encrypted in your browser, stored encrypted, and deleted immediately after viewing. |
| </p> |
| </div> |
|
|
| <form id="create-share-form"> |
| <div class="mb-3"> |
| <label class="form-label text-accent">Secret Message</label> |
| <textarea id="secret-text" class="form-control" rows="5" placeholder="Paste your password or secret key here..." required></textarea> |
| </div> |
| |
| <div class="mb-4"> |
| <label class="form-label text-accent">Self-Destruct View Time (Seconds)</label> |
| <div class="input-group"> |
| <span class="input-group-text bg-dark border-secondary text-warning"><i class="bi bi-stopwatch"></i></span> |
| <input type="number" id="view-time" class="form-control bg-dark text-white border-secondary" placeholder="e.g. 15" min="1" max="3600" required> |
| </div> |
| <small class="text-muted mt-1 d-block">Number of seconds the receiver has to read it before it wipes from their screen.</small> |
| </div> |
| |
| <button type="submit" class="btn btn-cyber w-100 py-3"> |
| <i class="bi bi-link-45deg me-2"></i>Generate Secure Link |
| </button> |
| </form> |
|
|
| <div id="result-area" class="d-none mt-4 p-3 border border-success rounded bg-dark"> |
| <label class="fw-bold text-success mb-2">Your One-Time Link:</label> |
| <div class="input-group"> |
| <input type="text" id="share-link" class="form-control text-success font-monospace" readonly> |
| <button class="btn btn-outline-success" onclick="copyLink()">Copy</button> |
| </div> |
| <div class="mt-2 text-danger small"> |
| <i class="bi bi-exclamation-triangle-fill"></i> Warning: If you close this tab, the link is lost forever. |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| {% endblock %} |
|
|
| {% block scripts %} |
| <script src="{{ url_for('static', filename='js/zk_crypto.js') }}"></script> |
| <script> |
| document.getElementById("create-share-form").addEventListener("submit", async (e) => { |
| e.preventDefault(); |
| |
| const password = Math.random().toString(36).slice(-10) + Math.random().toString(36).slice(-10); |
| const text = document.getElementById("secret-text").value; |
| const viewTime = document.getElementById("view-time").value; |
| |
| const enc = new TextEncoder(); |
| const salt = ZKCrypto.randomBytes(16); |
| const iv = ZKCrypto.randomBytes(12); |
| const key = await ZKCrypto.deriveKey(password, salt); |
| |
| const encryptedContent = await window.crypto.subtle.encrypt( |
| { name: "AES-GCM", iv: iv }, |
| key, |
| enc.encode(text) |
| ); |
| |
| const b64Data = btoa(String.fromCharCode(...new Uint8Array(encryptedContent))); |
| const b64Salt = btoa(String.fromCharCode(...salt)); |
| const b64Iv = btoa(String.fromCharCode(...iv)); |
| |
| const res = await fetch("/share/create", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| ciphertext: b64Data, |
| iv: b64Iv, |
| salt: b64Salt, |
| ttl: 60, |
| view_time: viewTime |
| }) |
| }); |
| |
| const data = await res.json(); |
| |
| let finalUrl = ""; |
| if (data.link) { |
| finalUrl = `${data.link}#${password}`; |
| } else { |
| finalUrl = `${window.location.origin}/share/v/${data.id}#${password}`; |
| } |
| |
| document.getElementById("share-link").value = finalUrl; |
| document.getElementById("result-area").classList.remove("d-none"); |
| }); |
| |
| function copyLink() { |
| const copyText = document.getElementById("share-link"); |
| copyText.select(); |
| document.execCommand("copy"); |
| alert("Link copied!"); |
| } |
| </script> |
| {% endblock %} |