Securecrypt / index.html
CultriX's picture
🐳 11/03 - 23:23 - Fix the layout so everything aligns better
688e8b5 verified
<!DOCTYPE html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SecureCrypt | Military-Grade Client-Side Encryption</title>
<meta name="description" content="Secure client-side encryption for text and files. Zero server storage, AES-256-GCM encryption.">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
900: '#1e3a8a',
},
crypto: {
encrypt: '#10b981',
decrypt: '#f59e0b',
danger: '#ef4444',
}
},
animation: {
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'float': 'float 6s ease-in-out infinite',
'shimmer': 'shimmer 2s linear infinite',
},
keyframes: {
float: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
},
shimmer: {
'0%': { backgroundPosition: '-1000px 0' },
'100%': { backgroundPosition: '1000px 0' },
}
}
}
}
}
</script>
<style>
.glass-panel {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.dark .glass-panel {
background: rgba(17, 24, 39, 0.7);
border: 1px solid rgba(255, 255, 255, 0.05);
}
.gradient-text {
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.security-grid {
background-image: radial-gradient(circle at 1px 1px, rgba(59, 130, 246, 0.15) 1px, transparent 0);
background-size: 20px 20px;
}
.drop-zone {
transition: all 0.3s ease;
}
.drop-zone.drag-over {
border-color: #3b82f6;
background: rgba(59, 130, 246, 0.1);
transform: scale(1.02);
}
.tab-active {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
color: white;
}
.strength-weak { background: #ef4444; }
.strength-fair { background: #f59e0b; }
.strength-good { background: #3b82f6; }
.strength-strong { background: #10b981; }
.toast {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.encrypt-mode { border-color: #10b981; }
.decrypt-mode { border-color: #f59e0b; }
</style>
</head>
<body class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-300 font-sans">
<!-- Navigation -->
<nav class="fixed w-full z-50 glass-panel border-b border-gray-200 dark:border-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16 items-center">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg">
<i data-lucide="shield-check" class="w-6 h-6 text-white"></i>
</div>
<span class="text-xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
SecureCrypt
</span>
</div>
<div class="flex items-center space-x-4">
<button onclick="toggleTheme()" class="p-2 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors">
<i data-lucide="sun" class="w-5 h-5 hidden dark:block"></i>
<i data-lucide="moon" class="w-5 h-5 block dark:hidden"></i>
</button>
<a href="#security" class="hidden md:block text-sm font-medium hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Security Specs
</a>
<div class="flex items-center space-x-2 px-3 py-1 rounded-full bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 text-xs font-semibold">
<span class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
<span>Client-Side Only</span>
</div>
</div>
</div>
</div>
</nav>
<!-- Hero Section -->
<section class="pt-32 pb-16 px-4 sm:px-6 lg:px-8 relative overflow-hidden">
<div class="absolute inset-0 security-grid opacity-50"></div>
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-full h-96 bg-gradient-to-b from-blue-500/10 to-transparent pointer-events-none"></div>
<div class="max-w-4xl mx-auto text-center relative z-10">
<div class="inline-flex items-center space-x-2 px-4 py-2 rounded-full bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 text-sm font-medium mb-6 animate-float">
<i data-lucide="lock" class="w-4 h-4"></i>
<span>Military-Grade AES-256-GCM Encryption</span>
</div>
<h1 class="text-5xl md:text-6xl font-bold mb-6 leading-tight">
Encrypt Your Data <br>
<span class="gradient-text">In the Browser</span>
</h1>
<p class="text-xl text-gray-600 dark:text-gray-400 mb-8 max-w-2xl mx-auto">
Zero server storage. Zero data transmission. Your files and text never leave your device.
Open-source, auditable, and completely private.
</p>
<div class="flex flex-wrap justify-center gap-4 text-sm text-gray-500 dark:text-gray-400">
<div class="flex items-center space-x-2">
<i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>
<span>AES-256-GCM</span>
</div>
<div class="flex items-center space-x-2">
<i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>
<span>PBKDF2 600k Iterations</span>
</div>
<div class="flex items-center space-x-2">
<i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>
<span>Open Source</span>
</div>
</div>
</div>
</section>
<!-- Main Application -->
<section class="pb-20 px-4 sm:px-6 lg:px-8">
<div class="max-w-6xl mx-auto">
<!-- Mode Tabs -->
<div class="flex justify-center mb-8">
<div class="bg-white dark:bg-gray-800 p-1 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-700 inline-flex">
<button onclick="switchMode('text')" id="tab-text" class="tab-active px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center space-x-2">
<i data-lucide="type" class="w-4 h-4"></i>
<span>Text</span>
</button>
<button onclick="switchMode('file')" id="tab-file" class="px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center space-x-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200">
<i data-lucide="file-up" class="w-4 h-4"></i>
<span>File</span>
</button>
</div>
</div>
<!-- Operation Toggle -->
<div class="flex justify-center mb-8">
<div class="bg-gray-100 dark:bg-gray-800 p-1 rounded-full inline-flex relative">
<div id="operation-indicator" class="absolute left-1 top-1 w-[calc(50%-4px)] h-[calc(100%-8px)] bg-white dark:bg-gray-700 rounded-full shadow-md transition-all duration-300"></div>
<button onclick="setOperation('encrypt')" class="relative z-10 px-8 py-2 rounded-full font-medium transition-colors duration-200 flex items-center space-x-2" id="btn-encrypt">
<i data-lucide="lock" class="w-4 h-4"></i>
<span>Encrypt</span>
</button>
<button onclick="setOperation('decrypt')" class="relative z-10 px-8 py-2 rounded-full font-medium transition-colors duration-200 flex items-center space-x-2 text-gray-600 dark:text-gray-400" id="btn-decrypt">
<i data-lucide="unlock" class="w-4 h-4"></i>
<span>Decrypt</span>
</button>
</div>
</div>
<!-- Text Mode Interface -->
<div id="interface-text" class="glass-panel rounded-3xl p-8 shadow-2xl border border-gray-200 dark:border-gray-700">
<div class="grid md:grid-cols-2 gap-6">
<!-- Input Section -->
<div class="space-y-4 h-full flex flex-col">
<div class="flex justify-between items-center h-8">
<label class="text-sm font-semibold text-gray-700 dark:text-gray-300 flex items-center space-x-2">
<i data-lucide="edit-3" class="w-4 h-4"></i>
<span>Input</span>
</label>
<button onclick="clearText()" class="text-xs text-gray-500 hover:text-red-500 transition-colors px-2 py-1 rounded hover:bg-red-50 dark:hover:bg-red-900/20">
Clear
</button>
</div>
<div class="relative flex-1">
<textarea
id="text-input"
placeholder="Enter text to encrypt or encrypted data to decrypt..."
class="w-full h-64 p-4 rounded-xl bg-gray-50 dark:bg-gray-900 border-2 border-gray-200 dark:border-gray-700 focus:border-blue-500 focus:ring-0 resize-none transition-all font-mono text-sm"
></textarea>
<div class="absolute bottom-4 right-4 text-xs text-gray-400 bg-gray-50 dark:bg-gray-900 px-2 py-1 rounded" id="char-count">0 chars</div>
</div>
</div>
<!-- Output Section -->
<div class="space-y-4 h-full flex flex-col">
<div class="flex justify-between items-center h-8">
<label class="text-sm font-semibold text-gray-700 dark:text-gray-300 flex items-center space-x-2">
<i data-lucide="shield" class="w-4 h-4"></i>
<span>Result</span>
</label>
<div class="flex space-x-2">
<button onclick="copyOutput()" class="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors" title="Copy to clipboard">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
<button onclick="downloadOutput()" class="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors" title="Download as file">
<i data-lucide="download" class="w-4 h-4"></i>
</button>
</div>
</div>
<div class="relative flex-1">
<textarea
id="text-output"
readonly
placeholder="Result will appear here..."
class="w-full h-64 p-4 rounded-xl bg-gray-100 dark:bg-gray-800/50 border-2 border-gray-200 dark:border-gray-700 resize-none font-mono text-sm text-gray-600 dark:text-gray-400"
></textarea>
<div id="output-overlay" class="absolute inset-0 flex items-center justify-center bg-gray-900/5 dark:bg-gray-900/20 rounded-xl backdrop-blur-sm hidden">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
</div>
</div>
</div>
<!-- Password Section -->
<div class="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
<div class="flex flex-col md:flex-row gap-4 items-start">
<div class="flex-1 w-full">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Password / Passphrase
</label>
<div class="relative">
<input
type="password"
id="password-input"
placeholder="Enter strong password..."
class="w-full px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-900 border-2 border-gray-200 dark:border-gray-700 focus:border-blue-500 focus:ring-0 transition-all pr-28"
oninput="checkPasswordStrength()"
>
<button onclick="togglePasswordVisibility()" class="absolute right-14 top-1/2 -translate-y-1/2 p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors" title="Show/Hide password">
<i data-lucide="eye" class="w-4 h-4" id="eye-icon"></i>
</button>
<button onclick="copyPassword()" class="absolute right-8 top-1/2 -translate-y-1/2 p-2 text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors" title="Copy password to clipboard">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
<button onclick="generatePassword()" class="absolute right-2 top-1/2 -translate-y-1/2 p-2 text-blue-500 hover:text-blue-700 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors" title="Generate secure password">
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
</button>
</div>
<!-- Password Strength -->
<div class="mt-2 flex items-center space-x-3">
<div class="flex-1 h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div id="strength-bar" class="h-full w-0 transition-all duration-300 strength-weak"></div>
</div>
<span id="strength-text" class="text-xs font-medium text-gray-500 min-w-[50px] text-right">Empty</span>
</div>
</div>
<div class="flex flex-col justify-end md:pt-7 w-full md:w-auto">
<button onclick="processText()" id="action-btn" class="w-full px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-semibold rounded-xl shadow-lg transform hover:scale-105 transition-all flex items-center justify-center space-x-2 min-w-[160px]">
<i data-lucide="lock" class="w-5 h-5" id="action-icon"></i>
<span id="action-text">Encrypt</span>
</button>
</div>
</div>
</div>
</div>
<!-- File Mode Interface -->
<div id="interface-file" class="hidden glass-panel rounded-3xl p-8 shadow-2xl border border-gray-200 dark:border-gray-700">
<!-- File Drop Zone -->
<div
id="drop-zone"
class="drop-zone border-3 border-dashed border-gray-300 dark:border-gray-600 rounded-2xl p-12 text-center cursor-pointer hover:border-blue-500 transition-all"
onclick="document.getElementById('file-input').click()"
ondrop="handleDrop(event)"
ondragover="handleDragOver(event)"
ondragleave="handleDragLeave(event)"
>
<input type="file" id="file-input" class="hidden" onchange="handleFileSelect(event)">
<div class="w-20 h-20 mx-auto mb-4 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center">
<i data-lucide="upload-cloud" class="w-10 h-10 text-blue-600 dark:text-blue-400"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Drop your file here</h3>
<p class="text-gray-500 dark:text-gray-400 mb-4">or click to browse (Max 10MB)</p>
<div id="file-info" class="hidden mt-4 p-4 bg-gray-50 dark:bg-gray-800 rounded-xl inline-flex items-center space-x-3">
<i data-lucide="file" class="w-8 h-8 text-blue-500"></i>
<div class="text-left">
<div id="file-name" class="font-medium">filename.txt</div>
<div id="file-size" class="text-sm text-gray-500">0 KB</div>
</div>
<button onclick="clearFile(event)" class="p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
</div>
<!-- File Password & Action -->
<div class="mt-6 flex flex-col md:flex-row gap-6 items-start">
<div class="flex-1 w-full">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Password Protection
</label>
<div class="relative">
<input
type="password"
id="file-password"
placeholder="Enter password..."
class="w-full px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-900 border-2 border-gray-200 dark:border-gray-700 focus:border-blue-500 focus:ring-0 transition-all pr-28"
>
<div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center space-x-1">
<button onclick="toggleFilePasswordVisibility()" class="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors" title="Show/Hide password">
<i data-lucide="eye" class="w-4 h-4" id="file-eye-icon"></i>
</button>
<button onclick="copyFilePassword()" class="p-2 text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors" title="Copy password to clipboard">
<i data-lucide="copy" class="w-4 h-4"></i>
</button>
<button onclick="generateFilePassword()" class="p-2 text-blue-500 hover:text-blue-700 rounded-lg hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors" title="Generate secure password">
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
<div class="flex flex-col justify-end md:pt-7 w-full md:w-auto">
<button onclick="processFile()" id="file-action-btn" class="w-full md:w-auto px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-semibold rounded-xl shadow-lg transform hover:scale-105 transition-all flex items-center justify-center space-x-2 disabled:opacity-50 disabled:cursor-not-allowed min-w-[160px]" disabled>
<i data-lucide="lock" class="w-5 h-5"></i>
<span>Encrypt File</span>
</button>
</div>
</div>
<!-- Progress Bar -->
<div id="progress-container" class="hidden mt-6">
<div class="flex justify-between text-sm mb-2">
<span class="text-gray-600 dark:text-gray-400">Processing...</span>
<span id="progress-text" class="font-medium">0%</span>
</div>
<div class="h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div id="progress-bar" class="h-full bg-gradient-to-r from-blue-500 to-purple-600 transition-all duration-300" style="width: 0%"></div>
</div>
</div>
<!-- Download Section -->
<div id="download-section" class="hidden mt-6 p-6 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-xl">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<div class="w-12 h-12 rounded-full bg-green-100 dark:bg-green-800 flex items-center justify-center">
<i data-lucide="check" class="w-6 h-6 text-green-600 dark:text-green-400"></i>
</div>
<div>
<h4 class="font-semibold text-green-900 dark:text-green-100">Ready!</h4>
<p class="text-sm text-green-700 dark:text-green-300">Your file has been processed successfully</p>
</div>
</div>
<button onclick="downloadProcessedFile()" class="px-6 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg font-medium transition-colors flex items-center space-x-2">
<i data-lucide="download" class="w-4 h-4"></i>
<span>Download</span>
</button>
</div>
</div>
</div>
</div>
</section>
<!-- Security Specifications -->
<section id="security" class="py-20 bg-white dark:bg-gray-800 border-y border-gray-200 dark:border-gray-700">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12">
<h2 class="text-3xl font-bold mb-4">Security Specifications</h2>
<p class="text-gray-600 dark:text-gray-400">Enterprise-grade encryption standards</p>
</div>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Spec Cards -->
<div class="p-6 rounded-2xl bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 rounded-xl bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center mb-4">
<i data-lucide="cpu" class="w-6 h-6 text-blue-600"></i>
</div>
<h3 class="font-semibold mb-2">Algorithm</h3>
<p class="text-2xl font-bold text-blue-600">AES-256-GCM</p>
<p class="text-sm text-gray-500 mt-1">Galois/Counter Mode</p>
</div>
<div class="p-6 rounded-2xl bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 rounded-xl bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center mb-4">
<i data-lucide="key" class="w-6 h-6 text-purple-600"></i>
</div>
<h3 class="font-semibold mb-2">Key Derivation</h3>
<p class="text-2xl font-bold text-purple-600">PBKDF2</p>
<p class="text-sm text-gray-500 mt-1">HMAC-SHA256</p>
</div>
<div class="p-6 rounded-2xl bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 rounded-xl bg-green-100 dark:bg-green-900/30 flex items-center justify-center mb-4">
<i data-lucide="repeat" class="w-6 h-6 text-green-600"></i>
</div>
<h3 class="font-semibold mb-2">Iterations</h3>
<p class="text-2xl font-bold text-green-600">600,000</p>
<p class="text-sm text-gray-500 mt-1">Brute-force resistant</p>
</div>
<div class="p-6 rounded-2xl bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 rounded-xl bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center mb-4">
<i data-lucide="fingerprint" class="w-6 h-6 text-orange-600"></i>
</div>
<h3 class="font-semibold mb-2">Salt Length</h3>
<p class="text-2xl font-bold text-orange-600">16 bytes</p>
<p class="text-sm text-gray-500 mt-1">128-bit random</p>
</div>
<div class="p-6 rounded-2xl bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 rounded-xl bg-red-100 dark:bg-red-900/30 flex items-center justify-center mb-4">
<i data-lucide="shuffle" class="w-6 h-6 text-red-600"></i>
</div>
<h3 class="font-semibold mb-2">IV Length</h3>
<p class="text-2xl font-bold text-red-600">12 bytes</p>
<p class="text-sm text-gray-500 mt-1">96-bit nonce</p>
</div>
<div class="p-6 rounded-2xl bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 rounded-xl bg-indigo-100 dark:bg-indigo-900/30 flex items-center justify-center mb-4">
<i data-lucide="badge-check" class="w-6 h-6 text-indigo-600"></i>
</div>
<h3 class="font-semibold mb-2">Auth Tag</h3>
<p class="text-2xl font-bold text-indigo-600">128-bit</p>
<p class="text-sm text-gray-500 mt-1">GCM Authentication</p>
</div>
</div>
<!-- Data Format -->
<div class="mt-12 p-6 rounded-2xl bg-gray-900 text-gray-100 font-mono text-sm overflow-x-auto">
<div class="flex items-center justify-between mb-4">
<span class="text-gray-400">Data Container Format</span>
<span class="text-xs bg-gray-800 px-2 py-1 rounded">ENCv1</span>
</div>
<code class="block text-green-400">
ENCv1:{Base64(JSON header + newline + ciphertext)}
</code>
<div class="mt-4 text-gray-400 text-xs">
{ "v": 1, "alg": "AES-GCM", "kdf": {"name": "PBKDF2", "hash": "SHA-256", "iters": 600000, "salt_b64": "β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’"}, "iv_b64": "β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’", "keyType": "passphrase", "created": "2025-01-15T12:00:00Z", "type": "text", "orig": null }
</div>
</div>
</div>
</section>
<!-- Features Grid -->
<section class="py-20 px-4 sm:px-6 lg:px-8">
<div class="max-w-6xl mx-auto">
<div class="grid md:grid-cols-3 gap-8">
<div class="text-center p-6">
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center shadow-lg">
<i data-lucide="server-off" class="w-8 h-8 text-white"></i>
</div>
<h3 class="text-xl font-bold mb-2">Zero Server Storage</h3>
<p class="text-gray-600 dark:text-gray-400">All encryption happens locally in your browser. No data is ever transmitted to any server.</p>
</div>
<div class="text-center p-6">
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-purple-500 to-purple-600 flex items-center justify-center shadow-lg">
<i data-lucide="code" class="w-8 h-8 text-white"></i>
</div>
<h3 class="text-xl font-bold mb-2">Open Source</h3>
<p class="text-gray-600 dark:text-gray-400">Fully auditable code. Verify the encryption implementation yourself. No backdoors possible.</p>
</div>
<div class="text-center p-6">
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center shadow-lg">
<i data-lucide="zap" class="w-8 h-8 text-white"></i>
</div>
<h3 class="text-xl font-bold mb-2">Fast & Efficient</h3>
<p class="text-gray-600 dark:text-gray-400">Optimized WebCrypto API implementation. Handle files up to 10MB with ease.</p>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-900 text-gray-400 py-12 border-t border-gray-800">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="flex items-center space-x-3 mb-4 md:mb-0">
<div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center">
<i data-lucide="shield-check" class="w-5 h-5 text-white"></i>
</div>
<span class="text-lg font-bold text-white">SecureCrypt</span>
</div>
<div class="text-sm">
<p>Client-side encryption tool. No data leaves your device.</p>
</div>
</div>
<div class="mt-8 pt-8 border-t border-gray-800 text-xs text-center">
<p class="mb-2"><strong>Security Notice:</strong> All operations occur locally. No key recovery possible. Protects at-rest data only.</p>
<p>&copy; 2025 SecureCrypt. Open source encryption utility.</p>
</div>
</div>
</footer>
<!-- Toast Container -->
<div id="toast-container" class="fixed bottom-4 right-4 z-50 flex flex-col space-y-2"></div>
<script>
// Initialize Lucide icons
lucide.createIcons();
// State management
let currentMode = 'text';
let currentOperation = 'encrypt';
let selectedFile = null;
let processedFileData = null;
// Theme toggle
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
}
// Initialize theme
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
// Mode switching
function switchMode(mode) {
currentMode = mode;
// Update tabs
document.getElementById('tab-text').className = mode === 'text'
? 'tab-active px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center space-x-2'
: 'px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center space-x-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200';
document.getElementById('tab-file').className = mode === 'file'
? 'tab-active px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center space-x-2'
: 'px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center space-x-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200';
// Show/hide interfaces
document.getElementById('interface-text').classList.toggle('hidden', mode !== 'text');
document.getElementById('interface-file').classList.toggle('hidden', mode !== 'file');
}
// Operation switching
function setOperation(op) {
currentOperation = op;
const indicator = document.getElementById('operation-indicator');
const btnEncrypt = document.getElementById('btn-encrypt');
const btnDecrypt = document.getElementById('btn-decrypt');
const actionBtn = document.getElementById('action-btn');
const actionIcon = document.getElementById('action-icon');
const actionText = document.getElementById('action-text');
const fileBtn = document.getElementById('file-action-btn');
if (op === 'encrypt') {
indicator.style.left = '4px';
btnEncrypt.classList.remove('text-gray-600', 'dark:text-gray-400');
btnDecrypt.classList.add('text-gray-600', 'dark:text-gray-400');
actionBtn.classList.remove('from-amber-500', 'to-orange-600');
actionBtn.classList.add('from-blue-600', 'to-purple-600');
actionIcon.setAttribute('data-lucide', 'lock');
actionText.textContent = 'Encrypt';
if (fileBtn) {
fileBtn.innerHTML = '<i data-lucide="lock" class="w-5 h-5"></i><span>Encrypt File</span>';
fileBtn.classList.remove('from-amber-500', 'to-orange-600');
fileBtn.classList.add('from-blue-600', 'to-purple-600');
}
} else {
indicator.style.left = 'calc(50% + 4px)';
btnDecrypt.classList.remove('text-gray-600', 'dark:text-gray-400');
btnEncrypt.classList.add('text-gray-600', 'dark:text-gray-400');
actionBtn.classList.remove('from-blue-600', 'to-purple-600');
actionBtn.classList.add('from-amber-500', 'to-orange-600');
actionIcon.setAttribute('data-lucide', 'unlock');
actionText.textContent = 'Decrypt';
if (fileBtn) {
fileBtn.innerHTML = '<i data-lucide="unlock" class="w-5 h-5"></i><span>Decrypt File</span>';
fileBtn.classList.remove('from-blue-600', 'to-purple-600');
fileBtn.classList.add('from-amber-500', 'to-orange-600');
}
}
lucide.createIcons();
}
// Password strength checker
function checkPasswordStrength() {
const password = document.getElementById('password-input').value;
const bar = document.getElementById('strength-bar');
const text = document.getElementById('strength-text');
let strength = 0;
if (password.length > 8) strength++;
if (password.length > 12) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^A-Za-z0-9]/.test(password)) strength++;
bar.className = 'h-full transition-all duration-300 ';
if (password.length === 0) {
bar.style.width = '0%';
text.textContent = 'Empty';
text.className = 'text-xs font-medium text-gray-500';
} else if (strength < 2) {
bar.style.width = '25%';
bar.classList.add('strength-weak');
text.textContent = 'Weak';
text.className = 'text-xs font-medium text-red-500';
} else if (strength < 4) {
bar.style.width = '50%';
bar.classList.add('strength-fair');
text.textContent = 'Fair';
text.className = 'text-xs font-medium text-amber-500';
} else if (strength < 5) {
bar.style.width = '75%';
bar.classList.add('strength-good');
text.textContent = 'Good';
text.className = 'text-xs font-medium text-blue-500';
} else {
bar.style.width = '100%';
bar.classList.add('strength-strong');
text.textContent = 'Strong';
text.className = 'text-xs font-medium text-green-500';
}
}
// Generate secure password
function generatePassword() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+~`|}{[]:;?><,./-=';
let password = '';
const length = 24;
const array = new Uint32Array(length);
crypto.getRandomValues(array);
for (let i = 0; i < length; i++) {
password += chars[array[i] % chars.length];
}
document.getElementById('password-input').value = password;
checkPasswordStrength();
showToast('Password generated!', 'success');
}
function generateFilePassword() {
generatePassword();
document.getElementById('file-password').value = document.getElementById('password-input').value;
}
// Toggle password visibility
function togglePasswordVisibility() {
const input = document.getElementById('password-input');
const icon = document.getElementById('eye-icon');
if (input.type === 'password') {
input.type = 'text';
icon.setAttribute('data-lucide', 'eye-off');
} else {
input.type = 'password';
icon.setAttribute('data-lucide', 'eye');
}
lucide.createIcons();
}
// Toggle file password visibility
function toggleFilePasswordVisibility() {
const input = document.getElementById('file-password');
const icon = document.getElementById('file-eye-icon');
if (input.type === 'password') {
input.type = 'text';
icon.setAttribute('data-lucide', 'eye-off');
} else {
input.type = 'password';
icon.setAttribute('data-lucide', 'eye');
}
lucide.createIcons();
}
// Copy password to clipboard
function copyPassword() {
const input = document.getElementById('password-input');
if (!input.value) {
showToast('No password to copy', 'error');
return;
}
input.select();
document.execCommand('copy');
showToast('Password copied to clipboard!', 'success');
}
// Copy file password to clipboard
function copyFilePassword() {
const input = document.getElementById('file-password');
if (!input.value) {
showToast('No password to copy', 'error');
return;
}
input.select();
document.execCommand('copy');
showToast('Password copied to clipboard!', 'success');
}
// Character count
document.getElementById('text-input').addEventListener('input', function() {
document.getElementById('char-count').textContent = this.value.length + ' chars';
});
// Clear functions
function clearText() {
document.getElementById('text-input').value = '';
document.getElementById('text-output').value = '';
document.getElementById('char-count').textContent = '0 chars';
}
// Crypto functions
async function deriveKey(password, salt) {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(password),
{ name: 'PBKDF2' },
false,
['deriveKey']
);
return crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: 600000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
}
async function encryptText(text, password) {
const encoder = new TextEncoder();
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const key = await deriveKey(password, salt);
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: iv },
key,
encoder.encode(text)
);
const header = {
v: 1,
alg: 'AES-GCM',
kdf: {
name: 'PBKDF2',
hash: 'SHA-256',
iters: 600000,
salt_b64: btoa(String.fromCharCode(...salt))
},
iv_b64: btoa(String.fromCharCode(...iv)),
keyType: 'passphrase',
created: new Date().toISOString(),
type: 'text',
orig: null
};
const ciphertext = btoa(String.fromCharCode(...new Uint8Array(encrypted)));
return 'ENCv1:' + btoa(JSON.stringify(header) + '\n' + ciphertext);
}
async function decryptText(encryptedData, password) {
if (!encryptedData.startsWith('ENCv1:')) {
throw new Error('Invalid format');
}
const payload = atob(encryptedData.slice(6));
const newlineIndex = payload.indexOf('\n');
const header = JSON.parse(payload.slice(0, newlineIndex));
const ciphertext = payload.slice(newlineIndex + 1);
const salt = Uint8Array.from(atob(header.kdf.salt_b64), c => c.charCodeAt(0));
const iv = Uint8Array.from(atob(header.iv_b64), c => c.charCodeAt(0));
const key = await deriveKey(password, salt);
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: iv },
key,
Uint8Array.from(atob(ciphertext), c => c.charCodeAt(0))
);
return new TextDecoder().decode(decrypted);
}
// Process text
async function processText() {
const input = document.getElementById('text-input').value;
const password = document.getElementById('password-input').value;
const output = document.getElementById('text-output');
const overlay = document.getElementById('output-overlay');
if (!input || !password) {
showToast('Please enter both text and password', 'error');
return;
}
overlay.classList.remove('hidden');
try {
if (currentOperation === 'encrypt') {
const result = await encryptText(input, password);
output.value = result;
showToast('Text encrypted successfully!', 'success');
} else {
const result = await decryptText(input, password);
output.value = result;
showToast('Text decrypted successfully!', 'success');
}
} catch (error) {
showToast('Error: ' + (currentOperation === 'decrypt' ? 'Invalid password or corrupted data' : error.message), 'error');
} finally {
overlay.classList.add('hidden');
}
}
// Copy output
function copyOutput() {
const output = document.getElementById('text-output');
if (!output.value) return;
output.select();
document.execCommand('copy');
showToast('Copied to clipboard!', 'success');
}
// Download output
function downloadOutput() {
const output = document.getElementById('text-output').value;
if (!output) return;
const blob = new Blob([output], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = currentOperation === 'encrypt' ? 'encrypted.txt' : 'decrypted.txt';
a.click();
URL.revokeObjectURL(url);
showToast('File downloaded!', 'success');
}
// File handling
function handleDragOver(e) {
e.preventDefault();
e.currentTarget.classList.add('drag-over');
}
function handleDragLeave(e) {
e.currentTarget.classList.remove('drag-over');
}
function handleDrop(e) {
e.preventDefault();
e.currentTarget.classList.remove('drag-over');
const files = e.dataTransfer.files;
if (files.length > 0) {
processSelectedFile(files[0]);
}
}
function handleFileSelect(e) {
if (e.target.files.length > 0) {
processSelectedFile(e.target.files[0]);
}
}
function processSelectedFile(file) {
if (file.size > 10 * 1024 * 1024) {
showToast('File too large. Max 10MB allowed.', 'error');
return;
}
selectedFile = file;
document.getElementById('file-info').classList.remove('hidden');
document.getElementById('file-name').textContent = file.name;
document.getElementById('file-size').textContent = (file.size / 1024).toFixed(1) + ' KB';
document.getElementById('file-action-btn').disabled = false;
}
function clearFile(e) {
e.stopPropagation();
selectedFile = null;
document.getElementById('file-info').classList.add('hidden');
document.getElementById('file-input').value = '';
document.getElementById('file-action-btn').disabled = true;
document.getElementById('download-section').classList.add('hidden');
}
async function processFile() {
if (!selectedFile) return;
const password = document.getElementById('file-password').value;
if (!password) {
showToast('Please enter a password', 'error');
return;
}
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
const btn = document.getElementById('file-action-btn');
progressContainer.classList.remove('hidden');
btn.disabled = true;
try {
const reader = new FileReader();
reader.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 50;
progressBar.style.width = percent + '%';
progressText.textContent = Math.round(percent) + '%';
}
};
reader.onload = async (e) => {
const content = e.target.result;
try {
let result;
if (currentOperation === 'encrypt') {
const bytes = new Uint8Array(content);
const base64 = btoa(String.fromCharCode(...bytes));
result = await encryptText(base64, password);
} else {
const decrypted = await decryptText(content, password);
result = Uint8Array.from(atob(decrypted), c => c.charCodeAt(0));
}
progressBar.style.width = '100%';
progressText.textContent = '100%';
processedFileData = result;
document.getElementById('download-section').classList.remove('hidden');
showToast('File processed successfully!', 'success');
} catch (error) {
showToast('Error processing file: ' + error.message, 'error');
}
};
if (currentOperation === 'encrypt') {
reader.readAsArrayBuffer(selectedFile);
} else {
reader.readAsText(selectedFile);
}
} catch (error) {
showToast('Error: ' + error.message, 'error');
btn.disabled = false;
}
}
function downloadProcessedFile() {
if (!processedFileData) return;
let blob, filename;
if (currentOperation === 'encrypt') {
blob = new Blob([processedFileData], { type: 'text/plain' });
filename = selectedFile.name + '.encrypted';
} else {
blob = new Blob([processedFileData], { type: 'application/octet-stream' });
filename = selectedFile.name.replace('.encrypted', '') + '.decrypted';
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
showToast('Download started!', 'success');
}
// Toast notifications
function showToast(message, type = 'info') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
const colors = {
success: 'bg-green-500',
error: 'bg-red-500',
info: 'bg-blue-500'
};
const icons = {
success: 'check-circle',
error: 'x-circle',
info: 'info'
};
toast.className = `toast ${colors[type]} text-white px-6 py-3 rounded-xl shadow-lg flex items-center space-x-2 min-w-[300px]`;
toast.innerHTML = `
<i data-lucide="${icons[type]}" class="w-5 h-5"></i>
<span class="font-medium">${message}</span>
`;
container.appendChild(toast);
lucide.createIcons();
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(100%)';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey) {
if (e.key === 'Enter') {
if (currentMode === 'text') {
processText();
} else {
processFile();
}
}
}
});
</script>
<script src="https://deepsite.hf.co/deepsite-badge.js"></script>
</body>
</html>