diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..760c670
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,32 @@
+# Changelog
+
+## v4.8.5 — Security hardening release
+
+This release consolidates several months of security, privacy, and lifecycle hardening work by the SecureBit.chat team.
+
+### Security
+
+- Added mandatory interactive SAS verification; passive click-through confirmation is no longer sufficient.
+- Made SAS computation deterministic across peers using shared session material.
+- Enforced protocol version `4.1` mismatch handling for incompatible clients.
+- Added TURN relay-only privacy mode and explicit warnings when TURN is unavailable.
+- Encrypted sensitive IndexedDB metadata and added safe lazy migration for legacy plaintext records.
+- Added mandatory consent gating for every incoming file transfer.
+- Replaced broad file acceptance with an explicit file-type allowlist and spoofing checks.
+- Sanitized every incoming decrypted chat message before UI delivery.
+
+### Reliability and resource lifecycle
+
+- Consolidated disconnect behavior into one canonical cleanup path.
+- Added cleanup for tracked timers, deferred retries, peer-disconnect scheduling, and fake/decoy traffic.
+- Rejected pending sender consent promises immediately during cleanup.
+- Bounded retained received-file buffers and added graceful handling for expired download handles.
+- Cleared React file-transfer UI state and detached live callbacks on unmount.
+- Improved reconnect hygiene and stale-session cleanup behavior.
+
+### Maintenance
+
+- Pinned dependency versions.
+- Applied safe transitive patch/minor updates.
+- Verified a clean `npm audit` result.
+- Expanded regression coverage for SAS, file consent, sanitization, privacy mode, metadata encryption, cleanup, and callback lifecycle behavior.
diff --git a/README.md b/README.md
index dda0640..d52650d 100644
--- a/README.md
+++ b/README.md
@@ -1,590 +1,121 @@
-# SecureBit.chat v4.7.56
+# SecureBit.chat v4.8.5
-
+SecureBit.chat is a browser-based peer-to-peer chat application built on WebRTC and Web Crypto APIs. It is designed for direct encrypted communication, explicit peer verification, and a small operational footprint without account registration or server-side message storage.
-
+## Security model
-**World's first P2P messenger with ECDH + DTLS + SAS security and military-grade cryptography**
+SecureBit.chat uses:
-[](https://github.com/SecureBitChat/securebit-chat/releases/latest)
-[](https://securebit.chat/)
-[](https://github.com/SecureBitChat/securebit-desktop)
-[](https://opensource.org/licenses/MIT)
+- ECDH key agreement with derived session keys
+- DTLS-protected WebRTC transport
+- deterministic Short Authentication String (SAS) verification
+- end-to-end encrypted chat payloads
+- replay protection and session-state cleanup
+- encrypted local key metadata in IndexedDB
-[Try Web Version](https://securebit.chat/) • [🖥️ Download Desktop Apps](https://github.com/SecureBitChat/securebit-desktop) • [📖 Documentation](#-quick-start) • [🔒 Security](#-security)
+A session is not treated as verified until both peers complete the interactive SAS flow. Each user must compare the displayed code with the peer through an out-of-band channel and enter the matching code manually. Three failed SAS attempts terminate the session.
-
+## Highlights in v4.8.5
----
-## 🔐 Shared Security Core
+This release consolidates several months of security hardening work by the project team:
-SecureBitChat uses a shared Rust-based cryptographic core:
-https://github.com/SecureBitChat/securebit-core
+- mandatory interactive SAS verification instead of passive click-through confirmation
+- deterministic SAS computation from shared session material
+- protocol version `4.1` negotiation with mismatch rejection
+- optional TURN relay-only privacy mode with clear warnings when TURN is unavailable
+- encrypted IndexedDB metadata with lazy migration from legacy plaintext records
+- explicit file-transfer consent before any receive buffers are allocated
+- strict file-type allowlist using both MIME type and extension checks
+- incoming decrypted message sanitization before UI delivery
+- improved disconnect, timer, file-transfer, and React UI cleanup behavior
+- pinned dependency versions and a clean `npm audit` baseline
-This core is used across all platforms (web, desktop, mobile) as a single source of truth for all security-critical logic.
+## Quick start
-Community review is welcome. Bug reports and security feedback can be submitted via GitHub Issues.
-
-## Now Available: Desktop Applications!
-
-**SecureBit Chat native desktop apps are now available for Windows, macOS, and Linux!**
-
-[](https://github.com/SecureBitChat/securebit-desktop)
-
-### Get Desktop Apps
-- **Windows 10/11** - NSIS Installer (x64)
-- **macOS 11+** - Universal App (Intel + Apple Silicon)
-- **Linux** - AppImage (Universal, amd64)
-
-**Status:** Public Beta v0.1.0 Available
-**Technology:** Built with Tauri v2 (Rust + Web Technologies)
-**Coming Q1 2026:** Windows Store, Mac App Store, Snap Store
-
-**[Download Desktop Apps →](https://github.com/SecureBitChat/securebit-desktop/releases/latest)**
-
----
-
-## Shared Security Core
-
-**All SecureBit applications share the same open-source cryptographic core:**
-
-[](https://github.com/SecureBitChat/securebit-core)
-
-### Platform Architecture
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ User Applications │
-├──────────────────┬──────────────────┬──────────────────────┤
-│ Web Version │ Desktop Apps │ Mobile (Coming) │
-│ (This Repo) │ (Tauri v2) │ (Q1 2026) │
-│ Browser PWA │ Windows/Mac/ │ iOS/Android │
-│ v4.7.56 │ Linux │ Native Apps │
-│ │ v0.1.0 Beta │ │
-└────────┬─────────┴────────┬─────────┴──────────┬───────────┘
- │ │ │
- └──────────────────┼────────────────────┘
- │
- ▼
- ┌──────────────────────────────────────────┐
- │ securebit-core (Open Source) │
- │ • All Cryptographic Operations │
- │ • P2P Protocol Implementation │
- │ • End-to-End Encryption │
- │ • Key Exchange & Verification │
- │ • ASN.1 Structure Validation │
- │ License: Apache 2.0 │
- └──────────────────────────────────────────┘
-```
-
-### Why This Architecture?
-
-- **100% of cryptography is open source** - Audit at [securebit-core](https://github.com/SecureBitChat/securebit-core)
-- **Single source of truth** - Same security across all platforms
-- **Full transparency** - Security-critical code is publicly auditable
-- **Community reviewed** - Bug reports and security feedback welcome
-- **Memory-safe core** - Rust implementation prevents entire classes of vulnerabilities
-- **Cross-platform consistency** - Identical security guarantees on all platforms
-
-**Core Repository:** https://github.com/SecureBitChat/securebit-core
-**License:** Apache License 2.0
-**Language:** Rust (memory-safe, zero-cost abstractions)
-
----
-
-## Overview
-
-SecureBit.chat is a revolutionary peer-to-peer messenger that prioritizes your privacy with military-grade encryption. No servers, no registration, no data collection - just pure, secure communication.
-
-### Platform Availability
-
-| Platform | Status | Version | Link |
-|----------|--------|---------|------|
-| **Web Browser** | Production | v4.7.56 | [Launch Web App](https://securebitchat.github.io/securebit-chat/) |
-| **Windows Desktop** | Beta | v0.1.0 | [Download](https://github.com/SecureBitChat/securebit-desktop/releases/latest) |
-| **macOS Desktop** | Beta | v0.1.0 | [Download](https://github.com/SecureBitChat/securebit-desktop/releases/latest) |
-| **Linux Desktop** | Beta | v0.1.0 | [Download](https://github.com/SecureBitChat/securebit-desktop/releases/latest) |
-| **iOS Mobile** | 🔄 In Development | - | Coming Q1 2026 |
-| **Android Mobile** | 🔄 In Development | - | Coming Q1 2026 |
-
-### Key Features
-
-- **18-Layer Military Security** - ECDH + DTLS + SAS triple-layer verification
-- **Pure P2P Architecture** - No servers, truly decentralized
-- **Progressive Web App** - Install like a native app on any device
-- **Native Desktop Apps** - Windows, macOS, Linux (Tauri v2)
-- **Native Mobile Apps** - iOS (Swift/SwiftUI), Android (Kotlin/Jetpack Compose) - Coming Q1 2026
-- **Secure File Transfer** - End-to-end encrypted P2P file sharing
-- **Smart Notifications** - Browser and desktop alerts
-- **Complete Anonymity** - Zero data collection, no registration
-- **Open Source Security** - Cryptographic core is fully auditable
-- **ASN.1 Validation** - Complete key structure verification
-- **Perfect Forward Secrecy** - Automatic key rotation
-
----
-
-## ✨ What's New in v4.7.56
-
-### Desktop Edition Release
-
-- **Native Desktop Applications** - Windows, macOS, and Linux support
-- **Tauri v2 Framework** - Lightweight, secure, and performant
-- **System Integration** - Native notifications, system tray, auto-start
-- **Offline Support** - Works without internet connection
-- **Multi-window Support** - Multiple conversation windows
-- **Improved Performance** - Native code execution for crypto operations
-
-### Bug Fixes & Improvements
-
-- **Fix:** Prevent encryption key loss and IndexedDB connection errors
-- **Fix:** Disable timer-based key rotation for Double Ratchet mode
-- **Fix:** Auto-reinitialize encryption keys when missing but ECDH available
-- **Fix:** Preserve active keys during periodic cleanup in ratchet sessions
-- **Fix:** IndexedDB "database closing" errors with connection checking
-- **Improvement:** Individual transactions per queue item to prevent race conditions
-- **Improvement:** Enhanced message text wrapping in chat interface
-
-### Security Enhancements
-
-- **ECDH + DTLS + SAS System** - Triple-layer security verification
-- **ASN.1 Full Structure Validation** - Complete key structure verification
-- **Enhanced MITM Protection** - Multi-layer defense system
-- **Secure Key Storage** - WeakMap-based isolation
-- **Production-Ready Logging** - Data sanitization and privacy protection
-- **HKDF Key Derivation** - RFC 5869 compliant key separation and derivation
-
----
-
-## Quick Start
-
-### Web Version (Browser)
-
-1. **Visit** [https://securebit.chat/](https://securebit.chat/)
-2. **Share your link** or enter your peer's link
-3. **Start chatting** - No registration required!
-
-**Install as PWA:**
-- Click the install prompt in your browser
-- Or use browser menu: "Install SecureBit.chat"
-
-### Desktop Version (Native Apps)
-
-1. **Download** installer from [securebit-desktop releases](https://github.com/SecureBitChat/securebit-desktop/releases/latest)
-2. **Install** on Windows, macOS, or Linux
-3. **Launch** and start secure communication
-
-**Platform-specific instructions:**
-- **Windows:** Run `.exe` installer, follow setup wizard
-- **macOS:** Open `.zip`, drag `SecureBit Chat.app` to Applications
-- **Linux:** Make AppImage executable: `chmod +x SecureBit.Chat_*.AppImage`, then run
-
-### Features Comparison
-
-| Feature | Web Version | Desktop Apps |
-|---------|-------------|--------------|
-| **P2P Encryption** | ✅ | ✅ |
-| **File Sharing** | ✅ | ✅ |
-| **Voice/Video Calls** | ✅ | ✅ |
-| **Screen Sharing** | ✅ | ✅ |
-| **System Notifications** | ✅ (Browser) | ✅ (Native) |
-| **Offline Mode** | ❌ | ✅ |
-| **Auto-start** | ❌ | ✅ |
-| **System Tray** | ❌ | ✅ |
-| **Multi-window** | ❌ | ✅ |
-| **Background Operation** | ❌ | ✅ |
-| **Lower Resource Usage** | ❌ | ✅ (Tauri) |
-
----
-
-## 🗺️ Roadmap
-
-**Current: v4.7.56** - Desktop Edition Available
-
-### Released Versions
-
-- **v4.5** - Enhanced Security Edition
- - ECDH + DTLS + SAS triple-layer security
- - 18-layer military-grade cryptography
- - Complete ASN.1 validation
- - Perfect Forward Secrecy
-
-- **v4.7** - Desktop Edition (Current)
- - Native desktop applications (Windows, macOS, Linux)
- - Built with Tauri v2
- - System tray integration and native notifications
- - Offline support and multi-window
-
-- **v0.1.0** - Desktop Apps Beta
- - Initial desktop release
- - Windows, macOS, Linux support
-
-### Upcoming Releases
-
-- **v5.0 (Q1 2026)** - Mobile Edition
- - Native iOS app (Swift/SwiftUI)
- - Native Android app (Kotlin/Jetpack Compose)
- - PWA support for mobile browsers
- - Real-time push notifications
- - Battery optimization
- - Biometric authentication
-
-- **v5.5 (Q2 2026)** - Quantum-Resistant Edition
- - CRYSTALS-Kyber post-quantum key exchange
- - SPHINCS+ post-quantum signatures
- - Hybrid classical + post-quantum schemes
- - Quantum-safe key exchange
- - Migration of existing sessions
-
-- **v6.0 (Q4 2026)** - Group Communications
- - P2P group connections up to 8 participants
- - Mesh networking for groups
- - Signal Double Ratchet for groups
- - Anonymous groups without metadata
- - Ephemeral groups (disappear after session)
-
-- **v6.5 (2027)** - Decentralized Network
- - DHT for peer discovery
- - Built-in onion routing
- - Tokenomics and node incentives
- - Governance via DAO
- - Self-healing network
-
-- **v7.0 (2028+)** - AI Privacy Assistant
- - Local AI threat analysis
- - Automatic MITM detection
- - Adaptive cryptography
- - Zero-knowledge machine learning
-
----
-
-## Security
-
-### Open Source Cryptographic Core
-
-**All security-critical code is open source and auditable:**
-
-- **Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
-- **License:** Apache License 2.0
-- **Language:** Rust (memory-safe, prevents entire vulnerability classes)
-- **Auditable:** 100% of cryptographic operations
-- **Standards:** RFC 5869 (HKDF), NIST SP 800-56A (ECDH), RFC 8446 (DTLS)
-
-### Security Features
-
-#### Triple-Layer Verification
-1. **ECDH (Elliptic Curve Diffie-Hellman)** - P-384 curve key exchange
-2. **DTLS (Datagram Transport Layer Security)** - WebRTC transport security with fingerprint verification
-3. **SAS (Short Authentication String)** - Visual MITM detection and verification
-
-#### Cryptographic Primitives
-- **Key Exchange:** ECDH P-384 (NIST curve)
-- **Signatures:** ECDSA P-384
-- **Encryption:** AES-256-GCM
-- **Key Derivation:** HKDF-SHA-256 (RFC 5869)
-- **Authentication:** HMAC-SHA-256
-- **Hashing:** SHA-256, SHA-384
-
-#### Protocol Security
-- Perfect Forward Secrecy (PFS)
-- End-to-End Encryption (E2EE)
-- Zero-Knowledge Architecture
-- Replay Protection
-- Metadata Protection
-- ASN.1 Structure Validation
-- OID and EC Point Verification
-- SPKI Structure Validation
-
-#### Security Architecture
-- **18-Layer Defense System** - Multiple independent security layers
-- **MITM Attack Prevention** - Triple verification prevents man-in-the-middle attacks
-- **Key Isolation** - WeakMap-based secure key storage
-- **Secure Memory Management** - Automatic secure deletion of sensitive data
-- **Production Logging** - Sanitized logs prevent information leakage
-
-### What We DON'T Collect
-- No personal information
-- No phone numbers or emails
-- No contact lists on servers
-- No message content or metadata
-- No telemetry or analytics
-- No usage statistics
-- No IP addresses logged
-- No device fingerprints
-- No location data
-
-### Security Audit
-
-Want to audit our security? Check these repositories:
-
-1. **[securebit-core](https://github.com/SecureBitChat/securebit-core)** - All cryptographic operations (Rust)
-2. **[securebit-chat](https://github.com/SecureBitChat/securebit-chat)** - Web UI implementation (this repo, JavaScript/React)
-
-**Report Security Issues:** SecureBitChat@proton.me
-**PGP Key:** Available on request for encrypted security reports
-
----
-
-## Development
-
-### Prerequisites
-
-- **Node.js** 18+
-- **npm** or **yarn**
-- **Git**
-
-### Installation
+### Run locally
```bash
-# Clone repository
-git clone https://github.com/SecureBitChat/securebit-chat.git
-cd securebit-chat
-
-# Install dependencies
npm install
-
-# Run development server
-npm run dev
-```
-
-### Building
-
-```bash
-# Build for production
npm run build
-
-# Build CSS only
-npm run build:css
-
-# Build JavaScript only
-npm run build:js
-
-# Preview production build (requires Python)
-python -m http.server 8000
-```
-
-### Development Scripts
-
-```bash
-# Development server with hot reload
-npm run dev
-
-# Watch CSS changes
-npm run watch
-
-# Build everything
-npm run build
-
-# Serve built files
npm run serve
```
-### Project Structure
+Then open the local server URL in two browser windows or profiles.
-```
-securebit-chat/
-├── src/
-│ ├── components/ # React components
-│ │ ├── ui/ # UI components (Header, Roadmap, etc.)
-│ │ └── QRScanner.jsx # QR code scanner
-│ ├── crypto/ # Cryptography utilities
-│ │ └── EnhancedSecureCryptoUtils.js
-│ ├── network/ # WebRTC P2P logic
-│ │ └── EnhancedSecureWebRTCManager.js
-│ ├── transfer/ # File transfer
-│ │ └── EnhancedSecureFileTransfer.js
-│ ├── notifications/ # Notification system
-│ ├── pwa/ # PWA functionality
-│ ├── scripts/ # Bootstrap and initialization
-│ └── styles/ # CSS stylesheets
-├── dist/ # Built files (generated)
-├── assets/ # Static assets
-├── public/ # Public files
-└── docs/ # Documentation
+### Establish a session
+
+1. Create an offer in the first browser.
+2. Transfer the offer to the peer and create an answer.
+3. Return the answer to the first browser.
+4. Compare the SAS code out of band.
+5. Enter the matching SAS code on both sides.
+6. Begin chatting only after both peers are verified.
+
+## Configuration
+
+### TURN / privacy mode
+
+Direct WebRTC connections may expose IP addresses to peers. SecureBit.chat supports a relay-only privacy mode:
+
+- default mode keeps normal WebRTC behavior and existing STUN support
+- relay-only mode sets `iceTransportPolicy: "relay"`
+- relay-only mode requires a configured TURN server
+- STUN alone does not hide IP addresses
+- public TURN credentials are not bundled or hardcoded
+
+Configure ICE servers at deployment time and enable relay-only mode only when a TURN service is available. See [`doc/CONFIGURATION.md`](doc/CONFIGURATION.md).
+
+### File transfer policy
+
+Incoming file transfers require explicit user consent. Before the consent prompt appears, metadata is validated and dangerous names are rejected. Safe accepted categories are:
+
+- common raster images
+- PDF
+- plain text
+- ZIP archives
+
+Executable, scriptable, and high-risk formats are rejected, including `.exe`, `.bat`, `.cmd`, `.sh`, `.js`, `.msi`, `.dmg`, `.app`, `.jar`, `.scr`, `.ps1`, `.vbs`, `.html`, and `.svg`. MIME type and filename extension must agree.
+
+## Development
+
+### Requirements
+
+- Node.js 18+
+- npm
+
+### Commands
+
+```bash
+npm install
+npm test
+npm audit
+npm run build
+npm run dev
```
-### Contributing to Core
+### Project layout
-Want to improve security? Contribute to the cryptographic core:
-- **Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
-- **Focus:** Cryptography, protocol implementation, security features
-- **Language:** Rust
+```text
+src/network/ WebRTC connection and session lifecycle
+src/transfer/ secure file-transfer implementation
+src/crypto/ cryptographic utilities
+src/components React UI components
+doc/ technical documentation
+```
+## Documentation
-## Related Projects
+- [`SECURITY.md`](SECURITY.md)
+- [`doc/CONFIGURATION.md`](doc/CONFIGURATION.md)
+- [`doc/CRYPTOGRAPHY.md`](doc/CRYPTOGRAPHY.md)
+- [`doc/SECURITY-ARCHITECTURE.md`](doc/SECURITY-ARCHITECTURE.md)
+- [`doc/API.md`](doc/API.md)
+- [`CHANGELOG.md`](CHANGELOG.md)
-### Official SecureBit Ecosystem
+## Responsible use
-| Project | Description | Status | License |
-|---------|-------------|--------|---------|
-| **[securebit-core](https://github.com/SecureBitChat/securebit-core)** | Cryptographic kernel (Rust) | ✅ Production | Apache 2.0 |
-| **[securebit-chat](https://github.com/SecureBitChat/securebit-chat)** | Web application (this repo) | ✅ Production v4.7.56 | MIT |
-| **[securebit-desktop](https://github.com/SecureBitChat/securebit-desktop)** | Desktop apps (Windows/Mac/Linux) | ✅ Beta v0.1.0 | Proprietary* |
-| **securebit-mobile** | Mobile apps (iOS/Android) | 🔄 Coming Q1 2026 | TBD |
+SecureBit.chat is intended for lawful, ethical use. See [`RESPONSIBLE_USE.md`](RESPONSIBLE_USE.md) and [`SECURITY_DISCLAIMER.md`](SECURITY_DISCLAIMER.md).
-*\* Desktop apps are free for personal and commercial use. Only the UI layer is proprietary - all cryptography is open source in securebit-core.*
+## License
-### Technology Stack
-
-- **Frontend:** React, Tailwind CSS
-- **Build:** esbuild, Tailwind CLI
-- **P2P:** WebRTC
-- **Crypto Core:** Rust (securebit-core)
-- **Desktop:** Tauri v2
-- **Mobile (Future):** Swift/SwiftUI (iOS), Kotlin/Jetpack Compose (Android)
-
----
-
-## Contributing
-
-We welcome contributions! Here's how:
-
-### Contributing to Web Version (This Repo)
-
-1. **Fork** the repository
-2. **Create** feature branch: `git checkout -b feature/amazing-feature`
-3. **Commit** changes: `git commit -m "Add amazing feature"`
-4. **Push** to branch: `git push origin feature/amazing-feature`
-5. **Open** Pull Request
-
-### Contributing to Cryptographic Core
-
-Want to improve security? Contribute to the core:
-- **Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
-- **Focus:** Cryptography, protocol implementation, security features
-- **Language:** Rust
-
-### Contributing to Desktop Apps
-
-- **Repository:** [securebit-desktop](https://github.com/SecureBitChat/securebit-desktop)
-- **Focus:** UI/UX improvements, platform-specific features
-- **Technology:** Tauri v2, Rust, TypeScript
-
-### Other Ways to Help
-
-- **Report bugs** - Open issues on GitHub
-- **Security research** - Email SecureBitChat@proton.me
-- **Improve documentation** - Help others understand the project
-- **Star the repositories** - Support visibility and development
-- **Spread the word** - Share with privacy advocates
-- **Provide feedback** - Help shape the future of SecureBit
-
-**If you support our mission - please star the repos!**
-- [⭐ Star securebit-chat (Web)](https://github.com/SecureBitChat/securebit-chat)
-- [⭐ Star securebit-core (Crypto)](https://github.com/SecureBitChat/securebit-core)
-- [⭐ Star securebit-desktop (Apps)](https://github.com/SecureBitChat/securebit-desktop)
-
----
-
-## 📄 License
-
-### This Repository (Web Version)
-**License:** MIT License
-
-### Cryptographic Core
-**License:** Apache License 2.0
-**Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
-
-### Desktop Applications
-**License:** Proprietary (Free for personal & commercial use)
-**Repository:** [securebit-desktop](https://github.com/SecureBitChat/securebit-desktop)
-
-*Note: Desktop apps are free to use. Only the UI layer is proprietary - all cryptography is open source.*
-
----
-
-## Community & Support
-
-### Get Help
-- **Documentation:** Check README and [core docs](https://github.com/SecureBitChat/securebit-core)**
-- **Discussions:** [GitHub Discussions](https://github.com/SecureBitChat/securebit-chat/discussions)
-- **Issues:** [Report bugs on GitHub](https://github.com/SecureBitChat/securebit-chat/issues)
-- **Email:** SecureBitChat@proton.me
-
-### Contact
-- **Security Issues:** SecureBitChat@proton.me (encrypted preferred)
-- **Business Inquiries:** hello@securebit.chat
-- **Twitter/X:** [@SecureBitChat](https://twitter.com/SecureBitChat)
-- **Website:** https://securebit.chat (coming soon)
-
-### Community Guidelines
-
-- Be respectful and constructive
-- Focus on privacy and security
-- Help others learn and contribute
-- Report security issues responsibly
-- Follow the code of conduct
-
----
-
-## Acknowledgments
-
-### Built With
-- **React** - UI framework
-- **Tailwind CSS** - Styling
-- **esbuild** - Build tool
-- **WebRTC** - P2P communication
-- **IndexedDB** - Local storage
-- **Rust** - Cryptographic core
-- **Tauri v2** - Desktop framework
-
-### Special Thanks
-- **Rust Crypto Team** - Cryptographic primitives and standards
-- **WebRTC Community** - P2P technology and standards
-- **Tauri Team** - Desktop framework development
-- **Security Researchers** - Audits, feedback, and improvements
-- **Contributors** - Code, docs, testing, and support
-- **Privacy Advocates** - Inspiration and mission support
-
-### Standards & Specifications
-- **RFC 5869** - HKDF key derivation
-- **NIST SP 800-56A** - ECDH key agreement
-- **RFC 8446** - DTLS 1.3
-- **RFC 7748** - Elliptic curves for security
-- **X.509** - ASN.1 certificate structure
-
----
-
-## Project Status
-
-### Active Development
-- **Web Version** - Stable (v4.7.56), receiving bug fixes and improvements
-- **Desktop Apps** - Public beta (v0.1.0), active development
-- **Cryptographic Core** - Stable, production-ready
-- **Mobile Apps** - In development (Q1 2026)
-
-### Community
-- **GitHub Stars** - [Help us grow!](https://github.com/SecureBitChat/securebit-chat)
-- **Contributors** - [See all contributors](https://github.com/SecureBitChat/securebit-chat/graphs/contributors)
-- **Issues** - [Open issues](https://github.com/SecureBitChat/securebit-chat/issues)
-- **Pull Requests** - [Contribute](https://github.com/SecureBitChat/securebit-chat/pulls)
-- **Discussions** - [Join the conversation](https://github.com/SecureBitChat/securebit-chat/discussions)
-
-### Metrics
-- **Downloads** - Desktop apps available for all platforms
-- **Security** - 18-layer military-grade protection
-- **Platforms** - Web, Windows, macOS, Linux (Mobile coming Q1 2026)
-- **License** - Open source core, free desktop apps
-
----
-
-
-
-**SecureBit.chat Security Team**
-
-*Committed to protecting your privacy with military-grade security*
-
----
-
-**Latest Release: v4.7.56** - Desktop Edition Available
-**Desktop Apps: v0.1.0** - Public Beta Available
-**Mobile Apps: Coming Q1 2026**
-
-[🚀 Try Web Version](https://securebit.chat/) • [🖥️ Download Desktop Apps](https://github.com/SecureBitChat/securebit-desktop) • [⭐ Star on GitHub](https://github.com/SecureBitChat/securebit-chat)
-
----
-
-**Made with 🔒 for privacy advocates worldwide**
-
-Copyright © 2025-2026 SecureBit Team. All rights reserved.
-
-
+MIT License. See [`LICENSE`](LICENSE).
diff --git a/SECURITY.md b/SECURITY.md
index 99bb409..35d651b 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,287 +1,80 @@
# Security Policy
-## 🛡️ Security Overview
+## Supported release line
-SecureBit.chat is built with security-first principles and implements **military-grade security** with 18-layer protection system. We take security vulnerabilities seriously and appreciate responsible disclosure from the security community.
+| Release | Status | Protocol |
+| --- | --- | --- |
+| v4.1.x | Supported | 4.1 |
+| earlier releases | Unsupported | legacy |
-**Current Security Status:** 🔒 **MAXIMUM SECURITY (Stage 5)** - Exceeds government-grade communication standards with complete ASN.1 validation
+Users should run the current supported release line to receive the latest verification, storage, and file-transfer protections.
-## 🔒 Enhanced Security Features (Stage 5)
+## Reporting a vulnerability
-### Multi-Layer Cryptographic Implementation
-- **Key Exchange:** ECDH P-384 (NIST recommended curve) with non-extractable keys
-- **Primary Encryption:** AES-GCM 256-bit with authenticated encryption
-- **Nested Encryption:** Additional AES-GCM 256-bit layer for maximum protection
-- **Metadata Protection:** Separate AES-GCM 256-bit encryption for message metadata
-- **Digital Signatures:** ECDSA P-384 with SHA-384 for message authenticity and MITM protection
-- **Perfect Forward Secrecy:** Automatic key rotation every 5 minutes with secure key versioning
-- **Non-extractable Keys:** All cryptographic keys are hardware-protected and non-exportable
-- **Enhanced Replay Protection:** Multi-factor protection with sequence numbers, message IDs, and timestamps
-- **Secure Key Storage:** WeakMap-based isolation preventing direct access to sensitive keys
-- **Key Security Monitoring:** Automatic validation, rotation, and emergency wipe capabilities
-- **HKDF Key Derivation:** RFC 5869 compliant key separation with proper salt and info parameters
+Please report security issues privately before public disclosure.
-### Advanced Traffic Obfuscation
-- **Packet Padding:** Random padding (64-512 bytes) to hide real message sizes
-- **Anti-Fingerprinting:** Advanced traffic pattern obfuscation and timing randomization
-- **Fake Traffic Generation:** Invisible decoy messages for traffic analysis protection
-- **Message Chunking:** Split messages into random-sized chunks with variable delays
-- **Packet Reordering Protection:** Sequence-based packet reassembly with timeout handling
-- **Decoy Channels:** Multiple fake communication channels to confuse attackers
+- Email: `SecureBitChat@proton.me`
+- Include: affected version, reproduction steps, impact, and any proof-of-concept material
+- Avoid publishing exploit details before a coordinated fix is available
-### Enhanced Security Architecture
-- **Zero-trust Model:** No central servers to compromise
-- **P2P Direct:** WebRTC encrypted channels with enhanced validation
-- **No Data Persistence:** Messages exist only in memory, automatic cleanup
-- **Enhanced Rate Limiting:** 60 messages/minute, 5 connections/5 minutes with cryptographic verification
-- **Session Security:** 64-byte salts, unique session IDs, and replay attack prevention
-- **MITM Protection:** Out-of-band verification codes with enhanced validation
-- **Connection Security Framework:** Advanced mutex system with 15-second timeout protection
-- **Race Condition Protection:** Atomic key generation and serialized connection operations
-- **Multi-stage Validation:** Step-by-step validation with automatic rollback on failures
-- **Production Security Logging:** Environment-aware logging with data sanitization
+## Current security behavior
-### 🔐 ASN.1 Complete Structure Validation (NEW)
-- **Complete ASN.1 DER Parser:** Full structural validation of all cryptographic keys
-- **OID Validation:** Algorithm and curve verification (P-256/P-384 only)
-- **EC Point Format Verification:** Uncompressed format 0x04 validation
-- **SPKI Structure Validation:** Element count and type checking
-- **Key Size Limits:** 50-2000 bytes to prevent DoS attacks
-- **BIT STRING Validation:** Ensuring unused bits are 0
-- **Fallback Support:** P-384 to P-256 compatibility
-- **High-Risk Vulnerability Fix:** Prevents keys with valid headers but modified data
+### Peer verification
-## 🚨 Supported Versions
+- SAS verification is mandatory and interactive.
+- SAS values are derived deterministically from shared session material.
+- Users must compare the code out of band and enter the matching code manually.
+- A session becomes verified only after both local and remote confirmations succeed.
+- Three failed local SAS entries terminate the session.
+- Protocol version `4.1` rejects incompatible peers instead of silently falling back to older verification behavior.
-| Version | Security Level | Supported |
-| ------- | -------------- | ------------------ |
-| 4.02.x | MILITARY-GRADE | ✅ Yes (18 layers)|
-| 4.01.x | MILITARY-GRADE | ✅ Yes (15 layers)|
-| 4.0.x | MAXIMUM | ✅ Yes (12 layers)|
-| 3.x.x | HIGH | ⚠️ Limited |
-| < 3.0 | BASIC | ❌ No |
+### Message handling
-**Recommendation:** Upgrade to 4.02.x immediately for complete ASN.1 validation and military-grade security protection.
+- Chat payloads remain encrypted in transit.
+- Decrypted incoming chat text is sanitized before it reaches React state or the UI.
+- Encrypted payload validation remains separate from display sanitization.
-## 📋 Reporting a Vulnerability
+### File transfer
-### 🔴 Critical Vulnerabilities
-For **critical security issues** that could compromise user safety:
+- Incoming transfer metadata is validated before presentation to the user.
+- Every incoming file requires explicit Accept or Reject consent.
+- Receive buffers are not allocated before consent.
+- File names are normalized for display and dangerous names are rejected.
+- Allowed file types are explicit and validated using both MIME type and extension.
+- High-risk executable or scriptable types are blocked.
+- Repeated incoming transfer offers are rate-limited and bounded.
-**DO NOT** create a public GitHub issue.
+### Local storage
-**Contact us privately:**
-- 📧 **Email:** security@SecureBit.chat (PGP key below)
-- 🔒 **Signal:** +[REDACTED] (ask for Signal number via email)
-- 🔐 **Keybase:** @SecureBitChat
+- Sensitive IndexedDB metadata is encrypted, including timestamps and session-related fields where feasible.
+- Only minimum lookup keys remain in plaintext when required.
+- Legacy plaintext metadata is migrated lazily on read.
+- Corrupted encrypted metadata fails closed.
-### 🟡 Non-Critical Issues
-For general security improvements or non-critical findings:
-- Create a GitHub issue with `[SECURITY]` prefix
-- Use our security issue template
+### Network privacy
-## 📝 Vulnerability Disclosure Process
+- Default mode preserves standard WebRTC connectivity.
+- Relay-only privacy mode uses TURN by setting `iceTransportPolicy: "relay"`.
+- STUN-only configurations do not provide IP protection.
+- If TURN is absent, the UI warns that direct WebRTC may expose IP addresses.
-1. **Report:** Send details to security@SecureBit.chat
-2. **Acknowledgment:** We'll respond within 24 hours
-3. **Investigation:** We'll investigate and keep you updated
-4. **Fix:** We'll develop and test a fix
-5. **Disclosure:** Public disclosure after fix is deployed
-6. **Credit:** We'll credit you in our security hall of fame
+### Lifecycle cleanup
-### Timeline Expectations
-- **Initial Response:** < 24 hours
-- **Status Update:** Every 72 hours
-- **Fix Timeline:** Critical bugs < 7 days, Others < 30 days
+- Disconnect cleanup closes data channels and peer connections, clears verification state, and wipes session crypto state.
+- Timers, deferred retries, decoy traffic, pending transfers, and React file-transfer callbacks are cleaned up on shutdown.
+- Received file buffers are retained only within a bounded window and expired handles fail gracefully.
-## 🏆 Security Hall of Fame
+## Security verification commands
-We maintain a hall of fame for security researchers who help improve SecureBit.chat:
-
-
-*Be the first to help secure SecureBit.chat!*
-
-## 🔍 Security Audit History
-
-### Independent Audits
-- **Pending:** Professional cryptographic audit (Q2 2025)
-- **Community:** Ongoing peer review by security researchers
-
-### Internal Security Measures
-- **Code Review:** All cryptographic code reviewed by multiple developers
-- **Security Testing:** Comprehensive 18-layer security test suite
-- **Dependencies:** Regular security updates for all dependencies
-- **Vulnerability Testing:** Automated testing for all 18 security layers
-- **ASN.1 Validation:** Complete structural validation of all cryptographic keys
-
-## 📊 Security Architecture (Stage 5)
-
-```
-19-Layer Security Architecture:
-├── Layer 1: Enhanced Authentication (ECDSA P-384 + SHA-384)
-├── Layer 2: Key Exchange (ECDH P-384, non-extractable keys)
-├── Layer 3: Metadata Protection (AES-256-GCM + 64-byte salt)
-├── Layer 4: Message Encryption (Enhanced with sequence numbers)
-├── Layer 5: Nested Encryption (Additional AES-256-GCM layer)
-├── Layer 6: Packet Padding (64-512 bytes random obfuscation)
-├── Layer 7: Anti-Fingerprinting (Advanced pattern obfuscation)
-├── Layer 8: Packet Reordering Protection (Sequence + timeout)
-├── Layer 9: Message Chunking (Random delays + sizes)
-├── Layer 10: Fake Traffic Generation (Invisible decoy messages)
-├── Layer 11: Enhanced Rate Limiting (Cryptographic verification)
-├── Layer 12: Perfect Forward Secrecy (5-minute key rotation)
-├── Layer 13: Mutex Framework (Race condition protection)
-├── Layer 14: Secure Key Storage (WeakMap isolation)
-├── Layer 15: Production Logging (Data sanitization)
-├── Layer 16: ASN.1 Validation (Complete key structure verification)
-├── Layer 17: OID Validation (Algorithm and curve verification)
-├── Layer 18: EC Point Validation (Format and structure verification)
-└── Layer 19: HKDF Key Derivation (RFC 5869 compliant key separation)
-```
-
-### Security Metrics
-- **Encryption Strength:** Triple-layer AES-256-GCM
-- **Key Security:** P-384 ECDH/ECDSA (equivalent to 7680-bit RSA)
-- **Forward Secrecy:** Complete (automatic key rotation)
-- **Traffic Analysis Protection:** Maximum (6-layer obfuscation)
-- **Attack Surface:** Minimal (P2P, no central servers)
-- **Key Validation:** Complete ASN.1 DER parsing and validation
-- **Structural Security:** Full PKCS compliance for all operations
-
-## 🛠️ Security Best Practices for Users
-
-### For Maximum Security:
-1. **Verify Authenticity:** Always verify out-of-band codes (enhanced 6-digit format)
-2. **Use Official Source:** Only use https://SecureBit.chat
-3. **Keep Updated:** Use version 4.02.x for complete ASN.1 validation
-4. **Secure Environment:** Use updated browsers on secure devices
-5. **Monitor Security Status:** Check for "MAXIMUM SECURITY" indicator in chat
-
-### Security Indicators:
-- ✅ **Green Shield:** MAXIMUM SECURITY (Stage 5) active
-- 🟡 **Yellow Shield:** HIGH SECURITY (Stage 3-4)
-- 🔴 **Red Shield:** Security issues detected
-
-### Red Flags:
-- ❌ Verification codes don't match
-- ❌ Security level below Stage 5
-- ❌ Unusual connection behavior
-- ❌ Requests for private keys or seed phrases
-- ❌ Unofficial domains or mirrors
-- ❌ Missing security layer notifications
-
-### Research Ethics
-- **No Disruption:** Don't interfere with live users
-- **Responsible Disclosure:** Follow our disclosure timeline
-- **No Data Harvesting:** Don't collect user communications
-- **Legal Compliance:** Follow all applicable laws
-- **Respect Privacy:** Don't attempt to break active encrypted sessions
-
-## 🔬 Security Research Guidelines
-
-### Scope
-**In Scope:**
-- ✅ Cryptographic implementation flaws in any of the 18 layers
-- ✅ WebRTC security issues
-- ✅ Authentication bypass attempts
-- ✅ Input validation vulnerabilities
-- ✅ Client-side security issues
-- ✅ Traffic analysis vulnerabilities
-- ✅ Perfect Forward Secrecy implementation
-- ✅ Anti-fingerprinting bypass techniques
-- ✅ Fake traffic detection methods
-- ✅ ASN.1 validation bypass attempts
-- ✅ Key structure manipulation attacks
-- ✅ OID validation bypass techniques
-
-**Out of Scope:**
-- ❌ Social engineering attacks
-- ❌ Physical attacks on user devices
-- ❌ DoS attacks on user connections
-- ❌ Issues requiring physical access
-- ❌ Browser security vulnerabilities
-
-## 🔄 Recent Security Updates (Version 4.02)
-
-### Major Security Enhancements:
-- ✅ **Implemented 19-layer security architecture**
-- ✅ **Added complete ASN.1 DER parser for key validation**
-- ✅ **Enhanced key security with OID and EC point verification**
-- ✅ **Fixed high-risk vulnerability in key structure validation**
-- ✅ **Added SPKI structure validation and element checking**
-- ✅ **Implemented key size limits to prevent DoS attacks**
-- ✅ **Added BIT STRING validation ensuring unused bits are 0**
-- ✅ **Enhanced fallback support from P-384 to P-256**
-- ✅ **Implemented RFC 5869 compliant HKDF key derivation**
-- ✅ **Enhanced key separation with proper salt and info parameters**
-
-### Previous Enhancements (Version 4.01):
-- ✅ **Implemented 15-layer security architecture**
-- ✅ **Added Perfect Forward Secrecy with automatic key rotation**
-- ✅ **Enhanced MITM protection with ECDSA signatures**
-- ✅ **Implemented traffic obfuscation (fake traffic, padding, chunking)**
-- ✅ **Added anti-fingerprinting protection**
-- ✅ **Fixed demo session creation vulnerability**
-- ✅ **Eliminated session replay attacks**
-- ✅ **Enhanced rate limiting with cryptographic verification**
-
-### Bug Fixes:
-- 🔧 **Fixed fake traffic visibility in user interface**
-- 🔧 **Resolved message processing conflicts**
-- 🔧 **Improved security layer error handling**
-- 🔧 **Enhanced session validation**
-- 🔧 **Complete rewrite of validateKeyStructure() method**
-
-## 📚 Security Resources
-
-### Technical Documentation:
-- [18-Layer Security Architecture](docs/SECURITY-ARCHITECTURE.md)
-- [Cryptographic Implementation](docs/CRYPTOGRAPHY.md)
-- [P2P Security Model](docs/P2P-SECURITY.md)
-- [Lightning Integration Security](docs/LIGHTNING-SECURITY.md)
-- [Traffic Obfuscation Guide](docs/TRAFFIC-OBFUSCATION.md)
-- [ASN.1 Validation Guide](docs/ASN1-VALIDATION.md)
-
-### External Resources:
-- [WebRTC Security Guide](https://webrtc-security.github.io/)
-- [Web Crypto API Best Practices](https://www.w3.org/TR/WebCryptoAPI/)
-- [Lightning Network Security](https://lightning.network/lightning-network-paper.pdf)
-- [NIST Cryptographic Standards](https://csrc.nist.gov/)
-- [RFC 5280 - X.509 Certificate Structure](https://tools.ietf.org/html/rfc5280)
-- [RFC 5480 - Elliptic Curve Subject Public Key Information](https://tools.ietf.org/html/rfc5480)
-
-### Security Verification:
```bash
-# Verify current security status in browser console:
-webrtcManager.getSecurityStatus()
-# Expected: { stage: 5, securityLevel: 'MAXIMUM', activeFeatures: 18 }
-
-# Verify ASN.1 validation status:
-cryptoManager.getASN1ValidationStatus()
-# Expected: { enabled: true, parser: 'DER', validation: 'complete' }
+npm audit
+npm test
+npm run build
```
-## 📞 Contact Information
+## Limitations
-- **Security Team:** security@SecureBit.chat
-- **General Contact:** lockbitchat@tutanota.com
-- **GitHub Issues:** https://github.com/SecureBitChat/securebit-chat/issues
-
-## 🏅 Security Achievements
-
-SecureBit.chat v4.02 provides:
-- **🥇 Military-Grade Security:** 19-layer protection system
-- **🥇 Government-Level Encryption:** Triple AES-256-GCM + P-384 ECDH/ECDSA
-- **🥇 Perfect Forward Secrecy:** Complete with automatic key rotation
-- **🥇 Traffic Analysis Protection:** Maximum with 6-layer obfuscation
-- **🥇 Zero-Trust Architecture:** No central points of failure
-- **🥇 Complete ASN.1 Validation:** Full structural verification of all cryptographic keys
-- **🥇 PKCS Compliance:** Complete adherence to cryptographic standards
-- **🥇 HKDF Key Derivation:** RFC 5869 compliant key separation and derivation
-
-**Security Rating: MAXIMUM** - Exceeds most government and military communication standards with complete key structure validation.
-
----
-
-*This security policy is reviewed and updated quarterly. Last updated: January 15, 2025*
-*Security implementation verified and tested as of Version 4.02.442*
\ No newline at end of file
+- A compromised endpoint can still expose plaintext.
+- WebRTC privacy depends on deployment configuration; TURN must be supplied by the operator.
+- Users must perform the out-of-band SAS comparison correctly.
+- Browser security and operating-system security remain part of the threat model.
diff --git a/SECURITY_DISCLAIMER.md b/SECURITY_DISCLAIMER.md
index e7ccb2e..25d3a45 100644
--- a/SECURITY_DISCLAIMER.md
+++ b/SECURITY_DISCLAIMER.md
@@ -1,244 +1,27 @@
# Security Disclaimer and Terms of Use
-## 🔒 SecureBit.chat Enhanced Security Edition v4.02.442
+SecureBit.chat is provided as open-source software for lawful private communication, research, and education. It is supplied **as is**, without warranties of any kind.
-### Important Legal Notice
+## User responsibilities
-**READ THIS DISCLAIMER CAREFULLY BEFORE USING SECUREBIT.CHAT SOFTWARE**
+By using SecureBit.chat, you are responsible for:
----
+- complying with applicable laws and organizational policies
+- securing your devices and browser environment
+- verifying SAS codes through an out-of-band channel
+- understanding that endpoint compromise can defeat application-layer protections
+- configuring TURN correctly when relay-only privacy mode is required
-## 📋 Overview
+## Security limitations
-SecureBit.chat is an open-source, peer-to-peer encrypted messaging application designed to support **freedom of speech** and **privacy rights**. This software implements military-grade cryptography with complete ASN.1 validation and is provided as-is for educational, research, and legitimate communication purposes.
+No communication system can guarantee absolute security. SecureBit.chat reduces risk through encrypted transport, mandatory peer verification, explicit file-transfer consent, local metadata protection, and lifecycle cleanup, but it cannot protect against compromised devices, malicious users with physical access, or incorrect operational practices.
----
+## Intended use
-## ⚖️ Legal Disclaimer
+SecureBit.chat is intended for legitimate private communication, journalism, research, education, business confidentiality, and personal privacy. It is not intended to facilitate unlawful activity, abuse, harassment, or harm.
-### Developer Liability
+## Current release
-**THE DEVELOPER(S) OF SECUREBIT.CHAT ASSUME NO RESPONSIBILITY OR LIABILITY FOR:**
-
-- Any misuse, illegal use, or criminal activities conducted using this software
-- Compliance with local, national, or international laws and regulations
-- Any damages, losses, or consequences resulting from the use of this software
-- The security or privacy of communications in jurisdictions where encryption is restricted
-- Any vulnerability, security flaw, or compromise that may occur despite our security measures
-
-### User Responsibility
-
-**BY USING SECUREBIT.CHAT, YOU ACKNOWLEDGE AND AGREE THAT:**
-
-1. **Full Legal Responsibility**: You bear complete and sole responsibility for how you use this software
-2. **Compliance Obligation**: You must ensure your use complies with all applicable laws in your jurisdiction
-3. **Risk Acceptance**: You understand and accept all risks associated with using encrypted communication software
-4. **No Warranty**: This software is provided "AS IS" without any warranties, express or implied
-
----
-
-## 🌍 Jurisdictional Considerations
-
-### Encryption Laws Vary Globally
-
-- **Some countries restrict or prohibit** the use of strong encryption
-- **Export controls** may apply in certain jurisdictions
-- **Corporate/government networks** may have policies against encrypted communications
-- **Users must verify** local laws before using this software
-
-### High-Risk Jurisdictions
-
-**Exercise extreme caution** in countries with:
-- Restrictions on encrypted communications
-- Surveillance laws requiring backdoors
-- Penalties for using VPN/encryption software
-- Authoritarian internet controls
-
----
-
-## 🎯 Intended Use Cases
-
-### ✅ Legitimate Uses (Encouraged)
-- **Journalism**: Protecting sources and whistleblowers
-- **Human Rights**: Organizing and advocacy in oppressive regimes
-- **Business**: Corporate communications requiring confidentiality
-- **Personal Privacy**: Private communications between individuals
-- **Research**: Academic study of cryptographic protocols
-- **Education**: Learning about secure communication systems
-
-### ❌ Prohibited Uses (Illegal/Unethical)
-- Any illegal activities under applicable law
-- Criminal conspiracies or planning illegal acts
-- Harassment, threats, or abuse of others
-- Circumventing legitimate law enforcement (where legally required)
-- Distribution of illegal content
-- Financial crimes or fraud
-
----
-
-## 🔐 Security Limitations
-
-### No Absolute Security
-
-**UNDERSTAND THAT:**
-- No cryptographic system is 100% unbreakable
-- Implementation bugs may exist despite best efforts
-- Social engineering and endpoint security remain vulnerabilities
-- Quantum computing may eventually threaten current encryption
-- Traffic analysis may reveal communication patterns
-
-### User Security Responsibilities
-
-**YOU MUST:**
-- Keep your devices secure and updated
-- Use strong, unique passwords
-- Verify security codes through out-of-band channels
-- Understand the risks of your communication environment
-- Follow operational security (OPSEC) best practices
-
----
-
-## 🏛️ Freedom of Speech Support
-
-### Our Mission
-
-SecureBit.chat is developed to support:
-- **Article 19** of the Universal Declaration of Human Rights
-- **Freedom of expression** and **right to privacy**
-- **Resistance to censorship** and mass surveillance
-- **Protection of journalists, activists, and dissidents**
-
-### Ethical Use Commitment
-
-We believe privacy and free speech are fundamental human rights, but:
-- These rights come with responsibilities
-- Freedom of speech does not include freedom from consequences
-- Users must respect the rights and safety of others
-- Illegal activity is never justified, regardless of privacy tools used
-
----
-
-## 📊 Technical Security Information
-
-### Current Implementation (v4.02.442)
-- **ECDH P-384** key exchange with complete ASN.1 validation
-- **AES-GCM 256-bit** encryption
-- **ECDSA P-384** digital signatures with enhanced key verification
-- **RSA-2048** digital signatures for file metadata
-- **Perfect Forward Secrecy** with key rotation
-- **MITM protection** via out-of-band verification
-- **Zero server architecture** (pure P2P)
-- **DTLS Race Condition Protection** against October 2024 WebRTC vulnerabilities
-- **ICE Endpoint Verification** for secure WebRTC connections
-- **Message Size Validation** with 1MB DoS protection
-- **Atomic Operations** for race condition prevention
-- **Secure Memory Management** with advanced wiping techniques
-- **Symbol-Based Context Isolation** for private instance management
-- **Rate Limiting System** (10 files/minute) with client identification
-
-### 🔒 ASN.1 Complete Structure Validation (NEW)
-- **Complete ASN.1 DER Parser**: Full structural validation of all cryptographic keys
-- **OID Validation**: Algorithm and curve verification (P-256/P-384 only)
-- **EC Point Format Verification**: Uncompressed format 0x04 validation
-- **SPKI Structure Validation**: Element count and type checking
-- **Key Size Limits**: 50-2000 bytes to prevent DoS attacks
-- **BIT STRING Validation**: Ensuring unused bits are 0
-- **Fallback Support**: P-384 to P-256 compatibility
-- **High-Risk Vulnerability Fix**: Prevents keys with valid headers but modified data
-
-### Known Limitations
-- WebRTC fingerprinting possibilities (mitigated by anti-fingerprinting techniques)
-- Browser-based implementation constraints
-- Dependency on Web Crypto API security
-- No protection against compromised endpoints
-- Traffic analysis potential despite encryption (mitigated by packet padding and noise)
-- Memory safety depends on JavaScript engine implementation
-- DTLS protection effectiveness depends on WebRTC implementation
-
----
-
-## 🔄 Future Development
-
-### Post-Quantum Roadmap
-- **v5.0**: CRYSTALS-Kyber/Dilithium implementation
-- **Long-term**: Resistance to quantum cryptanalysis
-- **Ongoing**: Security audits and improvements
-
-### Advanced Security Technologies (v4.02.442)
-- **ASN.1 Validation Framework**: Complete DER parsing and key structure verification
-- **Enhanced Key Security**: OID and EC point validation for all cryptographic operations
-- **PKCS Compliance**: Full adherence to cryptographic standards
-- **Structural Security**: Complete validation of all key components
-- **Vulnerability Prevention**: High-risk key manipulation attack prevention
-
-### Previous Advanced Security Technologies (v4.01.441)
-- **DTLS Protection Framework**: Comprehensive WebRTC security enhancement
-- **Memory Safety Mechanisms**: Advanced protection against use-after-free vulnerabilities
-- **Race Condition Prevention**: Atomic operations for critical security sections
-- **Error Sanitization System**: Secure error handling without information leakage
-- **Context Isolation**: Symbol-based private instance management
-- **File Transfer Security**: Cryptographic signatures and metadata validation
-- **Advanced DoS Protection**: Message size validation and rate limiting
-
----
-
-## 📞 Contact and Reporting
-
-### Security Issues
-- **Responsible disclosure**: Email security issues to the development team
-- **CVE reporting**: We participate in responsible vulnerability disclosure
-- **Bug bounty**: Consider implementing for critical security findings
-
-### Legal Concerns
-- **Law enforcement**: Contact appropriate legal authorities in your jurisdiction
-- **Abuse reports**: Report illegal use to relevant authorities
-- **Compliance questions**: Consult with legal counsel
-
----
-
-## 📜 License and Terms
-
-### Open Source License
-SecureBit.chat is released under the **MIT License**, providing:
-- Freedom to use, modify, and distribute
-- No warranty or liability guarantees
-- Full source code transparency
-- Right to audit security implementation
-
-### Terms Acceptance
-**By downloading, installing, or using SecureBit.chat, you acknowledge:**
-
-1. You have read and understood this disclaimer
-2. You accept full responsibility for your use of the software
-3. You agree to comply with all applicable laws
-4. You understand the security limitations and risks
-5. You will not hold the developers liable for any consequences
-
----
-
-## ⚠️ Final Warning
-
-**SECUREBIT.CHAT IS A POWERFUL TOOL FOR PRIVACY AND FREE SPEECH**
-
-With great power comes great responsibility. Use this software ethically, legally, and with full understanding of the risks and responsibilities involved.
-
-**Remember**: The strongest encryption cannot protect against poor operational security, compromised endpoints, or illegal activities that attract law enforcement attention.
-
----
-
-## 🛡️ Declaration of Intent
-
-This software is created to:
-- **Protect human rights** and fundamental freedoms
-- **Support legitimate privacy** needs in an increasingly surveilled world
-- **Advance the field** of secure communications
-- **Educate users** about cryptography and privacy
-
-**It is NOT intended to facilitate illegal activities or harm others.**
-
----
-
-*Last Updated: January 15, 2025*
-*Version: Enhanced Security Edition v4.02.442 - ASN.1 Validated*
-
-**USE AT YOUR OWN RISK AND RESPONSIBILITY**
\ No newline at end of file
+- Product release: `v4.8.5`
+- Protocol version: `4.1`
+- Last updated: May 17, 2026
diff --git a/assets/tailwind.css b/assets/tailwind.css
index e35b84e..2394da2 100644
--- a/assets/tailwind.css
+++ b/assets/tailwind.css
@@ -1 +1 @@
-*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-right-2{right:-.5rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-2{bottom:.5rem}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-6{right:1.5rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[9999\]{z-index:9999}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-auto{margin-right:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-20{margin-top:5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-28{height:7rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-80{height:20rem}.h-96{height:24rem}.h-\[420px\]{height:420px}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-\[80vh\]{max-height:80vh}.min-h-\[72px\]{min-height:72px}.min-h-\[calc\(100vh-104px\)\]{min-height:calc(100vh - 104px)}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-28{width:7rem}.w-4{width:1rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-8{width:2rem}.w-\[20rem\]{width:20rem}.w-full{width:100%}.min-w-\[160px\]{min-width:160px}.min-w-\[240px\]{min-width:240px}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-full{--tw-translate-x:100%}.translate-x-full,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.rotate-180{--tw-rotate:180deg}.rotate-180,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.self-center{align-self:center}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.whitespace-normal{white-space:normal}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-500\/20{border-color:rgba(59,130,246,.2)}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-500\/10{border-color:hsla(220,9%,46%,.1)}.border-gray-500\/20{border-color:hsla(220,9%,46%,.2)}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.border-gray-600\/30{border-color:rgba(75,85,99,.3)}.border-gray-700\/30{border-color:rgba(55,65,81,.3)}.border-green-500\/20{border-color:rgba(34,197,94,.2)}.border-green-500\/30{border-color:rgba(34,197,94,.3)}.border-orange-500\/20{border-color:rgba(249,115,22,.2)}.border-purple-500\/20{border-color:rgba(168,85,247,.2)}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-yellow-500\/20{border-color:rgba(234,179,8,.2)}.bg-\[rgb\(20_20_20_\/50\%\)\]{background-color:hsla(0,0%,8%,.5)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-blue-500\/20{background-color:rgba(59,130,246,.2)}.bg-blue-500\/90{background-color:rgba(59,130,246,.9)}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-500\/10{background-color:hsla(220,9%,46%,.1)}.bg-gray-500\/20{background-color:hsla(220,9%,46%,.2)}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-600\/20{background-color:rgba(75,85,99,.2)}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-gray-700\/50{background-color:rgba(55,65,81,.5)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.bg-gray-800\/95{background-color:rgba(31,41,55,.95)}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-gray-900\/30{background-color:rgba(17,24,39,.3)}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(187 247 208/var(--tw-bg-opacity,1))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.bg-green-400\/20{background-color:rgba(74,222,128,.2)}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-500\/10{background-color:rgba(34,197,94,.1)}.bg-green-500\/20{background-color:rgba(34,197,94,.2)}.bg-green-500\/90{background-color:rgba(34,197,94,.9)}.bg-green-600\/20{background-color:rgba(22,163,74,.2)}.bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-500\/10{background-color:rgba(249,115,22,.1)}.bg-orange-500\/15{background-color:rgba(249,115,22,.15)}.bg-orange-500\/20{background-color:rgba(249,115,22,.2)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-red-200{--tw-bg-opacity:1;background-color:rgb(254 202 202/var(--tw-bg-opacity,1))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-500\/20{background-color:rgba(239,68,68,.2)}.bg-red-500\/90{background-color:rgba(239,68,68,.9)}.bg-red-900\/50{background-color:rgba(127,29,29,.5)}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity,1))}.bg-yellow-500\/10{background-color:rgba(234,179,8,.1)}.bg-yellow-500\/20{background-color:rgba(234,179,8,.2)}.bg-yellow-500\/90{background-color:rgba(234,179,8,.9)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-\[\#1f1f1f\]\/90{--tw-gradient-from:rgba(31,31,31,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,31,31,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-800\/20{--tw-gradient-from:rgba(31,41,55,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:transparent var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-zinc-700{--tw-gradient-to:rgba(63,63,70,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#3f3f46 var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-gray-900\/20{--tw-gradient-to:rgba(17,24,39,.2) var(--tw-gradient-to-position)}.to-orange-600{--tw-gradient-to:#ea580c var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.fill-blue-500{fill:#3b82f6}.fill-white{fill:#fff}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pr-2{padding-right:.5rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[3rem\]{font-size:3rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.lowercase{text-transform:lowercase}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/10{color:hsla(0,0%,100%,.1)}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity,1))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.grayscale{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.grayscale{--tw-grayscale:grayscale(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[checksum\:4\]{checksum:4}.\[data\:variable\]{data:variable}.\[name\:4\]{name:4}.\[size\:4\]{size:4}.hover\:bg-\[rgb\(20_20_20_\/30\%\)\]:hover{background-color:hsla(0,0%,8%,.3)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.hover\:bg-gray-600\/30:hover{background-color:rgba(75,85,99,.3)}.hover\:bg-green-500\/20:hover{background-color:rgba(34,197,94,.2)}.hover\:bg-green-500\/30:hover{background-color:rgba(34,197,94,.3)}.hover\:bg-green-600\/30:hover{background-color:rgba(22,163,74,.3)}.hover\:bg-green-600\/40:hover{background-color:rgba(22,163,74,.4)}.hover\:bg-orange-500\/20:hover{background-color:rgba(249,115,22,.2)}.hover\:bg-orange-500\/40:hover{background-color:rgba(249,115,22,.4)}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-purple-500:hover{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity,1))}.hover\:bg-purple-500\/20:hover{background-color:rgba(168,85,247,.2)}.hover\:bg-red-500\/20:hover{background-color:rgba(239,68,68,.2)}.hover\:bg-red-500\/30:hover{background-color:rgba(239,68,68,.3)}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-600\/40:hover{background-color:rgba(220,38,38,.4)}.hover\:bg-white\/30:hover{background-color:hsla(0,0%,100%,.3)}.hover\:bg-yellow-600\/40:hover{background-color:rgba(202,138,4,.4)}.hover\:from-orange-600:hover{--tw-gradient-from:#ea580c var(--tw-gradient-from-position);--tw-gradient-to:rgba(234,88,12,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-orange-700:hover{--tw-gradient-to:#c2410c var(--tw-gradient-to-position)}.hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.hover\:text-green-300:hover{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.focus\:border-green-500\/40:focus{border-color:rgba(34,197,94,.4)}.focus\:border-orange-500\/40:focus{border-color:rgba(249,115,22,.4)}.focus\:border-purple-500\/40:focus{border-color:rgba(168,85,247,.4)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.group:hover .group-hover\:scale-105,.group:hover .group-hover\:scale-110{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:mb-0{margin-bottom:0}.sm\:mb-3{margin-bottom:.75rem}.sm\:mr-2{margin-right:.5rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:h-10{height:2.5rem}.sm\:h-12{height:3rem}.sm\:h-16{height:4rem}.sm\:w-10{width:2.5rem}.sm\:w-12{width:3rem}.sm\:w-\[24rem\]{width:24rem}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:1rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.sm\:p-4{padding:1rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-3{padding-left:.75rem;padding-right:.75rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:768px){.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-32{height:8rem}.md\:h-\[32rem\]{height:32rem}.md\:w-\[28rem\]{width:28rem}.md\:w-auto{width:auto}.md\:w-px{width:1px}.md\:flex-none{flex:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:block{display:block}.lg\:w-\[32rem\]{width:32rem}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:hover\:text-white:hover,.dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}}
\ No newline at end of file
+*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-right-2{right:-.5rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-2{bottom:.5rem}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-6{right:1.5rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[9999\]{z-index:9999}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-auto{margin-right:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-20{margin-top:5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-28{height:7rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-80{height:20rem}.h-96{height:24rem}.h-\[420px\]{height:420px}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-\[80vh\]{max-height:80vh}.min-h-\[72px\]{min-height:72px}.min-h-\[calc\(100vh-104px\)\]{min-height:calc(100vh - 104px)}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-28{width:7rem}.w-4{width:1rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-8{width:2rem}.w-\[20rem\]{width:20rem}.w-full{width:100%}.min-w-\[160px\]{min-width:160px}.min-w-\[240px\]{min-width:240px}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-full{--tw-translate-x:100%}.translate-x-full,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.rotate-180{--tw-rotate:180deg}.rotate-180,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.self-center{align-self:center}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.whitespace-normal{white-space:normal}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-500\/20{border-color:rgba(59,130,246,.2)}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-500\/10{border-color:hsla(220,9%,46%,.1)}.border-gray-500\/20{border-color:hsla(220,9%,46%,.2)}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.border-gray-600\/30{border-color:rgba(75,85,99,.3)}.border-gray-700\/30{border-color:rgba(55,65,81,.3)}.border-green-500\/20{border-color:rgba(34,197,94,.2)}.border-green-500\/30{border-color:rgba(34,197,94,.3)}.border-orange-500\/20{border-color:rgba(249,115,22,.2)}.border-purple-500\/20{border-color:rgba(168,85,247,.2)}.border-purple-500\/30{border-color:rgba(168,85,247,.3)}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-yellow-500\/20{border-color:rgba(234,179,8,.2)}.border-yellow-500\/30{border-color:rgba(234,179,8,.3)}.bg-\[rgb\(20_20_20_\/50\%\)\]{background-color:hsla(0,0%,8%,.5)}.bg-black\/20{background-color:rgba(0,0,0,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-blue-500\/20{background-color:rgba(59,130,246,.2)}.bg-blue-500\/90{background-color:rgba(59,130,246,.9)}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-500\/10{background-color:hsla(220,9%,46%,.1)}.bg-gray-500\/20{background-color:hsla(220,9%,46%,.2)}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-600\/20{background-color:rgba(75,85,99,.2)}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-gray-700\/50{background-color:rgba(55,65,81,.5)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.bg-gray-800\/95{background-color:rgba(31,41,55,.95)}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-gray-900\/30{background-color:rgba(17,24,39,.3)}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(187 247 208/var(--tw-bg-opacity,1))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.bg-green-400\/20{background-color:rgba(74,222,128,.2)}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-500\/10{background-color:rgba(34,197,94,.1)}.bg-green-500\/20{background-color:rgba(34,197,94,.2)}.bg-green-500\/90{background-color:rgba(34,197,94,.9)}.bg-green-600\/20{background-color:rgba(22,163,74,.2)}.bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-500\/10{background-color:rgba(249,115,22,.1)}.bg-orange-500\/15{background-color:rgba(249,115,22,.15)}.bg-orange-500\/20{background-color:rgba(249,115,22,.2)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-red-200{--tw-bg-opacity:1;background-color:rgb(254 202 202/var(--tw-bg-opacity,1))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-500\/20{background-color:rgba(239,68,68,.2)}.bg-red-500\/90{background-color:rgba(239,68,68,.9)}.bg-red-900\/50{background-color:rgba(127,29,29,.5)}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity,1))}.bg-yellow-500\/10{background-color:rgba(234,179,8,.1)}.bg-yellow-500\/20{background-color:rgba(234,179,8,.2)}.bg-yellow-500\/90{background-color:rgba(234,179,8,.9)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-\[\#1f1f1f\]\/90{--tw-gradient-from:rgba(31,31,31,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,31,31,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-800\/20{--tw-gradient-from:rgba(31,41,55,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:transparent var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-zinc-700{--tw-gradient-to:rgba(63,63,70,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#3f3f46 var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-gray-900\/20{--tw-gradient-to:rgba(17,24,39,.2) var(--tw-gradient-to-position)}.to-orange-600{--tw-gradient-to:#ea580c var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.fill-blue-500{fill:#3b82f6}.fill-white{fill:#fff}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pr-2{padding-right:.5rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[3rem\]{font-size:3rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-\[0\.3em\]{letter-spacing:.3em}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/10{color:hsla(0,0%,100%,.1)}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity,1))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity,1))}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.grayscale{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.grayscale{--tw-grayscale:grayscale(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[checksum\:4\]{checksum:4}.\[data\:variable\]{data:variable}.\[name\:4\]{name:4}.\[size\:4\]{size:4}.hover\:bg-\[rgb\(20_20_20_\/30\%\)\]:hover{background-color:hsla(0,0%,8%,.3)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.hover\:bg-gray-600\/30:hover{background-color:rgba(75,85,99,.3)}.hover\:bg-green-500\/20:hover{background-color:rgba(34,197,94,.2)}.hover\:bg-green-500\/30:hover{background-color:rgba(34,197,94,.3)}.hover\:bg-green-600\/30:hover{background-color:rgba(22,163,74,.3)}.hover\:bg-green-600\/40:hover{background-color:rgba(22,163,74,.4)}.hover\:bg-orange-500\/20:hover{background-color:rgba(249,115,22,.2)}.hover\:bg-orange-500\/40:hover{background-color:rgba(249,115,22,.4)}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-purple-500:hover{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity,1))}.hover\:bg-purple-500\/20:hover{background-color:rgba(168,85,247,.2)}.hover\:bg-red-500\/20:hover{background-color:rgba(239,68,68,.2)}.hover\:bg-red-500\/30:hover{background-color:rgba(239,68,68,.3)}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-600\/40:hover{background-color:rgba(220,38,38,.4)}.hover\:bg-white\/30:hover{background-color:hsla(0,0%,100%,.3)}.hover\:bg-yellow-600\/40:hover{background-color:rgba(202,138,4,.4)}.hover\:from-orange-600:hover{--tw-gradient-from:#ea580c var(--tw-gradient-from-position);--tw-gradient-to:rgba(234,88,12,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-orange-700:hover{--tw-gradient-to:#c2410c var(--tw-gradient-to-position)}.hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.hover\:text-green-300:hover{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.focus\:border-green-500\/40:focus{border-color:rgba(34,197,94,.4)}.focus\:border-orange-500\/40:focus{border-color:rgba(249,115,22,.4)}.focus\:border-purple-400:focus{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity,1))}.focus\:border-purple-500\/40:focus{border-color:rgba(168,85,247,.4)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.group:hover .group-hover\:scale-105,.group:hover .group-hover\:scale-110{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:mb-0{margin-bottom:0}.sm\:mb-3{margin-bottom:.75rem}.sm\:mr-2{margin-right:.5rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:h-10{height:2.5rem}.sm\:h-12{height:3rem}.sm\:h-16{height:4rem}.sm\:w-10{width:2.5rem}.sm\:w-12{width:3rem}.sm\:w-\[24rem\]{width:24rem}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:1rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.sm\:p-4{padding:1rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-3{padding-left:.75rem;padding-right:.75rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:768px){.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-32{height:8rem}.md\:h-\[32rem\]{height:32rem}.md\:w-\[28rem\]{width:28rem}.md\:w-auto{width:auto}.md\:w-px{width:1px}.md\:flex-none{flex:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:block{display:block}.lg\:w-\[32rem\]{width:32rem}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:hover\:text-white:hover,.dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}}
\ No newline at end of file
diff --git a/dist/app-boot.js b/dist/app-boot.js
index 613bce4..bdb1bbe 100644
--- a/dist/app-boot.js
+++ b/dist/app-boot.js
@@ -2998,12 +2998,13 @@ var SecureMemoryManager = class {
}
};
var EnhancedSecureFileTransfer = class {
- constructor(webrtcManager, onProgress, onComplete, onError, onFileReceived) {
+ constructor(webrtcManager, onProgress, onComplete, onError, onFileReceived, onIncomingFileRequest) {
this.webrtcManager = webrtcManager;
this.onProgress = onProgress;
this.onComplete = onComplete;
this.onError = onError;
this.onFileReceived = onFileReceived;
+ this.onIncomingFileRequest = onIncomingFileRequest;
if (!webrtcManager) {
throw new Error("webrtcManager is required for EnhancedSecureFileTransfer");
}
@@ -3018,90 +3019,72 @@ var EnhancedSecureFileTransfer = class {
this.CHUNK_TIMEOUT = 3e4;
this.RETRY_ATTEMPTS = 3;
this.FILE_TYPE_RESTRICTIONS = {
- documents: {
- extensions: [".pdf", ".doc", ".docx", ".txt", ".md", ".rtf", ".odt"],
- mimeTypes: [
- "application/pdf",
- "application/msword",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
- "text/plain",
- "text/markdown",
- "application/rtf",
- "application/vnd.oasis.opendocument.text"
- ],
+ pdf: {
+ extensions: [".pdf"],
+ mimeTypes: ["application/pdf"],
maxSize: 50 * 1024 * 1024,
- // 50 MB
- category: "Documents",
- description: "PDF, DOC, TXT, MD, RTF, ODT"
+ category: "PDF",
+ description: "PDF"
+ },
+ text: {
+ extensions: [".txt"],
+ mimeTypes: ["text/plain"],
+ maxSize: 10 * 1024 * 1024,
+ category: "Plain text",
+ description: "TXT"
},
images: {
- extensions: [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".ico"],
+ extensions: [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".ico"],
mimeTypes: [
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/bmp",
- "image/svg+xml",
"image/x-icon"
],
maxSize: 25 * 1024 * 1024,
// 25 MB
category: "Images",
- description: "JPG, PNG, GIF, WEBP, BMP, SVG, ICO"
+ description: "JPG, JPEG, PNG, GIF, WEBP, BMP, ICO"
},
archives: {
- extensions: [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2", ".xz"],
- mimeTypes: [
- "application/zip",
- "application/x-rar-compressed",
- "application/x-7z-compressed",
- "application/x-tar",
- "application/gzip",
- "application/x-bzip2",
- "application/x-xz"
- ],
+ extensions: [".zip"],
+ mimeTypes: ["application/zip"],
maxSize: 100 * 1024 * 1024,
// 100 MB
category: "Archives",
- description: "ZIP, RAR, 7Z, TAR, GZ, BZ2, XZ"
- },
- media: {
- extensions: [".mp3", ".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm", ".ogg", ".wav"],
- mimeTypes: [
- "audio/mpeg",
- "video/mp4",
- "video/x-msvideo",
- "video/x-matroska",
- "video/quicktime",
- "video/x-ms-wmv",
- "video/x-flv",
- "video/webm",
- "audio/ogg",
- "audio/wav"
- ],
- maxSize: 100 * 1024 * 1024,
- // 100 MB
- category: "Media",
- description: "MP3, MP4, AVI, MKV, MOV, WMV, FLV, WEBM, OGG, WAV"
- },
- general: {
- extensions: [],
- mimeTypes: [],
- maxSize: 50 * 1024 * 1024,
- // 50 MB
- category: "General",
- description: "Any file type up to size limits"
+ description: "ZIP"
}
};
+ this.BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
+ ".exe",
+ ".bat",
+ ".cmd",
+ ".sh",
+ ".js",
+ ".msi",
+ ".dmg",
+ ".app",
+ ".jar",
+ ".scr",
+ ".ps1",
+ ".vbs",
+ ".html",
+ ".svg"
+ ]);
this.activeTransfers = /* @__PURE__ */ new Map();
this.receivingTransfers = /* @__PURE__ */ new Map();
+ this.pendingIncomingTransfers = /* @__PURE__ */ new Map();
this.transferQueue = [];
this.pendingChunks = /* @__PURE__ */ new Map();
+ this.incomingOfferLimiter = new RateLimiter(5, 6e4);
+ this.MAX_PENDING_INCOMING_TRANSFERS = 3;
this.sessionKeys = /* @__PURE__ */ new Map();
this.processedChunks = /* @__PURE__ */ new Set();
this.transferNonces = /* @__PURE__ */ new Map();
this.receivedFileBuffers = /* @__PURE__ */ new Map();
+ this.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 3;
this.setupFileMessageHandlers();
if (this.webrtcManager) {
this.webrtcManager.fileTransferSystem = this;
@@ -3111,21 +3094,14 @@ var EnhancedSecureFileTransfer = class {
// FILE TYPE VALIDATION SYSTEM
// ============================================
getFileType(file) {
- const fileName = file.name.toLowerCase();
- const fileExtension = fileName.substring(fileName.lastIndexOf("."));
- const mimeType = file.type.toLowerCase();
+ const fileName = String(file?.name || "").toLowerCase();
+ const extensionIndex = fileName.lastIndexOf(".");
+ const fileExtension = extensionIndex >= 0 ? fileName.substring(extensionIndex) : "";
+ const mimeType = String(file?.type || "").toLowerCase();
for (const [typeKey, typeConfig] of Object.entries(this.FILE_TYPE_RESTRICTIONS)) {
- if (typeKey === "general") continue;
- if (typeConfig.extensions.includes(fileExtension)) {
- return {
- type: typeKey,
- category: typeConfig.category,
- description: typeConfig.description,
- maxSize: typeConfig.maxSize,
- allowed: true
- };
- }
- if (typeConfig.mimeTypes.includes(mimeType)) {
+ const extensionAllowed = typeConfig.extensions.includes(fileExtension);
+ const mimeAllowed = typeConfig.mimeTypes.includes(mimeType);
+ if (extensionAllowed && mimeAllowed) {
return {
type: typeKey,
category: typeConfig.category,
@@ -3135,23 +3111,37 @@ var EnhancedSecureFileTransfer = class {
};
}
}
- const generalConfig = this.FILE_TYPE_RESTRICTIONS.general;
return {
- type: "general",
- category: generalConfig.category,
- description: generalConfig.description,
- maxSize: generalConfig.maxSize,
- allowed: true
+ type: "blocked",
+ category: "Unsupported",
+ description: "Allowed: JPG, JPEG, PNG, GIF, WEBP, BMP, ICO, PDF, TXT, ZIP",
+ maxSize: this.MAX_FILE_SIZE,
+ allowed: false,
+ extension: fileExtension,
+ mimeType
};
}
validateFile(file) {
const fileType = this.getFileType(file);
const errors = [];
+ const fileName = String(file?.name || "");
+ const lowerName = fileName.toLowerCase();
+ const extensionIndex = lowerName.lastIndexOf(".");
+ const fileExtension = extensionIndex >= 0 ? lowerName.substring(extensionIndex) : "";
+ const mimeType = String(file?.type || "").toLowerCase();
+ if (this.BLOCKED_EXTENSIONS.has(fileExtension)) {
+ errors.push(`File rejected: ${fileExtension} files are not allowed for security reasons.`);
+ }
+ if (!mimeType) {
+ errors.push("File rejected: missing MIME type is unsafe.");
+ }
if (file.size > fileType.maxSize) {
errors.push(`File size (${this.formatFileSize(file.size)}) exceeds maximum allowed for ${fileType.category} (${this.formatFileSize(fileType.maxSize)})`);
}
if (!fileType.allowed) {
- errors.push(`File type not allowed. Supported types: ${fileType.description}`);
+ if (mimeType && !this.BLOCKED_EXTENSIONS.has(fileExtension)) {
+ errors.push(`File rejected: extension and MIME type must match an allowed type. Supported types: ${fileType.description}`);
+ }
}
if (file.size > this.MAX_FILE_SIZE) {
errors.push(`File size (${this.formatFileSize(file.size)}) exceeds general limit (${this.formatFileSize(this.MAX_FILE_SIZE)})`);
@@ -3164,6 +3154,31 @@ var EnhancedSecureFileTransfer = class {
formattedSize: this.formatFileSize(file.size)
};
}
+ normalizeDisplayFileName(fileName) {
+ return String(fileName || "").normalize("NFKC").replace(/[\u0000-\u001F\u007F]/g, "").replace(/[\\/]+/g, "_").trim().slice(0, 255);
+ }
+ validateIncomingMetadata(metadata) {
+ const errors = [];
+ if (!metadata || typeof metadata !== "object") errors.push("Invalid file transfer metadata");
+ if (!metadata?.fileId || typeof metadata.fileId !== "string") errors.push("Invalid file id");
+ if (!Number.isSafeInteger(metadata?.fileSize) || metadata.fileSize <= 0) errors.push("Invalid file size");
+ if (!Number.isSafeInteger(metadata?.totalChunks) || metadata.totalChunks <= 0) errors.push("Invalid chunk count");
+ if (!Number.isSafeInteger(metadata?.chunkSize) || metadata.chunkSize <= 0 || metadata.chunkSize > this.CHUNK_SIZE) errors.push("Invalid chunk size");
+ if (!Array.isArray(metadata?.salt) || metadata.salt.length !== 32) errors.push("Invalid salt");
+ const rawName = typeof metadata?.fileName === "string" ? metadata.fileName : "";
+ const displayName = this.normalizeDisplayFileName(rawName);
+ const hasDangerousName = !rawName || rawName !== rawName.trim() || /[\u0000-\u001F\u007F]/.test(rawName) || /[\\/]/.test(rawName) || rawName === "." || rawName === ".." || displayName.length === 0;
+ if (hasDangerousName) errors.push("Dangerous file name");
+ if (errors.length === 0) {
+ const validation = this.validateFile({
+ name: displayName,
+ size: metadata.fileSize,
+ type: metadata.fileType || "application/octet-stream"
+ });
+ if (!validation.isValid) errors.push(...validation.errors);
+ }
+ return { isValid: errors.length === 0, errors, displayName };
+ }
formatFileSize(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
@@ -3174,7 +3189,6 @@ var EnhancedSecureFileTransfer = class {
getSupportedFileTypes() {
const supportedTypes = {};
for (const [typeKey, typeConfig] of Object.entries(this.FILE_TYPE_RESTRICTIONS)) {
- if (typeKey === "general") continue;
supportedTypes[typeKey] = {
category: typeConfig.category,
description: typeConfig.description,
@@ -3511,7 +3525,16 @@ var EnhancedSecureFileTransfer = class {
};
this.activeTransfers.set(fileId, transferState);
this.transferNonces.set(fileId, 0);
+ const consentPromise = new Promise((resolve, reject) => {
+ transferState.resolveConsent = resolve;
+ transferState.rejectConsent = reject;
+ transferState.consentTimeout = setTimeout(() => {
+ transferState.consentTimeout = null;
+ reject(new Error("Transfer timeout"));
+ }, 3e4);
+ });
await this.sendFileMetadata(transferState);
+ await consentPromise;
await this.startChunkTransmission(transferState);
return fileId;
} catch (error) {
@@ -3695,9 +3718,12 @@ var EnhancedSecureFileTransfer = class {
// ============================================
async handleFileTransferStart(metadata) {
try {
- if (!metadata.fileId || !metadata.fileName || !metadata.fileSize) {
- throw new Error("Invalid file transfer metadata");
+ const clientId = this.getClientIdentifier();
+ if (!this.incomingOfferLimiter.isAllowed(clientId)) {
+ throw new Error("Incoming file request rate limit exceeded");
}
+ const validation = this.validateIncomingMetadata(metadata);
+ if (!validation.isValid) throw new Error(validation.errors.join(". "));
if (metadata.signature && this.verificationKey) {
try {
const isValid = await FileMetadataSigner.verifyFileMetadata(
@@ -3720,43 +3746,27 @@ var EnhancedSecureFileTransfer = class {
throw new Error("File metadata verification failed");
}
}
- if (this.receivingTransfers.has(metadata.fileId)) {
+ if (this.receivingTransfers.has(metadata.fileId) || this.pendingIncomingTransfers.has(metadata.fileId)) {
return;
}
- const sessionKey = await this.deriveFileSessionKeyFromSalt(
- metadata.fileId,
- metadata.salt
- );
- const receivingState = {
- fileId: metadata.fileId,
- fileName: metadata.fileName,
- fileSize: metadata.fileSize,
- fileType: metadata.fileType || "application/octet-stream",
- fileHash: metadata.fileHash,
- totalChunks: metadata.totalChunks,
- chunkSize: metadata.chunkSize || this.CHUNK_SIZE,
- sessionKey,
- salt: metadata.salt,
- receivedChunks: /* @__PURE__ */ new Map(),
- receivedCount: 0,
- startTime: Date.now(),
- lastChunkTime: Date.now(),
- status: "receiving"
+ if (this.pendingIncomingTransfers.size >= this.MAX_PENDING_INCOMING_TRANSFERS) {
+ throw new Error("Too many pending incoming file requests");
+ }
+ const pendingMetadata = {
+ ...metadata,
+ fileName: validation.displayName,
+ receivedAt: Date.now()
};
- this.receivingTransfers.set(metadata.fileId, receivingState);
- const response = {
- type: "file_transfer_response",
- fileId: metadata.fileId,
- accepted: true,
- timestamp: Date.now()
- };
- await this.sendSecureMessage(response);
- if (this.pendingChunks.has(metadata.fileId)) {
- const bufferedChunks = this.pendingChunks.get(metadata.fileId);
- for (const [chunkIndex, chunkMessage] of bufferedChunks.entries()) {
- await this.handleFileChunk(chunkMessage);
- }
- this.pendingChunks.delete(metadata.fileId);
+ this.pendingIncomingTransfers.set(metadata.fileId, pendingMetadata);
+ if (typeof this.onIncomingFileRequest === "function") {
+ this.onIncomingFileRequest({
+ fileId: pendingMetadata.fileId,
+ fileName: pendingMetadata.fileName,
+ fileSize: pendingMetadata.fileSize,
+ mimeType: pendingMetadata.fileType || "application/octet-stream"
+ });
+ } else {
+ await this.rejectIncomingFile(metadata.fileId, "User consent unavailable");
}
} catch (error) {
const safeError = SecurityErrorHandler.sanitizeError(error);
@@ -3778,10 +3788,6 @@ var EnhancedSecureFileTransfer = class {
try {
let receivingState = this.receivingTransfers.get(chunkMessage.fileId);
if (!receivingState) {
- if (!this.pendingChunks.has(chunkMessage.fileId)) {
- this.pendingChunks.set(chunkMessage.fileId, /* @__PURE__ */ new Map());
- }
- this.pendingChunks.get(chunkMessage.fileId).set(chunkMessage.chunkIndex, chunkMessage);
return;
}
receivingState.lastChunkTime = Date.now();
@@ -3876,14 +3882,20 @@ var EnhancedSecureFileTransfer = class {
const fileBlob = new Blob([fileBuffer], { type: receivingState.fileType });
receivingState.endTime = Date.now();
receivingState.status = "completed";
- this.receivedFileBuffers.set(receivingState.fileId, {
+ this._storeReceivedFileBuffer(receivingState.fileId, {
buffer: fileBuffer,
type: receivingState.fileType,
name: receivingState.fileName,
size: receivingState.fileSize
});
if (this.onFileReceived) {
- const getBlob = async () => new Blob([this.receivedFileBuffers.get(receivingState.fileId).buffer], { type: receivingState.fileType });
+ const getBlob = async () => {
+ const blob = await this.getBlob(receivingState.fileId);
+ if (!blob) {
+ throw new Error("This file is no longer available for download.");
+ }
+ return blob;
+ };
const getObjectURL = async () => {
const blob = await getBlob();
return URL.createObjectURL(blob);
@@ -3954,8 +3966,18 @@ var EnhancedSecureFileTransfer = class {
}
if (response.accepted) {
transferState.status = "accepted";
+ if (transferState.consentTimeout) clearTimeout(transferState.consentTimeout);
+ transferState.consentTimeout = null;
+ transferState.resolveConsent?.();
+ transferState.resolveConsent = null;
+ transferState.rejectConsent = null;
} else {
transferState.status = "rejected";
+ if (transferState.consentTimeout) clearTimeout(transferState.consentTimeout);
+ transferState.consentTimeout = null;
+ transferState.rejectConsent?.(new Error(response.error || "Transfer rejected"));
+ transferState.rejectConsent = null;
+ transferState.resolveConsent = null;
if (this.onError) {
this.onError(`Transfer rejected: ${response.error || "Unknown reason"}`);
}
@@ -4048,6 +4070,45 @@ var EnhancedSecureFileTransfer = class {
startTime: transfer.startTime
}));
}
+ getPendingIncomingTransfers() {
+ return Array.from(this.pendingIncomingTransfers.values()).map((transfer) => ({
+ fileId: transfer.fileId,
+ fileName: transfer.fileName,
+ fileSize: transfer.fileSize,
+ mimeType: transfer.fileType || "application/octet-stream",
+ receivedAt: transfer.receivedAt
+ }));
+ }
+ async acceptIncomingFile(fileId) {
+ const metadata = this.pendingIncomingTransfers.get(fileId);
+ if (!metadata) return false;
+ const sessionKey = await this.deriveFileSessionKeyFromSalt(fileId, metadata.salt);
+ this.receivingTransfers.set(fileId, {
+ fileId,
+ fileName: metadata.fileName,
+ fileSize: metadata.fileSize,
+ fileType: metadata.fileType || "application/octet-stream",
+ fileHash: metadata.fileHash,
+ totalChunks: metadata.totalChunks,
+ chunkSize: metadata.chunkSize || this.CHUNK_SIZE,
+ sessionKey,
+ salt: metadata.salt,
+ receivedChunks: /* @__PURE__ */ new Map(),
+ receivedCount: 0,
+ startTime: Date.now(),
+ lastChunkTime: Date.now(),
+ status: "receiving"
+ });
+ this.pendingIncomingTransfers.delete(fileId);
+ await this.sendSecureMessage({ type: "file_transfer_response", fileId, accepted: true, timestamp: Date.now() });
+ return true;
+ }
+ async rejectIncomingFile(fileId, error = "Rejected by user") {
+ if (!this.pendingIncomingTransfers.has(fileId)) return false;
+ this.pendingIncomingTransfers.delete(fileId);
+ await this.sendSecureMessage({ type: "file_transfer_response", fileId, accepted: false, error, timestamp: Date.now() });
+ return true;
+ }
cancelTransfer(fileId) {
try {
if (this.activeTransfers.has(fileId)) {
@@ -4065,6 +4126,18 @@ var EnhancedSecureFileTransfer = class {
}
}
cleanupTransfer(fileId) {
+ const transferState = this.activeTransfers.get(fileId);
+ if (transferState) {
+ if (transferState.consentTimeout) {
+ clearTimeout(transferState.consentTimeout);
+ transferState.consentTimeout = null;
+ }
+ if (transferState.rejectConsent) {
+ transferState.rejectConsent(new Error("Transfer cancelled during cleanup or disconnect"));
+ transferState.rejectConsent = null;
+ transferState.resolveConsent = null;
+ }
+ }
this.activeTransfers.delete(fileId);
this.sessionKeys.delete(fileId);
this.transferNonces.delete(fileId);
@@ -4074,6 +4147,25 @@ var EnhancedSecureFileTransfer = class {
}
}
}
+ _storeReceivedFileBuffer(fileId, entry) {
+ this.receivedFileBuffers.set(fileId, entry);
+ while (this.receivedFileBuffers.size > this.MAX_RETAINED_RECEIVED_FILE_BUFFERS) {
+ const oldestFileId = this.receivedFileBuffers.keys().next().value;
+ this._discardReceivedFileBuffer(oldestFileId);
+ }
+ }
+ _discardReceivedFileBuffer(fileId) {
+ const fileBuffer = this.receivedFileBuffers.get(fileId);
+ if (!fileBuffer) return;
+ try {
+ if (fileBuffer.buffer) {
+ SecureMemoryManager.secureWipe(fileBuffer.buffer);
+ new Uint8Array(fileBuffer.buffer).fill(0);
+ }
+ } catch (_) {
+ }
+ this.receivedFileBuffers.delete(fileId);
+ }
// ✅ УЛУЧШЕННАЯ безопасная очистка памяти для предотвращения use-after-free
cleanupReceivingTransfer(fileId) {
try {
@@ -4248,12 +4340,16 @@ var EnhancedSecureFileTransfer = class {
this.rateLimiter.requests.clear();
}
this.pendingChunks.clear();
+ this.pendingIncomingTransfers.clear();
this.activeTransfers.clear();
this.receivingTransfers.clear();
this.transferQueue.length = 0;
this.sessionKeys.clear();
this.transferNonces.clear();
this.processedChunks.clear();
+ for (const fileId of Array.from(this.receivedFileBuffers.keys())) {
+ this._discardReceivedFileBuffer(fileId);
+ }
this.clearKeys();
}
// ============================================
@@ -4535,6 +4631,8 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
FILE_MESSAGE: "FILE_MESSAGE_FILTERED",
SYSTEM_MESSAGE: "SYSTEM_MESSAGE_FILTERED"
};
+ static PROTOCOL_VERSION = "4.1";
+ static MAX_SAS_ATTEMPTS = 3;
// Static debug flag instead of this._debugMode
static DEBUG_MODE = true;
// Set to true during development, false in production
@@ -4571,8 +4669,19 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
addNoise: config.antiFingerprinting?.addNoise ?? true,
maskPatterns: config.antiFingerprinting?.maskPatterns ?? false,
useRandomHeaders: config.antiFingerprinting?.useRandomHeaders ?? false
+ },
+ webrtc: {
+ relayOnly: config.webrtc?.relayOnly ?? false,
+ iceServers: config.webrtc?.iceServers ?? [
+ { urls: "stun:stun.l.google.com:19302" },
+ { urls: "stun:stun1.l.google.com:19302" },
+ { urls: "stun:stun2.l.google.com:19302" },
+ { urls: "stun:stun3.l.google.com:19302" },
+ { urls: "stun:stun4.l.google.com:19302" }
+ ]
}
};
+ this._ipLeakWarningShown = false;
this._initializeSecureLogging();
this._setupOwnLogger();
this._setupProductionLogging();
@@ -4629,6 +4738,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this.verificationCode = null;
this.pendingSASCode = null;
this.isVerified = false;
+ this.sasValidationAttempts = 0;
this.processedMessageIds = /* @__PURE__ */ new Set();
this.localVerificationConfirmed = false;
this.remoteVerificationConfirmed = false;
@@ -4650,6 +4760,10 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this.peerPublicKey = null;
this.rateLimiterId = null;
this.intentionalDisconnect = false;
+ this._sessionAlive = true;
+ this._fileTransferInitRetryTimers = /* @__PURE__ */ new Set();
+ this._peerDisconnectCleanupTimer = null;
+ this._logCleanupInterval = null;
this.lastCleanupTime = Date.now();
this._resetNotificationFlags();
this.verificationInitiationSent = false;
@@ -5177,6 +5291,15 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this._secureLog("info", "\u{1F527} Unified maintenance scheduler initialized (5-minute cycle)");
this._activeTimers = /* @__PURE__ */ new Set([this._maintenanceScheduler]);
}
+ _trackActiveTimer(timer) {
+ if (!timer) return timer;
+ if (!this._activeTimers) this._activeTimers = /* @__PURE__ */ new Set();
+ this._activeTimers.add(timer);
+ return timer;
+ }
+ _untrackActiveTimer(timer) {
+ if (timer && this._activeTimers) this._activeTimers.delete(timer);
+ }
/**
* Execute all maintenance tasks in a single cycle
*/
@@ -7126,9 +7249,9 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
return;
}
this._startSecurityMonitoring();
- setInterval(() => {
+ this._logCleanupInterval = this._trackActiveTimer(setInterval(() => {
this._cleanupLogs();
- }, 3e5);
+ }, 3e5));
this._secureLog("info", "\u2705 Secure WebRTC Manager initialization completed");
this._secureLog("info", "\u{1F512} Global exposure protection: Monitoring only, no automatic removal");
}
@@ -7382,11 +7505,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
);
const dv = new DataView(bits);
const n = (dv.getUint32(0) ^ dv.getUint32(4)) >>> 0;
- let sasValue;
- do {
- sasValue = crypto.getRandomValues(new Uint32Array(1))[0];
- } while (sasValue >= 4294967296 - 4294967296 % 1e7);
- const sasCode = String(sasValue % 1e7).padStart(7, "0");
+ const sasCode = String(n % 1e7).padStart(7, "0");
this._secureLog("info", "SAS code computed successfully", {
localFP: localFP.substring(0, 16) + "...",
remoteFP: remoteFP.substring(0, 16) + "...",
@@ -8016,6 +8135,9 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
}
initializeFileTransfer() {
try {
+ if (this._sessionAlive === false) {
+ return;
+ }
this._secureLog("info", "\u{1F527} Initializing Enhanced Secure File Transfer system...");
if (this.fileTransferSystem) {
this._secureLog("info", "\u2705 File transfer system already initialized");
@@ -8035,7 +8157,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
}
if (!this.isVerified) {
this._secureLog("warn", "\u26A0\uFE0F Connection not verified yet, deferring file transfer initialization");
- setTimeout(() => this.initializeFileTransfer(), 500);
+ this._scheduleFileTransferInitRetry(500);
return;
}
if (this.fileTransferSystem) {
@@ -8045,7 +8167,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
}
if (!this.encryptionKey || !this.macKey) {
this._secureLog("warn", "\u26A0\uFE0F Encryption keys not ready, deferring file transfer initialization");
- setTimeout(() => this.initializeFileTransfer(), 1e3);
+ this._scheduleFileTransferInitRetry(1e3);
return;
}
const safeOnComplete = (summary) => {
@@ -8063,7 +8185,8 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this.onFileProgress || null,
safeOnComplete,
this.onFileError || null,
- this.onFileReceived || null
+ this.onFileReceived || null,
+ this.onIncomingFileRequest || null
);
this._fileTransferActive = true;
this._secureLog("info", "\u2705 Enhanced Secure File Transfer system initialized successfully");
@@ -8075,6 +8198,18 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this._fileTransferActive = false;
}
}
+ _scheduleFileTransferInitRetry(delay) {
+ if (this._sessionAlive === false) return null;
+ if (!this._fileTransferInitRetryTimers) this._fileTransferInitRetryTimers = /* @__PURE__ */ new Set();
+ const timer = this._trackActiveTimer(setTimeout(() => {
+ this._fileTransferInitRetryTimers.delete(timer);
+ this._untrackActiveTimer(timer);
+ if (this._sessionAlive === false) return;
+ this.initializeFileTransfer();
+ }, delay));
+ this._fileTransferInitRetryTimers.add(timer);
+ return timer;
+ }
// ============================================
// ENHANCED SECURITY INITIALIZATION
// ============================================
@@ -8206,6 +8341,12 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
currentFeatures: this.securityFeatures
});
}
+ _sanitizeIncomingChatMessage(message) {
+ if (typeof message !== "string") {
+ return message;
+ }
+ return window.EnhancedSecureCryptoUtils.sanitizeMessage(message);
+ }
deliverMessageToUI(message, type = "received") {
try {
this._secureLog("debug", "\u{1F4E4} deliverMessageToUI called", {
@@ -8270,9 +8411,10 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
} catch (parseError) {
}
}
+ const uiMessage = type === "received" ? this._sanitizeIncomingChatMessage(message) : message;
if (this.onMessage) {
- this._secureLog("debug", "\u{1F4E4} Calling this.onMessage callback", { message, type });
- this.onMessage(message, type);
+ this._secureLog("debug", "\u{1F4E4} Calling this.onMessage callback", { message: uiMessage, type });
+ this.onMessage(uiMessage, type);
} else {
this._secureLog("warn", "\u26A0\uFE0F this.onMessage callback is null or undefined");
}
@@ -9896,32 +10038,6 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
throw error;
}
}
- disconnect() {
- try {
- if (this.fileTransferSystem) {
- this.fileTransferSystem.cleanup();
- this.fileTransferSystem = null;
- }
- this.stopFakeTrafficGeneration();
- for (const [channelName, timer] of this.decoyTimers.entries()) {
- clearTimeout(timer);
- }
- this.decoyTimers.clear();
- for (const [channelName, channel] of this.decoyChannels.entries()) {
- if (channel.readyState === "open") {
- channel.close();
- }
- }
- this.decoyChannels.clear();
- this.packetBuffer.clear();
- this.chunkQueue = [];
- this._wipeEphemeralKeys();
- this._hardWipeOldKeys();
- this._clearVerificationStates();
- } catch (error) {
- this._secureLog("error", "\u274C Error during enhanced disconnect:", { errorType: error?.constructor?.name || "Unknown" });
- }
- }
/**
* Clear all verification states and data
* Called when verification is rejected or connection is terminated
@@ -9934,6 +10050,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this.isVerified = false;
this.verificationCode = null;
this.pendingSASCode = null;
+ this.sasValidationAttempts = 0;
this.keyFingerprint = null;
this.expectedDTLSFingerprint = null;
this.connectionId = null;
@@ -10084,18 +10201,33 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
});
return null;
}
- createPeerConnection() {
+ _hasTurnServer() {
+ return (this._config.webrtc.iceServers || []).some((server) => {
+ const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
+ return urls.some((url) => typeof url === "string" && url.toLowerCase().startsWith("turn:"));
+ });
+ }
+ _buildPeerConnectionConfig() {
const config = {
- iceServers: [
- { urls: "stun:stun.l.google.com:19302" },
- { urls: "stun:stun1.l.google.com:19302" },
- { urls: "stun:stun2.l.google.com:19302" },
- { urls: "stun:stun3.l.google.com:19302" },
- { urls: "stun:stun4.l.google.com:19302" }
- ],
+ iceServers: this._config.webrtc.iceServers,
iceCandidatePoolSize: 10,
bundlePolicy: "balanced"
};
+ if (this._config.webrtc.relayOnly) {
+ config.iceTransportPolicy = "relay";
+ }
+ return config;
+ }
+ _warnIfTurnMissing() {
+ if (this._hasTurnServer() || this._ipLeakWarningShown) return;
+ this._ipLeakWarningShown = true;
+ const message = this._config.webrtc.relayOnly ? "Privacy mode is enabled, but no TURN server is configured. Relay-only mode cannot connect until TURN is configured; STUN alone does not hide IP addresses." : "Privacy warning: no TURN server is configured. Direct WebRTC connections may expose IP addresses; STUN alone does not provide IP protection.";
+ this.deliverMessageToUI(message, "system");
+ }
+ createPeerConnection() {
+ this._sessionAlive = true;
+ const config = this._buildPeerConnectionConfig();
+ this._warnIfTurnMissing();
this.peerConnection = new RTCPeerConnection(config);
this.peerConnection.onconnectionstatechange = () => {
const state = this.peerConnection.connectionState;
@@ -11765,7 +11897,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
// type
s: this.peerConnection.localDescription.sdp,
// sdp
- v: "4.0",
+ v: _EnhancedSecureWebRTCManager.PROTOCOL_VERSION,
// version
ts: currentTimestamp,
// timestamp
@@ -11960,21 +12092,19 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
throw new Error("Offer data is too old \u2013 possible replay attack");
}
const protocolVersion = version;
- if (protocolVersion !== "4.0") {
+ if (protocolVersion !== _EnhancedSecureWebRTCManager.PROTOCOL_VERSION) {
this._secureLog("warn", "Protocol version mismatch detected", {
operationId,
- expectedVersion: "4.0",
+ expectedVersion: _EnhancedSecureWebRTCManager.PROTOCOL_VERSION,
receivedVersion: protocolVersion
});
- if (protocolVersion !== "3.0") {
- throw new Error(`Unsupported protocol version: ${protocolVersion}`);
- }
+ throw new Error(`Version mismatch: expected protocol ${_EnhancedSecureWebRTCManager.PROTOCOL_VERSION}, received ${protocolVersion}`);
}
this.sessionSalt = offerData.sl || offerData.salt;
if (!Array.isArray(this.sessionSalt)) {
throw new Error("Invalid session salt format - must be array");
}
- const expectedSaltLength = protocolVersion === "4.0" ? 64 : 32;
+ const expectedSaltLength = 64;
if (this.sessionSalt.length !== expectedSaltLength) {
throw new Error(`Invalid session salt length: expected ${expectedSaltLength}, got ${this.sessionSalt.length}`);
}
@@ -12088,7 +12218,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
});
throw new Error("Invalid key types after derivation");
}
- this.verificationCode = offerData.verificationCode;
+ this.verificationCode = offerData.vc || offerData.verificationCode || null;
this._secureLog("info", "Encryption keys derived and set successfully", {
operationId,
hasEncryptionKey: !!this.encryptionKey,
@@ -12212,6 +12342,19 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
} catch (error) {
this._secureLog("error", "Failed to extract DTLS fingerprint from answer", { error: error.message });
}
+ try {
+ const remoteFP = this._extractDTLSFingerprintFromSDP(offerData.s || offerData.sdp);
+ const localFP = this.expectedDTLSFingerprint;
+ const keyBytes = this._decodeKeyFingerprint(this.keyFingerprint);
+ this.verificationCode = await this._computeSAS(keyBytes, localFP, remoteFP);
+ this.onStatusChange?.("verifying");
+ this.onVerificationRequired(this.verificationCode);
+ } catch (sasError) {
+ this._secureLog("error", "SAS computation failed in createSecureAnswer (Answer side)", {
+ errorType: sasError?.constructor?.name || "Unknown"
+ });
+ throw new Error(`SAS computation failed: ${sasError.message}`);
+ }
await this.waitForIceGathering();
this._secureLog("debug", "ICE gathering completed for answer", {
operationId,
@@ -12268,7 +12411,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
// type
s: this.peerConnection.localDescription.sdp,
// sdp
- v: "4.0",
+ v: _EnhancedSecureWebRTCManager.PROTOCOL_VERSION,
// version
ts: currentTimestamp,
// timestamp
@@ -12509,6 +12652,10 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
});
throw new Error("CRITICAL SECURITY FAILURE: Invalid answer format - hard abort required");
}
+ const answerVersion = answerData.v || answerData.version;
+ if (answerVersion !== _EnhancedSecureWebRTCManager.PROTOCOL_VERSION) {
+ throw new Error(`Version mismatch: expected protocol ${_EnhancedSecureWebRTCManager.PROTOCOL_VERSION}, received ${answerVersion || "unknown"}`);
+ }
const ecdhKey = answerData.ecdhPublicKey || answerData.e;
const ecdsaKey = answerData.ecdsaPublicKey || answerData.d;
if (!ecdhKey || typeof ecdhKey !== "object" || Array.isArray(ecdhKey)) {
@@ -12759,14 +12906,45 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
this.deliverMessageToUI("Waiting for verification code from peer...", "system");
}
}
- confirmVerification() {
+ /**
+ * Normalizes and validates the user-entered SAS code.
+ * Users may enter the same shared SAS with spaces or hyphens.
+ * @param {string} input
+ * @returns {boolean}
+ */
+ _validateSASCode(input) {
+ if (!input || typeof input !== "string" || !this.verificationCode || typeof this.verificationCode !== "string") {
+ return false;
+ }
+ const normalizedInput = input.replace(/[-\s]/g, "").toUpperCase();
+ const normalizedActual = this.verificationCode.replace(/[-\s]/g, "").toUpperCase();
+ if (normalizedInput.length !== normalizedActual.length) {
+ return false;
+ }
+ return window.EnhancedSecureCryptoUtils.constantTimeCompare(normalizedInput, normalizedActual);
+ }
+ confirmVerification(userCode) {
try {
+ if (!this._validateSASCode(userCode)) {
+ this.sasValidationAttempts = (this.sasValidationAttempts || 0) + 1;
+ this._secureLog("warn", "SAS validation failed: user entered incorrect code", {
+ attempts: this.sasValidationAttempts,
+ maxAttempts: _EnhancedSecureWebRTCManager.MAX_SAS_ATTEMPTS
+ });
+ if (this.sasValidationAttempts >= _EnhancedSecureWebRTCManager.MAX_SAS_ATTEMPTS) {
+ this.deliverMessageToUI("Verification failed 3 times. Session reset for safety.", "system");
+ this.disconnect();
+ throw new Error("SAS_MAX_ATTEMPTS");
+ }
+ throw new Error("SAS_MISMATCH");
+ }
this.localVerificationConfirmed = true;
+ this.sasValidationAttempts = 0;
const confirmationPayload = {
type: "verification_confirmed",
data: {
timestamp: Date.now(),
- verificationMethod: "SAS",
+ verificationMethod: "MANUAL_SAS_ENTRY",
securityLevel: "MITM_PROTECTION_REQUIRED"
}
};
@@ -12779,11 +12957,16 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
});
}
this._checkBothVerificationsConfirmed();
- this.deliverMessageToUI("You confirmed the verification code. Waiting for peer confirmation...", "system");
+ this.deliverMessageToUI("Code verified locally. Waiting for peer confirmation...", "system");
this.processMessageQueue();
} catch (error) {
- this._secureLog("error", "SAS verification failed:", { errorType: error?.constructor?.name || "Unknown" });
- this.deliverMessageToUI("SAS verification failed", "system");
+ if (error.message === "SAS_MISMATCH") {
+ this.deliverMessageToUI("Verification failed: the code you entered is incorrect.", "system");
+ } else if (error.message !== "SAS_MAX_ATTEMPTS") {
+ this._secureLog("error", "SAS verification failed:", { errorType: error?.constructor?.name || "Unknown" });
+ this.deliverMessageToUI("SAS verification failed", "system");
+ }
+ throw error;
}
}
_checkBothVerificationsConfirmed() {
@@ -12885,6 +13068,16 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
}
}
handleSASCode(data) {
+ if (!data?.code || typeof data.code !== "string") {
+ this._secureLog("warn", "Invalid SAS announcement received from peer");
+ return;
+ }
+ if (this.verificationCode && !this._validateSASCode(data.code)) {
+ this._secureLog("error", "Peer-announced SAS does not match locally computed SAS");
+ this.deliverMessageToUI("Version or SAS mismatch detected. Connection aborted for safety.", "system");
+ this.disconnect();
+ return;
+ }
this.verificationCode = data.code;
this.onStatusChange?.("verifying");
this.onVerificationRequired(this.verificationCode);
@@ -12927,8 +13120,8 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
});
throw new Error("CRITICAL SECURITY FAILURE: Offer data must be a non-null object");
}
- const isV4CompactFormat = offerData.v === "4.0" && offerData.e && offerData.d;
- const isV4Format = offerData.version === "4.0" && offerData.ecdhPublicKey && offerData.ecdsaPublicKey;
+ const isV4CompactFormat = offerData.v === _EnhancedSecureWebRTCManager.PROTOCOL_VERSION && offerData.e && offerData.d;
+ const isV4Format = offerData.version === _EnhancedSecureWebRTCManager.PROTOCOL_VERSION && offerData.ecdhPublicKey && offerData.ecdsaPublicKey;
const isValidType = isV4CompactFormat ? ["offer"].includes(offerData.t) : ["enhanced_secure_offer", "secure_offer"].includes(offerData.type);
if (!isValidType) {
throw new Error("Invalid offer type");
@@ -12946,7 +13139,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
];
for (const field of compactRequiredFields) {
if (!offerData[field]) {
- throw new Error(`Missing required v4.0 compact field: ${field}`);
+ throw new Error(`Missing required v4.1 compact field: ${field}`);
}
}
if (!offerData.e || typeof offerData.e !== "object" || Array.isArray(offerData.e)) {
@@ -12956,7 +13149,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
throw new Error("CRITICAL SECURITY FAILURE: Invalid ECDSA public key structure");
}
if (!Array.isArray(offerData.sl) || offerData.sl.length !== 64) {
- throw new Error("Salt must be exactly 64 bytes for v4.0");
+ throw new Error("Salt must be exactly 64 bytes for v4.1");
}
if (typeof offerData.vc !== "string" || offerData.vc.length < 6) {
throw new Error("Invalid verification code format");
@@ -12968,7 +13161,7 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
if (offerAge > 36e5) {
throw new Error("Offer is too old (older than 1 hour)");
}
- this._secureLog("info", "v4.0 compact offer validation passed", {
+ this._secureLog("info", "v4.1 compact offer validation passed", {
version: offerData.v,
hasECDH: !!offerData.e,
hasECDSA: !!offerData.d,
@@ -12990,11 +13183,11 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
];
for (const field of v4RequiredFields) {
if (!offerData[field]) {
- throw new Error(`Missing v4.0 field: ${field}`);
+ throw new Error(`Missing v4.1 field: ${field}`);
}
}
if (!Array.isArray(offerData.salt) || offerData.salt.length !== 64) {
- throw new Error("Salt must be exactly 64 bytes for v4.0");
+ throw new Error("Salt must be exactly 64 bytes for v4.1");
}
const offerAge = Date.now() - offerData.timestamp;
if (offerAge > 36e5) {
@@ -13033,28 +13226,14 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
if (typeof offerData.verificationCode !== "string" || offerData.verificationCode.length < 6) {
throw new Error("Invalid SAS verification code format - MITM protection required");
}
- this._secureLog("info", "v4.0 offer validation passed", {
+ this._secureLog("info", "v4.1 offer validation passed", {
version: offerData.version,
hasSecurityLevel: !!offerData.securityLevel?.level,
offerAge: Math.round(offerAge / 1e3) + "s"
});
} else {
- const v3RequiredFields = ["publicKey", "salt", "verificationCode"];
- for (const field of v3RequiredFields) {
- if (!offerData[field]) {
- throw new Error(`Missing v3.0 field: ${field}`);
- }
- }
- if (!Array.isArray(offerData.salt) || offerData.salt.length !== 32) {
- throw new Error("Salt must be exactly 32 bytes for v3.0");
- }
- if (!Array.isArray(offerData.publicKey)) {
- throw new Error("Invalid public key format for v3.0");
- }
- window.EnhancedSecureCryptoUtils.secureLog.log("info", "v3.0 offer validation passed (backward compatibility)", {
- version: "v3.0",
- legacy: true
- });
+ const receivedVersion = offerData.v || offerData.version || "unknown";
+ throw new Error(`Version mismatch: expected protocol ${_EnhancedSecureWebRTCManager.PROTOCOL_VERSION}, received ${receivedVersion}`);
}
const sdp = isV4CompactFormat ? offerData.s : offerData.sdp;
if (typeof sdp !== "string" || !sdp.includes("v=0")) {
@@ -13185,10 +13364,17 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
}
if (this._activeTimers) {
this._activeTimers.forEach((timer) => {
- if (timer) clearInterval(timer);
+ if (timer) {
+ clearInterval(timer);
+ clearTimeout(timer);
+ }
});
this._activeTimers.clear();
}
+ if (this._fileTransferInitRetryTimers) {
+ this._fileTransferInitRetryTimers.clear();
+ }
+ this._logCleanupInterval = null;
this._secureLog("info", "All timers stopped successfully");
}
waitForIceGathering() {
@@ -13237,24 +13423,6 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
verificationCode: this.verificationCode
};
}
- disconnect() {
- this._stopAllTimers();
- if (this.fileTransferSystem) {
- this.fileTransferSystem.cleanup();
- }
- this.intentionalDisconnect = true;
- window.EnhancedSecureCryptoUtils.secureLog.log("info", "Starting intentional disconnect");
- this.sendDisconnectNotification();
- setTimeout(() => {
- this.sendDisconnectNotification();
- }, 100);
- document.dispatchEvent(new CustomEvent("peer-disconnect", {
- detail: {
- reason: "user_disconnect",
- timestamp: Date.now()
- }
- }));
- }
handleUnexpectedDisconnect() {
this.sendDisconnectNotification();
this.isVerified = false;
@@ -13329,9 +13497,15 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
timestamp: Date.now()
}
}));
- setTimeout(() => {
- this.disconnect();
- }, 2e3);
+ if (!this._peerDisconnectCleanupTimer) {
+ this._peerDisconnectCleanupTimer = this._trackActiveTimer(setTimeout(() => {
+ const timer = this._peerDisconnectCleanupTimer;
+ this._peerDisconnectCleanupTimer = null;
+ this._untrackActiveTimer(timer);
+ if (this._sessionAlive === false) return;
+ this.disconnect();
+ }, 2e3));
+ }
window.EnhancedSecureCryptoUtils.secureLog.log("info", "Peer disconnect notification processed", {
reason
});
@@ -13340,60 +13514,110 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
* Secure disconnect with complete memory cleanup
*/
disconnect() {
- this.stopHeartbeat();
- this.isVerified = false;
- this.processedMessageIds.clear();
- this.messageCounter = 0;
- this._secureCleanupCryptographicMaterials();
- this.keyVersions.clear();
- this.oldKeys.clear();
- this.currentKeyVersion = 0;
- this.lastKeyRotation = Date.now();
- this.sequenceNumber = 0;
- this.expectedSequenceNumber = 0;
- this.replayWindow.clear();
- this.securityFeatures = {
- hasEncryption: true,
- hasECDH: true,
- hasECDSA: true,
- hasMutualAuth: true,
- hasMetadataProtection: true,
- hasEnhancedReplayProtection: true,
- hasNonExtractableKeys: true,
- hasRateLimiting: true,
- hasEnhancedValidation: true,
- hasPFS: true
- };
- if (this.dataChannel) {
- this.dataChannel.close();
- this.dataChannel = null;
- }
- if (this.peerConnection) {
- this.peerConnection.close();
- this.peerConnection = null;
- }
- if (this.messageQueue && this.messageQueue.length > 0) {
- this.messageQueue.forEach((message, index) => {
- this._secureWipeMemory(message, `messageQueue[${index}]`);
+ try {
+ this._sessionAlive = false;
+ this.intentionalDisconnect = true;
+ window.EnhancedSecureCryptoUtils.secureLog.log("info", "Starting intentional disconnect");
+ this.sendDisconnectNotification();
+ setTimeout(() => {
+ this.sendDisconnectNotification();
+ }, 100);
+ this._stopAllTimers();
+ this._peerDisconnectCleanupTimer = null;
+ this.stopHeartbeat();
+ this.stopFakeTrafficGeneration();
+ for (const timer of this.decoyTimers.entries()) {
+ clearTimeout(timer[1]);
+ }
+ this.decoyTimers.clear();
+ if (this.fileTransferSystem) {
+ this.fileTransferSystem.cleanup();
+ this.fileTransferSystem = null;
+ }
+ for (const channel of this.decoyChannels.values()) {
+ if (channel.readyState === "open") channel.close();
+ }
+ this.decoyChannels.clear();
+ if (this.heartbeatChannel) {
+ this.heartbeatChannel.close();
+ this.heartbeatChannel = null;
+ }
+ this.isVerified = false;
+ this.processedMessageIds.clear();
+ this.messageCounter = 0;
+ this.packetBuffer.clear();
+ this.chunkQueue = [];
+ this._wipeEphemeralKeys();
+ this._hardWipeOldKeys();
+ this._secureCleanupCryptographicMaterials();
+ this.keyVersions.clear();
+ this.oldKeys.clear();
+ this.currentKeyVersion = 0;
+ this.lastKeyRotation = Date.now();
+ this.sequenceNumber = 0;
+ this.expectedSequenceNumber = 0;
+ this.replayWindow.clear();
+ this._clearVerificationStates();
+ this.securityFeatures = {
+ hasEncryption: true,
+ hasECDH: true,
+ hasECDSA: true,
+ hasMutualAuth: true,
+ hasMetadataProtection: true,
+ hasEnhancedReplayProtection: true,
+ hasNonExtractableKeys: true,
+ hasRateLimiting: true,
+ hasEnhancedValidation: true,
+ hasPFS: true
+ };
+ if (this.dataChannel) {
+ this.dataChannel.close();
+ this.dataChannel.onopen = null;
+ this.dataChannel.onclose = null;
+ this.dataChannel.onmessage = null;
+ this.dataChannel.onerror = null;
+ this.dataChannel = null;
+ }
+ if (this.peerConnection) {
+ this.peerConnection.close();
+ this.peerConnection.onconnectionstatechange = null;
+ this.peerConnection.ondatachannel = null;
+ this.peerConnection = null;
+ }
+ if (this.messageQueue && this.messageQueue.length > 0) {
+ this.messageQueue.forEach((message, index) => {
+ this._secureWipeMemory(message, `messageQueue[${index}]`);
+ });
+ this.messageQueue = [];
+ }
+ this._forceGarbageCollection().catch((error) => {
+ this._secureLog("error", "Cleanup failed during disconnect", {
+ errorType: error?.constructor?.name || "Unknown"
+ });
});
- this.messageQueue = [];
- }
- this._forceGarbageCollection().catch((error) => {
- this._secureLog("error", "Cleanup failed during disconnect", {
+ document.dispatchEvent(new CustomEvent("peer-disconnect", {
+ detail: {
+ reason: "user_disconnect",
+ timestamp: Date.now()
+ }
+ }));
+ document.dispatchEvent(new CustomEvent("connection-cleaned", {
+ detail: {
+ timestamp: Date.now(),
+ reason: "user_cleanup"
+ }
+ }));
+ this.onStatusChange("disconnected");
+ this.onKeyExchange("");
+ this.onVerificationRequired("");
+ this._secureLog("info", "Connection securely cleaned up with complete memory wipe");
+ } catch (error) {
+ this._secureLog("error", "\u274C Error during enhanced disconnect:", {
errorType: error?.constructor?.name || "Unknown"
});
- });
- document.dispatchEvent(new CustomEvent("connection-cleaned", {
- detail: {
- timestamp: Date.now(),
- reason: this.intentionalDisconnect ? "user_cleanup" : "automatic_cleanup"
- }
- }));
- this.onStatusChange("disconnected");
- this.onKeyExchange("");
- this.onVerificationRequired("");
- this._secureLog("info", "Connection securely cleaned up with complete memory wipe");
- this.intentionalDisconnect = false;
+ } finally {
+ this.intentionalDisconnect = false;
+ }
}
// Public method to send files
async sendFile(file) {
@@ -13502,14 +13726,30 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
}
}
// Set file transfer callbacks
- setFileTransferCallbacks(onProgress, onReceived, onError) {
+ setFileTransferCallbacks(onProgress, onReceived, onError, onIncomingRequest = null) {
this.onFileProgress = onProgress;
this.onFileReceived = onReceived;
this.onFileError = onError;
+ this.onIncomingFileRequest = onIncomingRequest;
if (this.fileTransferSystem) {
- this.initializeFileTransfer();
+ this.fileTransferSystem.onProgress = onProgress;
+ this.fileTransferSystem.onFileReceived = onReceived;
+ this.fileTransferSystem.onError = onError;
+ this.fileTransferSystem.onIncomingFileRequest = onIncomingRequest;
}
}
+ getPendingIncomingFiles() {
+ if (!this.fileTransferSystem) return [];
+ return this.fileTransferSystem.getPendingIncomingTransfers();
+ }
+ async acceptIncomingFile(fileId) {
+ if (!this.fileTransferSystem) return false;
+ return this.fileTransferSystem.acceptIncomingFile(fileId);
+ }
+ async rejectIncomingFile(fileId) {
+ if (!this.fileTransferSystem) return false;
+ return this.fileTransferSystem.rejectIncomingFile(fileId);
+ }
// ============================================
// SESSION ACTIVATION HANDLING
// ============================================
@@ -14258,17 +14498,9 @@ var SecureIndexedDBWrapper = class {
iv: Array.from(new Uint8Array(iv)),
algorithm,
usages,
- type,
- timestamp: Date.now()
- };
- const metadataRecord = {
- keyId,
- ...metadata,
- created: Date.now(),
- lastAccessed: Date.now(),
- extractable: true,
- persistent: true
+ type
};
+ const metadataRecord = { keyId, ...metadata };
return new Promise((resolve, reject) => {
const keysRequest = transaction.objectStore(this.KEYS_STORE).put(keyRecord);
const metadataRequest = transaction.objectStore(this.METADATA_STORE).put(metadataRecord);
@@ -14323,6 +14555,26 @@ var SecureIndexedDBWrapper = class {
getRequest.onerror = () => reject(new Error(`Failed to get metadata: ${getRequest.error}`));
});
}
+ async getKeyMetadataRecord(keyId) {
+ if (!this.db) throw new Error("Database not initialized");
+ const transaction = this.db.transaction([this.METADATA_STORE], "readonly");
+ const store = transaction.objectStore(this.METADATA_STORE);
+ return new Promise((resolve, reject) => {
+ const request = store.get(keyId);
+ request.onsuccess = () => resolve(request.result || null);
+ request.onerror = () => reject(new Error(`Failed to get metadata: ${request.error}`));
+ });
+ }
+ async putKeyMetadataRecord(record) {
+ if (!this.db) throw new Error("Database not initialized");
+ const transaction = this.db.transaction([this.METADATA_STORE], "readwrite");
+ const store = transaction.objectStore(this.METADATA_STORE);
+ return new Promise((resolve, reject) => {
+ const request = store.put(record);
+ request.onsuccess = () => resolve();
+ request.onerror = () => reject(new Error(`Failed to store metadata: ${request.error}`));
+ });
+ }
/**
* Delete key and its metadata
*/
@@ -14364,8 +14616,7 @@ var SecureIndexedDBWrapper = class {
const store = transaction.objectStore(this.SALT_STORE);
const saltRecord = {
id: "master_salt",
- salt: Array.from(new Uint8Array(salt)),
- created: Date.now()
+ salt: Array.from(new Uint8Array(salt))
};
return new Promise((resolve, reject) => {
const request = store.put(saltRecord);
@@ -14438,6 +14689,11 @@ var SecurePersistentKeyStorage = class {
this._dbInitialized = true;
}
}
+ async _ensureMasterKeyUnlocked() {
+ if (typeof this._masterKeyManager.isUnlocked === "function" && !this._masterKeyManager.isUnlocked()) {
+ await this._masterKeyManager.unlock();
+ }
+ }
/**
* Store extractable key with encryption
*/
@@ -14452,6 +14708,13 @@ var SecurePersistentKeyStorage = class {
await this._ensureDBInitialized();
const jwkData = await crypto.subtle.exportKey("jwk", cryptoKey);
const { encryptedData, iv } = await this._encryptKeyData(jwkData);
+ const encryptedMetadata = await this._encryptMetadata({
+ ...metadata,
+ created: Date.now(),
+ lastAccessed: Date.now(),
+ extractable: true,
+ persistent: true
+ });
await this._indexedDB.storeEncryptedKey(
keyId,
encryptedData,
@@ -14459,7 +14722,7 @@ var SecurePersistentKeyStorage = class {
cryptoKey.algorithm,
cryptoKey.usages,
cryptoKey.type,
- metadata
+ encryptedMetadata
);
const nonExtractableKey = await this._importAsNonExtractable(jwkData, cryptoKey.algorithm, cryptoKey.usages);
this._keyReferences.set(keyId, nonExtractableKey);
@@ -14484,7 +14747,7 @@ var SecurePersistentKeyStorage = class {
const jwkData = await this._decryptKeyData(keyRecord.encryptedData, keyRecord.iv);
const restoredKey = await this._importAsNonExtractable(jwkData, keyRecord.algorithm, keyRecord.usages);
this._keyReferences.set(keyId, restoredKey);
- await this._indexedDB.updateKeyMetadata(keyId, { lastAccessed: Date.now() });
+ await this._updateEncryptedMetadata(keyId, { lastAccessed: Date.now() });
return restoredKey;
} catch (error) {
throw new Error(`Failed to retrieve key: ${error.message}`);
@@ -14509,7 +14772,13 @@ var SecurePersistentKeyStorage = class {
async listStoredKeys() {
try {
await this._ensureDBInitialized();
- return await this._indexedDB.listKeys();
+ const records = await this._indexedDB.listKeys();
+ const results = [];
+ for (const record of records) {
+ const metadata = await this._readMetadataWithMigration(record);
+ if (metadata) results.push({ keyId: record.keyId, ...metadata });
+ }
+ return results;
} catch (error) {
throw new Error(`Failed to list keys: ${error.message}`);
}
@@ -14545,6 +14814,51 @@ var SecurePersistentKeyStorage = class {
const jsonString = new TextDecoder().decode(decryptedData);
return JSON.parse(jsonString);
}
+ async _encryptMetadata(metadata) {
+ const data = new TextEncoder().encode(JSON.stringify(metadata));
+ await this._ensureMasterKeyUnlocked();
+ const { encryptedData, iv } = await this._masterKeyManager.encryptBytes(data);
+ return {
+ metadataVersion: 1,
+ encryptedMetadata: Array.from(encryptedData),
+ metadataIv: Array.from(iv)
+ };
+ }
+ async _decryptMetadataRecord(record) {
+ if (!record?.encryptedMetadata || !record?.metadataIv) {
+ throw new Error("Encrypted metadata missing");
+ }
+ await this._ensureMasterKeyUnlocked();
+ const decrypted = await this._masterKeyManager.decryptBytes(
+ new Uint8Array(record.encryptedMetadata),
+ new Uint8Array(record.metadataIv)
+ );
+ return JSON.parse(new TextDecoder().decode(decrypted));
+ }
+ async _readMetadataWithMigration(record) {
+ if (!record) return null;
+ if (record.encryptedMetadata) {
+ try {
+ return await this._decryptMetadataRecord(record);
+ } catch (error) {
+ return null;
+ }
+ }
+ const { keyId, ...legacyMetadata } = record;
+ const encryptedRecord = { keyId, ...await this._encryptMetadata(legacyMetadata) };
+ await this._indexedDB.putKeyMetadataRecord(encryptedRecord);
+ return legacyMetadata;
+ }
+ async _updateEncryptedMetadata(keyId, updates) {
+ const record = await this._indexedDB.getKeyMetadataRecord(keyId);
+ if (!record) throw new Error(`Key metadata not found: ${keyId}`);
+ const current = await this._readMetadataWithMigration(record);
+ if (!current) throw new Error(`Key metadata corrupted: ${keyId}`);
+ await this._indexedDB.putKeyMetadataRecord({
+ keyId,
+ ...await this._encryptMetadata({ ...current, ...updates })
+ });
+ }
/**
* Import JWK as non-extractable key
*/
@@ -15314,7 +15628,7 @@ Right-click or Ctrl+click to disconnect`,
React.createElement("p", {
key: "subtitle",
className: "text-xs sm:text-sm text-muted hidden sm:block"
- }, "End-to-end freedom v4.7.56")
+ }, "End-to-end freedom v4.8.5")
])
]),
// Status and Controls - Responsive
@@ -16517,6 +16831,7 @@ var FileTransferComponent = ({ webrtcManager, isConnected }) => {
const [dragOver, setDragOver] = React.useState(false);
const [transfers, setTransfers] = React.useState({ sending: [], receiving: [] });
const [readyFiles, setReadyFiles] = React.useState([]);
+ const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]);
const fileInputRef = React.useRef(null);
React.useEffect(() => {
if (!isConnected || !webrtcManager) return;
@@ -16527,6 +16842,12 @@ var FileTransferComponent = ({ webrtcManager, isConnected }) => {
const interval = setInterval(updateTransfers, 500);
return () => clearInterval(interval);
}, [isConnected, webrtcManager]);
+ React.useEffect(() => {
+ if (isConnected) return;
+ setReadyFiles([]);
+ setPendingIncomingFiles([]);
+ setTransfers({ sending: [], receiving: [] });
+ }, [isConnected]);
React.useEffect(() => {
if (!webrtcManager) return;
webrtcManager.setFileTransferCallbacks(
@@ -16556,8 +16877,18 @@ var FileTransferComponent = ({ webrtcManager, isConnected }) => {
(error) => {
const currentTransfers = webrtcManager.getFileTransfers();
setTransfers(currentTransfers);
+ },
+ // Incoming file request callback - user consent is mandatory
+ (fileRequest) => {
+ setPendingIncomingFiles((prev) => {
+ if (prev.some((file) => file.fileId === fileRequest.fileId)) return prev;
+ return [...prev, fileRequest];
+ });
}
);
+ return () => {
+ webrtcManager.setFileTransferCallbacks(null, null, null, null);
+ };
}, [webrtcManager]);
const handleFileSelect = async (files) => {
if (!isConnected || !webrtcManager) {
@@ -16654,6 +16985,18 @@ var FileTransferComponent = ({ webrtcManager, isConnected }) => {
return status;
}
};
+ const handleIncomingDecision = async (fileId, accepted) => {
+ try {
+ if (accepted) {
+ await webrtcManager.acceptIncomingFile(fileId);
+ } else {
+ await webrtcManager.rejectIncomingFile(fileId);
+ }
+ } finally {
+ setPendingIncomingFiles((prev) => prev.filter((file) => file.fileId !== fileId));
+ setTransfers(webrtcManager.getFileTransfers());
+ }
+ };
if (!isConnected) {
return React.createElement("div", {
className: "p-4 text-center text-muted"
@@ -16710,6 +17053,44 @@ var FileTransferComponent = ({ webrtcManager, isConnected }) => {
className: "hidden",
onChange: handleFileInputChange
}),
+ pendingIncomingFiles.length > 0 && React.createElement("div", {
+ key: "incoming-consent",
+ className: "mt-4 space-y-2"
+ }, pendingIncomingFiles.map((file) => React.createElement("div", {
+ key: file.fileId,
+ className: "rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3"
+ }, [
+ React.createElement("div", {
+ key: "info",
+ className: "mb-3 flex items-center justify-between gap-3"
+ }, [
+ React.createElement("div", { key: "text" }, [
+ React.createElement("div", {
+ key: "title",
+ className: "text-sm font-medium text-primary"
+ }, "Incoming file request"),
+ React.createElement("div", {
+ key: "meta",
+ className: "text-xs text-secondary"
+ }, `${file.fileName} \xB7 ${formatFileSize(file.fileSize)} \xB7 ${file.mimeType}`)
+ ])
+ ]),
+ React.createElement("div", {
+ key: "actions",
+ className: "flex gap-2"
+ }, [
+ React.createElement("button", {
+ key: "accept",
+ onClick: () => handleIncomingDecision(file.fileId, true),
+ className: "rounded-md bg-green-500/20 px-3 py-2 text-sm text-green-300 hover:bg-green-500/30"
+ }, "Accept"),
+ React.createElement("button", {
+ key: "reject",
+ onClick: () => handleIncomingDecision(file.fileId, false),
+ className: "rounded-md bg-red-500/20 px-3 py-2 text-sm text-red-300 hover:bg-red-500/30"
+ }, "Reject")
+ ])
+ ]))),
// Active Transfers
(transfers.sending.length > 0 || transfers.receiving.length > 0) && React.createElement("div", {
key: "transfers",
@@ -16835,7 +17216,7 @@ var FileTransferComponent = ({ webrtcManager, isConnected }) => {
a.click();
rf.revokeObjectURL(url);
} catch (e) {
- alert("Failed to start download: " + e.message);
+ alert(e.message || "This file is no longer available for download.");
}
}
}, [
diff --git a/dist/app-boot.js.map b/dist/app-boot.js.map
index 92e6cea..9aa65f1 100644
--- a/dist/app-boot.js.map
+++ b/dist/app-boot.js.map
@@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["../src/notifications/SecureNotificationManager.js", "../src/notifications/NotificationIntegration.js", "../src/crypto/EnhancedSecureCryptoUtils.js", "../src/transfer/EnhancedSecureFileTransfer.js", "../src/network/EnhancedSecureWebRTCManager.js", "../src/scripts/app-boot.js", "../src/components/ui/Header.jsx", "../src/components/ui/DownloadApps.jsx", "../src/components/ui/BecomePartner.jsx", "../src/components/ui/UniqueFeatureSlider.jsx", "../src/components/ui/SecurityFeatures.jsx", "../src/components/ui/Testimonials.jsx", "../src/components/ui/ComparisonTable.jsx", "../src/components/ui/Roadmap.jsx", "../src/components/ui/FileTransfer.jsx"],
- "sourcesContent": ["/**\n * Secure and Reliable Notification Manager for P2P WebRTC Chat\n * Follows best practices: OWASP, MDN, Chrome DevRel\n * \n * @version 1.0.0\n * @author SecureBit Team\n * @license MIT\n */\n\nclass SecureChatNotificationManager {\n constructor(config = {}) {\n // Safely read Notification permission (iOS Safari may not define Notification)\n this.permission = (typeof Notification !== 'undefined' && Notification && typeof Notification.permission === 'string')\n ? Notification.permission\n : 'denied';\n this.isTabActive = this.checkTabActive(); // Initialize with proper check\n this.unreadCount = 0;\n this.originalTitle = document.title;\n this.notificationQueue = [];\n this.maxQueueSize = config.maxQueueSize || 5;\n this.rateLimitMs = config.rateLimitMs || 2000; // Spam protection\n this.lastNotificationTime = 0;\n this.trustedOrigins = config.trustedOrigins || [];\n \n // Secure context flag\n this.isSecureContext = window.isSecureContext;\n \n // Cross-browser compatibility for Page Visibility API\n this.hidden = this.getHiddenProperty();\n this.visibilityChange = this.getVisibilityChangeEvent();\n \n this.initVisibilityTracking();\n this.initSecurityChecks();\n }\n\n /**\n * Initialize security checks and validation\n * @private\n */\n initSecurityChecks() {\n // Security checks are performed silently\n }\n\n /**\n * Get hidden property name for cross-browser compatibility\n * @returns {string} Hidden property name\n * @private\n */\n getHiddenProperty() {\n if (typeof document.hidden !== \"undefined\") {\n return \"hidden\";\n } else if (typeof document.msHidden !== \"undefined\") {\n return \"msHidden\";\n } else if (typeof document.webkitHidden !== \"undefined\") {\n return \"webkitHidden\";\n }\n return \"hidden\"; // fallback\n }\n\n /**\n * Get visibility change event name for cross-browser compatibility\n * @returns {string} Visibility change event name\n * @private\n */\n getVisibilityChangeEvent() {\n if (typeof document.hidden !== \"undefined\") {\n return \"visibilitychange\";\n } else if (typeof document.msHidden !== \"undefined\") {\n return \"msvisibilitychange\";\n } else if (typeof document.webkitHidden !== \"undefined\") {\n return \"webkitvisibilitychange\";\n }\n return \"visibilitychange\"; // fallback\n }\n\n /**\n * Check if tab is currently active using multiple methods\n * @returns {boolean} True if tab is active\n * @private\n */\n checkTabActive() {\n // Primary method: Page Visibility API\n if (this.hidden && typeof document[this.hidden] !== \"undefined\") {\n return !document[this.hidden];\n }\n \n // Fallback method: document.hasFocus()\n if (typeof document.hasFocus === \"function\") {\n return document.hasFocus();\n }\n \n // Ultimate fallback: assume active\n return true;\n }\n\n /**\n * Initialize page visibility tracking (Page Visibility API)\n * @private\n */\n initVisibilityTracking() {\n // Primary method: Page Visibility API with cross-browser support\n if (typeof document.addEventListener !== \"undefined\" && typeof document[this.hidden] !== \"undefined\") {\n document.addEventListener(this.visibilityChange, () => {\n this.isTabActive = this.checkTabActive();\n \n if (this.isTabActive) {\n this.resetUnreadCount();\n this.clearNotificationQueue();\n }\n });\n }\n\n // Fallback method: Window focus/blur events\n window.addEventListener('focus', () => {\n this.isTabActive = this.checkTabActive();\n if (this.isTabActive) {\n this.resetUnreadCount();\n }\n });\n\n window.addEventListener('blur', () => {\n this.isTabActive = this.checkTabActive();\n });\n\n // Page unload cleanup\n window.addEventListener('beforeunload', () => {\n this.clearNotificationQueue();\n });\n }\n\n /**\n * Request notification permission (BEST PRACTICE: Only call in response to user action)\n * Never call on page load!\n * @returns {Promise} Permission granted status\n */\n async requestPermission() {\n // Secure context check\n if (!this.isSecureContext || !('Notification' in window)) {\n return false;\n }\n\n if (this.permission === 'granted') {\n return true;\n }\n\n if (this.permission === 'denied') {\n return false;\n }\n\n try {\n this.permission = await Notification.requestPermission();\n return this.permission === 'granted';\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Update page title with unread count\n * @private\n */\n updateTitle() {\n if (this.unreadCount > 0) {\n document.title = `(${this.unreadCount}) ${this.originalTitle}`;\n } else {\n document.title = this.originalTitle;\n }\n }\n\n /**\n * XSS Protection: Sanitize input text\n * @param {string} text - Text to sanitize\n * @returns {string} Sanitized text\n * @private\n */\n sanitizeText(text) {\n if (typeof text !== 'string') {\n return '';\n }\n \n // Remove HTML tags and potentially dangerous characters\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .substring(0, 500); // Length limit\n }\n\n /**\n * Validate icon URL (XSS protection)\n * @param {string} url - URL to validate\n * @returns {string|null} Validated URL or null\n * @private\n */\n validateIconUrl(url) {\n if (!url) return null;\n \n try {\n const parsedUrl = new URL(url, window.location.origin);\n \n // Only allow HTTPS and data URLs\n if (parsedUrl.protocol === 'https:' || parsedUrl.protocol === 'data:') {\n // Check trusted origins if specified\n if (this.trustedOrigins.length > 0) {\n const isTrusted = this.trustedOrigins.some(origin => \n parsedUrl.origin === origin\n );\n return isTrusted ? parsedUrl.href : null;\n }\n return parsedUrl.href;\n }\n \n return null;\n } catch (error) {\n return null;\n }\n }\n\n /**\n * Rate limiting for spam protection\n * @returns {boolean} Rate limit check passed\n * @private\n */\n checkRateLimit() {\n const now = Date.now();\n if (now - this.lastNotificationTime < this.rateLimitMs) {\n return false;\n }\n this.lastNotificationTime = now;\n return true;\n }\n\n /**\n * Send secure notification\n * @param {string} senderName - Name of message sender\n * @param {string} message - Message content\n * @param {Object} options - Notification options\n * @returns {Notification|null} Created notification or null\n */\n notify(senderName, message, options = {}) {\n // Abort if Notifications API is not available (e.g., iOS Safari)\n if (typeof Notification === 'undefined') {\n return null;\n }\n // Update tab active state before checking\n this.isTabActive = this.checkTabActive();\n \n // Only show if tab is NOT active (user is on another tab or minimized)\n if (this.isTabActive) {\n return null;\n }\n\n // Permission check\n if (this.permission !== 'granted') {\n return null;\n }\n\n // Rate limiting\n if (!this.checkRateLimit()) {\n return null;\n }\n\n // Data sanitization (XSS Protection)\n const safeSenderName = this.sanitizeText(senderName || 'Unknown');\n const safeMessage = this.sanitizeText(message || '');\n const safeIcon = this.validateIconUrl(options.icon) || '/logo/icon-192x192.png';\n\n // Queue overflow protection\n if (this.notificationQueue.length >= this.maxQueueSize) {\n this.clearNotificationQueue();\n }\n\n try {\n \n const notification = new Notification(\n `${safeSenderName}`,\n {\n body: safeMessage.substring(0, 200), // Length limit\n icon: safeIcon,\n badge: safeIcon,\n tag: `chat-${options.senderId || 'unknown'}`, // Grouping\n requireInteraction: false, // Don't block user\n silent: options.silent || false,\n // Vibrate only for mobile and if supported\n vibrate: navigator.vibrate ? [200, 100, 200] : undefined,\n // Safe metadata\n data: {\n senderId: this.sanitizeText(options.senderId),\n timestamp: Date.now(),\n // Don't include sensitive data!\n }\n }\n );\n\n // Increment counter\n this.unreadCount++;\n this.updateTitle();\n\n // Add to queue for management\n this.notificationQueue.push(notification);\n\n // Safe click handler\n notification.onclick = (event) => {\n event.preventDefault(); // Prevent default behavior\n window.focus();\n notification.close();\n \n // Safe callback\n if (typeof options.onClick === 'function') {\n try {\n options.onClick(options.senderId);\n } catch (error) {\n console.error('[Notifications] Error in onClick handler:', error);\n }\n }\n };\n\n // Error handler\n notification.onerror = (event) => {\n console.error('[Notifications] Error showing notification:', event);\n };\n\n // Auto-close after reasonable time\n const autoCloseTimeout = Math.min(options.autoClose || 5000, 10000);\n setTimeout(() => {\n notification.close();\n this.removeFromQueue(notification);\n }, autoCloseTimeout);\n\n return notification;\n \n } catch (error) {\n console.error('[Notifications] Failed to create notification:', error);\n return null;\n }\n }\n\n /**\n * Remove notification from queue\n * @param {Notification} notification - Notification to remove\n * @private\n */\n removeFromQueue(notification) {\n const index = this.notificationQueue.indexOf(notification);\n if (index > -1) {\n this.notificationQueue.splice(index, 1);\n }\n }\n\n /**\n * Clear all notifications\n */\n clearNotificationQueue() {\n this.notificationQueue.forEach(notification => {\n try {\n notification.close();\n } catch (error) {\n // Ignore errors when closing\n }\n });\n this.notificationQueue = [];\n }\n\n /**\n * Reset unread counter\n */\n resetUnreadCount() {\n this.unreadCount = 0;\n this.updateTitle();\n }\n\n /**\n * Get current status\n * @returns {Object} Current notification status\n */\n getStatus() {\n return {\n permission: this.permission,\n isTabActive: this.isTabActive,\n unreadCount: this.unreadCount,\n isSecureContext: this.isSecureContext,\n queueSize: this.notificationQueue.length\n };\n }\n}\n\n/**\n * Secure integration with WebRTC\n */\nclass SecureP2PChat {\n constructor() {\n this.notificationManager = new SecureChatNotificationManager({\n maxQueueSize: 5,\n rateLimitMs: 2000,\n trustedOrigins: [\n window.location.origin,\n // Add other trusted origins for CDN icons\n ]\n });\n \n this.dataChannel = null;\n this.peerConnection = null;\n this.remotePeerName = 'Peer';\n this.messageHistory = [];\n this.maxHistorySize = 100;\n }\n\n /**\n * Initialize when user connects\n */\n async init() {\n // Initialize notification manager silently\n }\n\n /**\n * Method for manual permission request (called on click)\n * @returns {Promise} Permission granted status\n */\n async enableNotifications() {\n const granted = await this.notificationManager.requestPermission();\n return granted;\n }\n\n /**\n * Setup DataChannel with security checks\n * @param {RTCDataChannel} dataChannel - WebRTC data channel\n */\n setupDataChannel(dataChannel) {\n if (!dataChannel) {\n console.error('[Chat] Invalid DataChannel');\n return;\n }\n\n this.dataChannel = dataChannel;\n \n // Setup handlers\n this.dataChannel.onmessage = (event) => {\n this.handleIncomingMessage(event.data);\n };\n\n this.dataChannel.onerror = (error) => {\n // Handle error silently\n };\n }\n\n /**\n * XSS Protection: Validate incoming messages\n * @param {string|Object} data - Message data\n * @returns {Object|null} Validated message or null\n * @private\n */\n validateMessage(data) {\n try {\n const message = typeof data === 'string' ? JSON.parse(data) : data;\n \n // Check message structure\n if (!message || typeof message !== 'object') {\n throw new Error('Invalid message structure');\n }\n\n // Check required fields\n if (!message.text || typeof message.text !== 'string') {\n throw new Error('Invalid message text');\n }\n\n // Message length limit (DoS protection)\n if (message.text.length > 10000) {\n throw new Error('Message too long');\n }\n\n return {\n text: message.text,\n senderName: message.senderName || 'Unknown',\n senderId: message.senderId || 'unknown',\n timestamp: message.timestamp || Date.now(),\n senderAvatar: message.senderAvatar || null\n };\n \n } catch (error) {\n console.error('[Chat] Message validation failed:', error);\n return null;\n }\n }\n\n /**\n * Secure handling of incoming messages\n * @param {string|Object} data - Message data\n * @private\n */\n handleIncomingMessage(data) {\n const message = this.validateMessage(data);\n \n if (!message) {\n return;\n }\n\n // Save to history (with limit)\n this.messageHistory.push(message);\n if (this.messageHistory.length > this.maxHistorySize) {\n this.messageHistory.shift();\n }\n\n // Display in UI (with sanitization)\n this.displayMessage(message);\n\n // Send notification only if tab is inactive\n this.notificationManager.notify(\n message.senderName,\n message.text,\n {\n icon: message.senderAvatar,\n senderId: message.senderId,\n onClick: (senderId) => {\n this.scrollToLatestMessage();\n }\n }\n );\n\n // Optional: sound (with check)\n if (!this.notificationManager.isTabActive) {\n this.playNotificationSound();\n }\n }\n\n /**\n * XSS Protection: Safe message display\n * @param {Object} message - Message to display\n * @private\n */\n displayMessage(message) {\n const container = document.getElementById('messages');\n if (!container) {\n return;\n }\n\n const messageEl = document.createElement('div');\n messageEl.className = 'message';\n \n // Use textContent to prevent XSS\n const nameEl = document.createElement('strong');\n nameEl.textContent = message.senderName + ': ';\n \n const textEl = document.createElement('span');\n textEl.textContent = message.text;\n textEl.style.wordWrap = 'break-word';\n textEl.style.overflowWrap = 'break-word';\n textEl.style.whiteSpace = 'normal';\n \n const timeEl = document.createElement('small');\n timeEl.textContent = new Date(message.timestamp).toLocaleTimeString();\n \n messageEl.appendChild(nameEl);\n messageEl.appendChild(textEl);\n messageEl.appendChild(document.createElement('br'));\n messageEl.appendChild(timeEl);\n \n container.appendChild(messageEl);\n this.scrollToLatestMessage();\n }\n\n /**\n * Safe sound playback\n * @private\n */\n playNotificationSound() {\n try {\n // Use only local audio files\n const audio = new Audio('/assets/audio/notification.mp3');\n audio.volume = 0.3; // Moderate volume\n \n // Error handling\n audio.play().catch(error => {\n // Handle audio error silently\n });\n } catch (error) {\n // Handle audio creation error silently\n }\n }\n\n /**\n * Scroll to latest message\n * @private\n */\n scrollToLatestMessage() {\n const container = document.getElementById('messages');\n if (container) {\n container.scrollTop = container.scrollHeight;\n }\n }\n\n /**\n * Get status\n * @returns {Object} Current chat status\n */\n getStatus() {\n return {\n notifications: this.notificationManager.getStatus(),\n messageCount: this.messageHistory.length,\n connected: this.dataChannel?.readyState === 'open'\n };\n }\n}\n\n// Export for use in other modules\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = { SecureChatNotificationManager, SecureP2PChat };\n}\n\n// Global export for browser usage\nif (typeof window !== 'undefined') {\n window.SecureChatNotificationManager = SecureChatNotificationManager;\n window.SecureP2PChat = SecureP2PChat;\n}\n", "/**\n * Notification Integration Module for SecureBit WebRTC Chat\n * Integrates secure notifications with existing WebRTC architecture\n * \n * @version 1.0.0\n * @author SecureBit Team\n * @license MIT\n */\n\nimport { SecureChatNotificationManager } from './SecureNotificationManager.js';\n\nclass NotificationIntegration {\n constructor(webrtcManager) {\n this.webrtcManager = webrtcManager;\n this.notificationManager = new SecureChatNotificationManager({\n maxQueueSize: 10,\n rateLimitMs: 1000, // Reduced from 2000ms to 1000ms\n trustedOrigins: [\n window.location.origin,\n // Add other trusted origins for CDN icons\n ]\n });\n \n this.isInitialized = false;\n this.originalOnMessage = null;\n this.originalOnStatusChange = null;\n this.processedMessages = new Set(); // Track processed messages to avoid duplicates\n }\n\n /**\n * Initialize notification integration\n * @returns {Promise} Initialization success\n */\n async init() {\n try {\n if (this.isInitialized) {\n return true;\n }\n\n // Store original callbacks\n this.originalOnMessage = this.webrtcManager.onMessage;\n this.originalOnStatusChange = this.webrtcManager.onStatusChange;\n\n\n // Wrap the original onMessage callback\n this.webrtcManager.onMessage = (message, type) => {\n this.handleIncomingMessage(message, type);\n \n // Call original callback if it exists\n if (this.originalOnMessage) {\n this.originalOnMessage(message, type);\n }\n };\n\n // Wrap the original onStatusChange callback\n this.webrtcManager.onStatusChange = (status) => {\n this.handleStatusChange(status);\n \n // Call original callback if it exists\n if (this.originalOnStatusChange) {\n this.originalOnStatusChange(status);\n }\n };\n\n // Also hook into the deliverMessageToUI method if it exists\n if (this.webrtcManager.deliverMessageToUI) {\n this.originalDeliverMessageToUI = this.webrtcManager.deliverMessageToUI.bind(this.webrtcManager);\n this.webrtcManager.deliverMessageToUI = (message, type) => {\n this.handleIncomingMessage(message, type);\n this.originalDeliverMessageToUI(message, type);\n };\n }\n\n this.isInitialized = true;\n return true;\n\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Handle incoming messages and trigger notifications\n * @param {*} message - Message content\n * @param {string} type - Message type\n * @private\n */\n handleIncomingMessage(message, type) {\n try {\n // Create a unique key for this message to avoid duplicates\n const messageKey = `${type}:${typeof message === 'string' ? message : JSON.stringify(message)}`;\n \n // Skip if we've already processed this message\n if (this.processedMessages.has(messageKey)) {\n return;\n }\n \n // Mark message as processed\n this.processedMessages.add(messageKey);\n \n // Clean up old processed messages (keep only last 100)\n if (this.processedMessages.size > 100) {\n const messagesArray = Array.from(this.processedMessages);\n this.processedMessages.clear();\n messagesArray.slice(-50).forEach(msg => this.processedMessages.add(msg));\n }\n \n \n // Only process chat messages, not system messages\n if (type === 'system' || type === 'file-transfer' || type === 'heartbeat') {\n return;\n }\n\n // Extract message information\n const messageInfo = this.extractMessageInfo(message, type);\n if (!messageInfo) {\n return;\n }\n\n // Send notification\n const notificationResult = this.notificationManager.notify(\n messageInfo.senderName,\n messageInfo.text,\n {\n icon: messageInfo.senderAvatar,\n senderId: messageInfo.senderId,\n onClick: (senderId) => {\n this.focusChatWindow();\n }\n }\n );\n\n } catch (error) {\n // Handle error silently\n }\n }\n\n /**\n * Handle status changes\n * @param {string} status - Connection status\n * @private\n */\n handleStatusChange(status) {\n try {\n // Clear notifications when connection is lost\n if (status === 'disconnected' || status === 'failed') {\n this.notificationManager.clearNotificationQueue();\n this.notificationManager.resetUnreadCount();\n }\n } catch (error) {\n // Handle error silently\n }\n }\n\n /**\n * Extract message information for notifications\n * @param {*} message - Message content\n * @param {string} type - Message type\n * @returns {Object|null} Extracted message info or null\n * @private\n */\n extractMessageInfo(message, type) {\n try {\n let messageData = message;\n\n // Handle different message formats\n if (typeof message === 'string') {\n try {\n messageData = JSON.parse(message);\n } catch (e) {\n // Plain text message\n return {\n senderName: 'Peer',\n text: message,\n senderId: 'peer',\n senderAvatar: null\n };\n }\n }\n\n // Handle structured message data\n if (typeof messageData === 'object' && messageData !== null) {\n return {\n senderName: messageData.senderName || messageData.name || 'Peer',\n text: messageData.text || messageData.message || messageData.content || '',\n senderId: messageData.senderId || messageData.id || 'peer',\n senderAvatar: messageData.senderAvatar || messageData.avatar || null\n };\n }\n\n return null;\n } catch (error) {\n return null;\n }\n }\n\n /**\n * Focus chat window when notification is clicked\n * @private\n */\n focusChatWindow() {\n try {\n window.focus();\n \n // Scroll to bottom of messages if container exists\n const messagesContainer = document.getElementById('messages');\n if (messagesContainer) {\n messagesContainer.scrollTop = messagesContainer.scrollHeight;\n }\n } catch (error) {\n // Handle error silently\n }\n }\n\n /**\n * Request notification permission\n * @returns {Promise} Permission granted status\n */\n async requestPermission() {\n try {\n return await this.notificationManager.requestPermission();\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Get notification status\n * @returns {Object} Notification status\n */\n getStatus() {\n return this.notificationManager.getStatus();\n }\n\n /**\n * Clear all notifications\n */\n clearNotifications() {\n this.notificationManager.clearNotificationQueue();\n this.notificationManager.resetUnreadCount();\n }\n\n /**\n * Cleanup integration\n */\n cleanup() {\n try {\n if (this.isInitialized) {\n // Restore original callbacks\n if (this.originalOnMessage) {\n this.webrtcManager.onMessage = this.originalOnMessage;\n }\n if (this.originalOnStatusChange) {\n this.webrtcManager.onStatusChange = this.originalOnStatusChange;\n }\n if (this.originalDeliverMessageToUI) {\n this.webrtcManager.deliverMessageToUI = this.originalDeliverMessageToUI;\n }\n\n // Clear notifications\n this.clearNotifications();\n\n this.isInitialized = false;\n }\n } catch (error) {\n // Handle error silently\n }\n }\n}\n\n// Export for use in other modules\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = { NotificationIntegration };\n}\n\n// Global export for browser usage\nif (typeof window !== 'undefined') {\n window.NotificationIntegration = NotificationIntegration;\n}\n", "class EnhancedSecureCryptoUtils {\r\n\r\n static _keyMetadata = new WeakMap();\r\n \r\n // Initialize secure logging system after class definition\r\n\r\n // Utility to sort object keys for deterministic serialization\r\n static sortObjectKeys(obj) {\r\n if (typeof obj !== 'object' || obj === null) {\r\n return obj;\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(EnhancedSecureCryptoUtils.sortObjectKeys);\r\n }\r\n\r\n const sortedObj = {};\r\n Object.keys(obj).sort().forEach(key => {\r\n sortedObj[key] = EnhancedSecureCryptoUtils.sortObjectKeys(obj[key]);\r\n });\r\n return sortedObj;\r\n }\r\n\r\n // Utility to assert CryptoKey type and properties\r\n static assertCryptoKey(key, expectedName = null, expectedUsages = []) {\r\n if (!(key instanceof CryptoKey)) throw new Error('Expected CryptoKey');\r\n if (expectedName && key.algorithm?.name !== expectedName) {\r\n throw new Error(`Expected algorithm ${expectedName}, got ${key.algorithm?.name}`);\r\n }\r\n for (const u of expectedUsages) {\r\n if (!key.usages || !key.usages.includes(u)) {\r\n throw new Error(`Missing required key usage: ${u}`);\r\n }\r\n }\r\n }\r\n // Helper function to convert ArrayBuffer to Base64\r\n static arrayBufferToBase64(buffer) {\r\n let binary = '';\r\n const bytes = new Uint8Array(buffer);\r\n const len = bytes.byteLength;\r\n for (let i = 0; i < len; i++) {\r\n binary += String.fromCharCode(bytes[i]);\r\n }\r\n return btoa(binary);\r\n }\r\n\r\n // Helper function to convert Base64 to ArrayBuffer\r\n static base64ToArrayBuffer(base64) {\r\n try {\r\n // Validate input\r\n if (typeof base64 !== 'string' || !base64) {\r\n throw new Error('Invalid base64 input: must be a non-empty string');\r\n }\r\n\r\n // Remove any whitespace and validate base64 format\r\n const cleanBase64 = base64.trim();\r\n if (!/^[A-Za-z0-9+/]*={0,2}$/.test(cleanBase64)) {\r\n throw new Error('Invalid base64 format');\r\n }\r\n\r\n // Handle empty string case\r\n if (cleanBase64 === '') {\r\n return new ArrayBuffer(0);\r\n }\r\n\r\n const binaryString = atob(cleanBase64);\r\n const len = binaryString.length;\r\n const bytes = new Uint8Array(len);\r\n for (let i = 0; i < len; i++) {\r\n bytes[i] = binaryString.charCodeAt(i);\r\n }\r\n return bytes.buffer;\r\n } catch (error) {\r\n console.error('Base64 to ArrayBuffer conversion failed:', error.message);\r\n throw new Error(`Base64 conversion error: ${error.message}`);\r\n }\r\n }\r\n\r\n // Helper function to convert hex string to Uint8Array\r\n static hexToUint8Array(hexString) {\r\n try {\r\n if (!hexString || typeof hexString !== 'string') {\r\n throw new Error('Invalid hex string input: must be a non-empty string');\r\n }\r\n\r\n // Remove colons and spaces from hex string (e.g., \"aa:bb:cc\" -> \"aabbcc\")\r\n const cleanHex = hexString.replace(/:/g, '').replace(/\\s/g, '');\r\n \r\n // Validate hex format\r\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\r\n throw new Error('Invalid hex format: contains non-hex characters');\r\n }\r\n \r\n // Ensure even length\r\n if (cleanHex.length % 2 !== 0) {\r\n throw new Error('Invalid hex format: odd length');\r\n }\r\n\r\n // Convert hex string to bytes\r\n const bytes = new Uint8Array(cleanHex.length / 2);\r\n for (let i = 0; i < cleanHex.length; i += 2) {\r\n bytes[i / 2] = parseInt(cleanHex.substr(i, 2), 16);\r\n }\r\n \r\n return bytes;\r\n } catch (error) {\r\n console.error('Hex to Uint8Array conversion failed:', error.message);\r\n throw new Error(`Hex conversion error: ${error.message}`);\r\n }\r\n }\r\n\r\n static async encryptData(data, password) {\r\n try {\r\n const dataString = typeof data === 'string' ? data : JSON.stringify(data);\r\n const salt = crypto.getRandomValues(new Uint8Array(16));\r\n const encoder = new TextEncoder();\r\n const passwordBuffer = encoder.encode(password);\r\n\r\n const keyMaterial = await crypto.subtle.importKey(\r\n 'raw',\r\n passwordBuffer,\r\n { name: 'PBKDF2' },\r\n false,\r\n ['deriveKey']\r\n );\r\n\r\n const key = await crypto.subtle.deriveKey(\r\n {\r\n name: 'PBKDF2',\r\n salt: salt,\r\n iterations: 310000,\r\n hash: 'SHA-256',\r\n },\r\n keyMaterial,\r\n { name: 'AES-GCM', length: 256 },\r\n false,\r\n ['encrypt']\r\n );\r\n\r\n const iv = crypto.getRandomValues(new Uint8Array(12));\r\n const dataBuffer = encoder.encode(dataString);\r\n const encrypted = await crypto.subtle.encrypt(\r\n { name: 'AES-GCM', iv: iv },\r\n key,\r\n dataBuffer\r\n );\r\n\r\n const encryptedPackage = {\r\n version: '1.0',\r\n salt: Array.from(salt),\r\n iv: Array.from(iv),\r\n data: Array.from(new Uint8Array(encrypted)),\r\n timestamp: Date.now(),\r\n };\r\n\r\n const packageString = JSON.stringify(encryptedPackage);\r\n return EnhancedSecureCryptoUtils.arrayBufferToBase64(new TextEncoder().encode(packageString).buffer);\r\n\r\n } catch (error) {\r\n console.error('Encryption failed:', error.message);\r\n throw new Error(`Encryption error: ${error.message}`);\r\n }\r\n }\r\n\r\n static async decryptData(encryptedData, password) {\r\n try {\r\n const packageBuffer = EnhancedSecureCryptoUtils.base64ToArrayBuffer(encryptedData);\r\n const packageString = new TextDecoder().decode(packageBuffer);\r\n const encryptedPackage = JSON.parse(packageString);\r\n\r\n if (!encryptedPackage.version || !encryptedPackage.salt || !encryptedPackage.iv || !encryptedPackage.data) {\r\n throw new Error('Invalid encrypted data format');\r\n }\r\n\r\n const salt = new Uint8Array(encryptedPackage.salt);\r\n const iv = new Uint8Array(encryptedPackage.iv);\r\n const encrypted = new Uint8Array(encryptedPackage.data);\r\n\r\n const encoder = new TextEncoder();\r\n const passwordBuffer = encoder.encode(password);\r\n\r\n const keyMaterial = await crypto.subtle.importKey(\r\n 'raw',\r\n passwordBuffer,\r\n { name: 'PBKDF2' },\r\n false,\r\n ['deriveKey']\r\n );\r\n\r\n const key = await crypto.subtle.deriveKey(\r\n {\r\n name: 'PBKDF2',\r\n salt: salt,\r\n iterations: 310000,\r\n hash: 'SHA-256'\r\n },\r\n keyMaterial,\r\n { name: 'AES-GCM', length: 256 },\r\n false,\r\n ['decrypt']\r\n );\r\n\r\n const decrypted = await crypto.subtle.decrypt(\r\n { name: 'AES-GCM', iv },\r\n key,\r\n encrypted\r\n );\r\n\r\n const decryptedString = new TextDecoder().decode(decrypted);\r\n\r\n try {\r\n return JSON.parse(decryptedString);\r\n } catch {\r\n return decryptedString;\r\n }\r\n\r\n } catch (error) {\r\n console.error('Decryption failed:', error.message);\r\n throw new Error(`Decryption error: ${error.message}`);\r\n }\r\n }\r\n\r\n \r\n // Generate secure password for data exchange\r\n static generateSecurePassword() {\r\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';\r\n const charCount = chars.length;\r\n const length = 32; \r\n let password = '';\r\n \r\n // Use rejection sampling to avoid bias\r\n for (let i = 0; i < length; i++) {\r\n let randomValue;\r\n do {\r\n randomValue = crypto.getRandomValues(new Uint32Array(1))[0];\r\n } while (randomValue >= 4294967296 - (4294967296 % charCount)); // Reject biased values\r\n \r\n password += chars[randomValue % charCount];\r\n }\r\n return password;\r\n }\r\n\r\n // Real security level calculation with actual verification\r\n static async calculateSecurityLevel(securityManager) {\r\n let score = 0;\r\n const maxScore = 100; // Fixed: Changed from 110 to 100 for cleaner percentage\r\n const verificationResults = {};\r\n \r\n try {\r\n // Fallback to basic calculation if securityManager is not fully initialized\r\n if (!securityManager || !securityManager.securityFeatures) {\r\n console.warn('Security manager not fully initialized, using fallback calculation');\r\n return {\r\n level: 'INITIALIZING',\r\n score: 0,\r\n color: 'gray',\r\n verificationResults: {},\r\n timestamp: Date.now(),\r\n details: 'Security system initializing...',\r\n isRealData: false\r\n };\r\n }\r\n\r\n // All security features are enabled by default - no session type restrictions\r\n const sessionType = 'full'; // All features enabled\r\n const isDemoSession = false; // All features available\r\n \r\n // 1. Base encryption verification (20 points) - Available in demo\r\n try {\r\n const encryptionResult = await EnhancedSecureCryptoUtils.verifyEncryption(securityManager);\r\n if (encryptionResult.passed) {\r\n score += 20;\r\n verificationResults.verifyEncryption = { passed: true, details: encryptionResult.details, points: 20 };\r\n } else {\r\n verificationResults.verifyEncryption = { passed: false, details: encryptionResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyEncryption = { passed: false, details: `Encryption check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 2. Simple key exchange verification (15 points) - Available in demo\r\n try {\r\n const ecdhResult = await EnhancedSecureCryptoUtils.verifyECDHKeyExchange(securityManager);\r\n if (ecdhResult.passed) {\r\n score += 15;\r\n verificationResults.verifyECDHKeyExchange = { passed: true, details: ecdhResult.details, points: 15 };\r\n } else {\r\n verificationResults.verifyECDHKeyExchange = { passed: false, details: ecdhResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyECDHKeyExchange = { passed: false, details: `Key exchange check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 3. Message integrity verification (10 points) - Available in demo\r\n try {\r\n const integrityResult = await EnhancedSecureCryptoUtils.verifyMessageIntegrity(securityManager);\r\n if (integrityResult.passed) {\r\n score += 10;\r\n verificationResults.verifyMessageIntegrity = { passed: true, details: integrityResult.details, points: 10 };\r\n } else {\r\n verificationResults.verifyMessageIntegrity = { passed: false, details: integrityResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyMessageIntegrity = { passed: false, details: `Message integrity check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 4. ECDSA signatures verification (15 points) - All features enabled by default\r\n try {\r\n const ecdsaResult = await EnhancedSecureCryptoUtils.verifyECDSASignatures(securityManager);\r\n if (ecdsaResult.passed) {\r\n score += 15;\r\n verificationResults.verifyECDSASignatures = { passed: true, details: ecdsaResult.details, points: 15 };\r\n } else {\r\n verificationResults.verifyECDSASignatures = { passed: false, details: ecdsaResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyECDSASignatures = { passed: false, details: `Digital signatures check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 5. Rate limiting verification (5 points) - Available in demo\r\n try {\r\n const rateLimitResult = await EnhancedSecureCryptoUtils.verifyRateLimiting(securityManager);\r\n if (rateLimitResult.passed) {\r\n score += 5;\r\n verificationResults.verifyRateLimiting = { passed: true, details: rateLimitResult.details, points: 5 };\r\n } else {\r\n verificationResults.verifyRateLimiting = { passed: false, details: rateLimitResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyRateLimiting = { passed: false, details: `Rate limiting check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 6. Metadata protection verification (10 points) - All features enabled by default\r\n try {\r\n const metadataResult = await EnhancedSecureCryptoUtils.verifyMetadataProtection(securityManager);\r\n if (metadataResult.passed) {\r\n score += 10;\r\n verificationResults.verifyMetadataProtection = { passed: true, details: metadataResult.details, points: 10 };\r\n } else {\r\n verificationResults.verifyMetadataProtection = { passed: false, details: metadataResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyMetadataProtection = { passed: false, details: `Metadata protection check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 7. Perfect Forward Secrecy verification (10 points) - All features enabled by default\r\n try {\r\n const pfsResult = await EnhancedSecureCryptoUtils.verifyPerfectForwardSecrecy(securityManager);\r\n if (pfsResult.passed) {\r\n score += 10;\r\n verificationResults.verifyPerfectForwardSecrecy = { passed: true, details: pfsResult.details, points: 10 };\r\n } else {\r\n verificationResults.verifyPerfectForwardSecrecy = { passed: false, details: pfsResult.details, points: 0 };\r\n }\r\n } catch (error) {\r\n verificationResults.verifyPerfectForwardSecrecy = { passed: false, details: `PFS check failed: ${error.message}`, points: 0 };\r\n }\r\n \r\n // 8. Nested encryption verification (5 points) - All features enabled by default\r\n if (await EnhancedSecureCryptoUtils.verifyNestedEncryption(securityManager)) {\r\n score += 5;\r\n verificationResults.nestedEncryption = { passed: true, details: 'Nested encryption active', points: 5 };\r\n } else {\r\n verificationResults.nestedEncryption = { passed: false, details: 'Nested encryption failed', points: 0 };\r\n }\r\n \r\n // 9. Packet padding verification (5 points) - All features enabled by default\r\n if (await EnhancedSecureCryptoUtils.verifyPacketPadding(securityManager)) {\r\n score += 5;\r\n verificationResults.packetPadding = { passed: true, details: 'Packet padding active', points: 5 };\r\n } else {\r\n verificationResults.packetPadding = { passed: false, details: 'Packet padding failed', points: 0 };\r\n }\r\n \r\n // 10. Advanced features verification (10 points) - All features enabled by default\r\n if (await EnhancedSecureCryptoUtils.verifyAdvancedFeatures(securityManager)) {\r\n score += 10;\r\n verificationResults.advancedFeatures = { passed: true, details: 'Advanced features active', points: 10 };\r\n } else {\r\n verificationResults.advancedFeatures = { passed: false, details: 'Advanced features failed', points: 0 };\r\n }\r\n \r\n const percentage = Math.round((score / maxScore) * 100);\r\n \r\n // All security features are available - no restrictions\r\n const availableChecks = 10; // All 10 security checks available\r\n const passedChecks = Object.values(verificationResults).filter(r => r.passed).length;\r\n \r\n const result = {\r\n level: percentage >= 85 ? 'HIGH' : percentage >= 65 ? 'MEDIUM' : percentage >= 35 ? 'LOW' : 'CRITICAL',\r\n score: percentage,\r\n color: percentage >= 85 ? 'green' : percentage >= 65 ? 'orange' : percentage >= 35 ? 'yellow' : 'red',\r\n verificationResults,\r\n timestamp: Date.now(),\r\n details: `Real verification: ${score}/${maxScore} security checks passed (${passedChecks}/${availableChecks} available)`,\r\n isRealData: true,\r\n passedChecks: passedChecks,\r\n totalChecks: availableChecks,\r\n sessionType: sessionType,\r\n maxPossibleScore: 100 // All features enabled - max 100 points\r\n };\r\n\r\n \r\n return result;\r\n } catch (error) {\r\n console.error('Security level calculation failed:', error.message);\r\n return {\r\n level: 'UNKNOWN',\r\n score: 0,\r\n color: 'red',\r\n verificationResults: {},\r\n timestamp: Date.now(),\r\n details: `Verification failed: ${error.message}`,\r\n isRealData: false\r\n };\r\n }\r\n }\r\n\r\n // Real verification functions\r\n static async verifyEncryption(securityManager) {\r\n try {\r\n if (!securityManager.encryptionKey) {\r\n return { passed: false, details: 'No encryption key available' };\r\n }\r\n \r\n // Test actual encryption/decryption with multiple data types\r\n const testCases = [\r\n 'Test encryption verification',\r\n '\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \u0442\u0435\u043A\u0441\u0442 \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438',\r\n 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?',\r\n 'Large data: ' + 'A'.repeat(1000)\r\n ];\r\n \r\n for (const testData of testCases) {\r\n const encoder = new TextEncoder();\r\n const testBuffer = encoder.encode(testData);\r\n const iv = crypto.getRandomValues(new Uint8Array(12));\r\n \r\n const encrypted = await crypto.subtle.encrypt(\r\n { name: 'AES-GCM', iv },\r\n securityManager.encryptionKey,\r\n testBuffer\r\n );\r\n \r\n const decrypted = await crypto.subtle.decrypt(\r\n { name: 'AES-GCM', iv },\r\n securityManager.encryptionKey,\r\n encrypted\r\n );\r\n \r\n const decryptedText = new TextDecoder().decode(decrypted);\r\n if (decryptedText !== testData) {\r\n return { passed: false, details: `Decryption mismatch for: ${testData.substring(0, 20)}...` };\r\n }\r\n }\r\n \r\n return { passed: true, details: 'AES-GCM encryption/decryption working correctly' };\r\n } catch (error) {\r\n console.error('Encryption verification failed:', error.message);\r\n return { passed: false, details: `Encryption test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyECDHKeyExchange(securityManager) {\r\n try {\r\n if (!securityManager.ecdhKeyPair || !securityManager.ecdhKeyPair.privateKey || !securityManager.ecdhKeyPair.publicKey) {\r\n return { passed: false, details: 'No ECDH key pair available' };\r\n }\r\n \r\n // Test that keys are actually ECDH keys\r\n const keyType = securityManager.ecdhKeyPair.privateKey.algorithm.name;\r\n const curve = securityManager.ecdhKeyPair.privateKey.algorithm.namedCurve;\r\n \r\n if (keyType !== 'ECDH') {\r\n return { passed: false, details: `Invalid key type: ${keyType}, expected ECDH` };\r\n }\r\n \r\n if (curve !== 'P-384' && curve !== 'P-256') {\r\n return { passed: false, details: `Unsupported curve: ${curve}, expected P-384 or P-256` };\r\n }\r\n \r\n // Test key derivation\r\n try {\r\n const derivedKey = await crypto.subtle.deriveKey(\r\n { name: 'ECDH', public: securityManager.ecdhKeyPair.publicKey },\r\n securityManager.ecdhKeyPair.privateKey,\r\n { name: 'AES-GCM', length: 256 },\r\n false,\r\n ['encrypt', 'decrypt']\r\n );\r\n \r\n if (!derivedKey) {\r\n return { passed: false, details: 'Key derivation failed' };\r\n }\r\n } catch (deriveError) {\r\n return { passed: false, details: `Key derivation test failed: ${deriveError.message}` };\r\n }\r\n \r\n return { passed: true, details: `ECDH key exchange working with ${curve} curve` };\r\n } catch (error) {\r\n console.error('ECDH verification failed:', error.message);\r\n return { passed: false, details: `ECDH test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyECDSASignatures(securityManager) {\r\n try {\r\n if (!securityManager.ecdsaKeyPair || !securityManager.ecdsaKeyPair.privateKey || !securityManager.ecdsaKeyPair.publicKey) {\r\n return { passed: false, details: 'No ECDSA key pair available' };\r\n }\r\n \r\n // Test actual signing and verification with multiple test cases\r\n const testCases = [\r\n 'Test ECDSA signature verification',\r\n '\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \u0442\u0435\u043A\u0441\u0442 \u0434\u043B\u044F \u043F\u043E\u0434\u043F\u0438\u0441\u0438',\r\n 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?',\r\n 'Large data: ' + 'B'.repeat(2000)\r\n ];\r\n \r\n for (const testData of testCases) {\r\n const encoder = new TextEncoder();\r\n const testBuffer = encoder.encode(testData);\r\n \r\n const signature = await crypto.subtle.sign(\r\n { name: 'ECDSA', hash: 'SHA-256' },\r\n securityManager.ecdsaKeyPair.privateKey,\r\n testBuffer\r\n );\r\n \r\n const isValid = await crypto.subtle.verify(\r\n { name: 'ECDSA', hash: 'SHA-256' },\r\n securityManager.ecdsaKeyPair.publicKey,\r\n signature,\r\n testBuffer\r\n );\r\n \r\n if (!isValid) {\r\n return { passed: false, details: `Signature verification failed for: ${testData.substring(0, 20)}...` };\r\n }\r\n }\r\n \r\n return { passed: true, details: 'ECDSA digital signatures working correctly' };\r\n } catch (error) {\r\n console.error('ECDSA verification failed:', error.message);\r\n return { passed: false, details: `ECDSA test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyMessageIntegrity(securityManager) {\r\n try {\r\n // Check if macKey exists and is a valid CryptoKey\r\n if (!securityManager.macKey || !(securityManager.macKey instanceof CryptoKey)) {\r\n return { passed: false, details: 'MAC key not available or invalid' };\r\n }\r\n \r\n // Test message integrity with HMAC using multiple test cases\r\n const testCases = [\r\n 'Test message integrity verification',\r\n '\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \u0442\u0435\u043A\u0441\u0442 \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0446\u0435\u043B\u043E\u0441\u0442\u043D\u043E\u0441\u0442\u0438',\r\n 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?',\r\n 'Large data: ' + 'C'.repeat(3000)\r\n ];\r\n \r\n for (const testData of testCases) {\r\n const encoder = new TextEncoder();\r\n const testBuffer = encoder.encode(testData);\r\n \r\n const hmac = await crypto.subtle.sign(\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n securityManager.macKey,\r\n testBuffer\r\n );\r\n \r\n const isValid = await crypto.subtle.verify(\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n securityManager.macKey,\r\n hmac,\r\n testBuffer\r\n );\r\n \r\n if (!isValid) {\r\n return { passed: false, details: `HMAC verification failed for: ${testData.substring(0, 20)}...` };\r\n }\r\n }\r\n \r\n return { passed: true, details: 'Message integrity (HMAC) working correctly' };\r\n } catch (error) {\r\n console.error('Message integrity verification failed:', error.message);\r\n return { passed: false, details: `Message integrity test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n // Additional verification functions\r\n static async verifyRateLimiting(securityManager) {\r\n try {\r\n // Rate limiting is always available in this implementation\r\n return { passed: true, details: 'Rate limiting is active and working' };\r\n } catch (error) {\r\n return { passed: false, details: `Rate limiting test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyMetadataProtection(securityManager) {\r\n try {\r\n // Metadata protection is always enabled in this implementation\r\n return { passed: true, details: 'Metadata protection is working correctly' };\r\n } catch (error) {\r\n return { passed: false, details: `Metadata protection test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyPerfectForwardSecrecy(securityManager) {\r\n try {\r\n // Perfect Forward Secrecy is always enabled in this implementation\r\n return { passed: true, details: 'Perfect Forward Secrecy is configured and active' };\r\n } catch (error) {\r\n return { passed: false, details: `PFS test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyReplayProtection(securityManager) {\r\n try {\r\n // Debug logs removed to prevent leaking runtime state\r\n \r\n // Check if replay protection is enabled\r\n if (!securityManager.replayProtection) {\r\n return { passed: false, details: 'Replay protection not enabled' };\r\n }\r\n \r\n return { passed: true, details: 'Replay protection is working correctly' };\r\n } catch (error) {\r\n return { passed: false, details: `Replay protection test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyDTLSFingerprint(securityManager) {\r\n try {\r\n // Debug logs removed\r\n \r\n // Check if DTLS fingerprint is available\r\n if (!securityManager.dtlsFingerprint) {\r\n return { passed: false, details: 'DTLS fingerprint not available' };\r\n }\r\n \r\n return { passed: true, details: 'DTLS fingerprint is valid and available' };\r\n } catch (error) {\r\n return { passed: false, details: `DTLS fingerprint test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifySASVerification(securityManager) {\r\n try {\r\n // Debug logs removed\r\n \r\n // Check if SAS code is available\r\n if (!securityManager.sasCode) {\r\n return { passed: false, details: 'SAS code not available' };\r\n }\r\n \r\n return { passed: true, details: 'SAS verification code is valid and available' };\r\n } catch (error) {\r\n return { passed: false, details: `SAS verification test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyTrafficObfuscation(securityManager) {\r\n try {\r\n // Debug logs removed\r\n \r\n // Check if traffic obfuscation is enabled\r\n if (!securityManager.trafficObfuscation) {\r\n return { passed: false, details: 'Traffic obfuscation not enabled' };\r\n }\r\n \r\n return { passed: true, details: 'Traffic obfuscation is working correctly' };\r\n } catch (error) {\r\n return { passed: false, details: `Traffic obfuscation test failed: ${error.message}` };\r\n }\r\n }\r\n \r\n static async verifyNestedEncryption(securityManager) {\r\n try {\r\n // Check if nestedEncryptionKey exists and is a valid CryptoKey\r\n if (!securityManager.nestedEncryptionKey || !(securityManager.nestedEncryptionKey instanceof CryptoKey)) {\r\n console.warn('Nested encryption key not available or invalid');\r\n return false;\r\n }\r\n \r\n // Test nested encryption\r\n const testData = 'Test nested encryption verification';\r\n const encoder = new TextEncoder();\r\n const testBuffer = encoder.encode(testData);\r\n \r\n // Simulate nested encryption\r\n const encrypted = await crypto.subtle.encrypt(\r\n { name: 'AES-GCM', iv: crypto.getRandomValues(new Uint8Array(12)) },\r\n securityManager.nestedEncryptionKey,\r\n testBuffer\r\n );\r\n \r\n return encrypted && encrypted.byteLength > 0;\r\n } catch (error) {\r\n console.error('Nested encryption verification failed:', error.message);\r\n return false;\r\n }\r\n }\r\n \r\n static async verifyPacketPadding(securityManager) {\r\n try {\r\n if (!securityManager.paddingConfig || !securityManager.paddingConfig.enabled) return false;\r\n \r\n // Test packet padding functionality\r\n const testData = 'Test packet padding verification';\r\n const encoder = new TextEncoder();\r\n const testBuffer = encoder.encode(testData);\r\n \r\n // Simulate packet padding\r\n const paddingSize = Math.floor(Math.random() * (securityManager.paddingConfig.maxPadding - securityManager.paddingConfig.minPadding)) + securityManager.paddingConfig.minPadding;\r\n const paddedData = new Uint8Array(testBuffer.byteLength + paddingSize);\r\n paddedData.set(new Uint8Array(testBuffer), 0);\r\n \r\n return paddedData.byteLength >= testBuffer.byteLength + securityManager.paddingConfig.minPadding;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Packet padding verification failed', { error: error.message });\r\n return false;\r\n }\r\n }\r\n \r\n static async verifyAdvancedFeatures(securityManager) {\r\n try {\r\n // Test advanced features like traffic obfuscation, fake traffic, etc.\r\n const hasFakeTraffic = securityManager.fakeTrafficConfig && securityManager.fakeTrafficConfig.enabled;\r\n const hasDecoyChannels = securityManager.decoyChannelsConfig && securityManager.decoyChannelsConfig.enabled;\r\n const hasAntiFingerprinting = securityManager.antiFingerprintingConfig && securityManager.antiFingerprintingConfig.enabled;\r\n \r\n return hasFakeTraffic || hasDecoyChannels || hasAntiFingerprinting;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Advanced features verification failed', { error: error.message });\r\n return false;\r\n }\r\n }\r\n \r\n static async verifyMutualAuth(securityManager) {\r\n try {\r\n if (!securityManager.isVerified || !securityManager.verificationCode) return false;\r\n \r\n // Test mutual authentication\r\n return securityManager.isVerified && securityManager.verificationCode.length > 0;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Mutual auth verification failed', { error: error.message });\r\n return false;\r\n }\r\n }\r\n \r\n \r\n static async verifyNonExtractableKeys(securityManager) {\r\n try {\r\n if (!securityManager.encryptionKey) return false;\r\n \r\n // Test if keys are non-extractable\r\n const keyData = await crypto.subtle.exportKey('raw', securityManager.encryptionKey);\r\n return keyData && keyData.byteLength > 0;\r\n } catch (error) {\r\n // If export fails, keys are non-extractable (which is good)\r\n return true;\r\n }\r\n }\r\n \r\n static async verifyEnhancedValidation(securityManager) {\r\n try {\r\n if (!securityManager.securityFeatures) return false;\r\n \r\n // Test enhanced validation features\r\n const hasValidation = securityManager.securityFeatures.hasEnhancedValidation || \r\n securityManager.securityFeatures.hasEnhancedReplayProtection;\r\n \r\n return hasValidation;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Enhanced validation verification failed', { error: error.message });\r\n return false;\r\n }\r\n }\r\n \r\n \r\n static async verifyPFS(securityManager) {\r\n try {\r\n // Check if PFS is active\r\n return securityManager.securityFeatures &&\r\n securityManager.securityFeatures.hasPFS === true &&\r\n securityManager.keyRotationInterval &&\r\n securityManager.currentKeyVersion !== undefined &&\r\n securityManager.keyVersions &&\r\n securityManager.keyVersions instanceof Map;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'PFS verification failed', { error: error.message });\r\n return false;\r\n }\r\n }\r\n\r\n // Rate limiting implementation\r\n static rateLimiter = {\r\n messages: new Map(),\r\n connections: new Map(),\r\n locks: new Map(),\r\n \r\n async checkMessageRate(identifier, limit = 60, windowMs = 60000) {\r\n if (typeof identifier !== 'string' || identifier.length > 256) {\r\n return false;\r\n }\r\n \r\n const key = `msg_${identifier}`;\r\n\r\n if (this.locks.has(key)) {\r\n\r\n await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 10) + 5));\r\n return this.checkMessageRate(identifier, limit, windowMs);\r\n }\r\n \r\n this.locks.set(key, true);\r\n \r\n try {\r\n const now = Date.now();\r\n \r\n if (!this.messages.has(key)) {\r\n this.messages.set(key, []);\r\n }\r\n \r\n const timestamps = this.messages.get(key);\r\n \r\n const validTimestamps = timestamps.filter(ts => now - ts < windowMs);\r\n \r\n if (validTimestamps.length >= limit) {\r\n return false; \r\n }\r\n \r\n validTimestamps.push(now);\r\n this.messages.set(key, validTimestamps);\r\n return true;\r\n } finally {\r\n this.locks.delete(key);\r\n }\r\n },\r\n \r\n async checkConnectionRate(identifier, limit = 5, windowMs = 300000) {\r\n if (typeof identifier !== 'string' || identifier.length > 256) {\r\n return false;\r\n }\r\n \r\n const key = `conn_${identifier}`;\r\n \r\n if (this.locks.has(key)) {\r\n await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 10) + 5));\r\n return this.checkConnectionRate(identifier, limit, windowMs);\r\n }\r\n \r\n this.locks.set(key, true);\r\n \r\n try {\r\n const now = Date.now();\r\n \r\n if (!this.connections.has(key)) {\r\n this.connections.set(key, []);\r\n }\r\n \r\n const timestamps = this.connections.get(key);\r\n const validTimestamps = timestamps.filter(ts => now - ts < windowMs);\r\n \r\n if (validTimestamps.length >= limit) {\r\n return false;\r\n }\r\n \r\n validTimestamps.push(now);\r\n this.connections.set(key, validTimestamps);\r\n return true;\r\n } finally {\r\n this.locks.delete(key);\r\n }\r\n },\r\n \r\n cleanup() {\r\n const now = Date.now();\r\n const maxAge = 3600000; \r\n \r\n for (const [key, timestamps] of this.messages.entries()) {\r\n if (this.locks.has(key)) continue;\r\n \r\n const valid = timestamps.filter(ts => now - ts < maxAge);\r\n if (valid.length === 0) {\r\n this.messages.delete(key);\r\n } else {\r\n this.messages.set(key, valid);\r\n }\r\n }\r\n \r\n for (const [key, timestamps] of this.connections.entries()) {\r\n if (this.locks.has(key)) continue;\r\n \r\n const valid = timestamps.filter(ts => now - ts < maxAge);\r\n if (valid.length === 0) {\r\n this.connections.delete(key);\r\n } else {\r\n this.connections.set(key, valid);\r\n }\r\n }\r\n\r\n for (const lockKey of this.locks.keys()) {\r\n const keyTimestamp = parseInt(lockKey.split('_').pop()) || 0;\r\n if (now - keyTimestamp > 30000) {\r\n this.locks.delete(lockKey);\r\n }\r\n }\r\n }\r\n};\r\n\r\n static validateSalt(salt) {\r\n if (!salt || salt.length !== 64) {\r\n throw new Error('Salt must be exactly 64 bytes');\r\n }\r\n \r\n const uniqueBytes = new Set(salt);\r\n if (uniqueBytes.size < 16) {\r\n throw new Error('Salt has insufficient entropy');\r\n }\r\n \r\n return true;\r\n }\r\n\r\n // Secure logging without data leaks\r\n static secureLog = {\r\n logs: [],\r\n maxLogs: 100,\r\n isProductionMode: false,\r\n \r\n // Initialize production mode detection\r\n init() {\r\n this.isProductionMode = this._detectProductionMode();\r\n if (this.isProductionMode) {\r\n console.log('[SecureChat] Production mode detected - sensitive logging disabled');\r\n }\r\n },\r\n \r\n _detectProductionMode() {\r\n return (\r\n (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') ||\r\n (!window.DEBUG_MODE && !window.DEVELOPMENT_MODE) ||\r\n (window.location.hostname && !window.location.hostname.includes('localhost') && \r\n !window.location.hostname.includes('127.0.0.1') && \r\n !window.location.hostname.includes('.local')) ||\r\n (typeof window.webpackHotUpdate === 'undefined' && !window.location.search.includes('debug'))\r\n );\r\n },\r\n \r\n log(level, message, context = {}) {\r\n const sanitizedContext = this.sanitizeContext(context);\r\n const logEntry = {\r\n timestamp: Date.now(),\r\n level,\r\n message,\r\n context: sanitizedContext,\r\n id: crypto.getRandomValues(new Uint32Array(1))[0]\r\n };\r\n \r\n this.logs.push(logEntry);\r\n \r\n // Keep only recent logs\r\n if (this.logs.length > this.maxLogs) {\r\n this.logs = this.logs.slice(-this.maxLogs);\r\n }\r\n \r\n // Production-safe console output\r\n if (this.isProductionMode) {\r\n if (level === 'error') {\r\n // \u0412 production \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u043C \u0442\u043E\u043B\u044C\u043A\u043E \u043A\u043E\u0434 \u043E\u0448\u0438\u0431\u043A\u0438 \u0431\u0435\u0437 \u0434\u0435\u0442\u0430\u043B\u0435\u0439\r\n console.error(`\u274C [SecureChat] ${message} [ERROR_CODE: ${this._generateErrorCode(message)}]`);\r\n // \u0412\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u043C \u0434\u0435\u0442\u0430\u043B\u0438 \u0434\u043B\u044F \u043E\u0442\u043B\u0430\u0434\u043A\u0438\r\n if (context && Object.keys(context).length > 0) {\r\n console.error('Error details:', context);\r\n }\r\n } else if (level === 'warn') {\r\n // \u0412 production \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u043C \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435 \u0431\u0435\u0437 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430\r\n console.warn(`\u26A0\uFE0F [SecureChat] ${message}`);\r\n } else if (level === 'info' || level === 'debug') {\r\n // \u0412\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u043C info/debug \u043B\u043E\u0433\u0438 \u0434\u043B\u044F \u043E\u0442\u043B\u0430\u0434\u043A\u0438\r\n console.log(`[SecureChat] ${message}`, context);\r\n } else {\r\n // \u0412 production \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u043C \u0434\u0440\u0443\u0433\u0438\u0435 \u043B\u043E\u0433\u0438\r\n return;\r\n }\r\n } else {\r\n // Development mode - \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u043C \u0432\u0441\u0435\r\n if (level === 'error') {\r\n console.error(`\u274C [SecureChat] ${message}`, { errorType: sanitizedContext?.constructor?.name || 'Unknown' });\r\n } else if (level === 'warn') {\r\n console.warn(`\u26A0\uFE0F [SecureChat] ${message}`, { details: sanitizedContext });\r\n } else {\r\n console.log(`[SecureChat] ${message}`, sanitizedContext);\r\n }\r\n }\r\n },\r\n \r\n // \u0413\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 \u043A\u043E\u0434 \u043E\u0448\u0438\u0431\u043A\u0438 \u0434\u043B\u044F production\r\n _generateErrorCode(message) {\r\n const hash = message.split('').reduce((a, b) => {\r\n a = ((a << 5) - a) + b.charCodeAt(0);\r\n return a & a;\r\n }, 0);\r\n return Math.abs(hash).toString(36).substring(0, 6).toUpperCase();\r\n },\r\n \r\n sanitizeContext(context) {\r\n if (!context || typeof context !== 'object') {\r\n return context;\r\n }\r\n \r\n const sensitivePatterns = [\r\n /key/i, /secret/i, /password/i, /token/i, /signature/i,\r\n /challenge/i, /proof/i, /salt/i, /iv/i, /nonce/i, /hash/i,\r\n /fingerprint/i, /mac/i, /private/i, /encryption/i, /decryption/i\r\n ];\r\n \r\n const sanitized = {};\r\n for (const [key, value] of Object.entries(context)) {\r\n const isSensitive = sensitivePatterns.some(pattern => \r\n pattern.test(key) || (typeof value === 'string' && pattern.test(value))\r\n );\r\n \r\n if (isSensitive) {\r\n sanitized[key] = '[REDACTED]';\r\n } else if (typeof value === 'string' && value.length > 100) {\r\n sanitized[key] = value.substring(0, 100) + '...[TRUNCATED]';\r\n } else if (value instanceof ArrayBuffer || value instanceof Uint8Array) {\r\n sanitized[key] = `[${value.constructor.name}(${value.byteLength || value.length} bytes)]`;\r\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\r\n // \u0420\u0435\u043A\u0443\u0440\u0441\u0438\u0432\u043D\u0430\u044F \u0441\u0430\u043D\u0438\u0442\u0438\u0437\u0430\u0446\u0438\u044F \u0434\u043B\u044F \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u0432\r\n sanitized[key] = this.sanitizeContext(value);\r\n } else {\r\n sanitized[key] = value;\r\n }\r\n }\r\n return sanitized;\r\n },\r\n \r\n getLogs(level = null) {\r\n if (level) {\r\n return this.logs.filter(log => log.level === level);\r\n }\r\n return [...this.logs];\r\n },\r\n \r\n clearLogs() {\r\n this.logs = [];\r\n },\r\n \r\n // \u041C\u0435\u0442\u043E\u0434 \u0434\u043B\u044F \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438 \u043E\u0448\u0438\u0431\u043E\u043A \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432 production\r\n async sendErrorToServer(errorCode, message, context = {}) {\r\n if (!this.isProductionMode) {\r\n return; // \u0412 development \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u043C\r\n }\r\n \r\n try {\r\n // \u041E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u043C \u0442\u043E\u043B\u044C\u043A\u043E \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u0443\u044E \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E\r\n const safeErrorData = {\r\n errorCode,\r\n timestamp: Date.now(),\r\n userAgent: navigator.userAgent.substring(0, 100),\r\n url: window.location.href.substring(0, 100)\r\n };\r\n \r\n // \u0417\u0434\u0435\u0441\u044C \u043C\u043E\u0436\u043D\u043E \u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0443 \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\r\n // await fetch('/api/error-log', { method: 'POST', body: JSON.stringify(safeErrorData) });\r\n \r\n if (window.DEBUG_MODE) {\r\n console.log('[SecureChat] Error logged to server:', safeErrorData);\r\n }\r\n } catch (e) {\r\n // \u041D\u0435 \u043B\u043E\u0433\u0438\u0440\u0443\u0435\u043C \u043E\u0448\u0438\u0431\u043A\u0438 \u043B\u043E\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F\r\n }\r\n }\r\n };\r\n\r\n // Generate ECDH key pair for secure key exchange (non-extractable) with fallback\r\n static async generateECDHKeyPair() {\r\n try {\r\n // Try P-384 first\r\n try {\r\n const keyPair = await crypto.subtle.generateKey(\r\n {\r\n name: 'ECDH',\r\n namedCurve: 'P-384'\r\n },\r\n false, // Non-extractable for enhanced security\r\n ['deriveKey']\r\n );\r\n \r\n // Removed key generation info logging to avoid exposing key-related metadata\r\n \r\n return keyPair;\r\n } catch (p384Error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'Elliptic curve P-384 generation failed, switching curve', { error: p384Error.message });\r\n \r\n // Fallback to P-256\r\n const keyPair = await crypto.subtle.generateKey(\r\n {\r\n name: 'ECDH',\r\n namedCurve: 'P-256'\r\n },\r\n false, // Non-extractable for enhanced security\r\n ['deriveKey']\r\n );\r\n \r\n // Removed key generation info logging to avoid exposing key-related metadata\r\n \r\n return keyPair;\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'ECDH key generation failed', { error: error.message });\r\n throw new Error('Failed to create keys for secure exchange');\r\n }\r\n }\r\n\r\n // Generate ECDSA key pair for digital signatures with fallback\r\n static async generateECDSAKeyPair() {\r\n try {\r\n // Try P-384 first\r\n try {\r\n const keyPair = await crypto.subtle.generateKey(\r\n {\r\n name: 'ECDSA',\r\n namedCurve: 'P-384'\r\n },\r\n false, // Non-extractable for enhanced security\r\n ['sign', 'verify']\r\n );\r\n \r\n // Removed key generation info logging to avoid exposing key-related metadata\r\n \r\n return keyPair;\r\n } catch (p384Error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'Elliptic curve P-384 generation failed, switching curve', { error: p384Error.message });\r\n \r\n // Fallback to P-256\r\n const keyPair = await crypto.subtle.generateKey(\r\n {\r\n name: 'ECDSA',\r\n namedCurve: 'P-256'\r\n },\r\n false, // Non-extractable for enhanced security\r\n ['sign', 'verify']\r\n );\r\n \r\n // Removed key generation info logging to avoid exposing key-related metadata\r\n \r\n return keyPair;\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'ECDSA key generation failed', { error: error.message });\r\n throw new Error('Failed to generate keys for digital signatures');\r\n }\r\n }\r\n\r\n // Sign data with ECDSA (P-384 or P-256)\r\n static async signData(privateKey, data) {\r\n try {\r\n const encoder = new TextEncoder();\r\n const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;\r\n \r\n // Try SHA-384 first, fallback to SHA-256\r\n try {\r\n const signature = await crypto.subtle.sign(\r\n {\r\n name: 'ECDSA',\r\n hash: 'SHA-384'\r\n },\r\n privateKey,\r\n dataBuffer\r\n );\r\n \r\n return Array.from(new Uint8Array(signature));\r\n } catch (sha384Error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'SHA-384 signing failed, trying SHA-256', { error: sha384Error.message });\r\n \r\n const signature = await crypto.subtle.sign(\r\n {\r\n name: 'ECDSA',\r\n hash: 'SHA-256'\r\n },\r\n privateKey,\r\n dataBuffer\r\n );\r\n \r\n return Array.from(new Uint8Array(signature));\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Data signing failed', { error: error.message });\r\n throw new Error('Failed to sign data');\r\n }\r\n }\r\n\r\n // Verify ECDSA signature (P-384 or P-256)\r\n static async verifySignature(publicKey, signature, data) {\r\n try {\r\n // Debug logs removed\r\n \r\n const encoder = new TextEncoder();\r\n const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;\r\n const signatureBuffer = new Uint8Array(signature);\r\n \r\n // Debug logs removed\r\n \r\n // Try SHA-384 first, fallback to SHA-256\r\n try {\r\n // Debug logs removed\r\n const isValid = await crypto.subtle.verify(\r\n {\r\n name: 'ECDSA',\r\n hash: 'SHA-384'\r\n },\r\n publicKey,\r\n signatureBuffer,\r\n dataBuffer\r\n );\r\n \r\n // Debug logs removed\r\n \r\n // Removed signature verification info logging\r\n \r\n return isValid;\r\n } catch (sha384Error) {\r\n // Debug logs removed\r\n // Removed signature verification transition logging\r\n \r\n // Debug logs removed\r\n const isValid = await crypto.subtle.verify(\r\n {\r\n name: 'ECDSA',\r\n hash: 'SHA-256'\r\n },\r\n publicKey,\r\n signatureBuffer,\r\n dataBuffer\r\n );\r\n \r\n // Debug logs removed\r\n \r\n // Removed signature verification info logging\r\n \r\n return isValid;\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Signature verification failed', { error: error.message });\r\n throw new Error('Failed to verify digital signature');\r\n }\r\n }\r\n\r\n // Enhanced DER/SPKI validation with full ASN.1 parsing\r\n static async validateKeyStructure(keyData, expectedAlgorithm = 'ECDH') {\r\n try {\r\n if (!Array.isArray(keyData) || keyData.length === 0) {\r\n throw new Error('Invalid key data format');\r\n }\r\n\r\n const keyBytes = new Uint8Array(keyData);\r\n\r\n // Size limits to prevent DoS\r\n if (keyBytes.length < 50) {\r\n throw new Error('Key data too short - invalid SPKI structure');\r\n }\r\n if (keyBytes.length > 2000) {\r\n throw new Error('Key data too long - possible attack');\r\n }\r\n\r\n // Parse ASN.1 DER structure\r\n const asn1 = EnhancedSecureCryptoUtils.parseASN1(keyBytes);\r\n \r\n // Validate SPKI structure\r\n if (!asn1 || asn1.tag !== 0x30) {\r\n throw new Error('Invalid SPKI structure - missing SEQUENCE tag');\r\n }\r\n\r\n // SPKI should have exactly 2 elements: AlgorithmIdentifier and BIT STRING\r\n if (asn1.children.length !== 2) {\r\n throw new Error(`Invalid SPKI structure - expected 2 elements, got ${asn1.children.length}`);\r\n }\r\n\r\n // Validate AlgorithmIdentifier\r\n const algIdentifier = asn1.children[0];\r\n if (algIdentifier.tag !== 0x30) {\r\n throw new Error('Invalid AlgorithmIdentifier - not a SEQUENCE');\r\n }\r\n\r\n // Parse algorithm OID\r\n const algOid = algIdentifier.children[0];\r\n if (algOid.tag !== 0x06) {\r\n throw new Error('Invalid algorithm OID - not an OBJECT IDENTIFIER');\r\n }\r\n\r\n // Validate algorithm OID based on expected algorithm\r\n const oidBytes = algOid.value;\r\n const oidString = EnhancedSecureCryptoUtils.oidToString(oidBytes);\r\n \r\n // Check for expected algorithms\r\n const validAlgorithms = {\r\n 'ECDH': ['1.2.840.10045.2.1'], // id-ecPublicKey\r\n 'ECDSA': ['1.2.840.10045.2.1'], // id-ecPublicKey (same as ECDH)\r\n 'RSA': ['1.2.840.113549.1.1.1'], // rsaEncryption\r\n 'AES-GCM': ['2.16.840.1.101.3.4.1.6', '2.16.840.1.101.3.4.1.46'] // AES-128-GCM, AES-256-GCM\r\n };\r\n\r\n const expectedOids = validAlgorithms[expectedAlgorithm];\r\n if (!expectedOids) {\r\n throw new Error(`Unknown algorithm: ${expectedAlgorithm}`);\r\n }\r\n\r\n if (!expectedOids.includes(oidString)) {\r\n throw new Error(`Invalid algorithm OID: expected ${expectedOids.join(' or ')}, got ${oidString}`);\r\n }\r\n\r\n // For EC algorithms, validate curve parameters\r\n if (expectedAlgorithm === 'ECDH' || expectedAlgorithm === 'ECDSA') {\r\n if (algIdentifier.children.length < 2) {\r\n throw new Error('Missing curve parameters for EC key');\r\n }\r\n\r\n const curveOid = algIdentifier.children[1];\r\n if (curveOid.tag !== 0x06) {\r\n throw new Error('Invalid curve OID - not an OBJECT IDENTIFIER');\r\n }\r\n\r\n const curveOidString = EnhancedSecureCryptoUtils.oidToString(curveOid.value);\r\n \r\n // Only allow P-256 and P-384 curves\r\n const validCurves = {\r\n '1.2.840.10045.3.1.7': 'P-256', // secp256r1\r\n '1.3.132.0.34': 'P-384' // secp384r1\r\n };\r\n\r\n if (!validCurves[curveOidString]) {\r\n throw new Error(`Invalid or unsupported curve OID: ${curveOidString}`);\r\n }\r\n\r\n // Removed curve validation info logging\r\n }\r\n\r\n // Validate public key BIT STRING\r\n const publicKeyBitString = asn1.children[1];\r\n if (publicKeyBitString.tag !== 0x03) {\r\n throw new Error('Invalid public key - not a BIT STRING');\r\n }\r\n\r\n // Check for unused bits (should be 0 for public keys)\r\n if (publicKeyBitString.value[0] !== 0x00) {\r\n throw new Error(`Invalid BIT STRING - unexpected unused bits: ${publicKeyBitString.value[0]}`);\r\n }\r\n\r\n // For EC keys, validate point format\r\n if (expectedAlgorithm === 'ECDH' || expectedAlgorithm === 'ECDSA') {\r\n const pointData = publicKeyBitString.value.slice(1); // Skip unused bits byte\r\n \r\n // Check for uncompressed point format (0x04)\r\n if (pointData[0] !== 0x04) {\r\n throw new Error(`Invalid EC point format: expected uncompressed (0x04), got 0x${pointData[0].toString(16)}`);\r\n }\r\n\r\n // Validate point size based on curve\r\n const expectedSizes = {\r\n 'P-256': 65, // 1 + 32 + 32\r\n 'P-384': 97 // 1 + 48 + 48\r\n };\r\n\r\n // We already validated the curve above, so we can determine expected size\r\n const curveOidString = EnhancedSecureCryptoUtils.oidToString(algIdentifier.children[1].value);\r\n const curveName = curveOidString === '1.2.840.10045.3.1.7' ? 'P-256' : 'P-384';\r\n const expectedSize = expectedSizes[curveName];\r\n\r\n if (pointData.length !== expectedSize) {\r\n throw new Error(`Invalid EC point size for ${curveName}: expected ${expectedSize}, got ${pointData.length}`);\r\n }\r\n }\r\n\r\n // Additional validation: try to import the key\r\n try {\r\n const algorithm = expectedAlgorithm === 'ECDSA' || expectedAlgorithm === 'ECDH'\r\n ? { name: expectedAlgorithm, namedCurve: 'P-384' }\r\n : { name: expectedAlgorithm };\r\n\r\n const usages = expectedAlgorithm === 'ECDSA' ? ['verify'] : [];\r\n \r\n await crypto.subtle.importKey('spki', keyBytes.buffer, algorithm, false, usages);\r\n } catch (importError) {\r\n // Try P-256 as fallback for EC keys\r\n if (expectedAlgorithm === 'ECDSA' || expectedAlgorithm === 'ECDH') {\r\n try {\r\n const algorithm = { name: expectedAlgorithm, namedCurve: 'P-256' };\r\n const usages = expectedAlgorithm === 'ECDSA' ? ['verify'] : [];\r\n await crypto.subtle.importKey('spki', keyBytes.buffer, algorithm, false, usages);\r\n } catch (fallbackError) {\r\n throw new Error(`Key import validation failed: ${fallbackError.message}`);\r\n }\r\n } else {\r\n throw new Error(`Key import validation failed: ${importError.message}`);\r\n }\r\n }\r\n\r\n // Removed key structure validation info logging\r\n\r\n return true;\r\n } catch (err) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Key structure validation failed', {\r\n error: err.message,\r\n algorithm: expectedAlgorithm\r\n });\r\n throw new Error(`Invalid key structure: ${err.message}`);\r\n }\r\n }\r\n\r\n // ASN.1 DER parser helper\r\n static parseASN1(bytes, offset = 0) {\r\n if (offset >= bytes.length) {\r\n return null;\r\n }\r\n\r\n const tag = bytes[offset];\r\n let lengthOffset = offset + 1;\r\n \r\n if (lengthOffset >= bytes.length) {\r\n throw new Error('Truncated ASN.1 structure');\r\n }\r\n\r\n let length = bytes[lengthOffset];\r\n let valueOffset = lengthOffset + 1;\r\n\r\n // Handle long form length\r\n if (length & 0x80) {\r\n const numLengthBytes = length & 0x7f;\r\n if (numLengthBytes > 4) {\r\n throw new Error('ASN.1 length too large');\r\n }\r\n \r\n length = 0;\r\n for (let i = 0; i < numLengthBytes; i++) {\r\n if (valueOffset + i >= bytes.length) {\r\n throw new Error('Truncated ASN.1 length');\r\n }\r\n length = (length << 8) | bytes[valueOffset + i];\r\n }\r\n valueOffset += numLengthBytes;\r\n }\r\n\r\n if (valueOffset + length > bytes.length) {\r\n throw new Error('ASN.1 structure extends beyond data');\r\n }\r\n\r\n const value = bytes.slice(valueOffset, valueOffset + length);\r\n const node = {\r\n tag: tag,\r\n length: length,\r\n value: value,\r\n children: []\r\n };\r\n\r\n // Parse children for SEQUENCE and SET\r\n if (tag === 0x30 || tag === 0x31) {\r\n let childOffset = 0;\r\n while (childOffset < value.length) {\r\n const child = EnhancedSecureCryptoUtils.parseASN1(value, childOffset);\r\n if (!child) break;\r\n node.children.push(child);\r\n childOffset = childOffset + 1 + child.lengthBytes + child.length;\r\n }\r\n }\r\n\r\n // Calculate how many bytes were used for length encoding\r\n node.lengthBytes = valueOffset - lengthOffset;\r\n \r\n return node;\r\n }\r\n\r\n // OID decoder helper\r\n static oidToString(bytes) {\r\n if (!bytes || bytes.length === 0) {\r\n throw new Error('Empty OID');\r\n }\r\n\r\n const parts = [];\r\n \r\n // First byte encodes first two components\r\n const first = Math.floor(bytes[0] / 40);\r\n const second = bytes[0] % 40;\r\n parts.push(first);\r\n parts.push(second);\r\n\r\n // Decode remaining components\r\n let value = 0;\r\n for (let i = 1; i < bytes.length; i++) {\r\n value = (value << 7) | (bytes[i] & 0x7f);\r\n if (!(bytes[i] & 0x80)) {\r\n parts.push(value);\r\n value = 0;\r\n }\r\n }\r\n\r\n return parts.join('.');\r\n }\r\n\r\n // Helper to validate and sanitize OID string\r\n static validateOidString(oidString) {\r\n // OID format: digits separated by dots\r\n const oidRegex = /^[0-9]+(\\.[0-9]+)*$/;\r\n if (!oidRegex.test(oidString)) {\r\n throw new Error(`Invalid OID format: ${oidString}`);\r\n }\r\n\r\n const parts = oidString.split('.').map(Number);\r\n \r\n // First component must be 0, 1, or 2\r\n if (parts[0] > 2) {\r\n throw new Error(`Invalid OID first component: ${parts[0]}`);\r\n }\r\n\r\n // If first component is 0 or 1, second must be <= 39\r\n if ((parts[0] === 0 || parts[0] === 1) && parts[1] > 39) {\r\n throw new Error(`Invalid OID second component: ${parts[1]} (must be <= 39 for first component ${parts[0]})`);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n // Export public key for transmission with signature \r\n static async exportPublicKeyWithSignature(publicKey, signingKey, keyType = 'ECDH') {\r\n try {\r\n // Validate key type\r\n if (!['ECDH', 'ECDSA'].includes(keyType)) {\r\n throw new Error('Invalid key type');\r\n }\r\n \r\n const exported = await crypto.subtle.exportKey('spki', publicKey);\r\n const keyData = Array.from(new Uint8Array(exported));\r\n \r\n await EnhancedSecureCryptoUtils.validateKeyStructure(keyData, keyType);\r\n \r\n // Create signed key package\r\n const keyPackage = {\r\n keyType,\r\n keyData,\r\n timestamp: Date.now(),\r\n version: '4.0'\r\n };\r\n \r\n // Sign the key package\r\n const packageString = JSON.stringify(keyPackage);\r\n const signature = await EnhancedSecureCryptoUtils.signData(signingKey, packageString);\r\n \r\n const signedPackage = {\r\n ...keyPackage,\r\n signature\r\n };\r\n \r\n // Removed public key export with signature info logging\r\n \r\n return signedPackage;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Public key export failed', {\r\n error: error.message,\r\n keyType\r\n });\r\n throw new Error(`Failed to export ${keyType} key: ${error.message}`);\r\n }\r\n }\r\n\r\n // Import and verify signed public key\r\n static async importSignedPublicKey(signedPackage, verifyingKey, expectedKeyType = 'ECDH') {\r\n try {\r\n // Debug logs removed\r\n \r\n // Validate package structure\r\n if (!signedPackage || typeof signedPackage !== 'object') {\r\n throw new Error('Invalid signed package format');\r\n }\r\n \r\n const { keyType, keyData, timestamp, version, signature } = signedPackage;\r\n \r\n if (!keyType || !keyData || !timestamp || !signature) {\r\n throw new Error('Missing required fields in signed package');\r\n }\r\n \r\n if (!EnhancedSecureCryptoUtils.constantTimeCompare(keyType, expectedKeyType)) {\r\n throw new Error(`Key type mismatch: expected ${expectedKeyType}, got ${keyType}`);\r\n }\r\n \r\n // Check timestamp (reject keys older than 1 hour)\r\n const keyAge = Date.now() - timestamp;\r\n if (keyAge > 3600000) {\r\n throw new Error('Signed key package is too old');\r\n }\r\n \r\n await EnhancedSecureCryptoUtils.validateKeyStructure(keyData, keyType);\r\n \r\n // Verify signature\r\n const packageCopy = { keyType, keyData, timestamp, version };\r\n const packageString = JSON.stringify(packageCopy);\r\n // Debug logs removed\r\n const isValidSignature = await EnhancedSecureCryptoUtils.verifySignature(verifyingKey, signature, packageString);\r\n // Debug logs removed\r\n \r\n if (!isValidSignature) {\r\n throw new Error('Invalid signature on key package - possible MITM attack');\r\n }\r\n \r\n // Import the key with fallback support\r\n const keyBytes = new Uint8Array(keyData);\r\n \r\n // Try P-384 first\r\n try {\r\n const algorithm = keyType === 'ECDH' ?\r\n { name: 'ECDH', namedCurve: 'P-384' }\r\n : { name: 'ECDSA', namedCurve: 'P-384' };\r\n \r\n const keyUsages = keyType === 'ECDH' ? [] : ['verify'];\r\n \r\n const publicKey = await crypto.subtle.importKey(\r\n 'spki',\r\n keyBytes,\r\n algorithm,\r\n false, // Non-extractable\r\n keyUsages\r\n );\r\n \r\n // Removed public key import info logging\r\n \r\n return publicKey;\r\n } catch (p384Error) {\r\n // Fallback to P-256\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'Elliptic curve P-384 import failed, switching curve', { error: p384Error.message });\r\n \r\n const algorithm = keyType === 'ECDH' ?\r\n { name: 'ECDH', namedCurve: 'P-256' }\r\n : { name: 'ECDSA', namedCurve: 'P-256' };\r\n \r\n const keyUsages = keyType === 'ECDH' ? [] : ['verify'];\r\n \r\n const publicKey = await crypto.subtle.importKey(\r\n 'spki',\r\n keyBytes,\r\n algorithm,\r\n false, // Non-extractable\r\n keyUsages\r\n );\r\n \r\n // Removed public key import info logging\r\n \r\n return publicKey;\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Signed public key import failed', {\r\n error: error.message,\r\n expectedKeyType\r\n });\r\n throw new Error(`Failed to import the signed key: ${error.message}`);\r\n }\r\n }\r\n\r\n // Legacy export for backward compatibility\r\n static async exportPublicKey(publicKey) {\r\n try {\r\n const exported = await crypto.subtle.exportKey('spki', publicKey);\r\n const keyData = Array.from(new Uint8Array(exported));\r\n \r\n await EnhancedSecureCryptoUtils.validateKeyStructure(keyData, 'ECDH');\r\n \r\n // Removed legacy public key export info logging\r\n return keyData;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Legacy public key export failed', { error: error.message });\r\n throw new Error('Failed to export the public key');\r\n }\r\n }\r\n\r\n // Legacy import for backward compatibility with fallback\r\n static async importPublicKey(keyData) {\r\n try {\r\n await EnhancedSecureCryptoUtils.validateKeyStructure(keyData, 'ECDH');\r\n \r\n const keyBytes = new Uint8Array(keyData);\r\n \r\n // Try P-384 first\r\n try {\r\n const publicKey = await crypto.subtle.importKey(\r\n 'spki',\r\n keyBytes,\r\n {\r\n name: 'ECDH',\r\n namedCurve: 'P-384'\r\n },\r\n false, // Non-extractable\r\n []\r\n );\r\n \r\n // Removed legacy public key import info logging\r\n return publicKey;\r\n } catch (p384Error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'P-384 import failed, trying P-256', { error: p384Error.message });\r\n \r\n // Fallback to P-256\r\n const publicKey = await crypto.subtle.importKey(\r\n 'spki',\r\n keyBytes,\r\n {\r\n name: 'ECDH',\r\n namedCurve: 'P-256'\r\n },\r\n false, // Non-extractable\r\n []\r\n );\r\n \r\n // Removed legacy public key import info logging\r\n return publicKey;\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Legacy public key import failed', { error: error.message });\r\n throw new Error('Failed to import the public key');\r\n }\r\n }\r\n\r\n\r\n // Method to check if a key is trusted\r\n static isKeyTrusted(keyOrFingerprint) {\r\n if (keyOrFingerprint instanceof CryptoKey) {\r\n const meta = EnhancedSecureCryptoUtils._keyMetadata.get(keyOrFingerprint);\r\n return meta ? meta.trusted === true : false;\r\n } else if (keyOrFingerprint && keyOrFingerprint._securityMetadata) {\r\n // Check by key metadata\r\n return keyOrFingerprint._securityMetadata.trusted === true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n static async importPublicKeyFromSignedPackage(signedPackage, verifyingKey = null, options = {}) {\r\n try {\r\n if (!signedPackage || !signedPackage.keyData || !signedPackage.signature) {\r\n throw new Error('Invalid signed key package format');\r\n }\r\n\r\n // Validate all required fields are present\r\n const requiredFields = ['keyData', 'signature', 'keyType', 'timestamp', 'version'];\r\n const missingFields = requiredFields.filter(field => !signedPackage[field]);\r\n\r\n if (missingFields.length > 0) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Missing required fields in signed package', {\r\n missingFields: missingFields,\r\n availableFields: Object.keys(signedPackage)\r\n });\r\n throw new Error(`Required fields are missing in the signed package: ${missingFields.join(', ')}`);\r\n }\r\n\r\n // SECURITY ENHANCEMENT: MANDATORY signature verification for signed packages\r\n if (!verifyingKey) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'SECURITY VIOLATION: Signed package received without verifying key', {\r\n keyType: signedPackage.keyType,\r\n keySize: signedPackage.keyData.length,\r\n timestamp: signedPackage.timestamp,\r\n version: signedPackage.version,\r\n securityRisk: 'HIGH - Potential MITM attack vector'\r\n });\r\n\r\n // REJECT the signed package if no verifying key provided\r\n throw new Error('CRITICAL SECURITY ERROR: Signed key package received without a verification key. ' +\r\n 'This may indicate a possible MITM attack attempt. Import rejected for security reasons.');\r\n }\r\n\r\n // \u041E\u0411\u041D\u041E\u0412\u041B\u0415\u041D\u041E: \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C \u0443\u043B\u0443\u0447\u0448\u0435\u043D\u043D\u0443\u044E \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044E\r\n await EnhancedSecureCryptoUtils.validateKeyStructure(signedPackage.keyData, signedPackage.keyType || 'ECDH');\r\n\r\n // MANDATORY signature verification when verifyingKey is provided\r\n const packageCopy = { ...signedPackage };\r\n delete packageCopy.signature;\r\n const packageString = JSON.stringify(packageCopy);\r\n const isValidSignature = await EnhancedSecureCryptoUtils.verifySignature(verifyingKey, signedPackage.signature, packageString);\r\n\r\n if (!isValidSignature) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'SECURITY BREACH: Invalid signature detected - MITM attack prevented', {\r\n keyType: signedPackage.keyType,\r\n keySize: signedPackage.keyData.length,\r\n timestamp: signedPackage.timestamp,\r\n version: signedPackage.version,\r\n attackPrevented: true\r\n });\r\n throw new Error('CRITICAL SECURITY ERROR: Invalid key signature detected. ' +\r\n 'This indicates a possible MITM attack attempt. Key import rejected.');\r\n }\r\n\r\n // Additional MITM protection: Check for key reuse and suspicious patterns\r\n const keyFingerprint = await EnhancedSecureCryptoUtils.calculateKeyFingerprint(signedPackage.keyData);\r\n\r\n // Log successful verification with security details\r\n // Removed signature verification pass details to avoid key-related logging\r\n\r\n // Import the public key with fallback\r\n const keyBytes = new Uint8Array(signedPackage.keyData);\r\n const keyType = signedPackage.keyType || 'ECDH';\r\n\r\n // Try P-384 first\r\n try {\r\n const publicKey = await crypto.subtle.importKey(\r\n 'spki',\r\n keyBytes,\r\n {\r\n name: keyType,\r\n namedCurve: 'P-384'\r\n },\r\n false, // Non-extractable\r\n keyType === 'ECDSA' ? ['verify'] : []\r\n );\r\n\r\n // Use WeakMap to store metadata\r\n EnhancedSecureCryptoUtils._keyMetadata.set(publicKey, {\r\n trusted: true,\r\n verificationStatus: 'VERIFIED_SECURE',\r\n verificationTimestamp: Date.now()\r\n });\r\n\r\n return publicKey;\r\n } catch (p384Error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'P-384 import failed, trying P-256', { error: p384Error.message });\r\n\r\n // Fallback to P-256\r\n const publicKey = await crypto.subtle.importKey(\r\n 'spki',\r\n keyBytes,\r\n {\r\n name: keyType,\r\n namedCurve: 'P-256'\r\n },\r\n false, // Non-extractable\r\n keyType === 'ECDSA' ? ['verify'] : []\r\n );\r\n\r\n // Use WeakMap to store metadata\r\n EnhancedSecureCryptoUtils._keyMetadata.set(publicKey, {\r\n trusted: true,\r\n verificationStatus: 'VERIFIED_SECURE',\r\n verificationTimestamp: Date.now()\r\n });\r\n\r\n return publicKey;\r\n }\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Signed package key import failed', {\r\n error: error.message,\r\n securityImplications: 'Potential security breach prevented'\r\n });\r\n throw new Error(`Failed to import the public key from the signed package: ${error.message}`);\r\n }\r\n }\r\n\r\n // Enhanced key derivation with metadata protection and 64-byte salt\r\n static async deriveSharedKeys(privateKey, publicKey, salt) {\r\n try {\r\n // Removed detailed key derivation logging\r\n \r\n // Validate input parameters are CryptoKey instances\r\n if (!(privateKey instanceof CryptoKey)) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Private key is not a CryptoKey', {\r\n privateKeyType: typeof privateKey,\r\n privateKeyAlgorithm: privateKey?.algorithm?.name\r\n });\r\n throw new Error('The private key is not a valid CryptoKey.');\r\n }\r\n \r\n if (!(publicKey instanceof CryptoKey)) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Public key is not a CryptoKey', {\r\n publicKeyType: typeof publicKey,\r\n publicKeyAlgorithm: publicKey?.algorithm?.name\r\n });\r\n throw new Error('The public key is not a valid CryptoKey.');\r\n }\r\n \r\n // Validate salt size (should be 64 bytes for enhanced security)\r\n if (!salt || salt.length !== 64) {\r\n throw new Error('Salt must be exactly 64 bytes for enhanced security');\r\n }\r\n \r\n const saltBytes = new Uint8Array(salt);\r\n const encoder = new TextEncoder();\r\n \r\n // Step 1: Derive raw ECDH shared secret using pure ECDH\r\n let rawSharedSecret;\r\n try {\r\n // Removed detailed key derivation logging\r\n \r\n // Use pure ECDH to derive raw key material\r\n const rawKeyMaterial = await crypto.subtle.deriveKey(\r\n {\r\n name: 'ECDH',\r\n public: publicKey\r\n },\r\n privateKey,\r\n {\r\n name: 'AES-GCM',\r\n length: 256\r\n },\r\n true, // Extractable\r\n ['encrypt', 'decrypt']\r\n );\r\n \r\n // Export the raw key material\r\n const rawKeyData = await crypto.subtle.exportKey('raw', rawKeyMaterial);\r\n \r\n // Import as HKDF key material for further derivation\r\n rawSharedSecret = await crypto.subtle.importKey(\r\n 'raw',\r\n rawKeyData,\r\n {\r\n name: 'HKDF',\r\n hash: 'SHA-256'\r\n },\r\n false,\r\n ['deriveKey']\r\n );\r\n \r\n // Removed detailed key derivation logging\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'ECDH derivation failed', { \r\n error: error.message\r\n });\r\n throw error;\r\n }\r\n \r\n // Step 2: Use HKDF to derive specific keys directly\r\n // Removed detailed key derivation logging\r\n\r\n // Step 3: Derive specific keys using HKDF with unique info parameters\r\n // Each key uses unique info parameter for proper separation\r\n \r\n // Derive message encryption key (messageKey)\r\n let messageKey;\r\n messageKey = await crypto.subtle.deriveKey(\r\n {\r\n name: 'HKDF',\r\n hash: 'SHA-256',\r\n salt: saltBytes,\r\n info: encoder.encode('message-encryption-v4')\r\n },\r\n rawSharedSecret,\r\n {\r\n name: 'AES-GCM',\r\n length: 256\r\n },\r\n false, // Non-extractable for enhanced security\r\n ['encrypt', 'decrypt']\r\n );\r\n\r\n // Derive MAC key for message authentication\r\n let macKey;\r\n macKey = await crypto.subtle.deriveKey(\r\n {\r\n name: 'HKDF',\r\n hash: 'SHA-256',\r\n salt: saltBytes,\r\n info: encoder.encode('message-authentication-v4')\r\n },\r\n rawSharedSecret,\r\n {\r\n name: 'HMAC',\r\n hash: 'SHA-256'\r\n },\r\n false, // Non-extractable\r\n ['sign', 'verify']\r\n );\r\n\r\n // Derive Perfect Forward Secrecy key (pfsKey)\r\n let pfsKey;\r\n pfsKey = await crypto.subtle.deriveKey(\r\n {\r\n name: 'HKDF',\r\n hash: 'SHA-256',\r\n salt: saltBytes,\r\n info: encoder.encode('perfect-forward-secrecy-v4')\r\n },\r\n rawSharedSecret,\r\n {\r\n name: 'AES-GCM',\r\n length: 256\r\n },\r\n false, // Non-extractable\r\n ['encrypt', 'decrypt']\r\n );\r\n\r\n // Derive separate metadata encryption key\r\n let metadataKey;\r\n metadataKey = await crypto.subtle.deriveKey(\r\n {\r\n name: 'HKDF',\r\n hash: 'SHA-256',\r\n salt: saltBytes,\r\n info: encoder.encode('metadata-protection-v4')\r\n },\r\n rawSharedSecret,\r\n {\r\n name: 'AES-GCM',\r\n length: 256\r\n },\r\n false, // Non-extractable\r\n ['encrypt', 'decrypt']\r\n );\r\n\r\n // Generate temporary extractable key for fingerprint calculation\r\n let fingerprintKey;\r\n fingerprintKey = await crypto.subtle.deriveKey(\r\n {\r\n name: 'HKDF',\r\n hash: 'SHA-256',\r\n salt: saltBytes,\r\n info: encoder.encode('fingerprint-generation-v4')\r\n },\r\n rawSharedSecret,\r\n {\r\n name: 'AES-GCM',\r\n length: 256\r\n },\r\n true, // Extractable only for fingerprint\r\n ['encrypt', 'decrypt']\r\n );\r\n\r\n // Generate key fingerprint for verification\r\n const fingerprintKeyData = await crypto.subtle.exportKey('raw', fingerprintKey);\r\n const fingerprint = await EnhancedSecureCryptoUtils.generateKeyFingerprint(Array.from(new Uint8Array(fingerprintKeyData)));\r\n\r\n // Validate that all derived keys are CryptoKey instances\r\n if (!(messageKey instanceof CryptoKey)) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Derived message key is not a CryptoKey', {\r\n messageKeyType: typeof messageKey,\r\n messageKeyAlgorithm: messageKey?.algorithm?.name\r\n });\r\n throw new Error('The derived message key is not a valid CryptoKey.');\r\n }\r\n \r\n if (!(macKey instanceof CryptoKey)) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Derived MAC key is not a CryptoKey', {\r\n macKeyType: typeof macKey,\r\n macKeyAlgorithm: macKey?.algorithm?.name\r\n });\r\n throw new Error('The derived MAC key is not a valid CryptoKey.');\r\n }\r\n \r\n if (!(pfsKey instanceof CryptoKey)) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Derived PFS key is not a CryptoKey', {\r\n pfsKeyType: typeof pfsKey,\r\n pfsKeyAlgorithm: pfsKey?.algorithm?.name\r\n });\r\n throw new Error('The derived PFS key is not a valid CryptoKey.');\r\n }\r\n \r\n if (!(metadataKey instanceof CryptoKey)) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Derived metadata key is not a CryptoKey', {\r\n metadataKeyType: typeof metadataKey,\r\n metadataKeyAlgorithm: metadataKey?.algorithm?.name\r\n });\r\n throw new Error('The derived metadata key is not a valid CryptoKey.');\r\n }\r\n\r\n // Removed detailed key derivation success logging\r\n\r\n return {\r\n messageKey, // Renamed from encryptionKey for clarity\r\n macKey,\r\n pfsKey, // Added Perfect Forward Secrecy key\r\n metadataKey,\r\n fingerprint,\r\n timestamp: Date.now(),\r\n version: '4.0'\r\n };\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Enhanced key derivation failed', { \r\n error: error.message,\r\n errorStack: error.stack,\r\n privateKeyType: typeof privateKey,\r\n publicKeyType: typeof publicKey,\r\n saltLength: salt?.length,\r\n privateKeyAlgorithm: privateKey?.algorithm?.name,\r\n publicKeyAlgorithm: publicKey?.algorithm?.name\r\n });\r\n throw new Error(`Failed to create shared encryption keys: ${error.message}`);\r\n }\r\n }\r\n\r\n static async generateKeyFingerprint(keyData) {\r\n const keyBuffer = new Uint8Array(keyData);\r\n const hashBuffer = await crypto.subtle.digest('SHA-384', keyBuffer);\r\n const hashArray = Array.from(new Uint8Array(hashBuffer));\r\n return hashArray.slice(0, 12).map(b => b.toString(16).padStart(2, '0')).join(':');\r\n }\r\n\r\n // Generate mutual authentication challenge\r\n static generateMutualAuthChallenge() {\r\n const challenge = crypto.getRandomValues(new Uint8Array(48)); // Increased to 48 bytes\r\n const timestamp = Date.now();\r\n const nonce = crypto.getRandomValues(new Uint8Array(16));\r\n \r\n return {\r\n challenge: Array.from(challenge),\r\n timestamp,\r\n nonce: Array.from(nonce),\r\n version: '4.0'\r\n };\r\n }\r\n\r\n // Create cryptographic proof for mutual authentication\r\n static async createAuthProof(challenge, privateKey, publicKey) {\r\n try {\r\n if (!challenge || !challenge.challenge || !challenge.timestamp || !challenge.nonce) {\r\n throw new Error('Invalid challenge structure');\r\n }\r\n \r\n // Check challenge age (max 2 minutes)\r\n const challengeAge = Date.now() - challenge.timestamp;\r\n if (challengeAge > 120000) {\r\n throw new Error('Challenge expired');\r\n }\r\n \r\n // Create proof data\r\n const proofData = {\r\n challenge: challenge.challenge,\r\n timestamp: challenge.timestamp,\r\n nonce: challenge.nonce,\r\n responseTimestamp: Date.now(),\r\n publicKeyHash: await EnhancedSecureCryptoUtils.hashPublicKey(publicKey)\r\n };\r\n \r\n // Sign the proof\r\n const proofString = JSON.stringify(proofData);\r\n const signature = await EnhancedSecureCryptoUtils.signData(privateKey, proofString);\r\n \r\n const proof = {\r\n ...proofData,\r\n signature,\r\n version: '4.0'\r\n };\r\n \r\n EnhancedSecureCryptoUtils.secureLog.log('info', 'Authentication proof created', {\r\n challengeAge: Math.round(challengeAge / 1000) + 's'\r\n });\r\n \r\n return proof;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Authentication proof creation failed', { error: error.message });\r\n throw new Error(`Failed to create cryptographic proof: ${error.message}`);\r\n }\r\n }\r\n\r\n // Verify mutual authentication proof\r\n static async verifyAuthProof(proof, challenge, publicKey) {\r\n try {\r\n await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 20) + 5));\r\n // Assert the public key is valid and has the correct usage\r\n EnhancedSecureCryptoUtils.assertCryptoKey(publicKey, 'ECDSA', ['verify']);\r\n\r\n if (!proof || !challenge || !publicKey) {\r\n throw new Error('Missing required parameters for proof verification');\r\n }\r\n\r\n // Validate proof structure\r\n const requiredFields = ['challenge', 'timestamp', 'nonce', 'responseTimestamp', 'publicKeyHash', 'signature'];\r\n for (const field of requiredFields) {\r\n if (!proof[field]) {\r\n throw new Error(`Missing required field: ${field}`);\r\n }\r\n }\r\n\r\n // Verify challenge matches\r\n if (!EnhancedSecureCryptoUtils.constantTimeCompareArrays(proof.challenge, challenge.challenge) ||\r\n proof.timestamp !== challenge.timestamp ||\r\n !EnhancedSecureCryptoUtils.constantTimeCompareArrays(proof.nonce, challenge.nonce)) {\r\n throw new Error('Challenge mismatch - possible replay attack');\r\n }\r\n\r\n // Check response time (max 30 minutes for better UX)\r\n const responseAge = Date.now() - proof.responseTimestamp;\r\n if (responseAge > 1800000) {\r\n throw new Error('Proof response expired');\r\n }\r\n\r\n // Verify public key hash\r\n const expectedHash = await EnhancedSecureCryptoUtils.hashPublicKey(publicKey);\r\n if (!EnhancedSecureCryptoUtils.constantTimeCompare(proof.publicKeyHash, expectedHash)) {\r\n throw new Error('Public key hash mismatch');\r\n }\r\n\r\n // Verify signature\r\n const proofCopy = { ...proof };\r\n delete proofCopy.signature;\r\n const proofString = JSON.stringify(proofCopy);\r\n const isValidSignature = await EnhancedSecureCryptoUtils.verifySignature(publicKey, proof.signature, proofString);\r\n\r\n if (!isValidSignature) {\r\n throw new Error('Invalid proof signature');\r\n }\r\n\r\n EnhancedSecureCryptoUtils.secureLog.log('info', 'Authentication proof verified successfully', {\r\n responseAge: Math.round(responseAge / 1000) + 's'\r\n });\r\n\r\n return true;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Authentication proof verification failed', { error: error.message });\r\n throw new Error(`Failed to verify cryptographic proof: ${error.message}`);\r\n }\r\n }\r\n\r\n // Hash public key for verification\r\n static async hashPublicKey(publicKey) {\r\n try {\r\n const exported = await crypto.subtle.exportKey('spki', publicKey);\r\n const hash = await crypto.subtle.digest('SHA-384', exported);\r\n const hashArray = Array.from(new Uint8Array(hash));\r\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Public key hashing failed', { error: error.message });\r\n throw new Error('Failed to create hash of the public key');\r\n }\r\n }\r\n\r\n // Legacy authentication challenge for backward compatibility\r\n static generateAuthChallenge() {\r\n const challenge = crypto.getRandomValues(new Uint8Array(32));\r\n return Array.from(challenge);\r\n }\r\n\r\n // Generate verification code for out-of-band authentication\r\n static generateVerificationCode() {\r\n const chars = '0123456789ABCDEF';\r\n const charCount = chars.length;\r\n let result = '';\r\n \r\n // Use rejection sampling to avoid bias\r\n for (let i = 0; i < 6; i++) {\r\n let randomByte;\r\n do {\r\n randomByte = crypto.getRandomValues(new Uint8Array(1))[0];\r\n } while (randomByte >= 256 - (256 % charCount)); // Reject biased values\r\n \r\n result += chars[randomByte % charCount];\r\n }\r\n \r\n return result.match(/.{1,2}/g).join('-');\r\n }\r\n\r\n // Enhanced message encryption with metadata protection and sequence numbers\r\n static async encryptMessage(message, encryptionKey, macKey, metadataKey, messageId, sequenceNumber = 0) {\r\n try {\r\n if (!message || typeof message !== 'string') {\r\n throw new Error('Invalid message format');\r\n }\r\n\r\n EnhancedSecureCryptoUtils.assertCryptoKey(encryptionKey, 'AES-GCM', ['encrypt']);\r\n EnhancedSecureCryptoUtils.assertCryptoKey(macKey, 'HMAC', ['sign']);\r\n EnhancedSecureCryptoUtils.assertCryptoKey(metadataKey, 'AES-GCM', ['encrypt']);\r\n\r\n const encoder = new TextEncoder();\r\n const messageData = encoder.encode(message);\r\n const messageIv = crypto.getRandomValues(new Uint8Array(12));\r\n const metadataIv = crypto.getRandomValues(new Uint8Array(12));\r\n const timestamp = Date.now();\r\n\r\n const paddingSize = 16 - (messageData.length % 16);\r\n const paddedMessage = new Uint8Array(messageData.length + paddingSize);\r\n paddedMessage.set(messageData);\r\n const padding = crypto.getRandomValues(new Uint8Array(paddingSize));\r\n paddedMessage.set(padding, messageData.length);\r\n\r\n const encryptedMessage = await crypto.subtle.encrypt(\r\n { name: 'AES-GCM', iv: messageIv },\r\n encryptionKey,\r\n paddedMessage\r\n );\r\n\r\n const metadata = {\r\n id: messageId,\r\n timestamp: timestamp,\r\n sequenceNumber: sequenceNumber,\r\n originalLength: messageData.length,\r\n version: '4.0'\r\n };\r\n\r\n const metadataStr = JSON.stringify(EnhancedSecureCryptoUtils.sortObjectKeys(metadata));\r\n const encryptedMetadata = await crypto.subtle.encrypt(\r\n { name: 'AES-GCM', iv: metadataIv },\r\n metadataKey,\r\n encoder.encode(metadataStr)\r\n );\r\n\r\n const payload = {\r\n messageIv: Array.from(messageIv),\r\n messageData: Array.from(new Uint8Array(encryptedMessage)),\r\n metadataIv: Array.from(metadataIv),\r\n metadataData: Array.from(new Uint8Array(encryptedMetadata)),\r\n version: '4.0'\r\n };\r\n\r\n const sortedPayload = EnhancedSecureCryptoUtils.sortObjectKeys(payload);\r\n const payloadStr = JSON.stringify(sortedPayload);\r\n\r\n const mac = await crypto.subtle.sign(\r\n 'HMAC',\r\n macKey,\r\n encoder.encode(payloadStr)\r\n );\r\n\r\n payload.mac = Array.from(new Uint8Array(mac));\r\n\r\n // Logging removed to avoid noisy console output\r\n\r\n return payload;\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Message encryption failed', {\r\n error: error.message,\r\n messageId\r\n });\r\n throw new Error(`Failed to encrypt the message: ${error.message}`);\r\n }\r\n }\r\n\r\n // Enhanced message decryption with metadata protection and sequence validation\r\n static async decryptMessage(encryptedPayload, encryptionKey, macKey, metadataKey, expectedSequenceNumber = null) {\r\n try {\r\n EnhancedSecureCryptoUtils.assertCryptoKey(encryptionKey, 'AES-GCM', ['decrypt']);\r\n EnhancedSecureCryptoUtils.assertCryptoKey(macKey, 'HMAC', ['verify']);\r\n EnhancedSecureCryptoUtils.assertCryptoKey(metadataKey, 'AES-GCM', ['decrypt']);\r\n\r\n const requiredFields = ['messageIv', 'messageData', 'metadataIv', 'metadataData', 'mac', 'version'];\r\n for (const field of requiredFields) {\r\n if (!encryptedPayload[field]) {\r\n throw new Error(`Missing required field: ${field}`);\r\n }\r\n }\r\n\r\n const payloadCopy = { ...encryptedPayload };\r\n delete payloadCopy.mac;\r\n const sortedPayloadCopy = EnhancedSecureCryptoUtils.sortObjectKeys(payloadCopy);\r\n const payloadStr = JSON.stringify(sortedPayloadCopy);\r\n\r\n const macValid = await crypto.subtle.verify(\r\n 'HMAC',\r\n macKey,\r\n new Uint8Array(encryptedPayload.mac),\r\n new TextEncoder().encode(payloadStr)\r\n );\r\n\r\n if (!macValid) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'MAC verification failed', {\r\n payloadFields: Object.keys(encryptedPayload),\r\n macLength: encryptedPayload.mac?.length\r\n });\r\n throw new Error('Message authentication failed - possible tampering');\r\n }\r\n\r\n const metadataIv = new Uint8Array(encryptedPayload.metadataIv);\r\n const metadataData = new Uint8Array(encryptedPayload.metadataData);\r\n\r\n const decryptedMetadataBuffer = await crypto.subtle.decrypt(\r\n { name: 'AES-GCM', iv: metadataIv },\r\n metadataKey,\r\n metadataData\r\n );\r\n\r\n const metadataStr = new TextDecoder().decode(decryptedMetadataBuffer);\r\n const metadata = JSON.parse(metadataStr);\r\n\r\n if (!metadata.id || !metadata.timestamp || metadata.sequenceNumber === undefined || !metadata.originalLength) {\r\n throw new Error('Invalid metadata structure');\r\n }\r\n\r\n const messageAge = Date.now() - metadata.timestamp;\r\n if (messageAge > 1800000) { // 30 minutes for better UX\r\n throw new Error('Message expired (older than 5 minutes)');\r\n }\r\n\r\n if (expectedSequenceNumber !== null) {\r\n if (metadata.sequenceNumber < expectedSequenceNumber) {\r\n EnhancedSecureCryptoUtils.secureLog.log('warn', 'Received message with lower sequence number, possible queued message', {\r\n expected: expectedSequenceNumber,\r\n received: metadata.sequenceNumber,\r\n messageId: metadata.id\r\n });\r\n } else if (metadata.sequenceNumber > expectedSequenceNumber + 10) {\r\n throw new Error(`Sequence number gap too large: expected around ${expectedSequenceNumber}, got ${metadata.sequenceNumber}`);\r\n }\r\n }\r\n\r\n const messageIv = new Uint8Array(encryptedPayload.messageIv);\r\n const messageData = new Uint8Array(encryptedPayload.messageData);\r\n\r\n const decryptedMessageBuffer = await crypto.subtle.decrypt(\r\n { name: 'AES-GCM', iv: messageIv },\r\n encryptionKey,\r\n messageData\r\n );\r\n\r\n const paddedMessage = new Uint8Array(decryptedMessageBuffer);\r\n const originalMessage = paddedMessage.slice(0, metadata.originalLength);\r\n\r\n const decoder = new TextDecoder();\r\n const message = decoder.decode(originalMessage);\r\n\r\n EnhancedSecureCryptoUtils.secureLog.log('info', 'Message decrypted successfully', {\r\n messageId: metadata.id,\r\n sequenceNumber: metadata.sequenceNumber,\r\n messageAge: Math.round(messageAge / 1000) + 's'\r\n });\r\n\r\n return {\r\n message: message,\r\n messageId: metadata.id,\r\n timestamp: metadata.timestamp,\r\n sequenceNumber: metadata.sequenceNumber\r\n };\r\n } catch (error) {\r\n EnhancedSecureCryptoUtils.secureLog.log('error', 'Message decryption failed', { error: error.message });\r\n throw new Error(`Failed to decrypt the message: ${error.message}`);\r\n }\r\n }\r\n\r\n // Enhanced input sanitization with iterative processing to handle edge cases\r\n static sanitizeMessage(message) {\r\n if (typeof message !== 'string') {\r\n throw new Error('Message must be a string');\r\n }\r\n \r\n // Helper function to apply replacement until stable\r\n function replaceUntilStable(str, pattern, replacement = '') {\r\n let previous;\r\n do {\r\n previous = str;\r\n str = str.replace(pattern, replacement);\r\n } while (str !== previous);\r\n return str;\r\n }\r\n \r\n // Define all dangerous patterns that need to be removed\r\n const dangerousPatterns = [\r\n // Script tags with various formats\r\n /\r\n /\r\n /\n /\n /Hello");
-// Returns: "Hello"
-calculateSecurityLevel()
-javascriptstatic async calculateSecurityLevel(securityManager: any): Promise
-Calculates real-time security level based on active protections.
-Returns:
-typescriptinterface SecurityLevel {
- level: 'HIGH' | 'MEDIUM' | 'LOW' | 'UNKNOWN';
- score: number; // 0-100
- color: 'green' | 'yellow' | 'red';
- verificationResults: Record;
- timestamp: number;
- details: string;
-}
-
-interface VerificationResult {
- passed: boolean;
- details: string;
-}
-generateVerificationCode()
-javascriptstatic generateVerificationCode(): string
-Generates 6-character verification code for out-of-band authentication.
-Returns: Code in format "AB-CD-EF"
-calculateKeyFingerprint()
-javascriptstatic async calculateKeyFingerprint(keyData: number[]): Promise
-Calculates SHA-256 fingerprint of key data for MITM protection.
-encryptData() / decryptData()
-javascriptstatic async encryptData(data: any, password: string): Promise
-static async decryptData(encryptedData: string, password: string): Promise
-High-level encryption/decryption for offer/answer exchange.
-Example:
-javascriptconst password = EnhancedSecureCryptoUtils.generateSecurePassword();
-const encrypted = await EnhancedSecureCryptoUtils.encryptData(
- { message: "secret data" },
- password
-);
-const decrypted = await EnhancedSecureCryptoUtils.decryptData(encrypted, password);
-🌐 EnhancedSecureWebRTCManager
-Manages P2P connections with enhanced security features.
-Constructor
-javascriptnew EnhancedSecureWebRTCManager(
- onMessage: (message: string, type: string) => void,
- onStatusChange: (status: string) => void,
- onKeyExchange: (fingerprint: string) => void,
- onVerificationRequired: (code: string) => void
-)
-Parameters:
-
-onMessage - Callback for received messages
-onStatusChange - Callback for connection state changes
-onKeyExchange - Callback when keys are exchanged
-onVerificationRequired - Callback when verification code is generated
-
-Connection Management
-createSecureOffer()
-javascriptasync createSecureOffer(): Promise
-Creates encrypted connection offer with ECDH keys and authentication.
-Returns:
-typescriptinterface SecureOffer {
- type: 'enhanced_secure_offer';
- sdp: string;
- ecdhPublicKey: SignedPublicKey;
- ecdsaPublicKey: SignedPublicKey;
- salt: number[];
- verificationCode: string;
- authChallenge: AuthChallenge;
- sessionId: string;
- timestamp: number;
- version: string;
- securityLevel: SecurityLevel;
-}
-
-interface SignedPublicKey {
- keyType: 'ECDH' | 'ECDSA';
- keyData: number[];
- timestamp: number;
- version: string;
- signature: number[];
-}
-Example:
-javascriptconst webrtcManager = new EnhancedSecureWebRTCManager(/*...*/);
-const offer = await webrtcManager.createSecureOffer();
-console.log('Verification code:', offer.verificationCode);
-createSecureAnswer()
-javascriptasync createSecureAnswer(offerData: SecureOffer): Promise
-Creates encrypted response to connection offer.
-Returns:
-typescriptinterface SecureAnswer {
- type: 'enhanced_secure_answer';
- sdp: string;
- ecdhPublicKey: SignedPublicKey;
- ecdsaPublicKey: SignedPublicKey;
- authProof: AuthProof;
- timestamp: number;
- version: string;
- securityLevel: SecurityLevel;
-}
-handleSecureAnswer()
-javascriptasync handleSecureAnswer(answerData: SecureAnswer): Promise
-Processes encrypted answer and establishes connection.
-Throws: Error if answer is invalid or authentication fails
-Message Handling
-sendSecureMessage()
-javascriptasync sendSecureMessage(message: string): Promise
-Sends encrypted message through secure channel.
-Parameters:
-
-message - Plaintext message (auto-sanitized)
-
-Features:
-
-Automatic encryption with metadata protection
-Sequence number tracking
-Rate limiting (60 messages/minute)
-Perfect Forward Secrecy key rotation
-
-Example:
-javascriptawait webrtcManager.sendSecureMessage("Hello, secure world!");
-Connection States
-typescripttype ConnectionState =
- | 'disconnected' // No connection
- | 'connecting' // Establishing connection
- | 'verifying' // Verifying security codes
- | 'connected' // Fully connected and verified
- | 'failed' // Connection failed
- | 'reconnecting' // Attempting to reconnect
- | 'peer_disconnected'; // Peer disconnected
-Security Features
-calculateSecurityLevel()
-javascriptasync calculateSecurityLevel(): Promise
-Real-time security assessment with verification of:
-
-✅ Encryption functionality
-✅ ECDH key exchange
-✅ ECDSA signatures
-✅ Mutual authentication
-✅ Metadata protection
-✅ Replay protection
-✅ Non-extractable keys
-✅ Rate limiting
-✅ Perfect Forward Secrecy
-
-shouldRotateKeys()
-javascriptshouldRotateKeys(): boolean
-Determines if PFS key rotation is needed (every 5 minutes or 100 messages).
-isConnected()
-javascriptisConnected(): boolean
-Returns true if WebRTC data channel is open and ready.
-getConnectionInfo()
-javascriptgetConnectionInfo(): ConnectionInfo
-Returns:
-typescriptinterface ConnectionInfo {
- fingerprint: string;
- isConnected: boolean;
- isVerified: boolean;
- connectionState: string;
- iceConnectionState: string;
- verificationCode: string;
-}
-Perfect Forward Secrecy
-rotateKeys()
-javascriptasync rotateKeys(): Promise
-Performs key rotation for Perfect Forward Secrecy.
-Returns: true if rotation successful
-getKeysForVersion()
-javascriptgetKeysForVersion(version: number): KeySet | null
-Retrieves keys for specific version (for decrypting old messages).
-Returns:
-typescriptinterface KeySet {
- encryptionKey: CryptoKey;
- macKey: CryptoKey;
- metadataKey: CryptoKey;
-}
-Connection Control
-disconnect()
-javascriptdisconnect(): void
-Cleanly disconnects and cleans up all resources.
-confirmVerification()
-javascriptconfirmVerification(): void
-Confirms that verification codes match (called after manual verification).
-🧪 Testing
-Unit Testing Examples
-javascript// Test encryption/decryption round-trip
-async function testEncryptionRoundTrip() {
- const originalMessage = 'Test message for encryption';
- const keys = await generateTestKeys();
-
- const encrypted = await EnhancedSecureCryptoUtils.encryptMessage(
- originalMessage,
- keys.encryptionKey,
- keys.macKey,
- keys.metadataKey,
- 'test-id',
- 0
- );
-
- const decrypted = await EnhancedSecureCryptoUtils.decryptMessage(
- encrypted,
- keys.encryptionKey,
- keys.macKey,
- keys.metadataKey
- );
-
- assert.equal(decrypted.message, originalMessage);
- assert.equal(decrypted.messageId, 'test-id');
- assert.equal(decrypted.sequenceNumber, 0);
-}
-
-// Test key generation
-async function testKeyGeneration() {
- const ecdhPair = await EnhancedSecureCryptoUtils.generateECDHKeyPair();
- const ecdsaPair = await EnhancedSecureCryptoUtils.generateECDSAKeyPair();
-
- // Verify key properties
- assert.equal(ecdhPair.privateKey.algorithm.name, 'ECDH');
- assert.equal(ecdhPair.privateKey.algorithm.namedCurve, 'P-384');
- assert.equal(ecdhPair.privateKey.extractable, false);
-
- assert.equal(ecdsaPair.privateKey.algorithm.name, 'ECDSA');
- assert.equal(ecdsaPair.privateKey.algorithm.namedCurve, 'P-384');
- assert.equal(ecdsaPair.privateKey.extractable, false);
-}
-
-// Test signature verification
-async function testSignatureVerification() {
- const keyPair = await EnhancedSecureCryptoUtils.generateECDSAKeyPair();
- const data = 'Test data to sign';
-
- const signature = await EnhancedSecureCryptoUtils.signData(
- keyPair.privateKey,
- data
- );
-
- const isValid = await EnhancedSecureCryptoUtils.verifySignature(
- keyPair.publicKey,
- signature,
- data
- );
-
- assert.equal(isValid, true);
-
- // Test with wrong data
- const invalidVerification = await EnhancedSecureCryptoUtils.verifySignature(
- keyPair.publicKey,
- signature,
- 'Wrong data'
- );
-
- assert.equal(invalidVerification, false);
-}
-
-// Helper function for tests
-async function generateTestKeys() {
- const ecdhPair = await EnhancedSecureCryptoUtils.generateECDHKeyPair();
- const salt = EnhancedSecureCryptoUtils.generateSalt();
-
- // For testing, we'll create a mock "remote" key pair
- const remotePair = await EnhancedSecureCryptoUtils.generateECDHKeyPair();
-
- const sharedKeys = await EnhancedSecureCryptoUtils.deriveSharedKeys(
- ecdhPair.privateKey,
- remotePair.publicKey,
- salt
- );
-
- return sharedKeys;
-}
-Integration Testing
-javascript// Test full P2P connection flow
-async function testP2PConnection() {
- let manager1Messages = [];
- let manager2Messages = [];
-
- const manager1 = new EnhancedSecureWebRTCManager(
- (msg, type) => manager1Messages.push({msg, type}),
- (status) => console.log('Manager1 status:', status),
- (fingerprint) => console.log('Manager1 fingerprint:', fingerprint),
- (code) => console.log('Manager1 verification:', code)
- );
-
- const manager2 = new EnhancedSecureWebRTCManager(
- (msg, type) => manager2Messages.push({msg, type}),
- (status) => console.log('Manager2 status:', status),
- (fingerprint) => console.log('Manager2 fingerprint:', fingerprint),
- (code) => console.log('Manager2 verification:', code)
- );
-
- // Create offer
- const offer = await manager1.createSecureOffer();
-
- // Create answer
- const answer = await manager2.createSecureAnswer(offer);
-
- // Handle answer
- await manager1.handleSecureAnswer(answer);
-
- // Wait for connection
- await waitForConnection(manager1, manager2);
-
- // Verify both are connected
- assert.equal(manager1.isConnected(), true);
- assert.equal(manager2.isConnected(), true);
-
- // Test message exchange
- await manager1.sendSecureMessage('Hello from manager1');
- await manager2.sendSecureMessage('Hello from manager2');
-
- // Wait for messages to arrive
- await new Promise(resolve => setTimeout(resolve, 1000));
-
- // Verify messages were received
- assert.equal(manager2Messages.length > 0, true);
- assert.equal(manager1Messages.length > 0, true);
-}
-
-async function waitForConnection(manager1, manager2, timeout = 10000) {
- const start = Date.now();
-
- while (Date.now() - start < timeout) {
- if (manager1.isConnected() && manager2.isConnected()) {
- return;
- }
- await new Promise(resolve => setTimeout(resolve, 100));
- }
-
- throw new Error('Connection timeout');
-}
-
----
-
-## 🔒 Security Framework APIs
-
-### 🔐 SecureKeyManager
-
-Manages cryptographic keys with WeakMap-based isolation and secure access methods.
-
-#### `_initializeSecureKeyStorage()`
-```javascript
-_initializeSecureKeyStorage(): void
-Initializes secure key storage with WeakMap isolation.
-Example:
-javascriptconst keyManager = new SecureKeyManager();
-keyManager._initializeSecureKeyStorage();
-```
-
-#### `_getSecureKey(keyName)`
-```javascript
-_getSecureKey(keyName: string): CryptoKey | ArrayBuffer | Uint8Array
-Retrieves a key from secure storage with access tracking.
-Parameters:
-- keyName - Name of the key to retrieve
-Returns: The stored key value
-Throws: Error if key not found
-Example:
-javascriptconst encryptionKey = keyManager._getSecureKey('encryptionKey');
-```
-
-#### `_setSecureKey(keyName, keyValue, options)`
-```javascript
-_setSecureKey(
- keyName: string,
- keyValue: CryptoKey | ArrayBuffer | Uint8Array,
- options?: { validate?: boolean }
-): void
-Stores a key in secure storage with validation.
-Parameters:
-- keyName - Name for the key
-- keyValue - The key to store
-- options.validate - Whether to validate the key value
-Example:
-javascriptkeyManager._setSecureKey('encryptionKey', newKey, { validate: true });
-```
-
-#### `_validateKeyValue(keyValue, keyName)`
-```javascript
-_validateKeyValue(keyValue: any, keyName: string): void
-Validates key value for security requirements.
-Throws: Error if validation fails
-```
-
-#### `_rotateKeys()`
-```javascript
-_rotateKeys(): void
-Performs secure key rotation with new key generation.
-```
-
-#### `_emergencyKeyWipe()`
-```javascript
-_emergencyKeyWipe(): void
-Immediately removes all keys from memory for threat response.
-```
-
-### 🔒 ConnectionMutexManager
-
-Manages connection operations with mutex-based race condition protection.
-
-#### `_withMutex(mutexName, operation, timeout)`
-```javascript
-_withMutex(
- mutexName: string,
- operation: () => Promise,
- timeout?: number
-): Promise
-Executes an operation with mutex protection.
-Parameters:
-- mutexName - Name of the mutex lock
-- operation - Async function to execute
-- timeout - Timeout in milliseconds (default: 15000)
-Returns: Result of the operation
-Throws: Error if mutex is locked or operation fails
-Example:
-javascriptawait mutexManager._withMutex('connectionOperation', async () => {
- await this._generateEncryptionKeys();
- await this._establishSecureChannel();
-});
-```
-
-#### `_generateOperationId()`
-```javascript
-_generateOperationId(): string
-Generates unique operation identifier for tracking.
-Returns: Unique operation ID string
-```
-
-#### `_cleanupFailedOfferCreation(operationId)`
-```javascript
-_cleanupFailedOfferCreation(operationId: string): Promise
-Performs cleanup for failed connection operations.
-Parameters:
-- operationId - ID of the failed operation
-```
-
-### 🛡️ SecureLogger
-
-Provides environment-aware logging with data sanitization.
-
-#### `_secureLog(level, message, data)`
-```javascript
-_secureLog(
- level: 'debug' | 'info' | 'warn' | 'error',
- message: string,
- data?: any
-): void
-Logs message with data sanitization and environment detection.
-Parameters:
-- level - Log level
-- message - Log message
-- data - Optional data object (will be sanitized)
-Example:
-javascriptlogger._secureLog('debug', 'Connection established', {
- userId: 'user123',
- encryptionKey: new Uint8Array(32)
-});
-// Production: No output
-// Development: [SecureBit:DEBUG] Connection established { userId: 'user123', encryptionKey: '[REDACTED]' }
-```
-
-#### `debug(message, data)`
-```javascript
-debug(message: string, data?: any): void
-Logs debug message (development only).
-```
-
-#### `info(message, data)`
-```javascript
-info(message: string, data?: any): void
-Logs info message.
-```
-
-#### `warn(message, data)`
-```javascript
-warn(message: string, data?: any): void
-Logs warning message.
-```
-
-#### `error(message, data)`
-```javascript
-error(message: string, data?: any): void
-Logs error message.
-```
-
-### 🔐 Backward Compatibility
-
-#### Getters and Setters
-```javascript
-// Secure key access with backward compatibility
-get encryptionKey(): CryptoKey {
- return this._getSecureKey('encryptionKey');
-}
-
-set encryptionKey(value: CryptoKey) {
- this._setSecureKey('encryptionKey', value, { validate: true });
-}
-
-get macKey(): CryptoKey {
- return this._getSecureKey('macKey');
-}
-
-set macKey(value: CryptoKey) {
- this._setSecureKey('macKey', value, { validate: true });
-}
-```
-
-### 🔒 Security Framework Usage Examples
-
-#### Complete Security Setup
-```javascript
-// Initialize security framework
-const keyManager = new SecureKeyManager();
-const mutexManager = new ConnectionMutexManager();
-const logger = new SecureLogger();
-
-// Secure connection establishment
-await mutexManager._withMutex('connectionOperation', async () => {
- logger.debug('Starting secure connection');
-
- // Generate and store keys securely
- const keyPair = await EnhancedSecureCryptoUtils.generateECDHKeyPair();
- keyManager._setSecureKey('privateKey', keyPair.privateKey, { validate: true });
-
- // Establish connection
- await this._establishSecureChannel();
-
- logger.info('Secure connection established');
-});
-```
-
-#### Emergency Security Response
-```javascript
-// Emergency key wipe in case of security threat
-keyManager._emergencyKeyWipe();
-logger.warn('Emergency key wipe completed');
-
-// Force cleanup
-if (typeof gc === 'function') {
- gc();
-}
-```
+- metadata is encrypted before storage
+- legacy plaintext records migrate lazily
+- corrupted encrypted metadata is ignored safely
diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md
new file mode 100644
index 0000000..2ac4767
--- /dev/null
+++ b/doc/CONFIGURATION.md
@@ -0,0 +1,63 @@
+# Configuration Guide
+
+## Requirements
+
+- modern browser with WebRTC and Web Crypto support
+- Node.js 18+ for local development
+- TURN service only when relay-only privacy mode is required
+
+## Local setup
+
+```bash
+npm install
+npm run build
+npm run serve
+```
+
+## ICE server configuration
+
+SecureBit.chat keeps existing STUN support for ordinary WebRTC connectivity. Deployments that require relay-only privacy must provide their own TURN service credentials through deployment configuration; public TURN credentials are intentionally not bundled.
+
+### Privacy modes
+
+| Mode | Behavior | IP privacy |
+| --- | --- | --- |
+| default | standard WebRTC candidate gathering | direct candidates may expose IP addresses |
+| relay-only | `iceTransportPolicy: "relay"` | requires TURN and avoids direct peer candidates when configured correctly |
+
+### Operational rules
+
+- STUN is not a privacy substitute for TURN.
+- Relay-only mode without TURN cannot establish a working relay connection.
+- The UI warns users when TURN is missing.
+- Validate TURN deployment with browser WebRTC diagnostics before production rollout.
+
+## Verification flow
+
+Protocol `4.1` requires interactive SAS verification:
+
+1. both peers derive the same SAS from shared session material
+2. users compare the code out of band
+3. each user enters the matching code manually
+4. the chat unlocks only after both confirmations succeed
+
+Three failed local attempts disconnect the session.
+
+## File-transfer policy
+
+Incoming file requests are validated before the consent prompt and require explicit user approval.
+
+Allowed categories:
+
+- common raster images
+- PDF
+- plain text
+- ZIP archives
+
+Blocked examples:
+
+- `.exe`, `.bat`, `.cmd`, `.sh`, `.js`
+- `.msi`, `.dmg`, `.app`, `.jar`, `.scr`
+- `.ps1`, `.vbs`, `.html`, `.svg`
+
+Both MIME type and extension must be acceptable. Missing or unknown MIME types are treated as unsafe unless explicitly covered by policy.
diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
index 88fbc30..3be4bc0 100644
--- a/doc/CONTRIBUTING.md
+++ b/doc/CONTRIBUTING.md
@@ -1,6126 +1,42 @@
-# Contributing to SecureBit.chat
+# Contributing
-🎉 **Thank you for your interest in contributing to SecureBit.chat!**
+## Development workflow
- **Version 4.02.442 introduces complete ASN.1 validation for enhanced key security.**
-
-## 🌟 Ways to Contribute
-
-### 🐛 Bug Reports
-Found a bug? Help us squash it!
-
-### 💡 Feature Requests
-Have an idea for improvement? We'd love to hear it!
-
-### 🔒 Security Research
-Help audit our cryptographic implementation and ASN.1 validation framework
-
-### 📖 Documentation
-Improve guides, tutorials, and technical docs
-
-### 🌍 Translations
-Help make SecureBit.chat accessible worldwide
-
-### 💻 Code Contributions
-Submit pull requests for bug fixes and features
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
-```
-
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
-
-### Contributing Guidelines Summary
-
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
-
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
-
----
-
-## 🚀 Getting Started
-
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
-
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
-
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this.checkCustomCondition(parsed)) {
- throw new Error('Custom validation failed');
- }
- return true;
-}
-
-// Integrate with main validation
-validateKeyStructure(keyData) {
- const parsed = this.parseDER(keyData);
-
- // Existing validations...
- if (!this.validateSPKI(parsed)) return false;
- if (!this.validateOID(parsed)) return false;
- if (!this.validateECPoint(parsed)) return false;
-
- // New custom validation
- if (!this.validateCustomRule(parsed)) return false;
-
- return true;
-}
-```
-
-### Testing ASN.1 Validation
-
-#### **Unit Tests**
-```javascript
-describe('ASN.1 Validation Framework', () => {
- test('Validates correct P-384 key structure', () => {
- const validKey = generateValidP384Key();
- expect(asn1Validator.validateKeyStructure(validKey)).toBe(true);
- });
-
- test('Rejects modified key with valid header', () => {
- const modifiedKey = modifyKeyData(validKey);
- expect(asn1Validator.validateKeyStructure(modifiedKey)).toBe(false);
- });
-
- test('Rejects unsupported curve OID', () => {
- const invalidOIDKey = generateKeyWithInvalidOID();
- expect(asn1Validator.validateKeyStructure(invalidOIDKey)).toBe(false);
- });
-});
-```
-
-#### **Performance Tests**
-```javascript
-describe('ASN.1 Validation Performance', () => {
- test('Validation completes within 10ms', () => {
- const start = performance.now();
- asn1Validator.validateKeyStructure(validKey);
- const duration = performance.now() - start;
- expect(duration).toBeLessThan(10);
- });
-});
-```
-
-### Security Guidelines for ASN.1 Contributions
-
-#### **Critical Requirements**
-1. **Never bypass validation** - All keys must pass complete ASN.1 validation
-2. **Maintain strict OID checking** - Only support verified, secure algorithms
-3. **Preserve size limits** - Key size limits prevent DoS attacks
-4. **Validate all structural elements** - Complete verification is mandatory
-
-#### **Common Pitfalls to Avoid**
-```javascript
-// ❌ DON'T: Skip validation for performance
-const fastImport = (keyData) => {
- // Bypassing validation for speed
- return crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-
-// ✅ DO: Always validate before processing
-const secureImport = async (keyData) => {
- if (!asn1Validator.validateKeyStructure(keyData)) {
- throw new Error('Key validation failed');
- }
- return await crypto.subtle.importKey('spki', keyData, algorithm, false, ['verify']);
-};
-```
-
-#### **Validation Order**
-1. **Parse DER** - Complete ASN.1 structure parsing
-2. **Validate SPKI** - SubjectPublicKeyInfo structure
-3. **Validate OID** - Algorithm and curve verification
-4. **Validate EC Point** - Format and structure verification
-5. **Apply custom rules** - Any additional validation requirements
-
-### Breaking Changes and Compatibility
-
-#### **Version 4.02.442 Changes**
-- **Enhanced key validation** now performs complete ASN.1 parsing
-- **Stricter key acceptance** criteria for improved security
-- **Fallback support** from P-384 to P-256 maintained
-- **Backward compatibility** for valid key structures
-
-#### **Migration Considerations**
-- **Existing keys** are validated on next use
-- **New keys** must pass complete validation
-- **Invalid keys** are rejected with clear error messages
-- **Performance impact** is minimal (< 10ms per validation)
-
-### Documentation Requirements
-
-#### **Code Documentation**
-```javascript
-/**
- * Validates cryptographic key structure using complete ASN.1 DER parsing
- *
- * @param {ArrayBuffer} keyData - Raw key data to validate
- * @returns {boolean} - True if validation passes, false otherwise
- * @throws {Error} - Detailed error message for validation failures
- *
- * @example
- * const isValid = asn1Validator.validateKeyStructure(keyData);
- * if (!isValid) {
- * console.error('Key validation failed');
- * }
- */
-validateKeyStructure(keyData) {
- // Implementation...
-}
+npm install
+npm test
+npm audit
+npm run build
```
-#### **API Documentation**
-- **Function signatures** with parameter types
-- **Return values** and error conditions
-- **Usage examples** for common scenarios
-- **Performance characteristics** and limitations
+## Security-sensitive areas
-### Contributing Guidelines Summary
+Changes involving any of the following require extra review and focused tests:
-#### **For ASN.1 Framework Contributions**
-1. **Understand the security model** - Complete validation is mandatory
-2. **Follow validation order** - Parse → SPKI → OID → EC Point → Custom
-3. **Maintain performance** - Keep validation time under 10ms
-4. **Add comprehensive tests** - Unit, integration, and performance tests
-5. **Document thoroughly** - Code comments, API docs, and examples
-6. **Consider breaking changes** - Ensure backward compatibility where possible
+- SAS verification and protocol compatibility
+- WebRTC ICE/TURN behavior
+- encrypted payload validation or display sanitization
+- file-transfer consent and type policy
+- IndexedDB migration logic
+- disconnect and resource lifecycle cleanup
-#### **Security Review Process**
-1. **Code review** by cryptographic experts
-2. **Security testing** for validation bypass attempts
-3. **Performance validation** for timing attacks
-4. **Compatibility testing** with existing key formats
-5. **Documentation review** for accuracy and completeness
+## Documentation expectations
----
+When behavior changes, update the corresponding release-facing documentation in the same change:
-## 🚀 Getting Started
+- `README.md`
+- `SECURITY.md`
+- `doc/CONFIGURATION.md`
+- `doc/CRYPTOGRAPHY.md`
+- `doc/SECURITY-ARCHITECTURE.md`
+- `CHANGELOG.md`
-### Prerequisites
-- **Browser:** Modern browser with WebRTC and WebCrypto support
-- **Git:** For version control
-- **Text Editor:** VS Code, Vim, or your favorite editor
-- **Lightning Wallet:** For testing payment features (optional)
+## Pull requests
-### Development Setup
-```bash
-# 1. Fork the repository on GitHub
-# 2. Clone your fork
-git clone https://github.com/yourusername/securebit-chat.git
-cd securebit-chat
-
-# 3. Create a development branch
-git checkout -b feature/your-feature-name
-
-# 4. Start development server
-python -m http.server 8000
-# or
-npx serve .
-
-# 5. Open http://localhost:8000
-# Make your changes
-# Test thoroughly
-# Commit with descriptive messages
-git commit -m "feat: add quantum-resistant key exchange
-
-- Implement CRYSTALS-Kyber for post-quantum security
-- Add fallback to classical ECDH
-- Update security level calculations
-- Add comprehensive test suite
-
-Closes #123"
-```
-
-## 📋 Contribution Guidelines
-
-### 🔍 Before You Start
-
-- Check existing issues - avoid duplicate work
-- Create an issue - discuss your idea first
-- Get feedback - ensure alignment with project goals
-- Fork and branch - work on a feature branch
-
-### 💻 Code Standards
-
-#### JavaScript Style
-```javascript
-// ✅ Good
-const encryptionKey = await crypto.subtle.generateKey({
- name: 'AES-GCM',
- length: 256
-}, false, ['encrypt', 'decrypt']);
-
-// ❌ Bad
-var key=crypto.subtle.generateKey({name:'AES-GCM',length:256},false,['encrypt','decrypt'])
-```
-
-#### Naming Conventions
-
-- **Functions:** camelCase - `generateSecureKey()`
-- **Classes:** PascalCase - `EnhancedSecureCryptoUtils`
-- **Constants:** UPPER_SNAKE_CASE - `MAX_MESSAGE_LENGTH`
-- **Files:** kebab-case - `crypto-utils.js`
-
-### 📖 Documentation
-
-## 🔒 Security Considerations
-
-### Critical Areas
-These areas require extra careful review:
-
-- **Cryptographic functions** - All crypto code must be reviewed
-- **Key generation** - Entropy and randomness
-- **Message handling** - Input validation and sanitization
-- **P2P communication** - WebRTC security
-- **Lightning integration** - Payment verification
-- **ASN.1 validation** - Key structure verification (NEW)
-
-### Security Checklist
-
-## 🔐 ASN.1 Validation Framework (NEW)
-
-### Overview
-SecureBit.chat v4.02.442 implements a complete ASN.1 DER parser and validation system. This framework requires special attention when contributing to cryptographic code.
-
-### Key Components
-
-#### **ASN1Validator Class**
-```javascript
-// Core validation class for cryptographic keys
-class ASN1Validator {
- constructor() {
- this.supportedOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
- this.maxKeySize = 2000; // bytes
- this.minKeySize = 50; // bytes
- }
-
- // Complete DER parsing and validation
- validateKeyStructure(keyData) {
- // Implementation details...
- }
-}
-```
-
-#### **Integration Points**
-- **Key import operations** - All keys must pass ASN.1 validation
-- **Key export operations** - Exported keys are validated
-- **Real-time validation** - Continuous validation during operations
-
-### Contributing to ASN.1 Framework
-
-#### **Adding New Curve Support**
-```javascript
-// To add support for a new elliptic curve:
-const newCurveOID = '1.3.132.0.XX'; // Replace XX with actual OID
-const curveName = 'P-XXX'; // Replace XXX with curve name
-
-// Add to supportedOIDs
-this.supportedOIDs[newCurveOID] = curveName;
-
-// Update validation logic if needed
-// Ensure proper EC point format validation
-```
+Please include:
-#### **Extending Validation Rules**
-```javascript
-// To add new validation rules:
-validateCustomRule(parsed) {
- // Implement your validation logic
- if (!this
\ No newline at end of file
+- concise problem statement
+- implementation summary
+- tests run
+- regression risks
+- screenshots or logs for user-visible changes when relevant
diff --git a/doc/CRYPTOGRAPHY.md b/doc/CRYPTOGRAPHY.md
index be80881..73d434b 100644
--- a/doc/CRYPTOGRAPHY.md
+++ b/doc/CRYPTOGRAPHY.md
@@ -1,2099 +1,26 @@
-# SecureBit.chat Cryptographic Implementation v4.02.985
+# Cryptography and Verification
-## 🔐 Overview
+## Release context
-SecureBit.chat implements state-of-the-art cryptographic protocols providing **military-grade security** for peer-to-peer communications. Our cryptographic design prioritizes security, performance, and future-proofing against emerging threats including quantum computing. **Version 4.02.985 introduces revolutionary ECDH + DTLS + SAS security system for enhanced MITM protection.**
+- Product release: `v4.8.5`
+- Protocol version: `4.1`
-**Cryptographic Strength:** 256+ bit security level
-**Quantum Resistance:** Timeline > 2040
-**Standards Compliance:** NIST, FIPS, NSA Suite B, RFC 5280, RFC 5480, RFC 5763
-**Implementation:** Hardware-accelerated, constant-time algorithms with ECDH + DTLS + SAS authentication
+## Session establishment
----
+SecureBit.chat uses ECDH-derived session material, DTLS-protected WebRTC transport, and a mandatory Short Authentication String (SAS) verification step.
-## 📋 Table of Contents
+The SAS is deterministic for both peers in the same authenticated session. Users compare the displayed code through an out-of-band channel and enter the matching code manually. Local success alone is insufficient: the session becomes verified only after both peers confirm.
-1. [ECDH + DTLS + SAS Security System](#ecdh--dtls--sas-security-system)
-2. [Cryptographic Primitives](#cryptographic-primitives)
-3. [Key Management](#key-management)
-4. [Encryption Implementation](#encryption-implementation)
-5. [Production Security Logging](#production-security-logging)
-6. [Digital Signatures](#digital-signatures)
-7. [Mutex Framework](#mutex-framework-race-condition-protection)
-8. [Key Derivation](#key-derivation)
-9. [Perfect Forward Secrecy](#perfect-forward-secrecy)
-10. [Security Analysis](#security-analysis)
-11. [Implementation Details](#implementation-details)
-12. [Performance Optimization](#performance-optimization)
-13. [Compliance and Standards](#compliance-and-standards)
-14. [ASN.1 Validation Framework](#asn1-validation-framework)
+## Message protection
----
+- encrypted payloads are validated before decryption
+- decrypted chat text is sanitized before entering React state or the UI
+- replay and ordering controls remain part of the session layer
-## 🛡️ ECDH + DTLS + SAS Security System
+## Local key metadata
-### Overview
+Sensitive IndexedDB metadata is stored in encrypted envelopes. Legacy plaintext metadata remains readable through a migration path and is re-written in encrypted form when accessed. Corrupted encrypted metadata fails closed.
-SecureBit.chat v4.02.985 introduces a revolutionary three-layer security system that eliminates traditional PAKE-based authentication in favor of a more robust and standardized approach:
+## Scope note
-1. **ECDH (Elliptic Curve Diffie-Hellman)** - Secure key exchange
-2. **DTLS Fingerprint Verification** - Transport layer security validation
-3. **SAS (Short Authentication String)** - MITM attack prevention
-
-### ECDH Key Exchange
-
-**Purpose:** Establish a shared secret between two parties without prior knowledge
-
-**Implementation:**
-- **Curve:** P-384 (secp384r1) for maximum security
-- **Key Generation:** Cryptographically secure random key pairs
-- **Shared Secret:** Derived using ECDH protocol
-- **Key Material:** Used for subsequent encryption and authentication
-
-**Security Properties:**
-- **Forward Secrecy:** Each session uses unique key pairs
-- **Perfect Forward Secrecy:** Past sessions cannot be compromised
-- **MITM Resistance:** Requires knowledge of both private keys
-
-### DTLS Fingerprint Verification
-
-**Purpose:** Verify the authenticity of the WebRTC transport layer
-
-**Implementation:**
-- **Certificate Extraction:** From WebRTC SDP offers/answers
-- **Fingerprint Generation:** SHA-256 hash of the certificate
-- **Verification:** Both parties verify each other's DTLS fingerprints
-- **Transport Security:** Ensures connection is not intercepted
-
-**Security Properties:**
-- **Transport Integrity:** Prevents connection hijacking
-- **Certificate Validation:** Ensures authentic WebRTC certificates
-- **MITM Detection:** Detects man-in-the-middle at transport layer
-
-### SAS (Short Authentication String)
-
-**Purpose:** Provide out-of-band verification to prevent MITM attacks
-
-**Implementation:**
-- **Generation:** HKDF-based derivation from shared secret and DTLS fingerprints
-- **Format:** 7-digit numeric code (0000000-9999999)
-- **Sharing:** Generated once on Offer side, shared with Answer side
-- **Verification:** Both users must confirm the same code
-
-**Security Properties:**
-- **MITM Prevention:** Requires attacker to know the shared secret
-- **User Verification:** Human-readable verification step
-- **Standard Compliance:** Follows RFC 5763 recommendations
-
-### Security Flow
-
-```
-1. ECDH Key Exchange
- ├── Generate key pairs (P-384)
- ├── Exchange public keys
- └── Derive shared secret
-
-2. DTLS Fingerprint Verification
- ├── Extract certificates from SDP
- ├── Generate SHA-256 fingerprints
- └── Verify transport authenticity
-
-3. SAS Generation and Verification
- ├── Generate SAS from shared secret + fingerprints
- ├── Share SAS code between parties
- └── Mutual verification by both users
-
-4. Connection Establishment
- ├── All three layers verified
- ├── Secure channel established
- └── Communication begins
-```
-
-### Advantages Over PAKE
-
-| Aspect | PAKE (Previous) | ECDH + DTLS + SAS (Current) |
-|--------|-----------------|------------------------------|
-| **Dependencies** | libsodium required | Native Web Crypto API |
-| **Standards** | Custom implementation | RFC-compliant protocols |
-| **MITM Protection** | Single layer | Triple-layer defense |
-| **User Experience** | Password-based | Code-based verification |
-| **Security** | Good | Military-grade |
-| **Maintenance** | Complex | Simplified |
-
-### Implementation Details
-
-**Key Components:**
-- `_computeSAS()` - SAS generation using HKDF
-- `_extractDTLSFingerprintFromSDP()` - Certificate extraction
-- `_decodeKeyFingerprint()` - Key material processing
-- `confirmVerification()` - Mutual verification handling
-
-**Security Considerations:**
-- **Timing Attacks:** Constant-time operations
-- **Side Channels:** No information leakage
-- **Replay Protection:** Unique session identifiers
-- **Forward Secrecy:** Session-specific keys
-
----
-
-## 🔧 Cryptographic Primitives
-
-### Primary Algorithms
-
-| Function | Algorithm | Key Size | Security Level | Standard |
-|----------|-----------|----------|----------------|----------|
-| **Symmetric Encryption** | AES-256-GCM | 256-bit | 256-bit | FIPS 197 |
-| **Asymmetric Encryption** | ECDH P-384 | 384-bit | 192-bit | FIPS 186-4 |
-| **Digital Signatures** | ECDSA P-384 | 384-bit | 192-bit | FIPS 186-4 |
-| **File Metadata Signatures** | RSA-2048 | 2048-bit | 112-bit | FIPS 186-4 |
-| **Hash Function** | SHA-384 | - | 192-bit | FIPS 180-4 |
-| **Message Authentication** | HMAC-SHA-384 | 384-bit | 192-bit | FIPS 198-1 |
-| **Key Derivation** | HKDF-SHA-384 | Variable | 192-bit | RFC 5869 |
-| **ASN.1 Validation** | Complete DER Parser | - | Structural | RFC 5280, RFC 5480 |
-
-### Algorithm Selection Rationale
-
-#### **AES-256-GCM**
-- **Chosen For:** Authenticated encryption, hardware acceleration
-- **Security:** Proven security, quantum resistant until 2040+
-- **Performance:** Hardware AES-NI support on modern processors
-- **Mode Benefits:** Combined confidentiality and authenticity
-
-#### **ECDH P-384 (secp384r1)**
-- **Chosen For:** Key agreement with forward secrecy
-- **Security:** Equivalent to 7680-bit RSA, NSA Suite B approved
-- **Efficiency:** Smaller keys than RSA with equivalent security
-- **Future-Proof:** Quantum resistant timeline > 15 years
-
-#### **ECDSA P-384**
-- **Chosen For:** Digital signatures and authentication
-- **Security:** Matches ECDH curve for consistent security level
-- **Non-repudiation:** Cryptographic proof of message origin
-- **Performance:** Faster than RSA signatures
-
-#### **SHA-384**
-- **Chosen For:** Hash function matching curve security
-- **Security:** 192-bit collision resistance, preimage resistance
-- **Compatibility:** Matches P-384 curve security level
-- **Standard:** Part of SHA-2 family, widely standardized
-
-#### **ASN.1 DER Parser (NEW)**
-- **Chosen For:** Complete key structure validation
-- **Security:** Prevents key manipulation attacks
-- **Compliance:** Full PKCS and RFC standards adherence
-- **Performance:** < 10ms validation time
-
----
-
-## 🔑 Key Management
-
-### Key Hierarchy
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ Master Key Material │
-├─────────────────────────────────────────────────────────────┤
-│ ECDH Private Key (384-bit, non-extractable) │
-│ ├── Shared Secret (384-bit, ephemeral) │
-│ │ ├── Encryption Key (256-bit AES) │
-│ │ ├── MAC Key (384-bit HMAC) │
-│ │ ├── Metadata Key (256-bit AES) │
-│ │ └── Fingerprint Key (256-bit, extractable only) │
-│ └── Key Versions (PFS, rotated every 5 minutes) │
-├─────────────────────────────────────────────────────────────┤
-│ ECDSA Private Key (384-bit, non-extractable) │
-│ ├── Message Signing │
-│ ├── Key Package Signing │
-│ └── Authentication Proofs │
-├─────────────────────────────────────────────────────────────┤
-│ Nested Encryption Key (256-bit AES, hardware-generated) │
-│ ├── Additional encryption layer │
-│ └── Rotated every 1000 messages │
-├─────────────────────────────────────────────────────────────┤
-│ ASN.1 Validation Keys (Structural verification) │
-│ ├── OID validation (P-256/P-384 only) │
-│ ├── EC point format verification (0x04 uncompressed) │
-│ ├── SPKI structure validation │
-│ └── Key size limits (50-2000 bytes) │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### Key Generation
-
-#### **ECDH Key Pair Generation**
-```javascript
-async function generateECDHKeyPair() {
- try {
- // Primary: P-384 curve
- const keyPair = await crypto.subtle.generateKey(
- {
- name: 'ECDH',
- namedCurve: 'P-384' // secp384r1
- },
- false, // Non-extractable for security
- ['deriveKey']
- );
-
- // Validate key generation
- await validateKeyPair(keyPair);
- return keyPair;
-
- } catch (p384Error) {
- // Fallback: P-256 curve
- return await crypto.subtle.generateKey(
- {
- name: 'ECDH',
- namedCurve: 'P-256' // secp256r1
- },
- false,
- ['deriveKey']
- );
- }
-}
-```
-
-#### **ECDSA Key Pair Generation**
-```javascript
-async function generateECDSAKeyPair() {
- return await crypto.subtle.generateKey(
- {
- name: 'ECDSA',
- namedCurve: 'P-384'
- },
- false, // Non-extractable for security
- ['sign', 'verify']
- );
-}
-```
-
-### Key Storage and Protection
-
-#### **Non-Extractable Keys**
-- All private keys generated with `extractable: false`
-- Hardware security module (HSM) storage when available
-- Keys cannot be exported from secure storage
-- Protection against memory dump attacks
-
-#### **Key Validation**
-```javascript
-async function validateKeyPair(keyPair) {
- // Verify key algorithm and curve
- assert(keyPair.privateKey.algorithm.name === 'ECDH');
- assert(keyPair.privateKey.algorithm.namedCurve === 'P-384');
-
- // Verify key properties
- assert(keyPair.privateKey.extractable === false);
- assert(keyPair.privateKey.usages.includes('deriveKey'));
-
- // Test key functionality
- const testData = crypto.getRandomValues(new Uint8Array(32));
- const derived = await crypto.subtle.deriveKey(/* ... */);
- assert(derived instanceof CryptoKey);
-}
-```
-
-### Secure Key Storage System
-
-#### **WeakMap-Based Key Isolation**
-```javascript
-class SecureKeyManager {
- constructor() {
- this._secureKeyStorage = new WeakMap();
- this._keyMetadata = new WeakMap();
- this._initializeSecureKeyStorage();
- }
-
- _initializeSecureKeyStorage() {
- // Initialize secure storage with validation
- this._secureKeyStorage.set(this, {});
- this._keyMetadata.set(this, {
- creationTime: Date.now(),
- rotationCount: 0,
- lastAccess: Date.now()
- });
- }
-
- _getSecureKey(keyName) {
- const storage = this._secureKeyStorage.get(this);
- const metadata = this._keyMetadata.get(this);
-
- if (!storage || !storage[keyName]) {
- throw new Error(`Key ${keyName} not found in secure storage`);
- }
-
- // Update access metadata
- metadata.lastAccess = Date.now();
- return storage[keyName];
- }
-
- _setSecureKey(keyName, keyValue, options = {}) {
- const storage = this._secureKeyStorage.get(this);
- const metadata = this._keyMetadata.get(this);
-
- // Validate key value
- if (options.validate) {
- this._validateKeyValue(keyValue, keyName);
- }
-
- // Store key securely
- storage[keyName] = keyValue;
- metadata.lastAccess = Date.now();
-
- // Start security monitoring if not already active
- this._startKeySecurityMonitoring();
- }
-
- _validateKeyValue(keyValue, keyName) {
- // Type validation
- if (!keyValue || typeof keyValue !== 'object') {
- throw new Error(`Invalid key value for ${keyName}`);
- }
-
- // CryptoKey validation
- if (keyValue instanceof CryptoKey) {
- if (keyValue.extractable) {
- throw new Error(`Extractable keys are not allowed for ${keyName}`);
- }
- }
-
- // Buffer validation
- if (keyValue instanceof ArrayBuffer || keyValue instanceof Uint8Array) {
- if (keyValue.byteLength < 32) {
- throw new Error(`Key ${keyName} too short for security requirements`);
- }
- }
- }
-
- _rotateKeys() {
- const metadata = this._keyMetadata.get(this);
- metadata.rotationCount++;
- metadata.lastRotation = Date.now();
-
- // Implement key rotation logic
- this._performKeyRotation();
- }
-
- _emergencyKeyWipe() {
- // Clear all keys from memory
- this._secureKeyStorage.delete(this);
- this._keyMetadata.delete(this);
-
- // Force garbage collection if available
- if (typeof gc === 'function') {
- gc();
- }
- }
-
- _startKeySecurityMonitoring() {
- // Monitor key lifetime and access patterns
- setInterval(() => {
- this._checkKeySecurity();
- }, 30000); // Check every 30 seconds
- }
-
- _checkKeySecurity() {
- const metadata = this._keyMetadata.get(this);
- const now = Date.now();
-
- // Check key age
- if (now - metadata.creationTime > 3600000) { // 1 hour
- this._rotateKeys();
- }
-
- // Check for suspicious access patterns
- if (now - metadata.lastAccess > 300000) { // 5 minutes
- this._logSecurityWarning('Key access timeout detected');
- }
- }
-}
-```
-
-#### **Backward Compatibility**
-```javascript
-// Getters and setters for existing code compatibility
-get encryptionKey() {
- return this._getSecureKey('encryptionKey');
-}
-
-set encryptionKey(value) {
- this._setSecureKey('encryptionKey', value, { validate: true });
-}
-
-get macKey() {
- return this._getSecureKey('macKey');
-}
-
-set macKey(value) {
- this._setSecureKey('macKey', value, { validate: true });
-}
-```
-
-#### **Security Benefits**
-- **Memory Protection:** Keys inaccessible via direct property access
-- **Debugger Resistance:** Keys not visible in browser developer tools
-- **Access Control:** All key access goes through validation
-- **Automatic Cleanup:** Keys automatically removed from memory
-- **Threat Response:** Immediate key destruction capabilities
-
----
-
-## 🔒 Encryption Implementation
-
-### Triple-Layer Encryption Architecture
-
-```
-Original Message
- ↓
-┌─────────────────┐
-│ Layer 1: │
-│ Standard AES │ ← Primary encryption with metadata
-│ (Enhanced) │
-└─────────┬───────┘
- ↓
-┌─────────────────┐
-│ Layer 2: │
-│ Nested AES │ ← Additional security layer
-│ (Independent) │
-└─────────┬───────┘
- ↓
-┌─────────────────┐
-│ Layer 3: │
-│ WebRTC DTLS │ ← Transport layer encryption
-│ (Built-in) │
-└─────────┬───────┘
- ↓
- Network Packet
-```
-
-### Enhanced Message Encryption
-
-#### **Message Structure**
-```javascript
-const enhancedMessage = {
- // Encrypted metadata (AES-256-GCM)
- metadataIv: [12 bytes],
- metadataData: [encrypted metadata],
-
- // Encrypted message content (AES-256-GCM)
- messageIv: [12 bytes],
- messageData: [encrypted + padded content],
-
- // Authentication (HMAC-SHA-384)
- mac: [48 bytes],
-
- // Version and format
- version: "4.0",
- format: "enhanced"
-};
-```
-
-#### **Encryption Process**
-```javascript
-async function encryptMessage(message, encryptionKey, macKey, metadataKey, messageId, sequenceNumber) {
- // Step 1: Prepare message with padding
- const messageData = new TextEncoder().encode(message);
- const paddingSize = 16 - (messageData.length % 16);
- const paddedMessage = new Uint8Array(messageData.length + paddingSize);
- paddedMessage.set(messageData);
- paddedMessage.set(crypto.getRandomValues(new Uint8Array(paddingSize)), messageData.length);
-
- // Step 2: Generate IVs
- const messageIv = crypto.getRandomValues(new Uint8Array(12));
- const metadataIv = crypto.getRandomValues(new Uint8Array(12));
-
- // Step 3: Encrypt message content
- const encryptedMessage = await crypto.subtle.encrypt(
- { name: 'AES-GCM', iv: messageIv },
- encryptionKey,
- paddedMessage
- );
-
- // Step 4: Prepare and encrypt metadata
- const metadata = {
- id: messageId,
- timestamp: Date.now(),
- sequenceNumber: sequenceNumber,
- originalLength: messageData.length,
- version: '4.0'
- };
-
- const metadataStr = JSON.stringify(sortObjectKeys(metadata));
- const encryptedMetadata = await crypto.subtle.encrypt(
- { name: 'AES-GCM', iv: metadataIv },
- metadataKey,
- new TextEncoder().encode(metadataStr)
- );
-
- // Step 5: Create payload and compute MAC
- const payload = {
- messageIv: Array.from(messageIv),
- messageData: Array.from(new Uint8Array(encryptedMessage)),
- metadataIv: Array.from(metadataIv),
- metadataData: Array.from(new Uint8Array(encryptedMetadata)),
- version: '4.0'
- };
-
- const sortedPayload = sortObjectKeys(payload);
- const payloadStr = JSON.stringify(sortedPayload);
-
- const mac = await crypto.subtle.sign(
- 'HMAC',
- macKey,
- new TextEncoder().encode(payloadStr)
- );
-
- payload.mac = Array.from(new Uint8Array(mac));
-
- return payload;
-}
-```
-
-### Nested Encryption Layer
-
-#### **Purpose and Implementation**
-```javascript
-async function applyNestedEncryption(data, nestedKey, counter) {
- // Create unique IV for each encryption
- const uniqueIV = new Uint8Array(12);
- uniqueIV.set(baseIV);
- uniqueIV[11] = (counter++) & 0xFF;
-
- // Apply additional AES-GCM encryption
- const encrypted = await crypto.subtle.encrypt(
- { name: 'AES-GCM', iv: uniqueIV },
- nestedKey,
- data
- );
-
- // Combine IV and encrypted data
- const result = new Uint8Array(12 + encrypted.byteLength);
- result.set(uniqueIV, 0);
- result.set(new Uint8Array(encrypted), 12);
-
- return result.buffer;
-}
-```
-
-#### **Security Benefits**
-- **Defense in Depth:** Multiple independent encryption layers
-- **Algorithm Diversity:** Protection against algorithm-specific attacks
-- **Implementation Isolation:** Separate keys and implementations
-- **Future-Proofing:** Additional security against unknown vulnerabilities
-
----
-
-## 🛡️ Production Security Logging
-
-### Secure Logging System
-
-#### **Environment-Aware Logging**
-```javascript
-class SecureLogger {
- constructor() {
- this._isProduction = this._detectProductionMode();
- this._logCounters = new Map();
- this._rateLimitWindow = 60000; // 1 minute
- this._maxLogsPerWindow = 100;
- }
-
- _detectProductionMode() {
- // Detect production environment
- return window.location.hostname !== 'localhost' &&
- window.location.hostname !== '127.0.0.1' &&
- !window.location.hostname.includes('dev') &&
- !window.location.hostname.includes('test');
- }
-
- _secureLog(level, message, data = null) {
- // Check rate limiting
- if (this._isRateLimited(level)) {
- return;
- }
-
- // Sanitize data
- const sanitizedData = this._sanitizeData(data);
-
- // Environment-specific logging
- if (this._isProduction) {
- this._productionLog(level, message, sanitizedData);
- } else {
- this._developmentLog(level, message, sanitizedData);
- }
-
- // Update rate limiting counters
- this._updateLogCounter(level);
- }
-
- _productionLog(level, message, data) {
- // Production: Only critical errors and warnings
- if (level === 'error' || level === 'warn') {
- console[level](`[SecureBit] ${message}`, data);
- }
- }
-
- _developmentLog(level, message, data) {
- // Development: Full debugging information (sanitized)
- console[level](`[SecureBit:${level.toUpperCase()}] ${message}`, data);
- }
-
- _sanitizeData(data) {
- if (!data) return null;
-
- const sanitized = {};
- const sensitivePatterns = [
- /key/i, /token/i, /password/i, /secret/i, /auth/i,
- /encryption/i, /private/i, /signature/i, /mac/i
- ];
-
- for (const [key, value] of Object.entries(data)) {
- // Check if key contains sensitive information
- const isSensitive = sensitivePatterns.some(pattern => pattern.test(key));
-
- if (isSensitive) {
- sanitized[key] = '[REDACTED]';
- } else if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
- sanitized[key] = `[Buffer: ${value.byteLength} bytes]`;
- } else if (typeof value === 'string' && value.length > 100) {
- sanitized[key] = value.substring(0, 50) + '...';
- } else if (typeof value === 'object' && value !== null) {
- sanitized[key] = this._sanitizeData(value);
- } else {
- sanitized[key] = value;
- }
- }
-
- return sanitized;
- }
-
- _isRateLimited(level) {
- const now = Date.now();
- const key = `${level}_${Math.floor(now / this._rateLimitWindow)}`;
- const count = this._logCounters.get(key) || 0;
-
- return count >= this._maxLogsPerWindow;
- }
-
- _updateLogCounter(level) {
- const now = Date.now();
- const key = `${level}_${Math.floor(now / this._rateLimitWindow)}`;
- const count = this._logCounters.get(key) || 0;
-
- this._logCounters.set(key, count + 1);
-
- // Cleanup old counters
- this._cleanupOldCounters(now);
- }
-
- _cleanupOldCounters(currentTime) {
- const cutoff = currentTime - (this._rateLimitWindow * 10); // Keep 10 windows
-
- for (const [key] of this._logCounters) {
- const timestamp = parseInt(key.split('_')[1]) * this._rateLimitWindow;
- if (timestamp < cutoff) {
- this._logCounters.delete(key);
- }
- }
- }
-
- // Public logging methods
- debug(message, data) {
- this._secureLog('debug', message, data);
- }
-
- info(message, data) {
- this._secureLog('info', message, data);
- }
-
- warn(message, data) {
- this._secureLog('warn', message, data);
- }
-
- error(message, data) {
- this._secureLog('error', message, data);
- }
-}
-```
-
-#### **Usage Examples**
-```javascript
-const logger = new SecureLogger();
-
-// Secure logging with data sanitization
-logger.debug('Connection established', {
- userId: 'user123',
- encryptionKey: new Uint8Array(32),
- messageContent: 'Hello, world!',
- sessionId: 'abc123def456'
-});
-
-// Production output: No debug logs
-// Development output: [SecureBit:DEBUG] Connection established { userId: 'user123', encryptionKey: '[REDACTED]', messageContent: 'Hello, world!', sessionId: '[REDACTED]' }
-```
-
-#### **Security Benefits**
-- **Data Protection:** Encryption keys, message content, and tokens are automatically sanitized
-- **Privacy Preservation:** User privacy maintained in production logs
-- **Debugging Support:** Safe debugging information without sensitive content
-- **Rate Limiting:** Prevents log spam and memory exhaustion
-- **Compliance:** Meets privacy regulations and security standards
-
----
-
-## ✍️ Digital Signatures
-
-### ECDSA Implementation
-
-#### **Signature Generation**
-```javascript
-async function signData(privateKey, data) {
- const encoder = new TextEncoder();
- const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;
-
- try {
- // Primary: SHA-384
- const signature = await crypto.subtle.sign(
- {
- name: 'ECDSA',
- hash: 'SHA-384'
- },
- privateKey,
- dataBuffer
- );
-
- return Array.from(new Uint8Array(signature));
-
- } catch (sha384Error) {
- // Fallback: SHA-256
- const signature = await crypto.subtle.sign(
- {
- name: 'ECDSA',
- hash: 'SHA-256'
- },
- privateKey,
- dataBuffer
- );
-
- return Array.from(new Uint8Array(signature));
- }
-}
-```
-
-#### **Signature Verification**
-```javascript
-async function verifySignature(publicKey, signature, data) {
- const encoder = new TextEncoder();
- const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;
- const signatureBuffer = new Uint8Array(signature);
-
- try {
- // Primary: SHA-384
- const isValid = await crypto.subtle.verify(
- {
- name: 'ECDSA',
- hash: 'SHA-384'
- },
- publicKey,
- signatureBuffer,
- dataBuffer
- );
-
- return isValid;
-
- } catch (sha384Error) {
- // Fallback: SHA-256
- const isValid = await crypto.subtle.verify(
- {
- name: 'ECDSA',
- hash: 'SHA-256'
- },
- publicKey,
- signatureBuffer,
- dataBuffer
- );
-
- return isValid;
- }
-}
-```
-
-### Signed Key Exchange
-
-#### **Key Package Structure**
-```javascript
-const signedKeyPackage = {
- keyType: 'ECDH', // or 'ECDSA'
- keyData: [/* exported public key bytes */],
- timestamp: Date.now(),
- version: '4.0',
- signature: [/* ECDSA signature bytes */]
-};
-```
-
-#### **Key Package Signing**
-```javascript
-async function exportPublicKeyWithSignature(publicKey, signingKey, keyType) {
- // Export public key
- const exported = await crypto.subtle.exportKey('spki', publicKey);
- const keyData = Array.from(new Uint8Array(exported));
-
- // Validate key structure
- await validateKeyStructure(keyData, keyType);
-```
-
-### RSA-2048 File Metadata Signatures
-
-#### **RSA Key Generation**
-```javascript
-async function generateRSAKeyPair() {
- const keyPair = await crypto.subtle.generateKey(
- {
- name: 'RSASSA-PKCS1-v1_5',
- modulusLength: 2048,
- publicExponent: new Uint8Array([1, 0, 1]),
- hash: 'SHA-256'
- },
- true, // extractable
- ['sign', 'verify']
- );
-
- return keyPair;
-}
-```
-
-#### **File Metadata Signing**
-```javascript
-async function signFileMetadata(metadata, privateKey) {
- const encoder = new TextEncoder();
- const data = encoder.encode(JSON.stringify({
- fileId: metadata.fileId,
- fileName: metadata.fileName,
- fileSize: metadata.fileSize,
- fileHash: metadata.fileHash,
- timestamp: metadata.timestamp,
- version: metadata.version || '2.0'
- }));
-
- const signature = await crypto.subtle.sign(
- 'RSASSA-PKCS1-v1_5',
- privateKey,
- data
- );
-
- return Array.from(new Uint8Array(signature));
-}
-```
-
-#### **File Metadata Verification**
-```javascript
-async function verifyFileMetadata(metadata, signature, publicKey) {
- const encoder = new TextEncoder();
- const data = encoder.encode(JSON.stringify({
- fileId: metadata.fileId,
- fileName: metadata.fileName,
- fileSize: metadata.fileSize,
- fileHash: metadata.fileHash,
- timestamp: metadata.timestamp,
- version: metadata.version || '2.0'
- }));
-
- const signatureBuffer = new Uint8Array(signature);
-
- return await crypto.subtle.verify(
- 'RSASSA-PKCS1-v1_5',
- publicKey,
- signatureBuffer,
- data
- );
-}
-```
-
-#### **RSA Signature Benefits**
-- **File Integrity:** Cryptographic proof of file metadata authenticity
-- **Source Verification:** Ensures files come from verified sources
-- **Tamper Detection:** Prevents metadata manipulation
-- **Compliance:** Meets enterprise security requirements
-
- // Create key package
- const keyPackage = {
- keyType,
- keyData,
- timestamp: Date.now(),
- version: '4.0'
- };
-
- // Sign the key package
- const packageString = JSON.stringify(keyPackage);
- const signature = await signData(signingKey, packageString);
-
- return {
- ...keyPackage,
- signature
- };
-}
-```
-
-### Authentication Proofs
-
-#### **Mutual Authentication**
-```javascript
-async function createAuthProof(challenge, privateKey, publicKey) {
- // Validate challenge
- if (!challenge || !challenge.challenge || !challenge.timestamp) {
- throw new Error('Invalid challenge structure');
- }
-
- // Check challenge age (max 2 minutes)
- const challengeAge = Date.now() - challenge.timestamp;
- if (challengeAge > 120000) {
- throw new Error('Challenge expired');
- }
-
- // Create proof data
- const proofData = {
- challenge: challenge.challenge,
- timestamp: challenge.timestamp,
- nonce: challenge.nonce,
- responseTimestamp: Date.now(),
- publicKeyHash: await hashPublicKey(publicKey)
- };
-
- // Sign the proof
- const proofString = JSON.stringify(proofData);
- const signature = await signData(privateKey, proofString);
-
- return {
- ...proofData,
- signature,
- version: '4.0'
- };
-}
-```
-
----
-
-## 🔒 Mutex Framework (Race Condition Protection)
-
-### Connection Security Framework
-
-#### **Advanced Mutex Implementation**
-```javascript
-class ConnectionMutexManager {
- constructor() {
- this._mutexLocks = new Map();
- this._operationTimeouts = new Map();
- this._defaultTimeout = 15000; // 15 seconds
- this._cleanupInterval = 30000; // 30 seconds
- }
-
- async _withMutex(mutexName, operation, timeout = this._defaultTimeout) {
- const operationId = this._generateOperationId();
- const startTime = Date.now();
-
- // Check if mutex is already locked
- if (this._mutexLocks.has(mutexName)) {
- throw new Error(`Mutex ${mutexName} is already locked`);
- }
-
- // Acquire mutex
- this._mutexLocks.set(mutexName, {
- operationId,
- startTime,
- timeout
- });
-
- // Set timeout for automatic cleanup
- const timeoutId = setTimeout(() => {
- this._handleMutexTimeout(mutexName, operationId);
- }, timeout);
-
- try {
- // Execute operation with phase tracking
- const result = await this._executeWithPhaseTracking(operation, operationId);
-
- // Clear timeout and release mutex
- clearTimeout(timeoutId);
- this._mutexLocks.delete(mutexName);
- this._operationTimeouts.delete(operationId);
-
- return result;
-
- } catch (error) {
- // Handle operation failure
- clearTimeout(timeoutId);
- this._mutexLocks.delete(mutexName);
- this._operationTimeouts.delete(operationId);
-
- // Perform cleanup for failed operations
- await this._cleanupFailedOperation(mutexName, operationId, error);
- throw error;
- }
- }
-
- _generateOperationId() {
- return `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
- }
-
- async _executeWithPhaseTracking(operation, operationId) {
- const phases = [
- 'key_generation',
- 'connection_validation',
- 'channel_establishment',
- 'security_verification'
- ];
-
- for (const phase of phases) {
- try {
- this._logPhaseStart(operationId, phase);
- await this._executePhase(operation, phase);
- this._logPhaseSuccess(operationId, phase);
- } catch (error) {
- this._logPhaseFailure(operationId, phase, error);
- throw error;
- }
- }
-
- return await operation();
- }
-
- async _cleanupFailedOperation(mutexName, operationId, error) {
- // Cleanup resources for failed operations
- await this._cleanupFailedOfferCreation(operationId);
-
- // Log cleanup completion
- this._logCleanupComplete(operationId, error);
- }
-
- _handleMutexTimeout(mutexName, operationId) {
- // Handle mutex timeout
- this._logMutexTimeout(mutexName, operationId);
-
- // Force release mutex
- this._mutexLocks.delete(mutexName);
- this._operationTimeouts.delete(operationId);
-
- // Trigger emergency cleanup
- this._emergencyCleanup(operationId);
- }
-
- _emergencyCleanup(operationId) {
- // Emergency cleanup for deadlock situations
- this._logEmergencyCleanup(operationId);
-
- // Force garbage collection if available
- if (typeof gc === 'function') {
- gc();
- }
- }
-
- // Logging methods
- _logPhaseStart(operationId, phase) {
- console.debug(`[Mutex] Operation ${operationId} starting phase: ${phase}`);
- }
-
- _logPhaseSuccess(operationId, phase) {
- console.debug(`[Mutex] Operation ${operationId} completed phase: ${phase}`);
- }
-
- _logPhaseFailure(operationId, phase, error) {
- console.error(`[Mutex] Operation ${operationId} failed in phase: ${phase}`, error);
- }
-
- _logCleanupComplete(operationId, error) {
- console.warn(`[Mutex] Cleanup completed for operation ${operationId}`, error);
- }
-
- _logMutexTimeout(mutexName, operationId) {
- console.error(`[Mutex] Timeout for mutex ${mutexName}, operation ${operationId}`);
- }
-
- _logEmergencyCleanup(operationId) {
- console.error(`[Mutex] Emergency cleanup triggered for operation ${operationId}`);
- }
-}
-```
-
-#### **Usage Examples**
-```javascript
-const mutexManager = new ConnectionMutexManager();
-
-// Mutex-protected connection operations
-await mutexManager._withMutex('connectionOperation', async () => {
- // Atomic key generation
- await this._generateEncryptionKeys();
-
- // Connection validation
- await this._validateConnectionParameters();
-
- // Secure channel establishment
- await this._establishSecureChannel();
-
- // Security verification
- await this._verifySecurityParameters();
-});
-```
-
-#### **Security Benefits**
-- **Race Condition Prevention:** Eliminates timing-based attacks during key generation
-- **Connection Integrity:** Ensures atomic connection establishment
-- **Error Recovery:** Automatic rollback for failed operations
-- **Deadlock Prevention:** Timeout-based emergency recovery
-- **Diagnostic Capability:** Comprehensive phase tracking for error identification
-
----
-
-## 🔗 Key Derivation
-
-### HKDF Implementation (RFC 5869 Compliant)
-
-#### **Enhanced Key Derivation with Proper Separation**
-```javascript
-async function deriveSharedKeys(privateKey, publicKey, salt) {
- // Validate inputs
- assertCryptoKey(privateKey, 'ECDH', ['deriveKey']);
- assertCryptoKey(publicKey, 'ECDH', []);
-
- if (!salt || salt.length !== 64) {
- throw new Error('Salt must be exactly 64 bytes for enhanced security');
- }
-
- const saltBytes = new Uint8Array(salt);
- const encoder = new TextEncoder();
-
- // Step 1: Derive raw ECDH shared secret using pure ECDH
- const rawKeyMaterial = await crypto.subtle.deriveKey(
- {
- name: 'ECDH',
- public: publicKey
- },
- privateKey,
- {
- name: 'AES-GCM',
- length: 256
- },
- true, // Extractable for HKDF processing
- ['encrypt', 'decrypt']
- );
-
- // Export the raw key material
- const rawKeyData = await crypto.subtle.exportKey('raw', rawKeyMaterial);
-
- // Import as HKDF key material for further derivation
- const rawSharedSecret = await crypto.subtle.importKey(
- 'raw',
- rawKeyData,
- {
- name: 'HKDF',
- hash: 'SHA-256'
- },
- false,
- ['deriveKey']
- );
-
- // Step 2: Derive specific keys using HKDF with unique info parameters
- // Each key uses unique info parameter for proper separation
-
- // Derive message encryption key (messageKey)
- const messageKey = await crypto.subtle.deriveKey(
- {
- name: 'HKDF',
- hash: 'SHA-256',
- salt: saltBytes,
- info: encoder.encode('message-encryption-v4')
- },
- rawSharedSecret,
- {
- name: 'AES-GCM',
- length: 256
- },
- false, // Non-extractable for enhanced security
- ['encrypt', 'decrypt']
- );
-
- // Derive MAC key for message authentication
- const macKey = await crypto.subtle.deriveKey(
- {
- name: 'HKDF',
- hash: 'SHA-256',
- salt: saltBytes,
- info: encoder.encode('message-authentication-v4')
- },
- rawSharedSecret,
- {
- name: 'HMAC',
- hash: 'SHA-256'
- },
- false, // Non-extractable
- ['sign', 'verify']
- );
-
- // Derive Perfect Forward Secrecy key (pfsKey)
- const pfsKey = await crypto.subtle.deriveKey(
- {
- name: 'HKDF',
- hash: 'SHA-256',
- salt: saltBytes,
- info: encoder.encode('perfect-forward-secrecy-v4')
- },
- rawSharedSecret,
- {
- name: 'AES-GCM',
- length: 256
- },
- false, // Non-extractable
- ['encrypt', 'decrypt']
- );
-
- // Derive separate metadata encryption key
- const metadataKey = await crypto.subtle.deriveKey(
- {
- name: 'HKDF',
- hash: 'SHA-256',
- salt: saltBytes,
- info: encoder.encode('metadata-protection-v4')
- },
- rawSharedSecret,
- {
- name: 'AES-GCM',
- length: 256
- },
- false, // Non-extractable
- ['encrypt', 'decrypt']
- );
-
- // Generate temporary extractable key for fingerprint calculation
- const fingerprintKey = await crypto.subtle.deriveKey(
- {
- name: 'HKDF',
- hash: 'SHA-256',
- salt: saltBytes,
- info: encoder.encode('fingerprint-generation-v4')
- },
- rawSharedSecret,
- {
- name: 'AES-GCM',
- length: 256
- },
- true, // Extractable only for fingerprint
- ['encrypt', 'decrypt']
- );
-
- // Generate key fingerprint for verification
- const fingerprintKeyData = await crypto.subtle.exportKey('raw', fingerprintKey);
- const fingerprint = await generateKeyFingerprint(Array.from(new Uint8Array(fingerprintKeyData)));
-
- return {
- messageKey,
- macKey,
- pfsKey,
- metadataKey,
- fingerprint,
- timestamp: Date.now(),
- version: '4.0'
- };
-}
-```
-
-#### **HKDF Security Properties**
-- **RFC 5869 Compliance:** Full adherence to HMAC-based Extract-and-Expand Key Derivation Function standard
-- **Proper Key Separation:** Each derived key uses unique `info` parameter to prevent key reuse
-- **Salt Security:** 64-byte cryptographically secure salt for each derivation
-- **Non-Extractable Keys:** All operational keys are hardware-protected and non-exportable
-- **Forward Secrecy:** Independent key derivation for each session prevents key compromise propagation
-- **Algorithm Consistency:** SHA-256 hash function for optimal compatibility and performance
-
-### Key Fingerprinting
-
-#### **Cryptographic Fingerprint Generation**
-```javascript
-async function generateKeyFingerprint(keyData) {
- const keyBuffer = new Uint8Array(keyData);
-
- // Use SHA-384 for fingerprint generation
- const hashBuffer = await crypto.subtle.digest('SHA-384', keyBuffer);
- const hashArray = Array.from(new Uint8Array(hashBuffer));
-
- // Format as colon-separated hex pairs (first 12 bytes)
- return hashArray.slice(0, 12)
- .map(b => b.toString(16).padStart(2, '0'))
- .join(':');
-}
-```
-
-#### **Public Key Hashing**
-```javascript
-async function hashPublicKey(publicKey) {
- const exported = await crypto.subtle.exportKey('spki', publicKey);
- const hash = await crypto.subtle.digest('SHA-384', exported);
- const hashArray = Array.from(new Uint8Array(hash));
-
- return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
-}
-```
-
----
-
-## ⏰ Perfect Forward Secrecy
-
-### Key Rotation Protocol
-
-#### **Automatic Key Rotation**
-```javascript
-class PFSKeyManager {
- constructor() {
- this.keyRotationInterval = 300000; // 5 minutes
- this.lastKeyRotation = Date.now();
- this.currentKeyVersion = 0;
- this.keyVersions = new Map();
- this.oldKeys = new Map();
- this.maxOldKeys = 3; // Keep last 3 versions
- }
-
- shouldRotateKeys() {
- const now = Date.now();
- const timeSinceLastRotation = now - this.lastKeyRotation;
-
- // Rotate every 5 minutes or after 100 messages
- return timeSinceLastRotation > this.keyRotationInterval ||
- this.messageCounter % 100 === 0;
- }
-
- async rotateKeys() {
- if (!this.isConnected() || !this.isVerified) {
- return false;
- }
-
- try {
- // Signal key rotation to peer
- const rotationSignal = {
- type: 'key_rotation_signal',
- newVersion: this.currentKeyVersion + 1,
- timestamp: Date.now()
- };
-
- this.dataChannel.send(JSON.stringify(rotationSignal));
-
- // Wait for peer confirmation
- return new Promise((resolve) => {
- this.pendingRotation = {
- newVersion: this.currentKeyVersion + 1,
- resolve: resolve
- };
-
- // Timeout after 5 seconds
- setTimeout(() => {
- if (this.pendingRotation) {
- this.pendingRotation.resolve(false);
- this.pendingRotation = null;
- }
- }, 5000);
- });
-
- } catch (error) {
- console.error('Key rotation failed:', error);
- return false;
- }
- }
-
- cleanupOldKeys() {
- const now = Date.now();
- const maxKeyAge = 900000; // 15 minutes
-
- for (const [version, keySet] of this.oldKeys.entries()) {
- if (now - keySet.timestamp > maxKeyAge) {
- this.oldKeys.delete(version);
- console.log(`Old PFS keys cleaned up: version ${version}`);
- }
- }
- }
-}
-```
-
-#### **Key Version Management**
-```javascript
-getKeysForVersion(version) {
- // Check old keys first
- const oldKeySet = this.oldKeys.get(version);
- if (oldKeySet && oldKeySet.encryptionKey && oldKeySet.macKey && oldKeySet.metadataKey) {
- return {
- encryptionKey: oldKeySet.encryptionKey,
- macKey: oldKeySet.macKey,
- metadataKey: oldKeySet.metadataKey
- };
- }
-
- // Check current version
- if (version === this.currentKeyVersion) {
- if (this.encryptionKey && this.macKey && this.metadataKey) {
- return {
- encryptionKey: this.encryptionKey,
- macKey: this.macKey,
- metadataKey: this.metadataKey
- };
- }
- }
-
- console.error(`No valid keys found for version ${version}`);
- return null;
-}
-```
-
-### Forward Secrecy Guarantees
-
-#### **Security Properties**
-- **Computational Forward Secrecy:** Past sessions cannot be decrypted even with current keys
-- **Perfect Forward Secrecy:** Past sessions cannot be decrypted even with long-term keys
-- **Post-Compromise Security:** Future sessions remain secure after key compromise
-- **Key Independence:** Each session uses independent cryptographic material
-
-#### **Implementation Verification**
-```javascript
-async function verifyPFS() {
- // Check key rotation is active
- const hasKeyRotation = this.keyRotationInterval > 0;
- const hasVersionTracking = this.currentKeyVersion !== undefined;
- const hasOldKeyCleanup = this.oldKeys instanceof Map;
-
- // Verify automatic rotation
- const rotationWorking = typeof this.shouldRotateKeys === 'function';
-
- return hasKeyRotation && hasVersionTracking && hasOldKeyCleanup && rotationWorking;
-}
-```
-
----
-
-## 🔬 Security Analysis
-
-### Cryptographic Security Levels
-
-| Component | Algorithm | Key Size | Classical Security | Quantum Security | Post-Quantum Timeline |
-|-----------|-----------|----------|-------------------|------------------|----------------------|
-| **ECDH** | P-384 | 384-bit | 192-bit | 64-bit | 2040+ |
-| **ECDSA** | P-384 | 384-bit | 192-bit | 64-bit | 2040+ |
-| **AES** | AES-256 | 256-bit | 256-bit | 128-bit | 2080+ |
-| **SHA** | SHA-384 | - | 192-bit | 96-bit | 2050+ |
-| **HMAC** | HMAC-SHA-384 | 384-bit | 192-bit | 96-bit | 2050+ |
-
-### Attack Resistance Analysis
-
-#### **Classical Attacks**
-- ✅ **Brute Force:** Computationally infeasible (2^256 operations)
-- ✅ **Cryptanalysis:** No known practical attacks on used algorithms
-- ✅ **Side-Channel:** Constant-time implementations, hardware protection
-- ✅ **Implementation:** Secure coding practices, input validation
-
-#### **Quantum Attacks**
-- ⚠️ **Shor's Algorithm:** Affects ECDH/ECDSA (timeline > 15 years)
-- ✅ **Grover's Algorithm:** AES-256 remains secure (128-bit post-quantum)
-- ✅ **Hash Functions:** SHA-384 maintains adequate security
-- 🔄 **Mitigation:** Post-quantum algorithms planned for 2026
-
-#### **Advanced Persistent Threats**
-- ✅ **Nation-State:** Multiple security layers, PFS
-- ✅ **Zero-Day Exploits:** Defense in depth, algorithm diversity
-- ✅ **Supply Chain:** Hardware-based key generation
-- ✅ **Insider Threats:** Non-extractable keys, audit trails
-
-### Cryptographic Assumptions
-
-#### **Computational Assumptions**
-1. **Elliptic Curve Discrete Logarithm Problem (ECDLP)** is hard
-2. **AES is a secure pseudorandom permutation**
-3. **SHA-384 is collision and preimage resistant**
-4. **Random number generators are cryptographically secure**
-
-#### **Implementation Assumptions**
-1. **Hardware provides secure random number generation**
-2. **Browser crypto APIs are correctly implemented**
-3. **Side-channel attacks are mitigated by hardware**
-4. **Memory protection prevents key extraction**
-
----
-
-## ⚡ Performance Optimization
-
-### Hardware Acceleration
-
-#### **AES-NI Utilization**
-```javascript
-// Leverages hardware AES acceleration when available
-const encryptWithHardwareAcceleration = async (data, key) => {
- // Browser automatically uses AES-NI if available
- return await crypto.subtle.encrypt(
- { name: 'AES-GCM', iv: generateIV() },
- key,
- data
- );
-};
-```
-
-#### **Performance Benchmarks**
-
-| Operation | Software (ms) | Hardware (ms) | Speedup |
-|-----------|---------------|---------------|---------|
-| **AES Encryption** | 2.5 | 0.3 | 8.3x |
-| **ECDH Key Agreement** | 15.0 | 5.0 | 3.0x |
-| **ECDSA Signing** | 12.0 | 4.0 | 3.0x |
-| **SHA-384 Hashing** | 1.5 | 0.5 | 3.0x |
-| **Overall Pipeline** | 31.0 | 9.8 | 3.2x |
-
-### Memory Management
-
-#### **Secure Memory Practices**
-```javascript
-class SecureMemoryManager {
- static clearSensitiveData(buffer) {
- // Overwrite sensitive data
- if (buffer instanceof ArrayBuffer) {
- const view = new Uint8Array(buffer);
- crypto.getRandomValues(view);
- }
- }
-
- static zeroMemory(array) {
- // Zero out array contents
- for (let i = 0; i < array.length; i++) {
- array[i] = 0;
- }
- }
-
- static secureDispose(cryptoKey) {
- // Browser handles secure disposal of CryptoKey objects
- // Keys are automatically cleared when no longer referenced
- cryptoKey = null;
- }
-}
-```
-
-### Constant-Time Operations
-
-#### **Timing Attack Prevention**
-```javascript
-// Constant-time comparison for preventing timing attacks
-function constantTimeEquals(a, b) {
- if (a.length !== b.length) {
- return false;
- }
-
- let result = 0;
- for (let i = 0; i < a.length; i++) {
- result |= a[i] ^ b[i];
- }
-
- return result === 0;
-}
-
-// Use constant-time comparison for MAC verification
-function verifyMAC(expected, actual) {
- return constantTimeEquals(
- new Uint8Array(expected),
- new Uint8Array(actual)
- );
-}
-```
-
----
-
-## 📊 Compliance and Standards
-
-### Standards Compliance
-
-#### **NIST Standards**
-- ✅ **NIST SP 800-57:** Key Management Guidelines
-- ✅ **FIPS 186-4:** Digital Signature Standard (ECDSA)
-- ✅ **FIPS 197:** Advanced Encryption Standard (AES)
-- ✅ **FIPS 180-4:** Secure Hash Standard (SHA-384)
-- ✅ **FIPS 198-1:** Keyed-Hash Message Authentication Code
-
-#### **RFC Standards**
-- ✅ **RFC 5869:** HMAC-based Extract-and-Expand Key Derivation Function
-- ✅ **RFC 6979:** Deterministic Usage of DSA and ECDSA
-- ✅ **RFC 7748:** Elliptic Curves for Security
-- ✅ **RFC 8439:** ChaCha20-Poly1305 (reference implementation)
-
-#### **Industry Standards**
-- ✅ **NSA Suite B:** Cryptographic algorithms for national security
-- ✅ **Common Criteria:** Security functional requirements
-- ✅ **ISO 27001:** Information security management
-- ✅ **PKCS #11:** Cryptographic Token Interface
-
-### Certification Status
-
-| Standard | Status | Timeline |
-|----------|--------|----------|
-| **FIPS 140-2 Level 2** | 🔄 In Progress | Q2 2025 |
-| **Common Criteria EAL4+** | 📋 Planned | Q3 2025 |
-| **FIPS 140-3** | 📋 Planned | Q4 2025 |
-| **NSA Commercial Solutions** | 🔄 In Progress | Q1 2026 |
-
-### Regulatory Compliance
-
-#### **Data Protection Regulations**
-- ✅ **GDPR Article 32:** Technical and organizational measures
-- ✅ **CCPA Section 1798.150:** Encryption requirements
-- ✅ **HIPAA Security Rule:** Administrative, physical, and technical safeguards
-- ✅ **SOX Section 404:** Internal controls over financial reporting
-
-#### **Export Control Compliance**
-- ✅ **ITAR Category XIII:** Cryptographic equipment compliance
-- ✅ **EAR Part 740.17:** Encryption commodities and software
-- ✅ **Wassenaar Arrangement:** Dual-use goods controls
-
----
-
-## 🧪 Testing and Validation
-
-### Cryptographic Test Vectors
-
-#### **ECDH Test Vectors**
-```javascript
-const ecdhTestVectors = [
- {
- curve: 'P-384',
- privateKeyA: '0x1234...', // Test private key A
- publicKeyB: '0x5678...', // Test public key B
- expectedSharedSecret: '0x9abc...', // Expected result
- description: 'NIST P-384 test vector #1'
- },
- // Additional test vectors...
-];
-
-async function validateECDHImplementation() {
- for (const vector of ecdhTestVectors) {
- const result = await performECDH(vector.privateKeyA, vector.publicKeyB);
- assert(result === vector.expectedSharedSecret,
- `ECDH test failed: ${vector.description}`);
- }
-}
-```
-
-#### **AES-GCM Test Vectors**
-```javascript
-const aesGcmTestVectors = [
- {
- key: '0x0123456789abcdef...',
- iv: '0x000000000000000000000000',
- plaintext: '0x00000000000000000000000000000000',
- aad: '0x',
- ciphertext: '0x0388dace60b6a392f328c2b971b2fe78',
- tag: '0xab6e47d42cec13bdf53a67b21257bddf',
- description: 'NIST GCM test vector #1'
- },
- // Additional test vectors...
-];
-```
-
-### Security Testing
-
-#### **Automated Security Tests**
-```javascript
-class CryptographicSecurityTester {
- async runAllTests() {
- const testResults = await Promise.all([
- this.testKeyGeneration(),
- this.testEncryptionDecryption(),
- this.testSignatureVerification(),
- this.testKeyDerivation(),
- this.testPerfectForwardSecrecy(),
- this.testReplayProtection(),
- this.testTimingAttacks(),
- this.testSideChannelResistance()
- ]);
-
- return testResults.every(result => result.passed);
- }
-
- async testKeyGeneration() {
- try {
- // Test ECDH key generation
- const ecdhKeyPair = await generateECDHKeyPair();
- assert(ecdhKeyPair.privateKey.algorithm.namedCurve === 'P-384');
- assert(ecdhKeyPair.privateKey.extractable === false);
-
- // Test ECDSA key generation
- const ecdsaKeyPair = await generateECDSAKeyPair();
- assert(ecdsaKeyPair.privateKey.algorithm.namedCurve === 'P-384');
- assert(ecdsaKeyPair.privateKey.extractable === false);
-
- return { passed: true, test: 'Key Generation' };
- } catch (error) {
- return { passed: false, test: 'Key Generation', error: error.message };
- }
- }
-
- async testTimingAttacks() {
- const iterations = 1000;
- const timings = [];
-
- // Measure signature verification times
- for (let i = 0; i < iterations; i++) {
- const start = performance.now();
- await verifySignature(/* test parameters */);
- const end = performance.now();
- timings.push(end - start);
- }
-
- // Statistical analysis for timing consistency
- const mean = timings.reduce((a, b) => a + b) / timings.length;
- const variance = timings.reduce((sum, time) => sum + Math.pow(time - mean, 2), 0) / timings.length;
- const coefficient = Math.sqrt(variance) / mean;
-
- // Should have low coefficient of variation (< 0.1)
- return {
- passed: coefficient < 0.1,
- test: 'Timing Attack Resistance',
- coefficient: coefficient
- };
- }
-}
-```
-
-### Fuzzing and Stress Testing
-
-#### **Input Validation Testing**
-```javascript
-class CryptographicFuzzTester {
- async fuzzEncryption() {
- const fuzzInputs = [
- new Uint8Array(0), // Empty input
- new Uint8Array(1).fill(0), // Single zero byte
- new Uint8Array(1024 * 1024).fill(255), // 1MB of 0xFF
- crypto.getRandomValues(new Uint8Array(65536)), // Random data
- // Malformed inputs, boundary conditions, etc.
- ];
-
- for (const input of fuzzInputs) {
- try {
- const encrypted = await encryptMessage(input, /* keys */);
- const decrypted = await decryptMessage(encrypted, /* keys */);
-
- // Verify round-trip integrity
- assert(this.arraysEqual(input, decrypted));
- } catch (error) {
- // Expected for some malformed inputs
- console.log(`Fuzz test handled gracefully: ${error.message}`);
- }
- }
- }
-
- arraysEqual(a, b) {
- return a.length === b.length && a.every((val, i) => val === b[i]);
- }
-}
-```
-
----
-
-## 🔮 Future Cryptographic Enhancements
-
-### Post-Quantum Cryptography Migration
-
-#### **Timeline and Strategy**
-```
-2025 Q4: Research and prototyping
-├── Evaluate NIST-standardized algorithms
-├── Performance benchmarking
-└── Hybrid classical/post-quantum implementation
-
-2026 Q2: Hybrid deployment
-├── CRYSTALS-Kyber for key encapsulation
-├── CRYSTALS-Dilithium for digital signatures
-└── Dual algorithm support with fallback
-
-2027 Q1: Full post-quantum transition
-├── Pure post-quantum algorithms
-├── Legacy compatibility layer
-└── Migration tools for existing users
-```
-
-#### **Planned Algorithms**
-| Function | Current Algorithm | Post-Quantum Algorithm | Timeline |
-|----------|-------------------|------------------------|----------|
-| **Key Exchange** | ECDH P-384 | CRYSTALS-Kyber-1024 | 2026 Q2 |
-| **Signatures** | ECDSA P-384 | CRYSTALS-Dilithium-5 | 2026 Q2 |
-| **Encryption** | AES-256-GCM | AES-256-GCM (quantum-safe) | Current |
-| **Hashing** | SHA-384 | SHA-3-384 | 2026 Q4 |
-
-### Advanced Cryptographic Features
-
-#### **Homomorphic Encryption**
-```javascript
-// Future implementation for privacy-preserving operations
-class HomomorphicEncryption {
- async encryptWithHomomorphism(data, publicKey) {
- // Enable computations on encrypted data
- // Useful for encrypted search, statistics
- }
-
- async computeOnEncrypted(encryptedData, operation) {
- // Perform operations without decryption
- return encryptedResult;
- }
-}
-```
-
-#### **Zero-Knowledge Proofs**
-```javascript
-// Future implementation for authentication without revelation
-class ZeroKnowledgeAuth {
- async generateProof(secret, challenge) {
- // Prove knowledge without revealing secret
- }
-
- async verifyProof(proof, challenge, publicData) {
- // Verify proof without learning secret
- }
-}
-```
-
-### Quantum Key Distribution
-
-#### **Hardware Integration Planning**
-```javascript
-// Future quantum key distribution integration
-class QuantumKeyDistribution {
- async establishQuantumChannel() {
- // Hardware-based quantum key generation
- // Ultimate forward secrecy
- }
-
- async detectEavesdropping() {
- // Quantum mechanics for eavesdropping detection
- // Automatic key invalidation on interference
- }
-}
-```
-
----
-
-## 📚 Implementation Examples
-
-### Complete Message Encryption Flow
-
-#### **Full Implementation Example**
-```javascript
-class SecureMessageProcessor {
- constructor(encryptionKey, macKey, metadataKey, signingKey) {
- this.encryptionKey = encryptionKey;
- this.macKey = macKey;
- this.metadataKey = metadataKey;
- this.signingKey = signingKey;
- this.sequenceNumber = 0;
- }
-
- async sendSecureMessage(message) {
- try {
- // Step 1: Generate message ID and increment sequence
- const messageId = `msg_${Date.now()}_${Math.random().toString(36)}`;
- const sequenceNumber = this.sequenceNumber++;
-
- // Step 2: Encrypt message with metadata protection
- const encryptedData = await encryptMessage(
- message,
- this.encryptionKey,
- this.macKey,
- this.metadataKey,
- messageId,
- sequenceNumber
- );
-
- // Step 3: Create enhanced message payload
- const payload = {
- type: 'enhanced_message',
- data: encryptedData,
- keyVersion: this.currentKeyVersion,
- version: '4.0'
- };
-
- // Step 4: Sign the entire payload
- const payloadString = JSON.stringify(payload);
- const signature = await signData(this.signingKey, payloadString);
- payload.signature = signature;
-
- // Step 5: Send through WebRTC channel
- this.dataChannel.send(JSON.stringify(payload));
-
- console.log(`✅ Secure message sent: ${messageId}`);
- return { success: true, messageId };
-
- } catch (error) {
- console.error('❌ Secure message sending failed:', error);
- return { success: false, error: error.message };
- }
- }
-
- async receiveSecureMessage(rawData) {
- try {
- // Step 1: Parse incoming message
- const payload = JSON.parse(rawData);
-
- // Step 2: Verify message signature
- if (payload.signature) {
- const payloadCopy = { ...payload };
- delete payloadCopy.signature;
- const payloadString = JSON.stringify(payloadCopy);
-
- const isValidSignature = await verifySignature(
- this.peerPublicKey,
- payload.signature,
- payloadString
- );
-
- if (!isValidSignature) {
- throw new Error('Invalid message signature');
- }
- }
-
- // Step 3: Get appropriate keys for decryption
- const keyVersion = payload.keyVersion || 0;
- const keys = this.getKeysForVersion(keyVersion);
-
- if (!keys) {
- throw new Error(`Keys not available for version ${keyVersion}`);
- }
-
- // Step 4: Decrypt message
- const decryptedData = await decryptMessage(
- payload.data,
- keys.encryptionKey,
- keys.macKey,
- keys.metadataKey
- );
-
- // Step 5: Verify sequence number and prevent replay
- if (this.processedMessageIds.has(decryptedData.messageId)) {
- throw new Error('Duplicate message detected - replay attack');
- }
- this.processedMessageIds.add(decryptedData.messageId);
-
- console.log(`✅ Secure message received: ${decryptedData.messageId}`);
- return {
- success: true,
- message: decryptedData.message,
- messageId: decryptedData.messageId,
- timestamp: decryptedData.timestamp
- };
-
- } catch (error) {
- console.error('❌ Secure message processing failed:', error);
- return { success: false, error: error.message };
- }
- }
-}
-```
-
-### Error Handling and Recovery
-
-#### **Cryptographic Error Recovery**
-```javascript
-class CryptographicErrorHandler {
- static async handleDecryptionError(error, retryCount = 0) {
- const maxRetries = 3;
-
- if (retryCount >= maxRetries) {
- throw new Error(`Decryption failed after ${maxRetries} attempts: ${error.message}`);
- }
-
- // Analyze error type and attempt recovery
- if (error.message.includes('InvalidAccessError')) {
- // Key might be corrupted, regenerate if possible
- console.warn('Key access error, attempting key recovery...');
- await this.attemptKeyRecovery();
- return { retry: true, newKeys: true };
- }
-
- if (error.message.includes('OperationError')) {
- // Generic operation error, retry with delay
- console.warn(`Cryptographic operation failed, retrying in ${retryCount * 1000}ms...`);
- await new Promise(resolve => setTimeout(resolve, retryCount * 1000));
- return { retry: true, delay: retryCount * 1000 };
- }
-
- // Unrecoverable error
- throw error;
- }
-
- static async validateCryptographicState() {
- // Comprehensive cryptographic state validation
- const checks = [
- this.validateKeyIntegrity(),
- this.validateAlgorithmSupport(),
- this.validateRandomNumberGenerator(),
- this.validateTimingConsistency()
- ];
-
- const results = await Promise.allSettled(checks);
- const failures = results.filter(r => r.status === 'rejected');
-
- if (failures.length > 0) {
- throw new Error(`Cryptographic state validation failed: ${failures.map(f => f.reason).join(', ')}`);
- }
-
- return { valid: true, timestamp: Date.now() };
- }
-}
-```
-
----
-
-## 📞 Support and Documentation
-
-### Technical Support
-
-For cryptographic implementation questions:
-- **Security Team:** security@SecureBit.chat
-- **Cryptographic Specialists:** crypto@SecureBit.chat
-- **GitHub Issues:** [Cryptography Issues](https://github.com/SecureBitChat/securebit-chat/issues?q=label%3Acryptography)
-
-### Additional Resources
-
-#### **Academic Papers**
-- [Elliptic Curve Cryptography](https://link.springer.com/article/10.1007/s00145-001-0020-9)
-- [AES-GCM Security Analysis](https://csrc.nist.gov/publications/detail/sp/800-38d/final)
-- [Post-Quantum Cryptography](https://csrc.nist.gov/Projects/post-quantum-cryptography)
-
-#### **Standards Documents**
-- [NIST SP 800-57 Part 1](https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final)
-- [FIPS 186-4 Digital Signature Standard](https://csrc.nist.gov/publications/detail/fips/186/4/final)
-- [RFC 5869 HKDF](https://tools.ietf.org/html/rfc5869)
-
-#### **Implementation Guides**
-- [Web Crypto API Best Practices](https://www.w3.org/TR/WebCryptoAPI/)
-- [Secure Coding Guidelines](https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard)
-- [Cryptographic Protocol Design](https://link.springer.com/book/10.1007/978-3-662-46447-2)
-
----
-
-## 🏁 Conclusion
-
-SecureBit.chat's cryptographic implementation represents the state-of-the-art in secure peer-to-peer communications. Our multi-layered approach combining classical cryptography with forward-looking security measures provides unprecedented protection against current and future threats.
-
-### Key Achievements
-
-- **🔒 Military-Grade Security:** Triple-layer encryption with 256+ bit security
-- **🛡️ Future-Proof Design:** Quantum-resistant timeline > 15 years
-- **⚡ High Performance:** Hardware acceleration with minimal latency impact
-- **📊 Standards Compliance:** NIST, FIPS, NSA Suite B certified algorithms
-- **🔄 Perfect Forward Secrecy:** Automatic key rotation every 5 minutes
-- **🎯 Zero Trust:** No reliance on external trusted parties
-
-### Security Guarantees
-
-Our cryptographic implementation provides:
-- **Confidentiality:** Triple-layer AES-256-GCM encryption
-- **Integrity:** HMAC-SHA-384 message authentication
-- **Authenticity:** ECDSA P-384 digital signatures
-- **Non-repudiation:** Cryptographic proof of message origin
-- **Forward Secrecy:** Past communications remain secure
-- **Replay Protection:** Comprehensive anti-replay mechanisms
-
-**This cryptographic foundation enables SecureBit.chat to provide the most secure peer-to-peer communications platform available today.**
-
----
-
-*This document reflects the current state of cryptographic implementation in SecureBit.chat v4.1. All algorithms and protocols are subject to ongoing security review and enhancement.*
-
-**Last Updated:** January 15, 2025
-**Document Version:** 4.1
-**Cryptographic Implementation:** Stage 5 - Military-Grade Security
-**Review Status:** ✅ Verified by Cryptographic Specialists
\ No newline at end of file
+This document describes the current browser implementation behavior relevant to the v4.8.5 hardening release. It does not replace independent cryptographic review.
diff --git a/doc/SECURITY-ARCHITECTURE.md b/doc/SECURITY-ARCHITECTURE.md
index 202b1dc..0d98ceb 100644
--- a/doc/SECURITY-ARCHITECTURE.md
+++ b/doc/SECURITY-ARCHITECTURE.md
@@ -1,774 +1,53 @@
-# SecureBit.chat Security Architecture v4.02.985
+# Security Architecture
-## 🛡️ Overview
+## Current baseline
-SecureBit.chat implements a revolutionary **18-layer security architecture** with ECDH + DTLS + SAS authentication that provides military-grade protection for peer-to-peer communications. This document details the technical implementation of our security system, which exceeds most government and enterprise communication standards.
+| Area | Current behavior |
+| --- | --- |
+| Protocol | `4.1` with mismatch rejection |
+| Peer verification | mandatory manual SAS entry |
+| Transport | WebRTC over DTLS |
+| Privacy mode | optional TURN relay-only mode |
+| Message UI safety | incoming decrypted text sanitized before display |
+| File transfer | validated metadata, explicit consent, allowlist policy |
+| Local metadata | encrypted IndexedDB envelopes with migration |
+| Lifecycle | unified disconnect cleanup and bounded resource retention |
-**Current Implementation:** Stage 5 - Maximum Security
-**Security Rating:** Maximum (ECDH + DTLS + SAS)
-**Active Layers:** 18/18
-**Threat Protection:** Comprehensive (MITM, Traffic Analysis, Replay Attacks, Session Hijacking, Race Conditions, Key Exposure, DTLS Race Conditions, Memory Safety, Use-After-Free, Key Structure Manipulation)
+## Verification state machine
----
-
-## 📋 Table of Contents
-
-1. [Security Architecture Overview](#security-architecture-overview)
-2. [Layer-by-Layer Analysis](#layer-by-layer-analysis)
-3. [Cryptographic Specifications](#cryptographic-specifications)
-4. [Threat Model](#threat-model)
-5. [Implementation Details](#implementation-details)
-6. [Security Verification](#security-verification)
-7. [Performance Impact](#performance-impact)
-8. [Compliance Standards](#compliance-standards)
-9. [ASN.1 Validation Framework](#asn1-validation-framework)
-
----
-
-## 🏗️ Security Architecture Overview
-
-### 19-Layer Defense System
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ APPLICATION LAYER │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 19: HKDF Key Derivation (RFC 5869 Compliant) │
-│ Layer 18: EC Point Validation (Format & Structure) │
-│ Layer 17: OID Validation (Algorithm & Curve Verification) │
-│ Layer 16: ASN.1 Validation (Complete Key Structure) │
-│ Layer 15: Production Security Logging (Data Sanitization) │
-│ Layer 14: Secure Key Storage (WeakMap Isolation) │
-│ Layer 13: Mutex Framework (Race Condition Protection) │
-├─────────────────────────────────────────────────────────────┤
-│ CRYPTOGRAPHIC LAYER │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 12: Perfect Forward Secrecy (Key Rotation) │
-│ Layer 11: Enhanced Rate Limiting (DDoS Protection) │
-│ Layer 10: Fake Traffic Generation (Traffic Analysis) │
-│ Layer 9: Message Chunking (Timing Analysis Protection) │
-│ Layer 8: Packet Reordering Protection (Sequence Security) │
-├─────────────────────────────────────────────────────────────┤
-│ OBFUSCATION LAYER │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 7: Anti-Fingerprinting (Pattern Obfuscation) │
-│ Layer 6: Packet Padding (Size Obfuscation) │
-│ Layer 5: Nested Encryption (Additional AES-GCM) │
-├─────────────────────────────────────────────────────────────┤
-│ ENCRYPTION LAYER │
-├─────────────────────────────────────────────────────────────┤
-│ Layer 4: Message Encryption (Enhanced AES-GCM) │
-│ Layer 3: Metadata Protection (Separate AES-GCM) │
-│ Layer 2: Key Exchange (ECDH P-384) │
-│ Layer 1: Enhanced Authentication (ECDSA P-384) │
-├─────────────────────────────────────────────────────────────┤
-│ TRANSPORT LAYER │
-│ (WebRTC/ICE/DTLS) │
-└─────────────────────────────────────────────────────────────┘
+```text
+connection established
+ ↓
+shared keys derived
+ ↓
+deterministic SAS displayed
+ ↓
+manual out-of-band comparison
+ ↓
+local input validated
+ ↓
+peer confirmation received
+ ↓
+verified session
```
-### Security Progression Stages
+The verified state is reached only when both local and remote confirmation flags are true.
-| Stage | Layers Active | Security Level | Target Threats |
-|-------|---------------|----------------|-----------------|
-| 1 | 1-5 | Basic Enhanced | Basic attacks, MITM |
-| 2 | 1-7 | Medium | + Traffic analysis |
-| 3 | 1-9 | High | + Timing attacks |
-| 4 | 1-12 | High Enhanced | + Advanced persistent threats |
-| 5 | 1-15 | Military-Grade | + Race conditions, Key exposure |
-| 6 | 1-19 | Maximum | + DTLS race conditions, Memory safety, Key structure validation, HKDF compliance |
+## File-transfer architecture
----
+1. sender emits metadata
+2. receiver validates name, size, type, and abuse limits
+3. receiver sees Accept / Reject prompt
+4. no receive buffers are allocated before acceptance
+5. sender transmits chunks only after acceptance
+6. completed received buffers are retained within a bounded window
-## 🔍 Layer-by-Layer Analysis
+## Disconnect cleanup
-### Layer 1: Enhanced Authentication (ECDSA P-384)
-**Purpose:** Cryptographic proof of message authenticity and sender verification
+The canonical disconnect path clears:
-**Technical Specifications:**
-- **Algorithm:** ECDSA with P-384 curve
-- **Hash Function:** SHA-384 (primary), SHA-256 (fallback)
-- **Key Size:** 384-bit (equivalent to 7680-bit RSA)
-- **Signature Size:** 96 bytes
-- **Key Properties:** Non-extractable, hardware-protected
-
-**Implementation:**
-```javascript
-// Self-signed key package for MITM protection
-const keyPackage = {
- keyType: 'ECDSA',
- keyData: exported384BitKey,
- timestamp: Date.now(),
- version: '4.02',
- signature: ecdsaSignature
-};
-```
-
-### Layer 16: ASN.1 Validation (Complete Key Structure)
-**Purpose:** Complete structural validation of all cryptographic keys according to PKCS standards
-
-**Technical Specifications:**
-- **Parser:** Complete ASN.1 DER parser
-- **Validation Scope:** Full key structure verification
-- **Standards:** RFC 5280, RFC 5480, PKCS compliance
-- **Performance:** < 10ms validation time
-- **Coverage:** All cryptographic operations
-
-**Implementation:**
-```javascript
-// Complete ASN.1 DER parsing and validation
-const validateKeyStructure = (keyData) => {
- const asn1Parser = new ASN1Validator();
- const parsed = asn1Parser.parseDER(keyData);
-
- // Validate complete structure
- if (!asn1Parser.validateSPKI(parsed)) {
- throw new Error('Invalid SPKI structure');
- }
-
- // Validate OID and curves
- if (!asn1Parser.validateOID(parsed)) {
- throw new Error('Invalid algorithm OID');
- }
-
- // Validate EC point format
- if (!asn1Parser.validateECPoint(parsed)) {
- throw new Error('Invalid EC point format');
- }
-
- return true;
-};
-```
-
-### Layer 17: OID Validation (Algorithm & Curve Verification)
-**Purpose:** Verification of cryptographic algorithms and elliptic curves
-
-**Technical Specifications:**
-- **Supported Curves:** P-256, P-384 only
-- **Algorithm Validation:** Complete OID verification
-- **Fallback Support:** P-384 to P-256 compatibility
-- **Security:** Prevents algorithm substitution attacks
-
-**Implementation:**
-```javascript
-// OID validation for algorithms and curves
-const validateOID = (parsed) => {
- const validOIDs = {
- '1.2.840.10045.3.1.7': 'P-256', // secp256r1
- '1.3.132.0.34': 'P-384' // secp384r1
- };
-
- const oid = parsed.algorithm.algorithm;
- if (!validOIDs[oid]) {
- throw new Error(`Unsupported curve: ${oid}`);
- }
-
- return validOIDs[oid];
-};
-```
-
-### Layer 19: HKDF Key Derivation (RFC 5869 Compliant)
-**Purpose:** RFC 5869 compliant key derivation with proper key separation and cryptographic security
-
-**Technical Specifications:**
-- **Standard:** RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function
-- **Hash Function:** SHA-256 for optimal compatibility and performance
-- **Salt Security:** 64-byte cryptographically secure salt for each derivation
-- **Key Separation:** Unique `info` parameters for each derived key type
-- **Non-Extractable Keys:** Hardware-protected keys for enhanced security
-
-**Implementation:**
-```javascript
-// HKDF key derivation with proper separation
-const deriveSharedKeys = async (privateKey, publicKey, salt) => {
- // Step 1: Pure ECDH derivation
- const rawKeyMaterial = await crypto.subtle.deriveKey(
- { name: 'ECDH', public: publicKey },
- privateKey,
- { name: 'AES-GCM', length: 256 },
- true, // Extractable for HKDF processing
- ['encrypt', 'decrypt']
- );
-
- // Export and import for HKDF
- const rawKeyData = await crypto.subtle.exportKey('raw', rawKeyMaterial);
- const rawSharedSecret = await crypto.subtle.importKey(
- 'raw', rawKeyData,
- { name: 'HKDF', hash: 'SHA-256' },
- false, ['deriveKey']
- );
-
- // Step 2: Derive specific keys with unique info parameters
- const messageKey = await crypto.subtle.deriveKey(
- {
- name: 'HKDF',
- hash: 'SHA-256',
- salt: saltBytes,
- info: encoder.encode('message-encryption-v4')
- },
- rawSharedSecret,
- { name: 'AES-GCM', length: 256 },
- false, ['encrypt', 'decrypt']
- );
-
- // Additional keys derived with unique info parameters...
- return { messageKey, macKey, pfsKey, metadataKey, fingerprint };
-};
-```
-
-### Layer 18: EC Point Validation (Format & Structure Verification)
-**Purpose:** Verification of elliptic curve point format and structure
-
-**Technical Specifications:**
-- **Format:** Uncompressed format 0x04 only
-- **Structure:** Complete point coordinate validation
-- **Size Limits:** 50-2000 bytes to prevent DoS attacks
-- **BIT STRING:** Unused bits must be 0
-
-**Implementation:**
-```javascript
-// EC point format and structure validation
-const validateECPoint = (parsed) => {
- const publicKey = parsed.subjectPublicKey;
-
- // Check format (uncompressed 0x04)
- if (publicKey[0] !== 0x04) {
- throw new Error('Only uncompressed EC point format supported');
- }
-
- // Validate size limits
- if (publicKey.length < 50 || publicKey.length > 2000) {
- throw new Error('Key size outside allowed range (50-2000 bytes)');
- }
-
- // Validate BIT STRING unused bits
- if (parsed.unusedBits !== 0) {
- throw new Error('BIT STRING unused bits must be 0');
- }
-
- return true;
-};
-```
-
----
-
-## 🔐 Cryptographic Specifications
-
-### Algorithm Selection Rationale
-
-| Component | Algorithm | Key Size | Rationale |
-|-----------|-----------|----------|-----------|
-| Key Exchange | ECDH P-384 | 384-bit | NSA Suite B, quantum-resistant timeline |
-| Signatures | ECDSA P-384 | 384-bit | Matches key exchange, proven security |
-| Encryption | AES-256-GCM | 256-bit | NIST recommended, authenticated encryption |
-| Hashing | SHA-384 | 384-bit | Matches curve size, collision resistant |
-| MAC | HMAC-SHA-384 | 384-bit | Proven security, matches hash function |
-
-### Security Strengths
-
-- **ECDH P-384:** Equivalent to 7680-bit RSA
-- **AES-256:** Quantum computer resistant until 2040+
-- **SHA-384:** 192-bit security level (collision resistance)
-- **Combined Security:** Exceeds 256-bit security level
-
-### Cryptographic Operations Performance
-
-| Operation | Time (ms) | CPU Usage | Memory Usage |
-|-----------|-----------|-----------|--------------|
-| Key Generation | 10-50 | Medium | Low |
-| ECDH Agreement | 5-15 | Low | Low |
-| AES Encryption | 0.1-1 | Very Low | Very Low |
-| ECDSA Signing | 2-8 | Low | Low |
-| Message Processing | 1-5 | Low | Low |
-
----
-
-## 🎯 Threat Model
-
-### Threat Classifications
-
-#### **🔴 Critical Threats (Fully Mitigated)**
-- **Nation-State Attacks:** Advanced persistent threats
-- **MITM Attacks:** Certificate pinning bypass attempts
-- **Cryptographic Attacks:** Implementation vulnerabilities
-- **Traffic Analysis:** Deep packet inspection and metadata analysis
-
-#### **🟡 High Threats (Substantially Mitigated)**
-- **Side-Channel Attacks:** Timing and power analysis
-- **Social Engineering:** User manipulation (partially mitigated)
-- **Endpoint Compromise:** Device-level attacks
-- **Quantum Computing:** Future quantum attacks (timeline > 15 years)
-
-#### **🟢 Medium Threats (Completely Mitigated)**
-- **Passive Eavesdropping:** Network traffic interception
-- **Replay Attacks:** Message reuse attempts
-- **DDoS Attacks:** Service disruption attempts
-- **Protocol Downgrade:** Forced weak encryption
-
-### Attack Scenarios and Defenses
-
-#### Scenario 1: Government Surveillance
-**Attack:** Comprehensive traffic monitoring and analysis
-**Defense Layers:** 6, 7, 10, 12 (traffic obfuscation)
-**Result:** Encrypted traffic indistinguishable from noise
-
-#### Scenario 2: Corporate Espionage
-**Attack:** Targeted interception with advanced tools
-**Defense Layers:** 1, 2, 3, 4, 5 (cryptographic protection)
-**Result:** Computationally infeasible to decrypt
-
-#### Scenario 3: ISP-Level Monitoring
-**Attack:** Deep packet inspection and metadata collection
-**Defense Layers:** 6, 7, 8, 9, 10 (pattern obfuscation)
-**Result:** No useful metadata or patterns extractable
-
-#### Scenario 4: Academic Cryptanalysis
-**Attack:** Advanced mathematical attacks on crypto
-**Defense Layers:** 2, 4, 5 (multiple algorithms)
-**Result:** Multiple independent cryptographic barriers
-
----
-
-## 🔧 Implementation Details
-
-### Message Flow Through Security Layers
-
-```
-┌─────────────────┐
-│ User Message │
-└─────────┬───────┘
- │
-┌─────────▼───────┐ ┌──────────────────┐
-│ Layer 7: Anti │ │ Outbound Process │
-│ Fingerprinting │◄───┤ (Sending) │
-└─────────┬───────┘ └──────────────────┘
- │
-┌─────────▼───────┐
-│ Layer 6: Packet│
-│ Padding │
-└─────────┬───────┘
- │
-┌─────────▼───────┐
-│ Layer 8: Packet│
-│ Reordering │
-└─────────┬───────┘
- │
-┌─────────▼───────┐
-│ Layer 5: Nested│
-│ Encryption │
-└─────────┬───────┘
- │
-┌─────────▼───────┐
-│ Layer 4: Message│
-│ Encryption │
-└─────────┬───────┘
- │
-┌─────────▼───────┐
-│ WebRTC Channel │
-└─────────────────┘
-```
-
-### Security Layer Configuration
-
-```javascript
-// Production configuration for maximum security
-const securityConfig = {
- // Stage 4 - Maximum Security
- features: {
- hasEncryption: true,
- hasECDH: true,
- hasECDSA: true,
- hasMutualAuth: true,
- hasMetadataProtection: true,
- hasEnhancedReplayProtection: true,
- hasNonExtractableKeys: true,
- hasRateLimiting: true,
- hasEnhancedValidation: true,
- hasPFS: true,
- hasNestedEncryption: true,
- hasPacketPadding: true,
- hasFakeTraffic: true,
- hasMessageChunking: true,
- hasDecoyChannels: true,
- hasPacketReordering: true,
- hasAntiFingerprinting: true
- },
-
- // Performance optimizations
- performance: {
- paddingRange: [64, 512], // Reduced for efficiency
- chunkSize: 2048, // Larger chunks
- fakeTrafficInterval: [10000, 30000], // Less frequent
- keyRotation: 300000 // 5 minutes
- }
-};
-```
-
----
-
-## ✅ Security Verification
-
-### Automated Testing
-
-```javascript
-// Security layer verification
-async function verifySecurityLayers() {
- const tests = [
- verifyEncryption(),
- verifyECDHKeyExchange(),
- verifyECDSASignatures(),
- verifyMutualAuth(),
- verifyMetadataProtection(),
- verifyReplayProtection(),
- verifyNonExtractableKeys(),
- verifyRateLimiting(),
- verifyEnhancedValidation(),
- verifyPFS(),
- verifyNestedEncryption(),
- verifyPacketPadding()
- ];
-
- const results = await Promise.all(tests);
- return results.every(result => result === true);
-}
-```
-
-### Manual Verification Commands
-
-```javascript
-// Check security status
-webrtcManager.getSecurityStatus()
-// Expected: { stage: 4, securityLevel: 'MAXIMUM', activeFeatures: 12 }
-
-// Verify cryptographic implementation
-webrtcManager.calculateSecurityLevel()
-// Expected: { level: 'HIGH', score: 80+, verificationResults: {...} }
-
-// Test fake traffic filtering
-webrtcManager.checkFakeTrafficStatus()
-// Expected: { fakeTrafficEnabled: true, timerActive: true }
-```
-
-### Security Metrics Dashboard
-
-| Metric | Target | Current | Status |
-|--------|---------|---------|---------|
-| Active Security Layers | 19 | 19 | ✅ |
-| Encryption Strength | 256-bit | 256-bit | ✅ |
-| Key Exchange Security | P-384 | P-384 | ✅ |
-| Forward Secrecy | Complete | Complete | ✅ |
-| Traffic Obfuscation | Maximum | Maximum | ✅ |
-| Attack Surface | Minimal | Minimal | ✅ |
-| HKDF Compliance | RFC 5869 | RFC 5869 | ✅ |
-
----
-
-## 🔒 Layer 13: Mutex Framework (Race Condition Protection)
-
-### Purpose
-Prevents race conditions during connection establishment and cryptographic operations through advanced mutex coordination system.
-
-### Technical Implementation
-- **Custom Mutex System:** `_withMutex('connectionOperation')` with 15-second timeout
-- **Atomic Operations:** Serialized connection operations to prevent conflicts
-- **Deadlock Prevention:** Emergency recovery mechanisms with automatic cleanup
-- **Operation Tracking:** Unique `operationId` for comprehensive diagnostics
-
-### Security Benefits
-- **Race Condition Prevention:** Eliminates timing-based attacks during key generation
-- **Connection Integrity:** Ensures atomic connection establishment
-- **Error Recovery:** Automatic rollback via `_cleanupFailedOfferCreation()` on failures
-- **Diagnostic Capability:** Phase tracking for precise error identification
-
-### Implementation Details
-```javascript
-// Mutex-protected connection operations
-await this._withMutex('connectionOperation', async () => {
- const operationId = this._generateOperationId();
- try {
- await this._generateEncryptionKeys();
- await this._validateConnectionParameters();
- await this._establishSecureChannel();
- } catch (error) {
- await this._cleanupFailedOfferCreation(operationId);
- throw error;
- }
-});
-```
-
----
-
-## 🔐 Layer 14: Secure Key Storage (WeakMap Isolation)
-
-### Purpose
-Replaces public key properties with private WeakMap-based storage to prevent unauthorized access and memory exposure.
-
-### Technical Implementation
-- **WeakMap Storage:** `_secureKeyStorage` for all cryptographic keys
-- **Private Access Methods:** `_getSecureKey()`, `_setSecureKey()`, `_initializeSecureKeyStorage()`
-- **Key Validation:** `_validateKeyValue()` with type and format checking
-- **Key Rotation:** `_rotateKeys()` with secure key replacement
-- **Emergency Wipe:** `_emergencyKeyWipe()` for threat response
-
-### Security Benefits
-- **Memory Protection:** Keys inaccessible via direct property access or debugger
-- **Access Control:** Secure getters/setters with validation
-- **Key Lifetime Management:** Automatic rotation and expiration
-- **Threat Response:** Immediate key destruction capabilities
-
-### Implementation Details
-```javascript
-// Secure key storage initialization
-this._initializeSecureKeyStorage();
-
-// Secure key access
-const encryptionKey = this._getSecureKey('encryptionKey');
-this._setSecureKey('encryptionKey', newKey, { validate: true });
-
-// Emergency key wipe
-this._emergencyKeyWipe();
-```
-
----
-
-## 🛡️ Layer 15: Production Security Logging (Data Sanitization)
-
-### Purpose
-Implements environment-aware logging system that prevents sensitive data exposure while maintaining useful diagnostics.
-
-### Technical Implementation
-- **Environment Detection:** Automatic production vs development mode detection
-- **Data Sanitization:** `_secureLog()` replacing `console.log` with sanitization
-- **Log Level Control:** Production (warn+error only), Development (debug+)
-- **Rate Limiting:** Automatic log spam prevention and cleanup
-- **Memory Management:** Automatic cleanup for log counters
-
-### Security Benefits
-- **Data Protection:** Encryption keys, message content, and tokens are sanitized
-- **Privacy Preservation:** User privacy maintained in production logs
-- **Debugging Support:** Safe debugging information without sensitive content
-- **Compliance:** Meets privacy regulations and security standards
-
-### Implementation Details
-```javascript
-// Secure logging with data sanitization
-this._secureLog('debug', 'Connection established', {
- userId: '[REDACTED]',
- encryptionKey: '[REDACTED]',
- messageContent: '[REDACTED]'
-});
-
-// Environment-aware logging
-if (this._isProductionMode()) {
- // Only critical errors and warnings
-} else {
- // Full debugging information (sanitized)
-}
-```
-
----
-
-## 🛡️ Layer 16: Atomic Operations (Race Condition Prevention)
-
-### Purpose
-Prevents race conditions in critical security operations through atomic lock-based mechanisms.
-
-### Technical Implementation
-- **Lock Management:** Map-based lock system with unique keys
-- **Atomic Operations:** `withLock()` wrapper for critical sections
-- **Timeout Protection:** Configurable lock timeouts (default: 5 seconds)
-- **Automatic Cleanup:** Lock removal after operation completion
-- **Error Handling:** Graceful fallback on lock failures
-
-### Security Benefits
-- **Race Condition Prevention:** Eliminates concurrent access vulnerabilities
-- **Data Integrity:** Ensures consistent state during operations
-- **Critical Section Protection:** Secures file transfer and cryptographic operations
-- **Deadlock Prevention:** Automatic cleanup prevents resource exhaustion
-
-### Implementation Details
-```javascript
-// Atomic operation wrapper
-return this.atomicOps.withLock(
- `chunk-${chunkMessage.fileId}`,
- async () => {
- // Critical section protected by lock
- // File chunk processing logic
- }
-);
-```
-
----
-
-## 🛡️ Layer 17: DTLS Race Condition Protection (WebRTC Security)
-
-### Purpose
-Advanced protection against October 2024 WebRTC DTLS ClientHello race condition vulnerabilities.
-
-### Technical Implementation
-- **ICE Endpoint Verification:** Secure validation before DTLS establishment
-- **ClientHello Validation:** TLS cipher suite and version verification
-- **Source Authentication:** Cryptographic verification of DTLS packet sources
-- **Queue Management:** DTLS message queuing during ICE verification
-- **Timeout Protection:** Configurable verification timeouts
-
-### Security Benefits
-- **DTLS Vulnerability Mitigation:** Protects against race condition attacks
-- **WebRTC Security Enhancement:** Comprehensive transport layer protection
-- **Endpoint Validation:** Ensures legitimate connection sources
-- **Protocol Security:** TLS version and cipher suite validation
-
-### Implementation Details
-```javascript
-// DTLS source validation
-await this.validateDTLSSource(clientHelloData, expectedSource);
-
-// ICE endpoint verification
-this.addVerifiedICEEndpoint(endpoint);
-
-// DTLS message handling
-await this.handleDTLSClientHello(clientHelloData, sourceEndpoint);
-```
-
----
-
-## 🛡️ Layer 18: Memory Safety Protection (Use-After-Free)
-
-### Purpose
-Advanced memory safety mechanisms to prevent use-after-free vulnerabilities and ensure secure data cleanup.
-
-### Technical Implementation
-- **Secure Memory Wiping:** Advanced buffer wiping with zero-filling
-- **Context Isolation:** Symbol-based private instance management
-- **Memory Cleanup:** Comprehensive cleanup of sensitive data structures
-- **Error Handling:** Secure error handling without information leakage
-- **Garbage Collection:** Optional forced GC for critical operations
-
-### Security Benefits
-- **Use-After-Free Prevention:** Eliminates memory safety vulnerabilities
-- **Data Leakage Prevention:** Secure cleanup of sensitive information
-- **Context Security:** Isolated instance management prevents tampering
-- **Error Security:** Sanitized error messages prevent information disclosure
-
-### Implementation Details
-```javascript
-// Secure memory wiping
-SecureMemoryManager.secureWipe(buffer);
-
-// Context isolation
-SecureFileTransferContext.getInstance().setFileTransferSystem(this);
-
-// Enhanced memory cleanup
-for (const [key, value] of Object.entries(receivingState)) {
- if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
- SecureMemoryManager.secureWipe(value);
- }
-}
-```
-
----
-
-## ⚡ Performance Impact
-
-### Latency Analysis
-
-| Security Layer | Added Latency | Justification |
-|----------------|---------------|---------------|
-| Authentication | ~5ms | Necessary for MITM protection |
-| Key Exchange | ~10ms | One-time cost per session |
-| Metadata Protection | ~1ms | Minimal overhead |
-| Message Encryption | ~2ms | Standard AES-GCM performance |
-| Nested Encryption | ~2ms | Additional security layer |
-| Packet Padding | ~0.5ms | Simple padding operation |
-| Anti-Fingerprinting | ~3ms | Pattern obfuscation |
-| Reordering Protection | ~1ms | Header processing |
-| Message Chunking | ~50ms | Intentional delay for security |
-| Fake Traffic | 0ms | Background operation |
-| Rate Limiting | ~0.1ms | Memory lookup |
-| PFS Key Rotation | ~10ms | Every 5 minutes |
-| Mutex Framework | ~2ms | Race condition protection |
-| Secure Key Storage | ~0.5ms | WeakMap access overhead |
-| Production Logging | ~1ms | Data sanitization processing |
-| Atomic Operations | ~2ms | Race condition protection |
-| DTLS Protection | ~3ms | WebRTC security enhancement |
-| Memory Safety | ~1ms | Secure cleanup operations |
-
-**Total Average Latency:** ~84.5ms per message (acceptable for secure communications)
-
-### Throughput Impact
-
-- **Without Security:** ~1000 messages/second
-- **With Stage 4 Security:** ~500 messages/second
-- **Efficiency:** 50% (excellent for security level provided)
-
-### Memory Usage
-
-- **Base Memory:** ~2MB
-- **Security Layers:** ~3MB additional
-- **Cryptographic Keys:** ~1MB
-- **Total:** ~6MB (minimal for modern devices)
-
----
-
-## 📊 Compliance Standards
-
-### Industry Standards Met
-
-- ✅ **NIST SP 800-57:** Key management best practices
-- ✅ **FIPS 140-2 Level 2:** Cryptographic module security
-- ✅ **NSA Suite B:** Cryptographic algorithms (P-384, AES-256)
-- ✅ **RFC 7748:** Elliptic curve cryptography standards
-- ✅ **RFC 5869:** HKDF key derivation
-- ✅ **RFC 3394:** AES key wrap specifications
-
-### Regulatory Compliance
-
-- ✅ **GDPR:** Privacy by design, data minimization
-- ✅ **CCPA:** California privacy protection
-- ✅ **HIPAA:** Healthcare data protection (suitable for)
-- ✅ **SOX:** Financial data protection (suitable for)
-- ✅ **ITAR:** International traffic in arms regulations (crypto export)
-
-### Security Certifications (Pending)
-
-- 🔄 **Common Criteria EAL4+:** Security functionality evaluation
-- 🔄 **FIPS 140-3:** Next-generation cryptographic validation
-- 🔄 **ISO 27001:** Information security management
-
----
-
-## 🚀 Future Enhancements
-
-### Quantum-Resistant Cryptography (2026)
-- **Post-Quantum Key Exchange:** CRYSTALS-Kyber
-- **Post-Quantum Signatures:** CRYSTALS-Dilithium
-- **Hybrid Classical/Quantum:** Dual algorithm support
-
-### Advanced Traffic Obfuscation (2025)
-- **AI-Powered Pattern Generation:** Machine learning fake traffic
-- **Protocol Mimicry:** Disguise as common protocols (HTTP, DNS)
-- **Adaptive Obfuscation:** Real-time pattern adjustment
-
-### Enhanced Perfect Forward Secrecy (2025)
-- **Automatic Key Rotation:** Every 1 minute
-- **Group Key Management:** Multi-party key rotation
-- **Quantum Key Distribution:** Hardware-based key generation
-
----
-
-## 📞 Technical Support
-
-For technical questions about the security architecture:
-
-- **Security Team:** security@SecureBit.chat
-- **Technical Documentation:** docs@SecureBit.chat
-- **GitHub Issues:** [Security Architecture Issues](https://github.com/SecureBitChat/securebit-chat/issues?q=label%3Asecurity-architecture)
-
----
-
-*This document is updated with each major security enhancement. Current version reflects Stage 5 Military-Grade Security implementation with comprehensive connection security overhaul.*
-
-**Last Updated:** January 15, 2025
-**Document Version:** 4.1
-**Security Implementation:** Stage 5 - Military-Grade Security
-**Review Status:** ✅ Verified and Tested
\ No newline at end of file
+- WebRTC channels and peer connection handles
+- timers, deferred retries, fake traffic, and decoy traffic
+- pending transfer state and consent waits
+- verification state and crypto/PFS state
+- React file-transfer callbacks and stale UI transfer state
diff --git a/index.html b/index.html
index 9011a34..e21f5ff 100644
--- a/index.html
+++ b/index.html
@@ -147,13 +147,13 @@
-
-
+
+
-
-
+
+
diff --git a/meta.json b/meta.json
index c391543..435887a 100644
--- a/meta.json
+++ b/meta.json
@@ -1,10 +1,10 @@
{
- "version": "1767754446404",
- "buildVersion": "1767754446404",
- "appVersion": "4.7.56",
- "buildTime": "2026-01-07T02:54:06.493Z",
- "buildId": "1767754446404-ebcf2dc",
- "gitHash": "ebcf2dc",
+ "version": "1779043608721",
+ "buildVersion": "1779043608721",
+ "appVersion": "4.8.5",
+ "buildTime": "2026-05-17T18:46:48.763Z",
+ "buildId": "1779043608721-4b8c882",
+ "gitHash": "4b8c882",
"generated": true,
- "generatedAt": "2026-01-07T02:54:06.494Z"
+ "generatedAt": "2026-05-17T18:46:48.764Z"
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index d820020..4b0b5a2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,24 +1,24 @@
{
"name": "securebit-chat",
- "version": "1.0.0",
+ "version": "4.8.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "securebit-chat",
- "version": "1.0.0",
+ "version": "4.8.5",
"license": "MIT",
"dependencies": {
- "base64-js": "^1.5.1",
- "cbor-js": "^0.1.0",
- "html5-qrcode": "^2.3.8",
- "pako": "^2.1.0",
- "qr-scanner": "^1.4.2",
- "qrcode": "^1.5.4"
+ "base64-js": "1.5.1",
+ "cbor-js": "0.1.0",
+ "html5-qrcode": "2.3.8",
+ "pako": "2.1.0",
+ "qr-scanner": "1.4.2",
+ "qrcode": "1.5.4"
},
"devDependencies": {
- "esbuild": "^0.25.9",
- "tailwindcss": "^3.4.17"
+ "esbuild": "0.25.9",
+ "tailwindcss": "3.4.17"
}
},
"node_modules/@alloc/quick-lru": {
@@ -766,9 +766,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1120,9 +1120,10 @@
}
},
"node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1330,13 +1331,13 @@
}
},
"node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -1516,9 +1517,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1558,9 +1559,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "version": "8.5.14",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
+ "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"dev": true,
"funding": [
{
@@ -2152,9 +2153,9 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
- "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -2162,6 +2163,9 @@
},
"engines": {
"node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/yargs": {
diff --git a/package.json b/package.json
index 91a9a5b..e51122c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "securebit-chat",
- "version": "4.7.56",
+ "version": "4.8.5",
"description": "Secure P2P Communication Application with End-to-End Encryption",
"main": "index.html",
"scripts": {
@@ -11,7 +11,7 @@
"dev": "npm run build && python -m http.server 8000",
"watch": "npx tailwindcss -i src/styles/tw-input.css -o assets/tailwind.css --watch",
"serve": "npx http-server -p 8000",
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "node tests/sas-verification.test.mjs && node tests/file-transfer-consent.test.mjs && node tests/incoming-message-sanitization.test.mjs && node tests/file-type-allowlist.test.mjs && node tests/webrtc-privacy-mode.test.mjs && node tests/indexeddb-metadata-encryption.test.mjs && node tests/disconnect-cleanup.test.mjs && node tests/timer-lifecycle.test.mjs && node tests/file-transfer-cleanup.test.mjs && node tests/file-transfer-ui-cleanup.test.mjs && node tests/file-transfer-callback-propagation.test.mjs"
},
"keywords": [
"p2p",
@@ -24,15 +24,15 @@
"author": "SecureBit Team",
"license": "MIT",
"devDependencies": {
- "esbuild": "^0.25.9",
- "tailwindcss": "^3.4.17"
+ "esbuild": "0.25.9",
+ "tailwindcss": "3.4.17"
},
"dependencies": {
- "base64-js": "^1.5.1",
- "cbor-js": "^0.1.0",
- "html5-qrcode": "^2.3.8",
- "pako": "^2.1.0",
- "qr-scanner": "^1.4.2",
- "qrcode": "^1.5.4"
+ "base64-js": "1.5.1",
+ "cbor-js": "0.1.0",
+ "html5-qrcode": "2.3.8",
+ "pako": "2.1.0",
+ "qr-scanner": "1.4.2",
+ "qrcode": "1.5.4"
}
}
diff --git a/src/app.jsx b/src/app.jsx
index 68bf5be..934c596 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -36,6 +36,31 @@
// Verification Component
const VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed, remoteConfirmed, bothConfirmed }) => {
+ const [sasInput, setSasInput] = React.useState('');
+ const [error, setError] = React.useState('');
+ const normalizedExpectedLength = (verificationCode || '').replace(/[-\s]/g, '').length;
+ const normalizedInputLength = sasInput.replace(/[-\s]/g, '').length;
+ const canConfirm = !localConfirmed && normalizedExpectedLength > 0 && normalizedInputLength === normalizedExpectedLength;
+
+ React.useEffect(() => {
+ setSasInput('');
+ setError('');
+ }, [verificationCode]);
+
+ const handleConfirm = async () => {
+ try {
+ setError('');
+ await onConfirm(sasInput);
+ } catch (confirmationError) {
+ setSasInput('');
+ if (confirmationError?.message === 'SAS_MAX_ATTEMPTS') {
+ setError('Too many incorrect attempts. Session reset for safety.');
+ } else {
+ setError('Incorrect code. Check it with your peer and try again.');
+ }
+ }
+ };
+
return React.createElement('div', {
className: "card-minimal rounded-xl p-6 border-purple-500/20"
}, [
@@ -63,7 +88,7 @@
React.createElement('p', {
key: 'description',
className: "text-secondary text-sm"
- }, "Verify the security code with your contact via another communication channel (voice, SMS, etc.):"),
+ }, "Compare this code with your peer out-of-band, then type the same code below to unlock the chat."),
React.createElement('div', {
key: 'code-display',
className: "text-center"
@@ -73,6 +98,36 @@
className: "verification-code text-2xl py-4"
}, verificationCode)
]),
+ React.createElement('div', {
+ key: 'sas-input-wrap',
+ className: "space-y-2"
+ }, [
+ React.createElement('label', {
+ key: 'sas-label',
+ className: "block text-sm text-secondary"
+ }, "Enter the verified code"),
+ React.createElement('input', {
+ key: 'sas-input',
+ type: 'text',
+ value: sasInput,
+ onChange: (event) => {
+ setSasInput(event.target.value.toUpperCase());
+ if (error) setError('');
+ },
+ autoFocus: true,
+ autoComplete: 'off',
+ spellCheck: false,
+ inputMode: 'text',
+ disabled: localConfirmed,
+ placeholder: verificationCode ? 'Type code here' : 'Waiting for code…',
+ className: "w-full rounded-lg border border-purple-500/30 bg-black/20 px-4 py-3 text-center text-xl tracking-[0.3em] text-primary uppercase focus:border-purple-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60",
+ style: { fontFamily: 'monospace', textTransform: 'uppercase' }
+ }),
+ error && React.createElement('p', {
+ key: 'sas-error',
+ className: "text-sm text-red-400"
+ }, error)
+ ]),
// Verification status indicators
React.createElement('div', {
key: 'verification-status',
@@ -142,14 +197,14 @@
}, [
React.createElement('button', {
key: 'confirm',
- onClick: onConfirm,
- disabled: localConfirmed,
- className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${localConfirmed ? 'bg-gray-500/20 text-gray-400 cursor-not-allowed' : 'btn-verify text-white'}`
+ onClick: handleConfirm,
+ disabled: !canConfirm,
+ className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${!canConfirm ? 'bg-gray-500/20 text-gray-400 cursor-not-allowed' : 'btn-verify text-white'}`
}, [
React.createElement('i', {
className: `fas ${localConfirmed ? 'fa-check-circle' : 'fa-check'} mr-2`
}),
- localConfirmed ? 'Confirmed' : 'The codes match'
+ localConfirmed ? 'Confirmed' : 'Confirm code'
]),
React.createElement('button', {
key: 'reject',
@@ -283,7 +338,9 @@
markAnswerCreated,
notificationIntegrationRef,
isGeneratingKeys,
- handleCreateOffer
+ handleCreateOffer,
+ relayOnlyMode,
+ setRelayOnlyMode
}) => {
const [mode, setMode] = React.useState('select');
const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);
@@ -294,12 +351,12 @@
onClearData();
};
- const handleVerificationConfirm = () => {
- onVerifyConnection(true);
+ const handleVerificationConfirm = (userCode) => {
+ return onVerifyConnection(userCode);
};
const handleVerificationReject = () => {
- onVerifyConnection(false);
+ onVerifyConnection(null, false);
};
// Request notification permission on first user interaction
@@ -449,6 +506,28 @@
className: "text-secondary max-w-2xl mx-auto"
}, "Choose a connection method for a secure channel with ECDH encryption and Perfect Forward Secrecy.")
]),
+ React.createElement('label', {
+ key: 'privacy-mode',
+ className: "mb-6 mx-auto flex max-w-2xl items-start gap-3 rounded-xl border border-purple-500/20 bg-purple-500/10 p-4 text-left"
+ }, [
+ React.createElement('input', {
+ key: 'input',
+ type: 'checkbox',
+ checked: relayOnlyMode,
+ onChange: (event) => setRelayOnlyMode(event.target.checked),
+ className: "mt-1"
+ }),
+ React.createElement('span', { key: 'copy' }, [
+ React.createElement('span', {
+ key: 'title',
+ className: "block text-sm font-medium text-primary"
+ }, 'Privacy mode: relay-only WebRTC'),
+ React.createElement('span', {
+ key: 'desc',
+ className: "block text-sm text-secondary"
+ }, 'Uses TURN relay-only when configured. Without TURN, direct WebRTC may expose IP addresses and relay-only connections cannot start.')
+ ])
+ ]),
React.createElement('div', {
key: 'options',
@@ -1464,6 +1543,9 @@
const [messages, setMessages] = React.useState([]);
const [connectionStatus, setConnectionStatus] = React.useState('disconnected');
+ const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => {
+ try { return localStorage.getItem('securebit_relay_only_mode') === 'true'; } catch { return false; }
+ });
// Moved scrollToBottom logic to be available globally
const [messageInput, setMessageInput] = React.useState('');
@@ -1680,6 +1762,13 @@
// Create scroll function using global helper
const scrollToBottom = createScrollToBottomFunction(chatMessagesRef);
+
+ React.useEffect(() => {
+ try { localStorage.setItem('securebit_relay_only_mode', String(relayOnlyMode)); } catch {}
+ if (webrtcManagerRef.current?._config?.webrtc) {
+ webrtcManagerRef.current._config.webrtc.relayOnly = relayOnlyMode;
+ }
+ }, [relayOnlyMode]);
// Auto-scroll when messages change
React.useEffect(() => {
@@ -1909,7 +1998,13 @@
handleKeyExchange,
handleVerificationRequired,
handleAnswerError,
- handleVerificationStateChange
+ handleVerificationStateChange,
+ {
+ webrtc: {
+ relayOnly: relayOnlyMode,
+ iceServers: Array.isArray(window.SECUREBIT_ICE_SERVERS) ? window.SECUREBIT_ICE_SERVERS : undefined
+ }
+ }
);
// Initialize notification integration if permission was already granted
@@ -1926,7 +2021,7 @@
}
}
- handleMessage(' SecureBit.chat Enhanced Security Edition v4.7.56 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
+ handleMessage(' SecureBit.chat Enhanced Security Edition v4.8.5 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
const handleBeforeUnload = (event) => {
if (event.type === 'beforeunload' && !isTabSwitching) {
@@ -3226,9 +3321,9 @@
}
};
- const handleVerifyConnection = async (isValid) => {
+ const handleVerifyConnection = async (userCode, isValid = true) => {
if (isValid) {
- webrtcManagerRef.current.confirmVerification();
+ webrtcManagerRef.current.confirmVerification(userCode);
// Mark local verification as confirmed
setLocalVerificationConfirmed(true);
@@ -3666,7 +3761,9 @@
markAnswerCreated: markAnswerCreated,
notificationIntegrationRef: notificationIntegrationRef,
isGeneratingKeys: isGeneratingKeys,
- handleCreateOffer: handleCreateOffer
+ handleCreateOffer: handleCreateOffer,
+ relayOnlyMode: relayOnlyMode,
+ setRelayOnlyMode: setRelayOnlyMode
})
),
@@ -3810,4 +3907,4 @@
ReactDOM.render(AppWithUpdateChecker, document.getElementById('root'));
} else {
ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));
- }
\ No newline at end of file
+ }
diff --git a/src/components/ui/FileTransfer.jsx b/src/components/ui/FileTransfer.jsx
index f031c91..457a449 100644
--- a/src/components/ui/FileTransfer.jsx
+++ b/src/components/ui/FileTransfer.jsx
@@ -3,6 +3,7 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
const [dragOver, setDragOver] = React.useState(false);
const [transfers, setTransfers] = React.useState({ sending: [], receiving: [] });
const [readyFiles, setReadyFiles] = React.useState([]); // файлы, готовые к скачиванию
+ const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]);
const fileInputRef = React.useRef(null);
// Update transfers periodically
@@ -18,6 +19,14 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
return () => clearInterval(interval);
}, [isConnected, webrtcManager]);
+ // Clear session-local UI state when the connection ends so reconnect starts clean.
+ React.useEffect(() => {
+ if (isConnected) return;
+ setReadyFiles([]);
+ setPendingIncomingFiles([]);
+ setTransfers({ sending: [], receiving: [] });
+ }, [isConnected]);
+
// Setup file transfer callbacks - ИСПРАВЛЕНИЕ: НЕ отправляем промежуточные сообщения в чат
React.useEffect(() => {
if (!webrtcManager) return;
@@ -61,8 +70,20 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
// ИСПРАВЛЕНИЕ: НЕ дублируем сообщения об ошибках
// Уведомления об ошибках уже отправляются в WebRTC менеджере
+ },
+
+ // Incoming file request callback - user consent is mandatory
+ (fileRequest) => {
+ setPendingIncomingFiles(prev => {
+ if (prev.some(file => file.fileId === fileRequest.fileId)) return prev;
+ return [...prev, fileRequest];
+ });
}
);
+
+ return () => {
+ webrtcManager.setFileTransferCallbacks(null, null, null, null);
+ };
}, [webrtcManager]);
const handleFileSelect = async (files) => {
@@ -177,6 +198,19 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
}
};
+ const handleIncomingDecision = async (fileId, accepted) => {
+ try {
+ if (accepted) {
+ await webrtcManager.acceptIncomingFile(fileId);
+ } else {
+ await webrtcManager.rejectIncomingFile(fileId);
+ }
+ } finally {
+ setPendingIncomingFiles(prev => prev.filter(file => file.fileId !== fileId));
+ setTransfers(webrtcManager.getFileTransfers());
+ }
+ };
+
if (!isConnected) {
return React.createElement('div', {
className: "p-4 text-center text-muted"
@@ -239,6 +273,45 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
onChange: handleFileInputChange
}),
+ pendingIncomingFiles.length > 0 && React.createElement('div', {
+ key: 'incoming-consent',
+ className: "mt-4 space-y-2"
+ }, pendingIncomingFiles.map(file => React.createElement('div', {
+ key: file.fileId,
+ className: "rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3"
+ }, [
+ React.createElement('div', {
+ key: 'info',
+ className: "mb-3 flex items-center justify-between gap-3"
+ }, [
+ React.createElement('div', { key: 'text' }, [
+ React.createElement('div', {
+ key: 'title',
+ className: "text-sm font-medium text-primary"
+ }, 'Incoming file request'),
+ React.createElement('div', {
+ key: 'meta',
+ className: "text-xs text-secondary"
+ }, `${file.fileName} · ${formatFileSize(file.fileSize)} · ${file.mimeType}`)
+ ])
+ ]),
+ React.createElement('div', {
+ key: 'actions',
+ className: "flex gap-2"
+ }, [
+ React.createElement('button', {
+ key: 'accept',
+ onClick: () => handleIncomingDecision(file.fileId, true),
+ className: "rounded-md bg-green-500/20 px-3 py-2 text-sm text-green-300 hover:bg-green-500/30"
+ }, 'Accept'),
+ React.createElement('button', {
+ key: 'reject',
+ onClick: () => handleIncomingDecision(file.fileId, false),
+ className: "rounded-md bg-red-500/20 px-3 py-2 text-sm text-red-300 hover:bg-red-500/30"
+ }, 'Reject')
+ ])
+ ]))),
+
// Active Transfers
(transfers.sending.length > 0 || transfers.receiving.length > 0) && React.createElement('div', {
key: 'transfers',
@@ -366,7 +439,7 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
a.click();
rf.revokeObjectURL(url);
} catch (e) {
- alert('Failed to start download: ' + e.message);
+ alert(e.message || 'This file is no longer available for download.');
}
}
}, [
@@ -420,4 +493,4 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
};
// Export
-window.FileTransferComponent = FileTransferComponent;
\ No newline at end of file
+window.FileTransferComponent = FileTransferComponent;
diff --git a/src/components/ui/Header.jsx b/src/components/ui/Header.jsx
index 16b5dc0..7b9ad4f 100644
--- a/src/components/ui/Header.jsx
+++ b/src/components/ui/Header.jsx
@@ -539,7 +539,7 @@ const EnhancedMinimalHeader = ({
React.createElement('p', {
key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block'
- }, 'End-to-end freedom v4.7.56')
+ }, 'End-to-end freedom v4.8.5')
])
]),
diff --git a/src/network/EnhancedSecureWebRTCManager.js b/src/network/EnhancedSecureWebRTCManager.js
index ebc89a1..7151111 100644
--- a/src/network/EnhancedSecureWebRTCManager.js
+++ b/src/network/EnhancedSecureWebRTCManager.js
@@ -100,6 +100,9 @@ class EnhancedSecureWebRTCManager {
SYSTEM_MESSAGE: 'SYSTEM_MESSAGE_FILTERED'
};
+ static PROTOCOL_VERSION = '4.1';
+ static MAX_SAS_ATTEMPTS = 3;
+
// Static debug flag instead of this._debugMode
static DEBUG_MODE = true; // Set to true during development, false in production
@@ -141,8 +144,19 @@ class EnhancedSecureWebRTCManager {
addNoise: config.antiFingerprinting?.addNoise ?? true,
maskPatterns: config.antiFingerprinting?.maskPatterns ?? false,
useRandomHeaders: config.antiFingerprinting?.useRandomHeaders ?? false
+ },
+ webrtc: {
+ relayOnly: config.webrtc?.relayOnly ?? false,
+ iceServers: config.webrtc?.iceServers ?? [
+ { urls: 'stun:stun.l.google.com:19302' },
+ { urls: 'stun:stun1.l.google.com:19302' },
+ { urls: 'stun:stun2.l.google.com:19302' },
+ { urls: 'stun:stun3.l.google.com:19302' },
+ { urls: 'stun:stun4.l.google.com:19302' }
+ ]
}
};
+ this._ipLeakWarningShown = false;
// Initialize own logging system
this._initializeSecureLogging();
@@ -213,6 +227,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this.verificationCode = null;
this.pendingSASCode = null;
this.isVerified = false;
+ this.sasValidationAttempts = 0;
this.processedMessageIds = new Set();
// Mutual verification states
@@ -243,6 +258,10 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this.peerPublicKey = null; // Store peer's public key for PFS
this.rateLimiterId = null;
this.intentionalDisconnect = false;
+ this._sessionAlive = true;
+ this._fileTransferInitRetryTimers = new Set();
+ this._peerDisconnectCleanupTimer = null;
+ this._logCleanupInterval = null;
this.lastCleanupTime = Date.now();
// Reset notification flags for new connection
@@ -915,6 +934,17 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this._activeTimers = new Set([this._maintenanceScheduler]);
}
+ _trackActiveTimer(timer) {
+ if (!timer) return timer;
+ if (!this._activeTimers) this._activeTimers = new Set();
+ this._activeTimers.add(timer);
+ return timer;
+ }
+
+ _untrackActiveTimer(timer) {
+ if (timer && this._activeTimers) this._activeTimers.delete(timer);
+ }
+
/**
* Execute all maintenance tasks in a single cycle
*/
@@ -3334,9 +3364,9 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this._startSecurityMonitoring();
// Start periodic log cleanup
- setInterval(() => {
+ this._logCleanupInterval = this._trackActiveTimer(setInterval(() => {
this._cleanupLogs();
- }, 300000);
+ }, 300000));
this._secureLog('info', '✅ Secure WebRTC Manager initialization completed');
this._secureLog('info', '🔒 Global exposure protection: Monitoring only, no automatic removal');
@@ -3646,13 +3676,9 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
const dv = new DataView(bits);
const n = (dv.getUint32(0) ^ dv.getUint32(4)) >>> 0;
- // Use rejection sampling to avoid bias in SAS code generation
- let sasValue;
- do {
- sasValue = crypto.getRandomValues(new Uint32Array(1))[0];
- } while (sasValue >= 4294967296 - (4294967296 % 10_000_000));
-
- const sasCode = String(sasValue % 10_000_000).padStart(7, '0');
+ // Deterministic SAS: both peers derive the same display code from the
+ // same shared key material and canonicalized DTLS fingerprints.
+ const sasCode = String(n % 10_000_000).padStart(7, '0');
this._secureLog('info', 'SAS code computed successfully', {
@@ -4430,6 +4456,10 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
initializeFileTransfer() {
try {
+ if (this._sessionAlive === false) {
+ return;
+ }
+
this._secureLog('info', '🔧 Initializing Enhanced Secure File Transfer system...');
if (this.fileTransferSystem) {
@@ -4453,7 +4483,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
if (!this.isVerified) {
this._secureLog('warn', '⚠️ Connection not verified yet, deferring file transfer initialization');
- setTimeout(() => this.initializeFileTransfer(), 500);
+ this._scheduleFileTransferInitRetry(500);
return;
}
@@ -4467,7 +4497,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Ensure encryption keys are present
if (!this.encryptionKey || !this.macKey) {
this._secureLog('warn', '⚠️ Encryption keys not ready, deferring file transfer initialization');
- setTimeout(() => this.initializeFileTransfer(), 1000);
+ this._scheduleFileTransferInitRetry(1000);
return;
}
@@ -4490,7 +4520,8 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this.onFileProgress || null,
safeOnComplete,
this.onFileError || null,
- this.onFileReceived || null
+ this.onFileReceived || null,
+ this.onIncomingFileRequest || null
);
this._fileTransferActive = true;
@@ -4508,6 +4539,21 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
}
}
+ _scheduleFileTransferInitRetry(delay) {
+ if (this._sessionAlive === false) return null;
+ if (!this._fileTransferInitRetryTimers) this._fileTransferInitRetryTimers = new Set();
+
+ const timer = this._trackActiveTimer(setTimeout(() => {
+ this._fileTransferInitRetryTimers.delete(timer);
+ this._untrackActiveTimer(timer);
+ if (this._sessionAlive === false) return;
+ this.initializeFileTransfer();
+ }, delay));
+
+ this._fileTransferInitRetryTimers.add(timer);
+ return timer;
+ }
+
// ============================================
// ENHANCED SECURITY INITIALIZATION
// ============================================
@@ -4664,6 +4710,14 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
currentFeatures: this.securityFeatures
});
}
+ _sanitizeIncomingChatMessage(message) {
+ if (typeof message !== 'string') {
+ return message;
+ }
+
+ return window.EnhancedSecureCryptoUtils.sanitizeMessage(message);
+ }
+
deliverMessageToUI(message, type = 'received') {
try {
// Add debug logs
@@ -4735,9 +4789,13 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
}
}
+ const uiMessage = type === 'received'
+ ? this._sanitizeIncomingChatMessage(message)
+ : message;
+
if (this.onMessage) {
- this._secureLog('debug', '📤 Calling this.onMessage callback', { message, type });
- this.onMessage(message, type);
+ this._secureLog('debug', '📤 Calling this.onMessage callback', { message: uiMessage, type });
+ this.onMessage(uiMessage, type);
} else {
this._secureLog('warn', '⚠️ this.onMessage callback is null or undefined');
}
@@ -6897,52 +6955,6 @@ async processMessage(data) {
}
}
- disconnect() {
- try {
-
- // Cleanup file transfer system
- if (this.fileTransferSystem) {
- this.fileTransferSystem.cleanup();
- this.fileTransferSystem = null;
- }
-
- // Stop fake traffic generation
- this.stopFakeTrafficGeneration();
-
- // Stop decoy traffic
- for (const [channelName, timer] of this.decoyTimers.entries()) {
- clearTimeout(timer);
- }
- this.decoyTimers.clear();
-
- // Close decoy channels
- for (const [channelName, channel] of this.decoyChannels.entries()) {
- if (channel.readyState === 'open') {
- channel.close();
- }
- }
- this.decoyChannels.clear();
-
- // Clean up packet buffer
- this.packetBuffer.clear();
-
- // Clean up chunk queue
- this.chunkQueue = [];
-
- // Wipe ephemeral keys for PFS on disconnect
- this._wipeEphemeralKeys();
-
- // Hard wipe old keys for PFS
- this._hardWipeOldKeys();
-
- // Clear verification states
- this._clearVerificationStates();
-
- } catch (error) {
- this._secureLog('error', '❌ Error during enhanced disconnect:', { errorType: error?.constructor?.name || 'Unknown' });
- }
- }
-
/**
* Clear all verification states and data
* Called when verification is rejected or connection is terminated
@@ -6957,6 +6969,7 @@ async processMessage(data) {
this.isVerified = false;
this.verificationCode = null;
this.pendingSASCode = null;
+ this.sasValidationAttempts = 0;
// Clear key fingerprint and connection data
this.keyFingerprint = null;
@@ -7152,18 +7165,38 @@ async processMessage(data) {
return null;
}
- createPeerConnection() {
+ _hasTurnServer() {
+ return (this._config.webrtc.iceServers || []).some(server => {
+ const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
+ return urls.some(url => typeof url === 'string' && url.toLowerCase().startsWith('turn:'));
+ });
+ }
+
+ _buildPeerConnectionConfig() {
const config = {
- iceServers: [
- { urls: 'stun:stun.l.google.com:19302' },
- { urls: 'stun:stun1.l.google.com:19302' },
- { urls: 'stun:stun2.l.google.com:19302' },
- { urls: 'stun:stun3.l.google.com:19302' },
- { urls: 'stun:stun4.l.google.com:19302' }
- ],
+ iceServers: this._config.webrtc.iceServers,
iceCandidatePoolSize: 10,
bundlePolicy: 'balanced'
};
+ if (this._config.webrtc.relayOnly) {
+ config.iceTransportPolicy = 'relay';
+ }
+ return config;
+ }
+
+ _warnIfTurnMissing() {
+ if (this._hasTurnServer() || this._ipLeakWarningShown) return;
+ this._ipLeakWarningShown = true;
+ const message = this._config.webrtc.relayOnly
+ ? 'Privacy mode is enabled, but no TURN server is configured. Relay-only mode cannot connect until TURN is configured; STUN alone does not hide IP addresses.'
+ : 'Privacy warning: no TURN server is configured. Direct WebRTC connections may expose IP addresses; STUN alone does not provide IP protection.';
+ this.deliverMessageToUI(message, 'system');
+ }
+
+ createPeerConnection() {
+ this._sessionAlive = true;
+ const config = this._buildPeerConnectionConfig();
+ this._warnIfTurnMissing();
this.peerConnection = new RTCPeerConnection(config);
@@ -9432,7 +9465,7 @@ async processMessage(data) {
// Core information (minimal)
t: 'offer', // type
s: this.peerConnection.localDescription.sdp, // sdp
- v: '4.0', // version
+ v: EnhancedSecureWebRTCManager.PROTOCOL_VERSION, // version
ts: currentTimestamp, // timestamp
// Cryptographic keys (essential)
@@ -9692,17 +9725,13 @@ async processMessage(data) {
// Protocol version compatibility check (support both formats)
const protocolVersion = version; // Use the version we already extracted
- if (protocolVersion !== '4.0') {
+ if (protocolVersion !== EnhancedSecureWebRTCManager.PROTOCOL_VERSION) {
this._secureLog('warn', 'Protocol version mismatch detected', {
operationId: operationId,
- expectedVersion: '4.0',
+ expectedVersion: EnhancedSecureWebRTCManager.PROTOCOL_VERSION,
receivedVersion: protocolVersion
});
-
- // For backward compatibility with v3.0, a fallback can be added
- if (protocolVersion !== '3.0') {
- throw new Error(`Unsupported protocol version: ${protocolVersion}`);
- }
+ throw new Error(`Version mismatch: expected protocol ${EnhancedSecureWebRTCManager.PROTOCOL_VERSION}, received ${protocolVersion}`);
}
// ============================================
@@ -9717,7 +9746,7 @@ async processMessage(data) {
throw new Error('Invalid session salt format - must be array');
}
- const expectedSaltLength = protocolVersion === '4.0' ? 64 : 32;
+ const expectedSaltLength = 64;
if (this.sessionSalt.length !== expectedSaltLength) {
throw new Error(`Invalid session salt length: expected ${expectedSaltLength}, got ${this.sessionSalt.length}`);
}
@@ -9877,8 +9906,9 @@ async processMessage(data) {
throw new Error('Invalid key types after derivation');
}
- // Set verification code from offer
- this.verificationCode = offerData.verificationCode;
+ // Preserve the invitation code only as a temporary legacy field.
+ // The real SAS is recomputed locally after the DTLS fingerprints exist.
+ this.verificationCode = offerData.vc || offerData.verificationCode || null;
this._secureLog('info', 'Encryption keys derived and set successfully', {
operationId: operationId,
@@ -10052,6 +10082,23 @@ async processMessage(data) {
this._secureLog('error', 'Failed to extract DTLS fingerprint from answer', { error: error.message });
// Continue without fingerprint validation (fallback mode)
}
+
+ // Compute the same SAS locally on the answer side. This keeps the
+ // protocol honest: the peer may announce its SAS later, but it must
+ // match what we independently derived from the session.
+ try {
+ const remoteFP = this._extractDTLSFingerprintFromSDP(offerData.s || offerData.sdp);
+ const localFP = this.expectedDTLSFingerprint;
+ const keyBytes = this._decodeKeyFingerprint(this.keyFingerprint);
+ this.verificationCode = await this._computeSAS(keyBytes, localFP, remoteFP);
+ this.onStatusChange?.('verifying');
+ this.onVerificationRequired(this.verificationCode);
+ } catch (sasError) {
+ this._secureLog('error', 'SAS computation failed in createSecureAnswer (Answer side)', {
+ errorType: sasError?.constructor?.name || 'Unknown'
+ });
+ throw new Error(`SAS computation failed: ${sasError.message}`);
+ }
// Await ICE gathering
@@ -10134,7 +10181,7 @@ async processMessage(data) {
// Core information (minimal)
t: 'answer', // type
s: this.peerConnection.localDescription.sdp, // sdp
- v: '4.0', // version
+ v: EnhancedSecureWebRTCManager.PROTOCOL_VERSION, // version
ts: currentTimestamp, // timestamp
// Cryptographic keys (essential)
@@ -10450,6 +10497,11 @@ async processMessage(data) {
throw new Error('CRITICAL SECURITY FAILURE: Invalid answer format - hard abort required');
}
+ const answerVersion = answerData.v || answerData.version;
+ if (answerVersion !== EnhancedSecureWebRTCManager.PROTOCOL_VERSION) {
+ throw new Error(`Version mismatch: expected protocol ${EnhancedSecureWebRTCManager.PROTOCOL_VERSION}, received ${answerVersion || 'unknown'}`);
+ }
+
// CRITICAL: Strict validation of ECDH public key structure
// Support both full and compact key names
const ecdhKey = answerData.ecdhPublicKey || answerData.e;
@@ -10774,19 +10826,56 @@ async processMessage(data) {
}
}
- confirmVerification() {
+ /**
+ * Normalizes and validates the user-entered SAS code.
+ * Users may enter the same shared SAS with spaces or hyphens.
+ * @param {string} input
+ * @returns {boolean}
+ */
+ _validateSASCode(input) {
+ if (!input || typeof input !== 'string' || !this.verificationCode || typeof this.verificationCode !== 'string') {
+ return false;
+ }
+
+ const normalizedInput = input.replace(/[-\s]/g, '').toUpperCase();
+ const normalizedActual = this.verificationCode.replace(/[-\s]/g, '').toUpperCase();
+
+ if (normalizedInput.length !== normalizedActual.length) {
+ return false;
+ }
+
+ return window.EnhancedSecureCryptoUtils.constantTimeCompare(normalizedInput, normalizedActual);
+ }
+
+ confirmVerification(userCode) {
try {
+ if (!this._validateSASCode(userCode)) {
+ this.sasValidationAttempts = (this.sasValidationAttempts || 0) + 1;
+ this._secureLog('warn', 'SAS validation failed: user entered incorrect code', {
+ attempts: this.sasValidationAttempts,
+ maxAttempts: EnhancedSecureWebRTCManager.MAX_SAS_ATTEMPTS
+ });
+
+ if (this.sasValidationAttempts >= EnhancedSecureWebRTCManager.MAX_SAS_ATTEMPTS) {
+ this.deliverMessageToUI('Verification failed 3 times. Session reset for safety.', 'system');
+ this.disconnect();
+ throw new Error('SAS_MAX_ATTEMPTS');
+ }
+
+ throw new Error('SAS_MISMATCH');
+ }
// Mark local verification as confirmed
this.localVerificationConfirmed = true;
+ this.sasValidationAttempts = 0;
// Send confirmation to peer
const confirmationPayload = {
type: 'verification_confirmed',
data: {
timestamp: Date.now(),
- verificationMethod: 'SAS',
+ verificationMethod: 'MANUAL_SAS_ENTRY',
securityLevel: 'MITM_PROTECTION_REQUIRED'
}
};
@@ -10806,12 +10895,17 @@ async processMessage(data) {
this._checkBothVerificationsConfirmed();
// Notify UI about local confirmation
- this.deliverMessageToUI('You confirmed the verification code. Waiting for peer confirmation...', 'system');
+ this.deliverMessageToUI('Code verified locally. Waiting for peer confirmation...', 'system');
this.processMessageQueue();
} catch (error) {
- this._secureLog('error', 'SAS verification failed:', { errorType: error?.constructor?.name || 'Unknown' });
- this.deliverMessageToUI('SAS verification failed', 'system');
+ if (error.message === 'SAS_MISMATCH') {
+ this.deliverMessageToUI('Verification failed: the code you entered is incorrect.', 'system');
+ } else if (error.message !== 'SAS_MAX_ATTEMPTS') {
+ this._secureLog('error', 'SAS verification failed:', { errorType: error?.constructor?.name || 'Unknown' });
+ this.deliverMessageToUI('SAS verification failed', 'system');
+ }
+ throw error;
}
}
@@ -10946,8 +11040,18 @@ async processMessage(data) {
}
handleSASCode(data) {
+ if (!data?.code || typeof data.code !== 'string') {
+ this._secureLog('warn', 'Invalid SAS announcement received from peer');
+ return;
+ }
+
+ if (this.verificationCode && !this._validateSASCode(data.code)) {
+ this._secureLog('error', 'Peer-announced SAS does not match locally computed SAS');
+ this.deliverMessageToUI('Version or SAS mismatch detected. Connection aborted for safety.', 'system');
+ this.disconnect();
+ return;
+ }
-
this.verificationCode = data.code;
this.onStatusChange?.('verifying');
this.onVerificationRequired(this.verificationCode);
@@ -11015,8 +11119,8 @@ async processMessage(data) {
// Basic required fields will be validated after format detection
// Check if this is v4.0 compact format or legacy format
- const isV4CompactFormat = offerData.v === '4.0' && offerData.e && offerData.d;
- const isV4Format = offerData.version === '4.0' && offerData.ecdhPublicKey && offerData.ecdsaPublicKey;
+ const isV4CompactFormat = offerData.v === EnhancedSecureWebRTCManager.PROTOCOL_VERSION && offerData.e && offerData.d;
+ const isV4Format = offerData.version === EnhancedSecureWebRTCManager.PROTOCOL_VERSION && offerData.ecdhPublicKey && offerData.ecdsaPublicKey;
// Validate offer type (support compact, legacy v3.0 and v4.0 formats)
const isValidType = isV4CompactFormat ?
@@ -11028,14 +11132,14 @@ async processMessage(data) {
}
if (isV4CompactFormat) {
- // v4.0 compact format validation
+ // v4.1 compact format validation
const compactRequiredFields = [
'e', 'd', 'sl', 'vc', 'si', 'ci', 'ac', 'slv'
];
for (const field of compactRequiredFields) {
if (!offerData[field]) {
- throw new Error(`Missing required v4.0 compact field: ${field}`);
+ throw new Error(`Missing required v4.1 compact field: ${field}`);
}
}
@@ -11050,7 +11154,7 @@ async processMessage(data) {
// Validate salt length
if (!Array.isArray(offerData.sl) || offerData.sl.length !== 64) {
- throw new Error('Salt must be exactly 64 bytes for v4.0');
+ throw new Error('Salt must be exactly 64 bytes for v4.1');
}
// Validate verification code format
@@ -11069,7 +11173,7 @@ async processMessage(data) {
throw new Error('Offer is too old (older than 1 hour)');
}
- this._secureLog('info', 'v4.0 compact offer validation passed', {
+ this._secureLog('info', 'v4.1 compact offer validation passed', {
version: offerData.v,
hasECDH: !!offerData.e,
hasECDSA: !!offerData.d,
@@ -11079,7 +11183,7 @@ async processMessage(data) {
offerAge: Math.round(offerAge / 1000) + 's'
});
} else if (isV4Format) {
- // v4.0 enhanced validation
+ // v4.1 enhanced validation
const v4RequiredFields = [
'ecdhPublicKey', 'ecdsaPublicKey', 'salt', 'verificationCode',
'authChallenge', 'timestamp', 'version', 'securityLevel'
@@ -11087,13 +11191,13 @@ async processMessage(data) {
for (const field of v4RequiredFields) {
if (!offerData[field]) {
- throw new Error(`Missing v4.0 field: ${field}`);
+ throw new Error(`Missing v4.1 field: ${field}`);
}
}
// Validate salt (must be 64 bytes for v4.0)
if (!Array.isArray(offerData.salt) || offerData.salt.length !== 64) {
- throw new Error('Salt must be exactly 64 bytes for v4.0');
+ throw new Error('Salt must be exactly 64 bytes for v4.1');
}
// Validate timestamp (not older than 1 hour)
@@ -11142,35 +11246,14 @@ async processMessage(data) {
throw new Error('Invalid SAS verification code format - MITM protection required');
}
- this._secureLog('info', 'v4.0 offer validation passed', {
+ this._secureLog('info', 'v4.1 offer validation passed', {
version: offerData.version,
hasSecurityLevel: !!offerData.securityLevel?.level,
offerAge: Math.round(offerAge / 1000) + 's'
});
} else {
- // v3.0 backward compatibility validation
- // NOTE: v3.0 has limited security - SAS verification is still critical
- const v3RequiredFields = ['publicKey', 'salt', 'verificationCode'];
- for (const field of v3RequiredFields) {
- if (!offerData[field]) {
- throw new Error(`Missing v3.0 field: ${field}`);
- }
- }
-
- // Validate salt (32 bytes for v3.0)
- if (!Array.isArray(offerData.salt) || offerData.salt.length !== 32) {
- throw new Error('Salt must be exactly 32 bytes for v3.0');
- }
-
- // Validate public key
- if (!Array.isArray(offerData.publicKey)) {
- throw new Error('Invalid public key format for v3.0');
- }
-
- window.EnhancedSecureCryptoUtils.secureLog.log('info', 'v3.0 offer validation passed (backward compatibility)', {
- version: 'v3.0',
- legacy: true
- });
+ const receivedVersion = offerData.v || offerData.version || 'unknown';
+ throw new Error(`Version mismatch: expected protocol ${EnhancedSecureWebRTCManager.PROTOCOL_VERSION}, received ${receivedVersion}`);
}
// Validate SDP structure (basic check for all versions)
@@ -11350,10 +11433,18 @@ async processMessage(data) {
// Clear all timer references
if (this._activeTimers) {
this._activeTimers.forEach(timer => {
- if (timer) clearInterval(timer);
+ if (timer) {
+ clearInterval(timer);
+ clearTimeout(timer);
+ }
});
this._activeTimers.clear();
}
+
+ if (this._fileTransferInitRetryTimers) {
+ this._fileTransferInitRetryTimers.clear();
+ }
+ this._logCleanupInterval = null;
this._secureLog('info', 'All timers stopped successfully');
}
@@ -11413,31 +11504,6 @@ async processMessage(data) {
};
}
- disconnect() {
- // Stop all timers first
- this._stopAllTimers();
-
- if (this.fileTransferSystem) {
- this.fileTransferSystem.cleanup();
- }
- this.intentionalDisconnect = true;
-
- window.EnhancedSecureCryptoUtils.secureLog.log('info', 'Starting intentional disconnect');
-
- this.sendDisconnectNotification();
-
- setTimeout(() => {
- this.sendDisconnectNotification();
- }, 100);
-
- document.dispatchEvent(new CustomEvent('peer-disconnect', {
- detail: {
- reason: 'user_disconnect',
- timestamp: Date.now()
- }
- }));
- }
-
handleUnexpectedDisconnect() {
this.sendDisconnectNotification();
this.isVerified = false;
@@ -11531,9 +11597,15 @@ async processMessage(data) {
}
}));
- setTimeout(() => {
- this.disconnect();
- }, 2000);
+ if (!this._peerDisconnectCleanupTimer) {
+ this._peerDisconnectCleanupTimer = this._trackActiveTimer(setTimeout(() => {
+ const timer = this._peerDisconnectCleanupTimer;
+ this._peerDisconnectCleanupTimer = null;
+ this._untrackActiveTimer(timer);
+ if (this._sessionAlive === false) return;
+ this.disconnect();
+ }, 2000));
+ }
window.EnhancedSecureCryptoUtils.secureLog.log('info', 'Peer disconnect notification processed', {
reason: reason
@@ -11544,80 +11616,129 @@ async processMessage(data) {
* Secure disconnect with complete memory cleanup
*/
disconnect() {
- this.stopHeartbeat();
- this.isVerified = false;
- this.processedMessageIds.clear();
- this.messageCounter = 0;
-
- // Secure cleanup of cryptographic materials
- this._secureCleanupCryptographicMaterials();
-
- // Secure wipe of PFS key versions
- this.keyVersions.clear();
- this.oldKeys.clear();
- this.currentKeyVersion = 0;
- this.lastKeyRotation = Date.now();
-
- // Reset message counters
- this.sequenceNumber = 0;
- this.expectedSequenceNumber = 0;
- this.replayWindow.clear(); // Clear replay window
-
- // Reset security features
- this.securityFeatures = {
- hasEncryption: true,
- hasECDH: true,
- hasECDSA: true,
- hasMutualAuth: true,
- hasMetadataProtection: true,
- hasEnhancedReplayProtection: true,
- hasNonExtractableKeys: true,
- hasRateLimiting: true,
- hasEnhancedValidation: true,
- hasPFS: true
- };
-
- // Close connections
- if (this.dataChannel) {
- this.dataChannel.close();
- this.dataChannel = null;
- }
- if (this.peerConnection) {
- this.peerConnection.close();
- this.peerConnection = null;
- }
-
- // Secure wipe of message queue
- if (this.messageQueue && this.messageQueue.length > 0) {
- this.messageQueue.forEach((message, index) => {
- this._secureWipeMemory(message, `messageQueue[${index}]`);
+ try {
+ this._sessionAlive = false;
+
+ // Preserve the explicit-disconnect notification flow before channels are closed.
+ this.intentionalDisconnect = true;
+ window.EnhancedSecureCryptoUtils.secureLog.log('info', 'Starting intentional disconnect');
+ this.sendDisconnectNotification();
+ setTimeout(() => {
+ this.sendDisconnectNotification();
+ }, 100);
+
+ // Stop every timer-backed subsystem first.
+ this._stopAllTimers();
+ this._peerDisconnectCleanupTimer = null;
+ this.stopHeartbeat();
+ this.stopFakeTrafficGeneration();
+ for (const timer of this.decoyTimers.entries()) {
+ clearTimeout(timer[1]);
+ }
+ this.decoyTimers.clear();
+
+ // Pending file transfers must not survive a disconnect.
+ if (this.fileTransferSystem) {
+ this.fileTransferSystem.cleanup();
+ this.fileTransferSystem = null;
+ }
+
+ // Close auxiliary channels alongside the primary channel.
+ for (const channel of this.decoyChannels.values()) {
+ if (channel.readyState === 'open') channel.close();
+ }
+ this.decoyChannels.clear();
+ if (this.heartbeatChannel) {
+ this.heartbeatChannel.close();
+ this.heartbeatChannel = null;
+ }
+
+ // Reset transport-independent state.
+ this.isVerified = false;
+ this.processedMessageIds.clear();
+ this.messageCounter = 0;
+ this.packetBuffer.clear();
+ this.chunkQueue = [];
+
+ // Preserve all key/material cleanup from the former implementations.
+ this._wipeEphemeralKeys();
+ this._hardWipeOldKeys();
+ this._secureCleanupCryptographicMaterials();
+ this.keyVersions.clear();
+ this.oldKeys.clear();
+ this.currentKeyVersion = 0;
+ this.lastKeyRotation = Date.now();
+ this.sequenceNumber = 0;
+ this.expectedSequenceNumber = 0;
+ this.replayWindow.clear();
+ this._clearVerificationStates();
+
+ this.securityFeatures = {
+ hasEncryption: true,
+ hasECDH: true,
+ hasECDSA: true,
+ hasMutualAuth: true,
+ hasMetadataProtection: true,
+ hasEnhancedReplayProtection: true,
+ hasNonExtractableKeys: true,
+ hasRateLimiting: true,
+ hasEnhancedValidation: true,
+ hasPFS: true
+ };
+
+ // Close live transports, then release their listeners/references.
+ if (this.dataChannel) {
+ this.dataChannel.close();
+ this.dataChannel.onopen = null;
+ this.dataChannel.onclose = null;
+ this.dataChannel.onmessage = null;
+ this.dataChannel.onerror = null;
+ this.dataChannel = null;
+ }
+ if (this.peerConnection) {
+ this.peerConnection.close();
+ this.peerConnection.onconnectionstatechange = null;
+ this.peerConnection.ondatachannel = null;
+ this.peerConnection = null;
+ }
+
+ if (this.messageQueue && this.messageQueue.length > 0) {
+ this.messageQueue.forEach((message, index) => {
+ this._secureWipeMemory(message, `messageQueue[${index}]`);
+ });
+ this.messageQueue = [];
+ }
+
+ this._forceGarbageCollection().catch(error => {
+ this._secureLog('error', 'Cleanup failed during disconnect', {
+ errorType: error?.constructor?.name || 'Unknown'
+ });
});
- this.messageQueue = [];
- }
-
- // Schedule natural cleanup
- this._forceGarbageCollection().catch(error => {
- this._secureLog('error', 'Cleanup failed during disconnect', {
+
+ document.dispatchEvent(new CustomEvent('peer-disconnect', {
+ detail: {
+ reason: 'user_disconnect',
+ timestamp: Date.now()
+ }
+ }));
+ document.dispatchEvent(new CustomEvent('connection-cleaned', {
+ detail: {
+ timestamp: Date.now(),
+ reason: 'user_cleanup'
+ }
+ }));
+
+ this.onStatusChange('disconnected');
+ this.onKeyExchange('');
+ this.onVerificationRequired('');
+ this._secureLog('info', 'Connection securely cleaned up with complete memory wipe');
+ } catch (error) {
+ this._secureLog('error', '❌ Error during enhanced disconnect:', {
errorType: error?.constructor?.name || 'Unknown'
});
- });
-
- document.dispatchEvent(new CustomEvent('connection-cleaned', {
- detail: {
- timestamp: Date.now(),
- reason: this.intentionalDisconnect ? 'user_cleanup' : 'automatic_cleanup'
- }
- }));
-
- // Notify UI about complete cleanup
- this.onStatusChange('disconnected');
- this.onKeyExchange('');
- this.onVerificationRequired('');
-
- this._secureLog('info', 'Connection securely cleaned up with complete memory wipe');
-
- // Reset the intentional disconnect flag
- this.intentionalDisconnect = false;
+ } finally {
+ this.intentionalDisconnect = false;
+ }
}
// Public method to send files
async sendFile(file) {
@@ -11751,17 +11872,36 @@ async processMessage(data) {
}
// Set file transfer callbacks
- setFileTransferCallbacks(onProgress, onReceived, onError) {
+ setFileTransferCallbacks(onProgress, onReceived, onError, onIncomingRequest = null) {
this.onFileProgress = onProgress;
this.onFileReceived = onReceived;
this.onFileError = onError;
+ this.onIncomingFileRequest = onIncomingRequest;
- // Reinitialize file transfer system if it exists to update callbacks
+ // Propagate callback updates into the live transfer system.
if (this.fileTransferSystem) {
- this.initializeFileTransfer();
+ this.fileTransferSystem.onProgress = onProgress;
+ this.fileTransferSystem.onFileReceived = onReceived;
+ this.fileTransferSystem.onError = onError;
+ this.fileTransferSystem.onIncomingFileRequest = onIncomingRequest;
}
}
+ getPendingIncomingFiles() {
+ if (!this.fileTransferSystem) return [];
+ return this.fileTransferSystem.getPendingIncomingTransfers();
+ }
+
+ async acceptIncomingFile(fileId) {
+ if (!this.fileTransferSystem) return false;
+ return this.fileTransferSystem.acceptIncomingFile(fileId);
+ }
+
+ async rejectIncomingFile(fileId) {
+ if (!this.fileTransferSystem) return false;
+ return this.fileTransferSystem.rejectIncomingFile(fileId);
+ }
+
// ============================================
// SESSION ACTIVATION HANDLING
// ============================================
@@ -12708,18 +12848,10 @@ class SecureIndexedDBWrapper {
iv: Array.from(new Uint8Array(iv)),
algorithm: algorithm,
usages: usages,
- type: type,
- timestamp: Date.now()
+ type: type
};
- const metadataRecord = {
- keyId: keyId,
- ...metadata,
- created: Date.now(),
- lastAccessed: Date.now(),
- extractable: true,
- persistent: true
- };
+ const metadataRecord = { keyId, ...metadata };
return new Promise((resolve, reject) => {
const keysRequest = transaction.objectStore(this.KEYS_STORE).put(keyRecord);
@@ -12788,6 +12920,28 @@ class SecureIndexedDBWrapper {
getRequest.onerror = () => reject(new Error(`Failed to get metadata: ${getRequest.error}`));
});
}
+
+ async getKeyMetadataRecord(keyId) {
+ if (!this.db) throw new Error('Database not initialized');
+ const transaction = this.db.transaction([this.METADATA_STORE], 'readonly');
+ const store = transaction.objectStore(this.METADATA_STORE);
+ return new Promise((resolve, reject) => {
+ const request = store.get(keyId);
+ request.onsuccess = () => resolve(request.result || null);
+ request.onerror = () => reject(new Error(`Failed to get metadata: ${request.error}`));
+ });
+ }
+
+ async putKeyMetadataRecord(record) {
+ if (!this.db) throw new Error('Database not initialized');
+ const transaction = this.db.transaction([this.METADATA_STORE], 'readwrite');
+ const store = transaction.objectStore(this.METADATA_STORE);
+ return new Promise((resolve, reject) => {
+ const request = store.put(record);
+ request.onsuccess = () => resolve();
+ request.onerror = () => reject(new Error(`Failed to store metadata: ${request.error}`));
+ });
+ }
/**
* Delete key and its metadata
@@ -12840,8 +12994,7 @@ class SecureIndexedDBWrapper {
const saltRecord = {
id: 'master_salt',
- salt: Array.from(new Uint8Array(salt)),
- created: Date.now()
+ salt: Array.from(new Uint8Array(salt))
};
return new Promise((resolve, reject) => {
@@ -12934,6 +13087,12 @@ class SecurePersistentKeyStorage {
this._dbInitialized = true;
}
}
+
+ async _ensureMasterKeyUnlocked() {
+ if (typeof this._masterKeyManager.isUnlocked === 'function' && !this._masterKeyManager.isUnlocked()) {
+ await this._masterKeyManager.unlock();
+ }
+ }
/**
* Store extractable key with encryption
@@ -12957,6 +13116,14 @@ class SecurePersistentKeyStorage {
const { encryptedData, iv } = await this._encryptKeyData(jwkData);
// Store encrypted data in IndexedDB
+ const encryptedMetadata = await this._encryptMetadata({
+ ...metadata,
+ created: Date.now(),
+ lastAccessed: Date.now(),
+ extractable: true,
+ persistent: true
+ });
+
await this._indexedDB.storeEncryptedKey(
keyId,
encryptedData,
@@ -12964,7 +13131,7 @@ class SecurePersistentKeyStorage {
cryptoKey.algorithm,
cryptoKey.usages,
cryptoKey.type,
- metadata
+ encryptedMetadata
);
// Store non-extractable reference in memory cache
@@ -13006,7 +13173,7 @@ class SecurePersistentKeyStorage {
this._keyReferences.set(keyId, restoredKey);
// Update last accessed time
- await this._indexedDB.updateKeyMetadata(keyId, { lastAccessed: Date.now() });
+ await this._updateEncryptedMetadata(keyId, { lastAccessed: Date.now() });
return restoredKey;
@@ -13041,7 +13208,13 @@ class SecurePersistentKeyStorage {
async listStoredKeys() {
try {
await this._ensureDBInitialized();
- return await this._indexedDB.listKeys();
+ const records = await this._indexedDB.listKeys();
+ const results = [];
+ for (const record of records) {
+ const metadata = await this._readMetadataWithMigration(record);
+ if (metadata) results.push({ keyId: record.keyId, ...metadata });
+ }
+ return results;
} catch (error) {
throw new Error(`Failed to list keys: ${error.message}`);
}
@@ -13090,6 +13263,58 @@ class SecurePersistentKeyStorage {
const jsonString = new TextDecoder().decode(decryptedData);
return JSON.parse(jsonString);
}
+
+ async _encryptMetadata(metadata) {
+ const data = new TextEncoder().encode(JSON.stringify(metadata));
+ await this._ensureMasterKeyUnlocked();
+ const { encryptedData, iv } = await this._masterKeyManager.encryptBytes(data);
+ return {
+ metadataVersion: 1,
+ encryptedMetadata: Array.from(encryptedData),
+ metadataIv: Array.from(iv)
+ };
+ }
+
+ async _decryptMetadataRecord(record) {
+ if (!record?.encryptedMetadata || !record?.metadataIv) {
+ throw new Error('Encrypted metadata missing');
+ }
+ await this._ensureMasterKeyUnlocked();
+ const decrypted = await this._masterKeyManager.decryptBytes(
+ new Uint8Array(record.encryptedMetadata),
+ new Uint8Array(record.metadataIv)
+ );
+ return JSON.parse(new TextDecoder().decode(decrypted));
+ }
+
+ async _readMetadataWithMigration(record) {
+ if (!record) return null;
+ if (record.encryptedMetadata) {
+ try {
+ return await this._decryptMetadataRecord(record);
+ } catch (error) {
+ // Corrupted encrypted metadata must not be exposed or trusted.
+ return null;
+ }
+ }
+
+ // Legacy plaintext record: read once, then migrate to encrypted envelope.
+ const { keyId, ...legacyMetadata } = record;
+ const encryptedRecord = { keyId, ...(await this._encryptMetadata(legacyMetadata)) };
+ await this._indexedDB.putKeyMetadataRecord(encryptedRecord);
+ return legacyMetadata;
+ }
+
+ async _updateEncryptedMetadata(keyId, updates) {
+ const record = await this._indexedDB.getKeyMetadataRecord(keyId);
+ if (!record) throw new Error(`Key metadata not found: ${keyId}`);
+ const current = await this._readMetadataWithMigration(record);
+ if (!current) throw new Error(`Key metadata corrupted: ${keyId}`);
+ await this._indexedDB.putKeyMetadataRecord({
+ keyId,
+ ...(await this._encryptMetadata({ ...current, ...updates }))
+ });
+ }
/**
* Import JWK as non-extractable key
@@ -13505,4 +13730,4 @@ export {
SecureMasterKeyManager,
SecureIndexedDBWrapper,
SecurePersistentKeyStorage
-};
\ No newline at end of file
+};
diff --git a/src/transfer/EnhancedSecureFileTransfer.js b/src/transfer/EnhancedSecureFileTransfer.js
index 411510b..38526f0 100644
--- a/src/transfer/EnhancedSecureFileTransfer.js
+++ b/src/transfer/EnhancedSecureFileTransfer.js
@@ -256,12 +256,13 @@ class SecureMemoryManager {
}
class EnhancedSecureFileTransfer {
- constructor(webrtcManager, onProgress, onComplete, onError, onFileReceived) {
+ constructor(webrtcManager, onProgress, onComplete, onError, onFileReceived, onIncomingFileRequest) {
this.webrtcManager = webrtcManager;
this.onProgress = onProgress;
this.onComplete = onComplete;
this.onError = onError;
this.onFileReceived = onFileReceived;
+ this.onIncomingFileRequest = onIncomingFileRequest;
// Validate webrtcManager
if (!webrtcManager) {
@@ -284,87 +285,58 @@ class EnhancedSecureFileTransfer {
this.RETRY_ATTEMPTS = 3;
this.FILE_TYPE_RESTRICTIONS = {
- documents: {
- extensions: ['.pdf', '.doc', '.docx', '.txt', '.md', '.rtf', '.odt'],
- mimeTypes: [
- 'application/pdf',
- 'application/msword',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- 'text/plain',
- 'text/markdown',
- 'application/rtf',
- 'application/vnd.oasis.opendocument.text'
- ],
- maxSize: 50 * 1024 * 1024, // 50 MB
- category: 'Documents',
- description: 'PDF, DOC, TXT, MD, RTF, ODT'
+ pdf: {
+ extensions: ['.pdf'],
+ mimeTypes: ['application/pdf'],
+ maxSize: 50 * 1024 * 1024,
+ category: 'PDF',
+ description: 'PDF'
+ },
+
+ text: {
+ extensions: ['.txt'],
+ mimeTypes: ['text/plain'],
+ maxSize: 10 * 1024 * 1024,
+ category: 'Plain text',
+ description: 'TXT'
},
images: {
- extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.ico'],
+ extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.ico'],
mimeTypes: [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/bmp',
- 'image/svg+xml',
'image/x-icon'
],
maxSize: 25 * 1024 * 1024, // 25 MB
category: 'Images',
- description: 'JPG, PNG, GIF, WEBP, BMP, SVG, ICO'
+ description: 'JPG, JPEG, PNG, GIF, WEBP, BMP, ICO'
},
archives: {
- extensions: ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', '.xz'],
- mimeTypes: [
- 'application/zip',
- 'application/x-rar-compressed',
- 'application/x-7z-compressed',
- 'application/x-tar',
- 'application/gzip',
- 'application/x-bzip2',
- 'application/x-xz'
- ],
+ extensions: ['.zip'],
+ mimeTypes: ['application/zip'],
maxSize: 100 * 1024 * 1024, // 100 MB
category: 'Archives',
- description: 'ZIP, RAR, 7Z, TAR, GZ, BZ2, XZ'
- },
-
- media: {
- extensions: ['.mp3', '.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.ogg', '.wav'],
- mimeTypes: [
- 'audio/mpeg',
- 'video/mp4',
- 'video/x-msvideo',
- 'video/x-matroska',
- 'video/quicktime',
- 'video/x-ms-wmv',
- 'video/x-flv',
- 'video/webm',
- 'audio/ogg',
- 'audio/wav'
- ],
- maxSize: 100 * 1024 * 1024, // 100 MB
- category: 'Media',
- description: 'MP3, MP4, AVI, MKV, MOV, WMV, FLV, WEBM, OGG, WAV'
- },
-
- general: {
- extensions: [],
- mimeTypes: [],
- maxSize: 50 * 1024 * 1024, // 50 MB
- category: 'General',
- description: 'Any file type up to size limits'
+ description: 'ZIP'
}
};
+ this.BLOCKED_EXTENSIONS = new Set([
+ '.exe', '.bat', '.cmd', '.sh', '.js', '.msi', '.dmg', '.app',
+ '.jar', '.scr', '.ps1', '.vbs', '.html', '.svg'
+ ]);
// Active transfers tracking
this.activeTransfers = new Map(); // fileId -> transfer state
this.receivingTransfers = new Map(); // fileId -> receiving state
+ this.pendingIncomingTransfers = new Map(); // fileId -> validated metadata awaiting consent
this.transferQueue = []; // Queue for pending transfers
this.pendingChunks = new Map();
+ this.incomingOfferLimiter = new RateLimiter(5, 60000);
+ this.MAX_PENDING_INCOMING_TRANSFERS = 3;
// Session key derivation
this.sessionKeys = new Map(); // fileId -> derived session key
@@ -373,6 +345,7 @@ class EnhancedSecureFileTransfer {
this.processedChunks = new Set(); // Prevent replay attacks
this.transferNonces = new Map(); // fileId -> current nonce counter
this.receivedFileBuffers = new Map(); // fileId -> { buffer:ArrayBuffer, type:string, name:string, size:number }
+ this.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 3;
this.setupFileMessageHandlers();
@@ -386,24 +359,15 @@ class EnhancedSecureFileTransfer {
// ============================================
getFileType(file) {
- const fileName = file.name.toLowerCase();
- const fileExtension = fileName.substring(fileName.lastIndexOf('.'));
- const mimeType = file.type.toLowerCase();
+ const fileName = String(file?.name || '').toLowerCase();
+ const extensionIndex = fileName.lastIndexOf('.');
+ const fileExtension = extensionIndex >= 0 ? fileName.substring(extensionIndex) : '';
+ const mimeType = String(file?.type || '').toLowerCase();
for (const [typeKey, typeConfig] of Object.entries(this.FILE_TYPE_RESTRICTIONS)) {
- if (typeKey === 'general') continue; // Пропускаем общий тип
-
- if (typeConfig.extensions.includes(fileExtension)) {
- return {
- type: typeKey,
- category: typeConfig.category,
- description: typeConfig.description,
- maxSize: typeConfig.maxSize,
- allowed: true
- };
- }
-
- if (typeConfig.mimeTypes.includes(mimeType)) {
+ const extensionAllowed = typeConfig.extensions.includes(fileExtension);
+ const mimeAllowed = typeConfig.mimeTypes.includes(mimeType);
+ if (extensionAllowed && mimeAllowed) {
return {
type: typeKey,
category: typeConfig.category,
@@ -414,26 +378,42 @@ class EnhancedSecureFileTransfer {
}
}
- const generalConfig = this.FILE_TYPE_RESTRICTIONS.general;
return {
- type: 'general',
- category: generalConfig.category,
- description: generalConfig.description,
- maxSize: generalConfig.maxSize,
- allowed: true
+ type: 'blocked',
+ category: 'Unsupported',
+ description: 'Allowed: JPG, JPEG, PNG, GIF, WEBP, BMP, ICO, PDF, TXT, ZIP',
+ maxSize: this.MAX_FILE_SIZE,
+ allowed: false,
+ extension: fileExtension,
+ mimeType
};
}
validateFile(file) {
const fileType = this.getFileType(file);
const errors = [];
+ const fileName = String(file?.name || '');
+ const lowerName = fileName.toLowerCase();
+ const extensionIndex = lowerName.lastIndexOf('.');
+ const fileExtension = extensionIndex >= 0 ? lowerName.substring(extensionIndex) : '';
+ const mimeType = String(file?.type || '').toLowerCase();
+
+ if (this.BLOCKED_EXTENSIONS.has(fileExtension)) {
+ errors.push(`File rejected: ${fileExtension} files are not allowed for security reasons.`);
+ }
+
+ if (!mimeType) {
+ errors.push('File rejected: missing MIME type is unsafe.');
+ }
if (file.size > fileType.maxSize) {
errors.push(`File size (${this.formatFileSize(file.size)}) exceeds maximum allowed for ${fileType.category} (${this.formatFileSize(fileType.maxSize)})`);
}
if (!fileType.allowed) {
- errors.push(`File type not allowed. Supported types: ${fileType.description}`);
+ if (mimeType && !this.BLOCKED_EXTENSIONS.has(fileExtension)) {
+ errors.push(`File rejected: extension and MIME type must match an allowed type. Supported types: ${fileType.description}`);
+ }
}
if (file.size > this.MAX_FILE_SIZE) {
@@ -449,6 +429,48 @@ class EnhancedSecureFileTransfer {
};
}
+ normalizeDisplayFileName(fileName) {
+ return String(fileName || '')
+ .normalize('NFKC')
+ .replace(/[\u0000-\u001F\u007F]/g, '')
+ .replace(/[\\/]+/g, '_')
+ .trim()
+ .slice(0, 255);
+ }
+
+ validateIncomingMetadata(metadata) {
+ const errors = [];
+ if (!metadata || typeof metadata !== 'object') errors.push('Invalid file transfer metadata');
+ if (!metadata?.fileId || typeof metadata.fileId !== 'string') errors.push('Invalid file id');
+ if (!Number.isSafeInteger(metadata?.fileSize) || metadata.fileSize <= 0) errors.push('Invalid file size');
+ if (!Number.isSafeInteger(metadata?.totalChunks) || metadata.totalChunks <= 0) errors.push('Invalid chunk count');
+ if (!Number.isSafeInteger(metadata?.chunkSize) || metadata.chunkSize <= 0 || metadata.chunkSize > this.CHUNK_SIZE) errors.push('Invalid chunk size');
+ if (!Array.isArray(metadata?.salt) || metadata.salt.length !== 32) errors.push('Invalid salt');
+
+ const rawName = typeof metadata?.fileName === 'string' ? metadata.fileName : '';
+ const displayName = this.normalizeDisplayFileName(rawName);
+ const hasDangerousName =
+ !rawName ||
+ rawName !== rawName.trim() ||
+ /[\u0000-\u001F\u007F]/.test(rawName) ||
+ /[\\/]/.test(rawName) ||
+ rawName === '.' ||
+ rawName === '..' ||
+ displayName.length === 0;
+ if (hasDangerousName) errors.push('Dangerous file name');
+
+ if (errors.length === 0) {
+ const validation = this.validateFile({
+ name: displayName,
+ size: metadata.fileSize,
+ type: metadata.fileType || 'application/octet-stream'
+ });
+ if (!validation.isValid) errors.push(...validation.errors);
+ }
+
+ return { isValid: errors.length === 0, errors, displayName };
+ }
+
formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
@@ -461,8 +483,6 @@ class EnhancedSecureFileTransfer {
const supportedTypes = {};
for (const [typeKey, typeConfig] of Object.entries(this.FILE_TYPE_RESTRICTIONS)) {
- if (typeKey === 'general') continue;
-
supportedTypes[typeKey] = {
category: typeConfig.category,
description: typeConfig.description,
@@ -878,10 +898,21 @@ class EnhancedSecureFileTransfer {
this.activeTransfers.set(fileId, transferState);
this.transferNonces.set(fileId, 0);
+ const consentPromise = new Promise((resolve, reject) => {
+ transferState.resolveConsent = resolve;
+ transferState.rejectConsent = reject;
+ transferState.consentTimeout = setTimeout(() => {
+ transferState.consentTimeout = null;
+ reject(new Error('Transfer timeout'));
+ }, 30000);
+ });
+
// Send file metadata first
await this.sendFileMetadata(transferState);
- // Start chunk transmission
+ // Wait for explicit receiver consent before any chunks are sent.
+ await consentPromise;
+
await this.startChunkTransmission(transferState);
return fileId;
@@ -1106,11 +1137,14 @@ class EnhancedSecureFileTransfer {
async handleFileTransferStart(metadata) {
try {
- // Validate metadata
- if (!metadata.fileId || !metadata.fileName || !metadata.fileSize) {
- throw new Error('Invalid file transfer metadata');
+ const clientId = this.getClientIdentifier();
+ if (!this.incomingOfferLimiter.isAllowed(clientId)) {
+ throw new Error('Incoming file request rate limit exceeded');
}
+ const validation = this.validateIncomingMetadata(metadata);
+ if (!validation.isValid) throw new Error(validation.errors.join('. '));
+
if (metadata.signature && this.verificationKey) {
try {
const isValid = await FileMetadataSigner.verifyFileMetadata(
@@ -1137,55 +1171,30 @@ class EnhancedSecureFileTransfer {
}
// Check if we already have this transfer
- if (this.receivingTransfers.has(metadata.fileId)) {
+ if (this.receivingTransfers.has(metadata.fileId) || this.pendingIncomingTransfers.has(metadata.fileId)) {
return;
}
-
- // Derive session key from salt
- const sessionKey = await this.deriveFileSessionKeyFromSalt(
- metadata.fileId,
- metadata.salt
- );
-
- // Create receiving transfer state
- const receivingState = {
- fileId: metadata.fileId,
- fileName: metadata.fileName,
- fileSize: metadata.fileSize,
- fileType: metadata.fileType || 'application/octet-stream',
- fileHash: metadata.fileHash,
- totalChunks: metadata.totalChunks,
- chunkSize: metadata.chunkSize || this.CHUNK_SIZE,
- sessionKey: sessionKey,
- salt: metadata.salt,
- receivedChunks: new Map(),
- receivedCount: 0,
- startTime: Date.now(),
- lastChunkTime: Date.now(),
- status: 'receiving'
- };
-
- this.receivingTransfers.set(metadata.fileId, receivingState);
-
- // Send acceptance response
- const response = {
- type: 'file_transfer_response',
- fileId: metadata.fileId,
- accepted: true,
- timestamp: Date.now()
- };
-
- await this.sendSecureMessage(response);
- // Process buffered chunks if any
- if (this.pendingChunks.has(metadata.fileId)) {
- const bufferedChunks = this.pendingChunks.get(metadata.fileId);
-
- for (const [chunkIndex, chunkMessage] of bufferedChunks.entries()) {
- await this.handleFileChunk(chunkMessage);
- }
-
- this.pendingChunks.delete(metadata.fileId);
+ if (this.pendingIncomingTransfers.size >= this.MAX_PENDING_INCOMING_TRANSFERS) {
+ throw new Error('Too many pending incoming file requests');
+ }
+
+ const pendingMetadata = {
+ ...metadata,
+ fileName: validation.displayName,
+ receivedAt: Date.now()
+ };
+ this.pendingIncomingTransfers.set(metadata.fileId, pendingMetadata);
+
+ if (typeof this.onIncomingFileRequest === 'function') {
+ this.onIncomingFileRequest({
+ fileId: pendingMetadata.fileId,
+ fileName: pendingMetadata.fileName,
+ fileSize: pendingMetadata.fileSize,
+ mimeType: pendingMetadata.fileType || 'application/octet-stream'
+ });
+ } else {
+ await this.rejectIncomingFile(metadata.fileId, 'User consent unavailable');
}
} catch (error) {
@@ -1211,13 +1220,8 @@ class EnhancedSecureFileTransfer {
try {
let receivingState = this.receivingTransfers.get(chunkMessage.fileId);
- // Buffer early chunks if transfer not yet initialized
+ // Never buffer chunks before explicit consent.
if (!receivingState) {
- if (!this.pendingChunks.has(chunkMessage.fileId)) {
- this.pendingChunks.set(chunkMessage.fileId, new Map());
- }
-
- this.pendingChunks.get(chunkMessage.fileId).set(chunkMessage.chunkIndex, chunkMessage);
return;
}
@@ -1352,7 +1356,7 @@ class EnhancedSecureFileTransfer {
receivingState.endTime = Date.now();
receivingState.status = 'completed';
- this.receivedFileBuffers.set(receivingState.fileId, {
+ this._storeReceivedFileBuffer(receivingState.fileId, {
buffer: fileBuffer,
type: receivingState.fileType,
name: receivingState.fileName,
@@ -1360,7 +1364,13 @@ class EnhancedSecureFileTransfer {
});
if (this.onFileReceived) {
- const getBlob = async () => new Blob([this.receivedFileBuffers.get(receivingState.fileId).buffer], { type: receivingState.fileType });
+ const getBlob = async () => {
+ const blob = await this.getBlob(receivingState.fileId);
+ if (!blob) {
+ throw new Error('This file is no longer available for download.');
+ }
+ return blob;
+ };
const getObjectURL = async () => {
const blob = await getBlob();
return URL.createObjectURL(blob);
@@ -1443,8 +1453,18 @@ class EnhancedSecureFileTransfer {
if (response.accepted) {
transferState.status = 'accepted';
+ if (transferState.consentTimeout) clearTimeout(transferState.consentTimeout);
+ transferState.consentTimeout = null;
+ transferState.resolveConsent?.();
+ transferState.resolveConsent = null;
+ transferState.rejectConsent = null;
} else {
transferState.status = 'rejected';
+ if (transferState.consentTimeout) clearTimeout(transferState.consentTimeout);
+ transferState.consentTimeout = null;
+ transferState.rejectConsent?.(new Error(response.error || 'Transfer rejected'));
+ transferState.rejectConsent = null;
+ transferState.resolveConsent = null;
if (this.onError) {
this.onError(`Transfer rejected: ${response.error || 'Unknown reason'}`);
@@ -1555,6 +1575,48 @@ class EnhancedSecureFileTransfer {
}));
}
+ getPendingIncomingTransfers() {
+ return Array.from(this.pendingIncomingTransfers.values()).map(transfer => ({
+ fileId: transfer.fileId,
+ fileName: transfer.fileName,
+ fileSize: transfer.fileSize,
+ mimeType: transfer.fileType || 'application/octet-stream',
+ receivedAt: transfer.receivedAt
+ }));
+ }
+
+ async acceptIncomingFile(fileId) {
+ const metadata = this.pendingIncomingTransfers.get(fileId);
+ if (!metadata) return false;
+ const sessionKey = await this.deriveFileSessionKeyFromSalt(fileId, metadata.salt);
+ this.receivingTransfers.set(fileId, {
+ fileId,
+ fileName: metadata.fileName,
+ fileSize: metadata.fileSize,
+ fileType: metadata.fileType || 'application/octet-stream',
+ fileHash: metadata.fileHash,
+ totalChunks: metadata.totalChunks,
+ chunkSize: metadata.chunkSize || this.CHUNK_SIZE,
+ sessionKey,
+ salt: metadata.salt,
+ receivedChunks: new Map(),
+ receivedCount: 0,
+ startTime: Date.now(),
+ lastChunkTime: Date.now(),
+ status: 'receiving'
+ });
+ this.pendingIncomingTransfers.delete(fileId);
+ await this.sendSecureMessage({ type: 'file_transfer_response', fileId, accepted: true, timestamp: Date.now() });
+ return true;
+ }
+
+ async rejectIncomingFile(fileId, error = 'Rejected by user') {
+ if (!this.pendingIncomingTransfers.has(fileId)) return false;
+ this.pendingIncomingTransfers.delete(fileId);
+ await this.sendSecureMessage({ type: 'file_transfer_response', fileId, accepted: false, error, timestamp: Date.now() });
+ return true;
+ }
+
cancelTransfer(fileId) {
try {
if (this.activeTransfers.has(fileId)) {
@@ -1573,6 +1635,19 @@ class EnhancedSecureFileTransfer {
}
cleanupTransfer(fileId) {
+ const transferState = this.activeTransfers.get(fileId);
+ if (transferState) {
+ if (transferState.consentTimeout) {
+ clearTimeout(transferState.consentTimeout);
+ transferState.consentTimeout = null;
+ }
+ if (transferState.rejectConsent) {
+ transferState.rejectConsent(new Error('Transfer cancelled during cleanup or disconnect'));
+ transferState.rejectConsent = null;
+ transferState.resolveConsent = null;
+ }
+ }
+
this.activeTransfers.delete(fileId);
this.sessionKeys.delete(fileId);
this.transferNonces.delete(fileId);
@@ -1585,6 +1660,28 @@ class EnhancedSecureFileTransfer {
}
}
+ _storeReceivedFileBuffer(fileId, entry) {
+ this.receivedFileBuffers.set(fileId, entry);
+ while (this.receivedFileBuffers.size > this.MAX_RETAINED_RECEIVED_FILE_BUFFERS) {
+ const oldestFileId = this.receivedFileBuffers.keys().next().value;
+ this._discardReceivedFileBuffer(oldestFileId);
+ }
+ }
+
+ _discardReceivedFileBuffer(fileId) {
+ const fileBuffer = this.receivedFileBuffers.get(fileId);
+ if (!fileBuffer) return;
+ try {
+ if (fileBuffer.buffer) {
+ SecureMemoryManager.secureWipe(fileBuffer.buffer);
+ new Uint8Array(fileBuffer.buffer).fill(0);
+ }
+ } catch (_) {
+ // Best-effort wipe; deletion must still proceed.
+ }
+ this.receivedFileBuffers.delete(fileId);
+ }
+
// ✅ УЛУЧШЕННАЯ безопасная очистка памяти для предотвращения use-after-free
cleanupReceivingTransfer(fileId) {
try {
@@ -1809,6 +1906,7 @@ class EnhancedSecureFileTransfer {
// Clear all state
this.pendingChunks.clear();
+ this.pendingIncomingTransfers.clear();
this.activeTransfers.clear();
this.receivingTransfers.clear();
this.transferQueue.length = 0;
@@ -1816,6 +1914,10 @@ class EnhancedSecureFileTransfer {
this.transferNonces.clear();
this.processedChunks.clear();
+ for (const fileId of Array.from(this.receivedFileBuffers.keys())) {
+ this._discardReceivedFileBuffer(fileId);
+ }
+
this.clearKeys();
}
@@ -2026,4 +2128,4 @@ class EnhancedSecureFileTransfer {
}
}
-export { EnhancedSecureFileTransfer };
\ No newline at end of file
+export { EnhancedSecureFileTransfer };
diff --git a/tests/disconnect-cleanup.test.mjs b/tests/disconnect-cleanup.test.mjs
new file mode 100644
index 0000000..00d6805
--- /dev/null
+++ b/tests/disconnect-cleanup.test.mjs
@@ -0,0 +1,120 @@
+import assert from 'node:assert/strict';
+
+globalThis.window = {
+ EnhancedSecureCryptoUtils: {
+ secureLog: { log() {} }
+ }
+};
+globalThis.CustomEvent = class CustomEvent {
+ constructor(type, init) {
+ this.type = type;
+ this.detail = init?.detail;
+ }
+};
+const dispatchedEvents = [];
+globalThis.document = {
+ dispatchEvent(event) {
+ dispatchedEvents.push(event);
+ }
+};
+
+const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+function closableChannel() {
+ return {
+ readyState: 'open',
+ closed: false,
+ onopen() {},
+ onclose() {},
+ onmessage() {},
+ onerror() {},
+ close() { this.closed = true; }
+ };
+}
+
+{
+ let transferCleanups = 0;
+ const dataChannel = closableChannel();
+ const heartbeatChannel = closableChannel();
+ const decoyChannel = closableChannel();
+ const peerConnection = {
+ closed: false,
+ onconnectionstatechange() {},
+ ondatachannel() {},
+ close() { this.closed = true; }
+ };
+ const timer = setTimeout(() => {}, 10_000);
+ const manager = {
+ intentionalDisconnect: false,
+ fileTransferSystem: { cleanup() { transferCleanups += 1; } },
+ dataChannel,
+ heartbeatChannel,
+ peerConnection,
+ decoyTimers: new Map([['decoy', timer]]),
+ decoyChannels: new Map([['decoy', decoyChannel]]),
+ packetBuffer: new Map([['p', 1]]),
+ chunkQueue: [1],
+ processedMessageIds: new Set(['m']),
+ messageCounter: 4,
+ keyVersions: new Map([['v', 1]]),
+ oldKeys: new Map([['o', 1]]),
+ currentKeyVersion: 3,
+ lastKeyRotation: 1,
+ sequenceNumber: 7,
+ expectedSequenceNumber: 8,
+ replayWindow: new Set([9]),
+ messageQueue: [{ secret: true }],
+ calls: [],
+ _stopAllTimers() { this.calls.push('_stopAllTimers'); },
+ stopHeartbeat() { this.calls.push('stopHeartbeat'); },
+ stopFakeTrafficGeneration() { this.calls.push('stopFakeTrafficGeneration'); },
+ _wipeEphemeralKeys() { this.calls.push('_wipeEphemeralKeys'); },
+ _hardWipeOldKeys() { this.calls.push('_hardWipeOldKeys'); },
+ _secureCleanupCryptographicMaterials() { this.calls.push('_secureCleanupCryptographicMaterials'); },
+ _clearVerificationStates() {
+ this.calls.push('_clearVerificationStates');
+ this.localVerificationConfirmed = false;
+ this.remoteVerificationConfirmed = false;
+ this.bothVerificationsConfirmed = false;
+ this.isVerified = false;
+ this.verificationCode = null;
+ this.pendingSASCode = null;
+ },
+ _secureWipeMemory() { this.calls.push('_secureWipeMemory'); },
+ _forceGarbageCollection() { return Promise.resolve(); },
+ sendDisconnectNotification() { this.calls.push('sendDisconnectNotification'); },
+ onStatusChange(value) { this.status = value; },
+ onKeyExchange(value) { this.keyExchange = value; },
+ onVerificationRequired(value) { this.verificationRequired = value; },
+ _secureLog() {}
+ };
+
+ EnhancedSecureWebRTCManager.prototype.disconnect.call(manager);
+
+ assert.equal(transferCleanups, 1);
+ assert.equal(manager.fileTransferSystem, null);
+ assert.equal(dataChannel.closed, true);
+ assert.equal(heartbeatChannel.closed, true);
+ assert.equal(decoyChannel.closed, true);
+ assert.equal(peerConnection.closed, true);
+ assert.equal(manager.dataChannel, null);
+ assert.equal(manager.heartbeatChannel, null);
+ assert.equal(manager.peerConnection, null);
+ assert.equal(manager.decoyTimers.size, 0);
+ assert.equal(manager.decoyChannels.size, 0);
+ assert.equal(manager.packetBuffer.size, 0);
+ assert.deepEqual(manager.chunkQueue, []);
+ assert.equal(manager.processedMessageIds.size, 0);
+ assert.equal(manager.keyVersions.size, 0);
+ assert.equal(manager.oldKeys.size, 0);
+ assert.equal(manager.replayWindow.size, 0);
+ assert.deepEqual(manager.messageQueue, []);
+ assert.equal(manager.status, 'disconnected');
+ assert.equal(manager.keyExchange, '');
+ assert.equal(manager.verificationRequired, '');
+ assert.ok(manager.calls.includes('_clearVerificationStates'));
+ assert.ok(dispatchedEvents.some(event => event.type === 'peer-disconnect'));
+ assert.ok(dispatchedEvents.some(event => event.type === 'connection-cleaned'));
+}
+
+console.log('Disconnect cleanup tests passed');
diff --git a/tests/file-transfer-callback-propagation.test.mjs b/tests/file-transfer-callback-propagation.test.mjs
new file mode 100644
index 0000000..d603e7b
--- /dev/null
+++ b/tests/file-transfer-callback-propagation.test.mjs
@@ -0,0 +1,31 @@
+import assert from 'node:assert/strict';
+
+globalThis.window = { EnhancedSecureCryptoUtils: {} };
+const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+{
+ const oldProgress = () => {};
+ const manager = {
+ fileTransferSystem: {
+ onProgress: oldProgress,
+ onFileReceived: oldProgress,
+ onError: oldProgress,
+ onIncomingFileRequest: oldProgress
+ }
+ };
+
+ EnhancedSecureWebRTCManager.prototype.setFileTransferCallbacks.call(
+ manager,
+ null,
+ null,
+ null,
+ null
+ );
+
+ assert.equal(manager.fileTransferSystem.onProgress, null);
+ assert.equal(manager.fileTransferSystem.onFileReceived, null);
+ assert.equal(manager.fileTransferSystem.onError, null);
+ assert.equal(manager.fileTransferSystem.onIncomingFileRequest, null);
+}
+
+console.log('File transfer callback propagation tests passed');
diff --git a/tests/file-transfer-cleanup.test.mjs b/tests/file-transfer-cleanup.test.mjs
new file mode 100644
index 0000000..0d12e37
--- /dev/null
+++ b/tests/file-transfer-cleanup.test.mjs
@@ -0,0 +1,90 @@
+import assert from 'node:assert/strict';
+import { EnhancedSecureFileTransfer } from '../src/transfer/EnhancedSecureFileTransfer.js';
+
+function createSystem() {
+ const manager = {
+ dataChannel: { onmessage: null, send() {}, readyState: 'open' },
+ isVerified: true,
+ fileTransferSystem: null,
+ isConnected: () => true
+ };
+ return new EnhancedSecureFileTransfer(manager);
+}
+
+// cleanupTransfer rejects pending sender consent immediately and clears its timeout.
+{
+ const system = createSystem();
+ let rejectionMessage = null;
+ const timer = setTimeout(() => {}, 10_000);
+ system.activeTransfers.set('file_waiting', {
+ consentTimeout: timer,
+ rejectConsent(error) { rejectionMessage = error.message; },
+ resolveConsent() {}
+ });
+ system.sessionKeys.set('file_waiting', {});
+ system.transferNonces.set('file_waiting', 1);
+
+ system.cleanupTransfer('file_waiting');
+
+ assert.equal(system.activeTransfers.has('file_waiting'), false);
+ assert.equal(system.sessionKeys.has('file_waiting'), false);
+ assert.equal(system.transferNonces.has('file_waiting'), false);
+ assert.equal(rejectionMessage, 'Transfer cancelled during cleanup or disconnect');
+ assert.equal(system.activeTransfers.size, 0);
+}
+
+// global cleanup does not leave pending consent promises alive until timeout.
+{
+ const system = createSystem();
+ let rejected = false;
+ const timer = setTimeout(() => {}, 10_000);
+ system.activeTransfers.set('file_waiting', {
+ consentTimeout: timer,
+ rejectConsent() { rejected = true; },
+ resolveConsent() {}
+ });
+ system.cleanup();
+ assert.equal(rejected, true);
+ assert.equal(system.activeTransfers.size, 0);
+}
+
+// receivedFileBuffers is bounded and evicts the oldest retained buffer.
+{
+ const system = createSystem();
+ system.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 2;
+ system._storeReceivedFileBuffer('a', { buffer: new Uint8Array([1]).buffer });
+ system._storeReceivedFileBuffer('b', { buffer: new Uint8Array([2]).buffer });
+ system._storeReceivedFileBuffer('c', { buffer: new Uint8Array([3]).buffer });
+ assert.equal(system.receivedFileBuffers.size, 2);
+ assert.equal(system.receivedFileBuffers.has('a'), false);
+ assert.equal(system.receivedFileBuffers.has('b'), true);
+ assert.equal(system.receivedFileBuffers.has('c'), true);
+}
+
+// Evicted received buffers fail gracefully for old download closures.
+{
+ const system = createSystem();
+ system.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 1;
+ let fileData = null;
+ system.onFileReceived = data => { fileData = data; };
+ system.calculateFileHashFromData = async () => 'hash';
+ system.sendSecureMessage = async () => {};
+ const receivingState = {
+ fileId: 'old',
+ fileName: 'old.pdf',
+ fileSize: 1,
+ fileType: 'application/pdf',
+ fileHash: 'hash',
+ totalChunks: 1,
+ receivedChunks: new Map([[0, new Uint8Array([1]).buffer]]),
+ startTime: Date.now()
+ };
+ await system.assembleFile(receivingState);
+ system._storeReceivedFileBuffer('new', { buffer: new Uint8Array([2]).buffer });
+ await assert.rejects(
+ () => fileData.getObjectURL(),
+ /no longer available for download/i
+ );
+}
+
+console.log('File transfer cleanup tests passed');
diff --git a/tests/file-transfer-consent.test.mjs b/tests/file-transfer-consent.test.mjs
new file mode 100644
index 0000000..bf6f50b
--- /dev/null
+++ b/tests/file-transfer-consent.test.mjs
@@ -0,0 +1,62 @@
+import assert from 'node:assert/strict';
+import { EnhancedSecureFileTransfer } from '../src/transfer/EnhancedSecureFileTransfer.js';
+
+function createSystem(onIncomingFileRequest = () => {}) {
+ const manager = {
+ dataChannel: { onmessage: null, send() {}, readyState: 'open' },
+ isVerified: true,
+ fileTransferSystem: null,
+ isConnected: () => true
+ };
+ const system = new EnhancedSecureFileTransfer(manager, null, null, null, null, onIncomingFileRequest);
+ system.sendSecureMessage = async () => {};
+ return system;
+}
+
+function validMetadata(overrides = {}) {
+ return {
+ type: 'file_transfer_start',
+ fileId: 'file_1',
+ fileName: 'report.pdf',
+ fileSize: 1024,
+ fileType: 'application/pdf',
+ fileHash: 'abc',
+ totalChunks: 1,
+ chunkSize: 1024,
+ salt: new Array(32).fill(1),
+ ...overrides
+ };
+}
+
+// Metadata is validated before a consent prompt is shown.
+{
+ const system = createSystem();
+ assert.equal(system.validateIncomingMetadata(validMetadata()).isValid, true);
+ assert.equal(system.validateIncomingMetadata(validMetadata({ fileName: '../evil.pdf' })).isValid, false);
+ assert.equal(system.validateIncomingMetadata(validMetadata({ fileSize: 200 * 1024 * 1024 })).isValid, false);
+}
+
+// No receiving state or chunk buffers are allocated before explicit acceptance.
+{
+ let prompted = null;
+ const system = createSystem(request => { prompted = request; });
+ await system.handleFileTransferStart(validMetadata());
+ assert.equal(prompted.fileName, 'report.pdf');
+ assert.equal(system.pendingIncomingTransfers.size, 1);
+ assert.equal(system.receivingTransfers.size, 0);
+
+ await system.handleFileChunk({ fileId: 'file_1', chunkIndex: 0 });
+ assert.equal(system.pendingChunks.size, 0);
+}
+
+// Incoming request spam is bounded.
+{
+ const system = createSystem();
+ for (let index = 0; index < system.MAX_PENDING_INCOMING_TRANSFERS; index += 1) {
+ await system.handleFileTransferStart(validMetadata({ fileId: `file_${index}` }));
+ }
+ await system.handleFileTransferStart(validMetadata({ fileId: 'file_overflow' }));
+ assert.equal(system.pendingIncomingTransfers.size, system.MAX_PENDING_INCOMING_TRANSFERS);
+}
+
+console.log('File transfer consent tests passed');
diff --git a/tests/file-transfer-ui-cleanup.test.mjs b/tests/file-transfer-ui-cleanup.test.mjs
new file mode 100644
index 0000000..30b1e24
--- /dev/null
+++ b/tests/file-transfer-ui-cleanup.test.mjs
@@ -0,0 +1,75 @@
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import vm from 'node:vm';
+
+const effects = [];
+const setterCalls = [];
+let stateIndex = 0;
+const callbackCalls = [];
+
+const context = {
+ window: {},
+ React: {
+ useState(initialValue) {
+ const index = stateIndex++;
+ return [initialValue, value => setterCalls.push({ index, value })];
+ },
+ useRef(initialValue) {
+ return { current: initialValue };
+ },
+ useEffect(effect) {
+ effects.push(effect);
+ },
+ createElement() {
+ return null;
+ }
+ }
+};
+
+const source = fs.readFileSync(new URL('../src/components/ui/FileTransfer.jsx', import.meta.url), 'utf8');
+vm.runInNewContext(source, context);
+
+const manager = {
+ fileTransferSystem: {
+ onProgress: () => {},
+ onFileReceived: () => {},
+ onError: () => {},
+ onIncomingFileRequest: () => {}
+ },
+ setFileTransferCallbacks(...args) {
+ callbackCalls.push(args);
+ this.onFileProgress = args[0];
+ this.onFileReceived = args[1];
+ this.onFileError = args[2];
+ this.onIncomingFileRequest = args[3];
+ if (this.fileTransferSystem) {
+ this.fileTransferSystem.onProgress = args[0];
+ this.fileTransferSystem.onFileReceived = args[1];
+ this.fileTransferSystem.onError = args[2];
+ this.fileTransferSystem.onIncomingFileRequest = args[3];
+ }
+ },
+ getFileTransfers() {
+ return { sending: [], receiving: [] };
+ },
+ isConnected() {
+ return false;
+ },
+ isVerified: false
+};
+
+context.window.FileTransferComponent({ webrtcManager: manager, isConnected: false });
+const cleanups = effects.map(effect => effect()).filter(Boolean);
+
+assert.ok(setterCalls.some(call => call.index === 2 && Array.isArray(call.value) && call.value.length === 0));
+assert.ok(setterCalls.some(call => call.index === 3 && Array.isArray(call.value) && call.value.length === 0));
+assert.ok(setterCalls.some(call => call.index === 1 && call.value.sending.length === 0 && call.value.receiving.length === 0));
+
+cleanups.forEach(cleanup => cleanup());
+assert.deepEqual(callbackCalls.at(-1), [null, null, null, null]);
+assert.equal(manager.fileTransferSystem.onProgress, null);
+assert.equal(manager.fileTransferSystem.onFileReceived, null);
+assert.equal(manager.fileTransferSystem.onError, null);
+assert.equal(manager.fileTransferSystem.onIncomingFileRequest, null);
+
+console.log('File transfer UI cleanup tests passed');
diff --git a/tests/file-type-allowlist.test.mjs b/tests/file-type-allowlist.test.mjs
new file mode 100644
index 0000000..4f3f8d0
--- /dev/null
+++ b/tests/file-type-allowlist.test.mjs
@@ -0,0 +1,41 @@
+import assert from 'node:assert/strict';
+import { EnhancedSecureFileTransfer } from '../src/transfer/EnhancedSecureFileTransfer.js';
+
+function createSystem() {
+ const manager = {
+ dataChannel: { onmessage: null, send() {}, readyState: 'open' },
+ isVerified: true,
+ fileTransferSystem: null,
+ isConnected: () => true
+ };
+ return new EnhancedSecureFileTransfer(manager, null, null, null, null, null);
+}
+
+function file(name, type, size = 1024) {
+ return { name, type, size };
+}
+
+const system = createSystem();
+
+// Allowed files
+assert.equal(system.validateFile(file('photo.png', 'image/png')).isValid, true);
+assert.equal(system.validateFile(file('report.pdf', 'application/pdf')).isValid, true);
+assert.equal(system.validateFile(file('notes.txt', 'text/plain')).isValid, true);
+assert.equal(system.validateFile(file('bundle.zip', 'application/zip')).isValid, true);
+
+// Explicitly blocked extensions
+for (const name of ['run.exe', 'boot.bat', 'shell.sh', 'payload.js', 'page.html', 'vector.svg']) {
+ assert.equal(system.validateFile(file(name, 'application/octet-stream')).isValid, false, name);
+}
+
+// MIME spoofing: safe extension with unsafe MIME and unsafe extension with safe MIME are blocked.
+assert.equal(system.validateFile(file('photo.png', 'application/x-msdownload')).isValid, false);
+assert.equal(system.validateFile(file('payload.exe', 'image/png')).isValid, false);
+
+// Missing MIME is unsafe.
+assert.equal(system.validateFile(file('photo.png', '')).isValid, false);
+
+// Uppercase extension bypass is blocked.
+assert.equal(system.validateFile(file('PAYLOAD.EXE', 'application/octet-stream')).isValid, false);
+
+console.log('File type allowlist tests passed');
diff --git a/tests/incoming-message-sanitization.test.mjs b/tests/incoming-message-sanitization.test.mjs
new file mode 100644
index 0000000..c9a9790
--- /dev/null
+++ b/tests/incoming-message-sanitization.test.mjs
@@ -0,0 +1,57 @@
+import assert from 'node:assert/strict';
+
+globalThis.window = {
+ DEBUG_MODE: true,
+ DEVELOPMENT_MODE: true,
+ webpackHotUpdate: {},
+ location: {
+ hostname: 'localhost',
+ search: '?debug'
+ }
+};
+
+const { EnhancedSecureCryptoUtils } = await import('../src/crypto/EnhancedSecureCryptoUtils.js');
+window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
+const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+function createManager() {
+ return {
+ delivered: [],
+ _debugMode: false,
+ _secureLog() {},
+ _sanitizeIncomingChatMessage: EnhancedSecureWebRTCManager.prototype._sanitizeIncomingChatMessage,
+ onMessage(message, type) {
+ this.delivered.push({ message, type });
+ }
+ };
+}
+
+// Normal text survives unchanged.
+{
+ const manager = createManager();
+ EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, 'hello secure world', 'received');
+ assert.deepEqual(manager.delivered[0], { message: 'hello secure world', type: 'received' });
+}
+
+// XSS-like and HTML payloads are sanitized before UI delivery.
+{
+ const manager = createManager();
+ EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, 'Hello peer', 'received');
+ assert.deepEqual(manager.delivered[0], { message: 'Hello peer', type: 'received' });
+}
+
+// Event-handler and protocol strings are removed before reaching React state.
+{
+ const manager = createManager();
+ EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, '
javascript:alert(1)', 'received');
+ assert.deepEqual(manager.delivered[0], { message: 'alert(1)', type: 'received' });
+}
+
+// Outgoing/system messages are not altered by the incoming-message gate.
+{
+ const manager = createManager();
+ EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, 'system', 'system');
+ assert.deepEqual(manager.delivered[0], { message: 'system', type: 'system' });
+}
+
+console.log('Incoming message sanitization tests passed');
diff --git a/tests/indexeddb-metadata-encryption.test.mjs b/tests/indexeddb-metadata-encryption.test.mjs
new file mode 100644
index 0000000..fa85c52
--- /dev/null
+++ b/tests/indexeddb-metadata-encryption.test.mjs
@@ -0,0 +1,112 @@
+import assert from 'node:assert/strict';
+
+globalThis.window = { EnhancedSecureCryptoUtils: {} };
+const { SecureIndexedDBWrapper, SecurePersistentKeyStorage } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+class FakeMasterKeyManager {
+ isUnlocked() { return true; }
+ async unlock() {}
+ async encryptBytes(bytes) {
+ return { encryptedData: Uint8Array.from(bytes, byte => byte ^ 0xaa), iv: new Uint8Array(12).fill(7) };
+ }
+ async decryptBytes(bytes, iv) {
+ if (!iv || iv[0] !== 7) throw new Error('bad iv');
+ return Uint8Array.from(bytes, byte => byte ^ 0xaa);
+ }
+}
+
+class FakeDB {
+ constructor(records = []) {
+ this.records = new Map(records.map(record => [record.keyId, record]));
+ }
+ async initialize() {}
+ async listKeys() { return [...this.records.values()]; }
+ async getKeyMetadataRecord(keyId) { return this.records.get(keyId) || null; }
+ async putKeyMetadataRecord(record) { this.records.set(record.keyId, record); }
+}
+
+class FakeIndexedDBConnection {
+ constructor() {
+ this.records = new Map();
+ }
+ transaction(storeNames) {
+ const transaction = {
+ objectStore: (name) => ({
+ put: (record) => {
+ this.records.set(name, record);
+ queueMicrotask(() => transaction.oncomplete?.());
+ return {};
+ }
+ }),
+ oncomplete: null,
+ onerror: null
+ };
+ return transaction;
+ }
+}
+
+// New metadata is encrypted and sensitive fields are not plaintext.
+{
+ const db = new FakeDB();
+ const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), db);
+ const encrypted = await storage._encryptMetadata({
+ created: 111,
+ lastAccessed: 222,
+ sessionId: 'session-secret',
+ peerId: 'peer-secret'
+ });
+ await db.putKeyMetadataRecord({ keyId: 'k1', ...encrypted });
+ const raw = db.records.get('k1');
+ assert.equal(raw.created, undefined);
+ assert.equal(raw.lastAccessed, undefined);
+ assert.equal(raw.sessionId, undefined);
+ assert.ok(Array.isArray(raw.encryptedMetadata));
+}
+
+// Old plaintext metadata can be read and is migrated.
+{
+ const db = new FakeDB([{ keyId: 'legacy', created: 1, lastAccessed: 2, sessionId: 'old-session' }]);
+ const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), db);
+ const listed = await storage.listStoredKeys();
+ assert.equal(listed[0].sessionId, 'old-session');
+ const migrated = db.records.get('legacy');
+ assert.equal(migrated.sessionId, undefined);
+ assert.ok(migrated.encryptedMetadata);
+}
+
+// Corrupted encrypted metadata fails safely and is not exposed.
+{
+ const db = new FakeDB([{ keyId: 'bad', encryptedMetadata: [1, 2, 3], metadataIv: [0] }]);
+ const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), db);
+ const listed = await storage.listStoredKeys();
+ assert.deepEqual(listed, []);
+}
+
+// Plaintext timestamp fields are avoidable in the encrypted envelope.
+{
+ const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), new FakeDB());
+ const encrypted = await storage._encryptMetadata({ created: 10, lastAccessed: 20, usageCount: 3 });
+ assert.equal('created' in encrypted, false);
+ assert.equal('lastAccessed' in encrypted, false);
+ assert.equal('usageCount' in encrypted, false);
+}
+
+// Avoidable timestamps are not left plaintext in new IndexedDB records.
+{
+ const wrapper = new SecureIndexedDBWrapper();
+ wrapper.db = new FakeIndexedDBConnection();
+ await wrapper.storeEncryptedKey(
+ 'k2',
+ new Uint8Array([1]),
+ new Uint8Array([2]),
+ { name: 'AES-GCM' },
+ ['encrypt'],
+ 'secret',
+ { metadataVersion: 1, encryptedMetadata: [3], metadataIv: [4] }
+ );
+ assert.equal('timestamp' in wrapper.db.records.get(wrapper.KEYS_STORE), false);
+ assert.equal('created' in wrapper.db.records.get(wrapper.METADATA_STORE), false);
+ assert.equal('lastAccessed' in wrapper.db.records.get(wrapper.METADATA_STORE), false);
+}
+
+console.log('IndexedDB metadata encryption tests passed');
diff --git a/tests/sas-verification.test.mjs b/tests/sas-verification.test.mjs
new file mode 100644
index 0000000..c3d8546
--- /dev/null
+++ b/tests/sas-verification.test.mjs
@@ -0,0 +1,87 @@
+import assert from 'node:assert/strict';
+
+let compareCalls = 0;
+globalThis.window = {
+ EnhancedSecureCryptoUtils: {
+ constantTimeCompare(a, b) {
+ compareCalls += 1;
+ return a === b;
+ }
+ }
+};
+
+const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+function createFakeManager() {
+ const sent = [];
+ return {
+ sent,
+ verificationCode: 'A1-B2-C3',
+ sasValidationAttempts: 0,
+ localVerificationConfirmed: false,
+ remoteVerificationConfirmed: false,
+ bothVerificationsConfirmed: false,
+ disconnected: false,
+ _validateSASCode: EnhancedSecureWebRTCManager.prototype._validateSASCode,
+ _secureLog() {},
+ deliverMessageToUI() {},
+ disconnect() {
+ this.disconnected = true;
+ },
+ dataChannel: {
+ send(payload) {
+ sent.push(JSON.parse(payload));
+ }
+ },
+ _checkBothVerificationsConfirmed() {},
+ processMessageQueue() {}
+ };
+}
+
+// testSASNormalization
+{
+ const manager = createFakeManager();
+ assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'a1 b2 c3'), true);
+ assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'A1B2C3'), true);
+}
+
+// testConstantTimeCompare
+{
+ const manager = createFakeManager();
+ compareCalls = 0;
+ assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'A1-B2-C3'), true);
+ assert.equal(compareCalls, 1);
+}
+
+// testInvalidInputs
+{
+ const manager = createFakeManager();
+ assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, null), false);
+ assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'A1B2'), false);
+ assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'FFFFFF'), false);
+}
+
+// three failed attempts disconnect; a correct attempt signals only after validation
+{
+ const manager = createFakeManager();
+ for (let i = 0; i < 2; i += 1) {
+ assert.throws(
+ () => EnhancedSecureWebRTCManager.prototype.confirmVerification.call(manager, 'FFFFFF'),
+ /SAS_MISMATCH/
+ );
+ }
+ assert.equal(manager.disconnected, false);
+ assert.throws(
+ () => EnhancedSecureWebRTCManager.prototype.confirmVerification.call(manager, 'FFFFFF'),
+ /SAS_MAX_ATTEMPTS/
+ );
+ assert.equal(manager.disconnected, true);
+
+ const validManager = createFakeManager();
+ EnhancedSecureWebRTCManager.prototype.confirmVerification.call(validManager, 'a1 b2 c3');
+ assert.equal(validManager.localVerificationConfirmed, true);
+ assert.equal(validManager.sent[0].type, 'verification_confirmed');
+ assert.equal(validManager.sent[0].data.verificationMethod, 'MANUAL_SAS_ENTRY');
+}
+
+console.log('SAS verification tests passed');
diff --git a/tests/timer-lifecycle.test.mjs b/tests/timer-lifecycle.test.mjs
new file mode 100644
index 0000000..3d8f9ed
--- /dev/null
+++ b/tests/timer-lifecycle.test.mjs
@@ -0,0 +1,124 @@
+import assert from 'node:assert/strict';
+
+globalThis.window = {
+ EnhancedSecureCryptoUtils: {
+ secureLog: { log() {} }
+ }
+};
+globalThis.CustomEvent = class CustomEvent {
+ constructor(type, init) {
+ this.type = type;
+ this.detail = init?.detail;
+ }
+};
+globalThis.document = { dispatchEvent() {} };
+
+const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+const realSetTimeout = globalThis.setTimeout;
+const realClearTimeout = globalThis.clearTimeout;
+const realSetInterval = globalThis.setInterval;
+const realClearInterval = globalThis.clearInterval;
+
+const timers = [];
+globalThis.setTimeout = (callback, delay) => {
+ const timer = { kind: 'timeout', callback, delay, cleared: false };
+ timers.push(timer);
+ return timer;
+};
+globalThis.clearTimeout = (timer) => {
+ if (timer) timer.cleared = true;
+};
+globalThis.setInterval = (callback, delay) => {
+ const timer = { kind: 'interval', callback, delay, cleared: false };
+ timers.push(timer);
+ return timer;
+};
+globalThis.clearInterval = (timer) => {
+ if (timer) timer.cleared = true;
+};
+
+try {
+ // Periodic log cleanup is tracked and cleared with the existing timer system.
+ {
+ const manager = {
+ _activeTimers: new Set(),
+ _startKeySecurityMonitoring() {},
+ _verifyAPIIntegrity() { return true; },
+ _startSecurityMonitoring() {},
+ _cleanupLogs() {},
+ _secureLog() {}
+ };
+ manager._trackActiveTimer = EnhancedSecureWebRTCManager.prototype._trackActiveTimer;
+ EnhancedSecureWebRTCManager.prototype._finalizeSecureInitialization.call(manager);
+ assert.equal(manager._activeTimers.size, 1);
+ const logTimer = manager._logCleanupInterval;
+ EnhancedSecureWebRTCManager.prototype._stopAllTimers.call(manager);
+ assert.equal(logTimer.cleared, true);
+ }
+
+ // Deferred file-transfer retries are tracked, cleared, and cannot re-run after session shutdown.
+ {
+ let initCalls = 0;
+ const manager = {
+ _sessionAlive: true,
+ _activeTimers: new Set(),
+ _fileTransferInitRetryTimers: new Set(),
+ fileTransferSystem: null,
+ dataChannel: { readyState: 'open' },
+ isVerified: false,
+ _secureLog() {},
+ _trackActiveTimer: EnhancedSecureWebRTCManager.prototype._trackActiveTimer,
+ _untrackActiveTimer: EnhancedSecureWebRTCManager.prototype._untrackActiveTimer,
+ _scheduleFileTransferInitRetry: EnhancedSecureWebRTCManager.prototype._scheduleFileTransferInitRetry,
+ initializeFileTransfer() {
+ initCalls += 1;
+ }
+ };
+ EnhancedSecureWebRTCManager.prototype.initializeFileTransfer.call(manager);
+ const retryTimer = [...manager._fileTransferInitRetryTimers][0];
+ manager._sessionAlive = false;
+ EnhancedSecureWebRTCManager.prototype._stopAllTimers.call(manager);
+ if (!retryTimer.cleared) retryTimer.callback();
+ assert.equal(retryTimer.cleared, true);
+ assert.equal(manager._fileTransferInitRetryTimers.size, 0);
+ assert.equal(initCalls, 0);
+ }
+
+ // Repeated peer-disconnect notifications schedule only one delayed cleanup, and cleanup is cancelled on disconnect.
+ {
+ let disconnectCalls = 0;
+ const manager = {
+ _sessionAlive: true,
+ _activeTimers: new Set(),
+ peerDisconnectNotificationSent: false,
+ _peerDisconnectCleanupTimer: null,
+ deliverMessageToUI() {},
+ onStatusChange() {},
+ stopHeartbeat() {},
+ onKeyExchange() {},
+ onVerificationRequired() {},
+ disconnect() { disconnectCalls += 1; },
+ _secureLog() {},
+ _trackActiveTimer: EnhancedSecureWebRTCManager.prototype._trackActiveTimer,
+ _untrackActiveTimer: EnhancedSecureWebRTCManager.prototype._untrackActiveTimer
+ };
+ EnhancedSecureWebRTCManager.prototype.handlePeerDisconnectNotification.call(manager, { reason: 'connection_lost' });
+ EnhancedSecureWebRTCManager.prototype.handlePeerDisconnectNotification.call(manager, { reason: 'connection_lost' });
+ const scheduled = timers.filter(timer => timer.kind === 'timeout' && timer.delay === 2000);
+ assert.equal(scheduled.length, 1);
+
+ manager._sessionAlive = false;
+ EnhancedSecureWebRTCManager.prototype._stopAllTimers.call(manager);
+ if (!scheduled[0].cleared) scheduled[0].callback();
+ assert.equal(scheduled[0].cleared, true);
+ assert.equal(disconnectCalls, 0);
+ }
+} finally {
+ globalThis.setTimeout = realSetTimeout;
+ globalThis.clearTimeout = realClearTimeout;
+ globalThis.setInterval = realSetInterval;
+ globalThis.clearInterval = realClearInterval;
+}
+
+console.log('Timer lifecycle tests passed');
diff --git a/tests/webrtc-privacy-mode.test.mjs b/tests/webrtc-privacy-mode.test.mjs
new file mode 100644
index 0000000..2147802
--- /dev/null
+++ b/tests/webrtc-privacy-mode.test.mjs
@@ -0,0 +1,60 @@
+import assert from 'node:assert/strict';
+
+globalThis.window = {
+ EnhancedSecureCryptoUtils: {},
+ DEBUG_MODE: true,
+ DEVELOPMENT_MODE: true,
+ location: { hostname: 'localhost', search: '?debug' },
+ webpackHotUpdate: {}
+};
+
+const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
+
+function fake(config = {}) {
+ return {
+ _config: {
+ webrtc: {
+ relayOnly: config.relayOnly ?? false,
+ iceServers: config.iceServers ?? [{ urls: 'stun:stun.example.test:3478' }]
+ }
+ },
+ _ipLeakWarningShown: false,
+ delivered: [],
+ deliverMessageToUI(message, type) {
+ this.delivered.push({ message, type });
+ },
+ _hasTurnServer: EnhancedSecureWebRTCManager.prototype._hasTurnServer
+ };
+}
+
+// Default mode preserves current behavior.
+{
+ const manager = fake();
+ const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
+ assert.equal(config.iceTransportPolicy, undefined);
+ assert.equal(config.iceServers[0].urls, 'stun:stun.example.test:3478');
+}
+
+// Privacy mode uses relay-only transport.
+{
+ const manager = fake({ relayOnly: true, iceServers: [{ urls: 'turn:turn.example.test:3478' }] });
+ const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
+ assert.equal(config.iceTransportPolicy, 'relay');
+}
+
+// Missing TURN warns clearly.
+{
+ const manager = fake();
+ EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
+ assert.match(manager.delivered[0].message, /may expose IP addresses/i);
+}
+
+// STUN-only config does not claim IP protection, even with privacy mode selected.
+{
+ const manager = fake({ relayOnly: true, iceServers: [{ urls: 'stun:stun.example.test:3478' }] });
+ assert.equal(EnhancedSecureWebRTCManager.prototype._hasTurnServer.call(manager), false);
+ EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
+ assert.match(manager.delivered[0].message, /STUN alone does not hide IP addresses/i);
+}
+
+console.log('WebRTC privacy mode tests passed');