2025-12-27 00:49:07 +00:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Decrypt Secure Support Reply< / title >
< style >
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
padding: 2rem;
color: #e0e0e0;
}
.container {
max-width: 800px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 0.5rem;
font-size: 1.8rem;
background: linear-gradient(90deg, #00d9ff, #00ff94);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
text-align: center;
color: #888;
margin-bottom: 2rem;
font-size: 0.9rem;
}
.card {
background: rgba(255,255,255,0.05);
border-radius: 16px;
padding: 2rem;
margin-bottom: 1.5rem;
border: 1px solid rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
}
.card h2 {
font-size: 1.1rem;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.card h2 .icon {
font-size: 1.3rem;
}
.input-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
color: #aaa;
font-size: 0.85rem;
}
textarea, input[type="password"], input[type="text"] {
width: 100%;
padding: 0.8rem 1rem;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
background: rgba(0,0,0,0.3);
color: #fff;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.85rem;
resize: vertical;
}
textarea:focus, input:focus {
outline: none;
border-color: #00d9ff;
box-shadow: 0 0 0 3px rgba(0, 217, 255, 0.1);
}
textarea.encrypted {
min-height: 120px;
font-size: 0.75rem;
word-break: break-all;
}
.password-row {
display: flex;
gap: 1rem;
align-items: flex-end;
}
.password-row .input-group {
flex: 1;
margin-bottom: 0;
}
button {
padding: 0.8rem 2rem;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
font-size: 0.9rem;
}
button.primary {
background: linear-gradient(135deg, #00d9ff 0%, #00ff94 100%);
color: #000;
}
button.primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 217, 255, 0.4);
}
button.primary:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
button.secondary {
background: rgba(255,255,255,0.1);
color: #fff;
border: 1px solid rgba(255,255,255,0.2);
}
button.secondary:hover {
background: rgba(255,255,255,0.15);
}
.hint-banner {
background: rgba(255, 193, 7, 0.1);
border: 1px solid rgba(255, 193, 7, 0.3);
border-radius: 8px;
padding: 0.8rem 1rem;
margin-bottom: 1rem;
display: none;
}
.hint-banner.visible {
display: flex;
align-items: center;
gap: 0.5rem;
}
.hint-banner .hint-icon {
font-size: 1.2rem;
}
.hint-banner .hint-text {
color: #ffc107;
font-size: 0.9rem;
}
.message-container {
display: none;
}
.message-container.visible {
display: block;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.message-from {
font-weight: 600;
color: #00d9ff;
}
.message-date {
font-size: 0.8rem;
color: #888;
}
.message-subject {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 1rem;
}
.message-body {
line-height: 1.7;
white-space: pre-wrap;
}
.attachments {
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid rgba(255,255,255,0.1);
}
.attachments h3 {
font-size: 0.9rem;
color: #888;
margin-bottom: 0.8rem;
}
.attachment-item {
display: flex;
align-items: center;
gap: 0.8rem;
padding: 0.6rem 1rem;
background: rgba(0,0,0,0.2);
border-radius: 8px;
margin-bottom: 0.5rem;
}
.attachment-icon {
font-size: 1.5rem;
}
.attachment-info {
flex: 1;
}
.attachment-name {
font-weight: 500;
}
.attachment-meta {
font-size: 0.75rem;
color: #888;
}
.attachment-download {
padding: 0.4rem 0.8rem;
font-size: 0.8rem;
}
.reply-key-banner {
background: rgba(0, 217, 255, 0.1);
border: 1px solid rgba(0, 217, 255, 0.3);
border-radius: 8px;
padding: 1rem;
margin-top: 1.5rem;
display: none;
}
.reply-key-banner.visible {
display: block;
}
.reply-key-banner h4 {
font-size: 0.9rem;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.reply-key-banner p {
font-size: 0.8rem;
color: #aaa;
margin-bottom: 0.8rem;
}
.reply-key-value {
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.7rem;
background: rgba(0,0,0,0.3);
padding: 0.5rem;
border-radius: 4px;
word-break: break-all;
}
.error-banner {
background: rgba(255, 82, 82, 0.1);
border: 1px solid rgba(255, 82, 82, 0.3);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
display: none;
color: #ff5252;
}
.error-banner.visible {
display: block;
}
.status-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.85rem;
padding: 0.5rem 0;
}
.status-indicator .dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-indicator.loading .dot {
background: #ffc107;
animation: pulse 1s infinite;
}
.status-indicator.ready .dot {
background: #00ff94;
}
.status-indicator.error .dot {
background: #ff5252;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.demo-section {
border-top: 1px dashed rgba(255,255,255,0.1);
padding-top: 1.5rem;
margin-top: 1.5rem;
}
.demo-section h3 {
font-size: 0.9rem;
color: #888;
margin-bottom: 1rem;
}
.example-messages {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.example-messages button {
padding: 0.5rem 1rem;
font-size: 0.8rem;
}
.nav-links {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 1.5rem;
}
.nav-links a {
color: #00d9ff;
text-decoration: none;
font-size: 0.85rem;
padding: 0.5rem 1rem;
border-radius: 20px;
background: rgba(0, 217, 255, 0.1);
transition: all 0.2s;
}
.nav-links a:hover {
background: rgba(0, 217, 255, 0.2);
}
.nav-links a.active {
background: rgba(0, 217, 255, 0.3);
}
< / style >
< / head >
< body >
< div class = "container" >
< h1 > Secure Support Reply< / h1 >
< p class = "subtitle" > Decrypt password-protected messages from support< / p >
< nav class = "nav-links" >
< a href = "index.html" > Form Encryption< / a >
< a href = "support-reply.html" class = "active" > Decrypt Messages< / a >
2026-01-06 16:53:58 +00:00
< a href = "media-player.html" > Media Player< / a >
< a href = "artist-portal.html" > Artist Portal< / a >
2025-12-27 00:49:07 +00:00
< / nav >
< div id = "wasm-status" class = "status-indicator loading" >
< span class = "dot" > < / span >
< span > Loading encryption module...< / span >
< / div >
< div class = "card" >
< h2 > < span class = "icon" > 📨< / span > Encrypted Message< / h2 >
< div class = "input-group" >
< label for = "encrypted-message" > Paste the encrypted message you received:< / label >
< textarea id = "encrypted-message" class = "encrypted" placeholder = "U01TRy4uLg==" > < / textarea >
< / div >
< div id = "hint-banner" class = "hint-banner" >
< span class = "hint-icon" > 💡< / span >
< span class = "hint-text" > Password hint: < strong id = "hint-text" > < / strong > < / span >
< / div >
< div id = "error-banner" class = "error-banner" > < / div >
< div class = "password-row" >
< div class = "input-group" >
< label for = "password" > Password:< / label >
< input type = "password" id = "password" placeholder = "Enter your password" >
< / div >
< button id = "decrypt-btn" class = "primary" disabled > Decrypt< / button >
< / div >
< / div >
< div id = "message-container" class = "card message-container" >
< h2 > < span class = "icon" > 📬< / span > Decrypted Message< / h2 >
< div class = "message-header" >
< div >
< div class = "message-from" id = "msg-from" > Support Team< / div >
< div id = "msg-subject" class = "message-subject" > < / div >
< / div >
< div class = "message-date" id = "msg-date" > < / div >
< / div >
< div class = "message-body" id = "msg-body" > < / div >
< div id = "attachments-container" class = "attachments" style = "display: none;" >
< h3 > Attachments< / h3 >
< div id = "attachments-list" > < / div >
< / div >
< div id = "reply-key-banner" class = "reply-key-banner" >
< h4 > < span > 🔐< / span > Authenticated Reply Key< / h4 >
< p > This message includes a public key for secure replies. Use this to encrypt your response:< / p >
< div class = "reply-key-value" id = "reply-key" > < / div >
< / div >
< / div >
< div class = "card" >
< div class = "demo-section" >
< h3 > Demo: Try with sample messages< / h3 >
< p style = "font-size: 0.85rem; color: #888; margin-bottom: 1rem;" >
Click a button to load a pre-encrypted sample message. All use password: < code style = "background: rgba(0,0,0,0.3); padding: 0.2rem 0.4rem; border-radius: 4px;" > demo123< / code >
< / p >
< div class = "example-messages" id = "example-buttons" > < / div >
< / div >
< / div >
< / div >
< script src = "wasm_exec.js" > < / script >
< script >
// Example encrypted messages (will be populated by WASM generation)
const EXAMPLES = {
'simple': '',
'with-attachment': '',
'with-hint': '',
'with-reply-key': ''
};
// Store attachment data for downloads
const attachmentData = new Map();
let wasmReady = false;
// Initialize WASM
async function initWasm() {
const statusEl = document.getElementById('wasm-status');
try {
const go = new Go();
const result = await WebAssembly.instantiateStreaming(
fetch('stmf.wasm'),
go.importObject
);
go.run(result.instance);
// Wait for BorgSMSG to be ready
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('WASM init timeout')), 5000);
if (typeof BorgSMSG !== 'undefined' & & BorgSMSG.ready) {
clearTimeout(timeout);
resolve();
return;
}
document.addEventListener('borgstmf:ready', () => {
clearTimeout(timeout);
resolve();
});
});
wasmReady = true;
updateStatus(statusEl, 'ready', 'Encryption module ready (v' + BorgSMSG.version + ')');
document.getElementById('decrypt-btn').disabled = false;
// Generate example messages
await generateExamples();
setupExampleButtons();
} catch (err) {
updateStatus(statusEl, 'error', 'Failed to load: ' + err.message);
console.error('WASM init error:', err);
}
}
// Update status indicator safely
function updateStatus(el, status, message) {
el.className = 'status-indicator ' + status;
// Clear and rebuild safely
while (el.firstChild) el.removeChild(el.firstChild);
const dot = document.createElement('span');
dot.className = 'dot';
const text = document.createElement('span');
text.textContent = message;
el.appendChild(dot);
el.appendChild(text);
}
// Setup example buttons safely
function setupExampleButtons() {
const container = document.getElementById('example-buttons');
const examples = [
{ key: 'simple', label: 'Simple Message' },
{ key: 'with-attachment', label: 'With Attachment' },
{ key: 'with-hint', label: 'With Password Hint' },
{ key: 'with-reply-key', label: 'With Reply Key' }
];
examples.forEach(ex => {
const btn = document.createElement('button');
btn.className = 'secondary';
btn.textContent = ex.label;
btn.addEventListener('click', () => loadExample(ex.key));
container.appendChild(btn);
});
}
// Generate example encrypted messages
async function generateExamples() {
try {
// Simple message
EXAMPLES['simple'] = await BorgSMSG.encrypt({
body: 'Hello! Thank you for contacting our support team.\n\nWe have reviewed your request and are happy to help. Please let us know if you have any other questions.\n\nBest regards,\nThe Support Team',
subject: 'Re: Your Support Request #12345',
from: 'support@example.com'
}, 'demo123');
// With attachment
const fileContent = btoa('This is the content of the attached file.\nIt contains important information.');
EXAMPLES['with-attachment'] = await BorgSMSG.encrypt({
body: 'Please find the requested document attached to this message.\n\nThe file contains the information you requested about your account.',
subject: 'Document Attached',
from: 'documents@example.com',
attachments: [{
name: 'account-details.txt',
content: fileContent,
mime: 'text/plain'
}]
}, 'demo123');
// With password hint
EXAMPLES['with-hint'] = await BorgSMSG.encrypt({
body: 'This is a confidential message that requires your password to view.\n\nYour account has been updated as requested.',
subject: 'Account Update Confirmation',
from: 'security@example.com'
}, 'demo123', 'demo + 123');
// With reply key
EXAMPLES['with-reply-key'] = await BorgSMSG.encrypt({
body: 'This message includes a public key for secure replies.\n\nWhen you reply, use the attached public key to encrypt your response. This ensures only we can read your reply.',
subject: 'Secure Communication Channel',
from: 'secure@example.com',
replyKey: {
publicKey: 'dGVzdHB1YmxpY2tleWZvcmRlbW9wdXJwb3Nlcw=='
}
}, 'demo123');
console.log('Example messages generated');
} catch (err) {
console.error('Failed to generate examples:', err);
}
}
// Load example message
function loadExample(type) {
const textarea = document.getElementById('encrypted-message');
textarea.value = EXAMPLES[type];
checkForHint();
}
// Check for password hint
async function checkForHint() {
const encryptedB64 = document.getElementById('encrypted-message').value.trim();
const hintBanner = document.getElementById('hint-banner');
const hintText = document.getElementById('hint-text');
hintBanner.classList.remove('visible');
if (!encryptedB64 || !wasmReady) return;
try {
const info = await BorgSMSG.getInfo(encryptedB64);
if (info.hint) {
hintText.textContent = info.hint;
hintBanner.classList.add('visible');
}
} catch (err) {
// Silently ignore - invalid format
}
}
// Decrypt message
async function decryptMessage() {
const encryptedB64 = document.getElementById('encrypted-message').value.trim();
const password = document.getElementById('password').value;
const errorBanner = document.getElementById('error-banner');
const messageContainer = document.getElementById('message-container');
errorBanner.classList.remove('visible');
messageContainer.classList.remove('visible');
if (!encryptedB64) {
showError('Please paste an encrypted message');
return;
}
if (!password) {
showError('Please enter the password');
return;
}
try {
const message = await BorgSMSG.decrypt(encryptedB64, password);
displayMessage(message);
} catch (err) {
showError('Decryption failed: ' + err.message);
}
}
// Show error
function showError(msg) {
const errorBanner = document.getElementById('error-banner');
errorBanner.textContent = msg;
errorBanner.classList.add('visible');
}
// Display decrypted message
function displayMessage(msg) {
document.getElementById('msg-from').textContent = msg.from || 'Unknown Sender';
document.getElementById('msg-subject').textContent = msg.subject || '(No Subject)';
document.getElementById('msg-body').textContent = msg.body;
// Format date
if (msg.timestamp) {
const date = new Date(msg.timestamp * 1000);
document.getElementById('msg-date').textContent = date.toLocaleString();
} else {
document.getElementById('msg-date').textContent = '';
}
// Handle attachments
const attachmentsContainer = document.getElementById('attachments-container');
const attachmentsList = document.getElementById('attachments-list');
// Clear previous attachments
while (attachmentsList.firstChild) {
attachmentsList.removeChild(attachmentsList.firstChild);
}
attachmentData.clear();
if (msg.attachments & & msg.attachments.length > 0) {
attachmentsContainer.style.display = 'block';
msg.attachments.forEach((att, index) => {
// Store attachment data
const attId = 'att-' + index;
attachmentData.set(attId, {
name: att.name,
content: att.content,
mime: att.mime
});
const item = document.createElement('div');
item.className = 'attachment-item';
const iconSpan = document.createElement('span');
iconSpan.className = 'attachment-icon';
iconSpan.textContent = getFileIcon(att.mime);
const infoDiv = document.createElement('div');
infoDiv.className = 'attachment-info';
const nameDiv = document.createElement('div');
nameDiv.className = 'attachment-name';
nameDiv.textContent = att.name;
const metaDiv = document.createElement('div');
metaDiv.className = 'attachment-meta';
metaDiv.textContent = att.mime || 'unknown type';
infoDiv.appendChild(nameDiv);
infoDiv.appendChild(metaDiv);
const downloadBtn = document.createElement('button');
downloadBtn.className = 'secondary attachment-download';
downloadBtn.textContent = 'Download';
downloadBtn.dataset.attId = attId;
downloadBtn.addEventListener('click', function() {
downloadAttachment(this.dataset.attId);
});
item.appendChild(iconSpan);
item.appendChild(infoDiv);
item.appendChild(downloadBtn);
attachmentsList.appendChild(item);
});
} else {
attachmentsContainer.style.display = 'none';
}
// Handle reply key
const replyKeyBanner = document.getElementById('reply-key-banner');
if (msg.replyKey & & msg.replyKey.publicKey) {
document.getElementById('reply-key').textContent = msg.replyKey.publicKey;
replyKeyBanner.classList.add('visible');
} else {
replyKeyBanner.classList.remove('visible');
}
document.getElementById('message-container').classList.add('visible');
}
// Get file icon based on mime type
function getFileIcon(mime) {
if (!mime) return '📄';
if (mime.startsWith('image/')) return '🖼️';
if (mime.startsWith('video/')) return '🎬';
if (mime.startsWith('audio/')) return '🎵';
if (mime.includes('pdf')) return '📕';
if (mime.includes('zip') || mime.includes('tar') || mime.includes('gzip')) return '📦';
if (mime.includes('json') || mime.includes('xml')) return '📋';
return '📄';
}
// Download attachment
function downloadAttachment(attId) {
const att = attachmentData.get(attId);
if (!att) {
alert('Attachment not found');
return;
}
try {
const binary = atob(att.content);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length ; i + + ) {
bytes[i] = binary.charCodeAt(i);
}
const blob = new Blob([bytes], { type: att.mime || 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = att.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (err) {
alert('Failed to download: ' + err.message);
}
}
// Event listeners
document.getElementById('decrypt-btn').addEventListener('click', decryptMessage);
document.getElementById('password').addEventListener('keypress', (e) => {
if (e.key === 'Enter') decryptMessage();
});
document.getElementById('encrypted-message').addEventListener('input', checkForHint);
// Initialize
initWasm();
< / script >
< / body >
< / html >