48 Commits
Author SHA1 Message Date
lockbitchat 3c2bac588c ci: switch mirror workflow to HTTPS token authentication
CodeQL Analysis / Analyze CodeQL (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
Mirror to PrivacyGuides / mirror (push) Has been cancelled
2025-10-20 12:51:27 -04:00
lockbitchat 2ae65eb749 ci: switch mirror workflow to HTTPS token authentication 2025-10-20 12:44:59 -04:00
lockbitchat 4a3249b2a6 test 2025-10-20 12:39:55 -04:00
lockbitchat 456545f055 Add GitHub Actions workflow to mirror repository to PrivacyGuides 2025-10-20 12:34:10 -04:00
lockbitchat 40381cc0a1 Increase session timeout to 60min and inactivity to 30min
Fix sendMessage isUnlocked check to prevent immediate lock
Move session extension logic to SecureMasterKeyManager
Improve error messages
Keep validation of crypto keys before encryption
2025-10-20 02:06:42 -04:00
lockbitchat 0d7835cfa2 fix: resolve incomplete multi-character sanitization in sanitizeMessage
Use stable replacement loop for all multi-character patterns to prevent malicious input from reappearing after sanitization.
2025-10-20 01:20:02 -04:00
lockbitchat 7604381584 fix: eliminate division on crypto random in getSafeRandomFloat 2025-10-20 01:10:25 -04:00
lockbitchat a7a5861e0a fix: replace modulo with bitwise mask in crypto random 2025-10-20 01:02:07 -04:00
lockbitchat 63a19e6a4c fix: resolve CodeQL biased crypto random warning
Use unsigned right shift (>>>) to properly handle 32-bit random values and eliminate statistical bias.
2025-10-20 00:51:03 -04:00
lockbitchat b18e943abd Fix cryptographic random bias in fingerprint mask generation 2025-10-20 00:42:12 -04:00
lockbitchat 9c1258cd1e Fix cryptographic random bias in fingerprint mask generation 2025-10-20 00:42:03 -04:00
lockbitchat 60e4bb6b8a Fix cryptographic random bias in getUnbiasedRandomInRange
Reworked getUnbiasedRandomInRange() to eliminate modulo bias by using
rejection sampling combined with bucket indexing instead of the `%` operator.
Added getUnbiasedRandomFloat() to safely generate unbiased floats for
sizeVariation and sizeMultiplier.
2025-10-20 00:34:17 -04:00
lockbitchat 4233ba3d7e Fix cryptographic random bias (CodeQL js/biased-cryptographic-random)
Reimplemented getUnbiasedRandomInRange() using rejection sampling for all range sizes to eliminate modulo bias and ensure uniform cryptographically secure randomness.
2025-10-20 00:25:50 -04:00
lockbitchat 333c4b87b5 fix: improve sanitization to prevent XSS
- Now the sanitization function protects against:
  - Nested script tags: <scrip<script>alert("XSS")</script>t>
  - HTML comments containing scripts: <!-- <script>alert("XSS")</script> -->
  - Multiple overlapping tags: <script><script>alert("XSS")</script></script>
  - Attributes in closing tags: </script foo="bar">
  - Complex nested structures combining different tags
  - All known XSS vectors
2025-10-19 23:55:41 -04:00
lockbitchat 5096a09811 fix: improve sanitization to prevent XSS
- Now the sanitization function protects against:
  - Nested script tags: <scrip<script>alert("XSS")</script>t>
  - HTML comments containing scripts: <!-- <script>alert("XSS")</script> -->
  - Multiple overlapping tags: <script><script>alert("XSS")</script></script>
  - Attributes in closing tags: </script foo="bar">
  - Complex nested structures combining different tags
  - All known XSS vectors
2025-10-19 23:54:26 -04:00
lockbitchat 5c658cdd6c Security updates for CodeQL 2025-10-19 23:39:14 -04:00
lockbitchat e209d3e6f5 fix: improve sanitization to prevent XSS
- Now the sanitization function protects against:
  - Nested script tags: <scrip<script>alert("XSS")</script>t>
  - HTML comments containing scripts: <!-- <script>alert("XSS")</script> -->
  - Multiple overlapping tags: <script><script>alert("XSS")</script></script>
  - Attributes in closing tags: </script foo="bar">
  - Complex nested structures combining different tags
  - All known XSS vectors
2025-10-19 23:23:58 -04:00
lockbitchat 49e5a054cb fix: security and build improvements
- Fixed biased cryptographic random with modulo operations
- Corrected biased cryptographic random with addition operations
- Resolved infinite page loading issue
- Rebuilt all distribution files
2025-10-19 23:05:24 -04:00
lockbitchat 9d0372484a Add CodeQL security scanning workflow 2025-10-19 22:08:31 -04:00
lockbitchat 69fa5ffc28 Add CodeQL security scanning workflow 2025-10-19 22:04:07 -04:00
lockbitchat ed169b4c8c Add CodeQL security scanning workflow 2025-10-19 22:02:28 -04:00
lockbitchat 906562333e fix(ios): prevent chat crash when Notifications API is unavailable on iPhones
- Guarded all Notification API usage to avoid ReferenceError on iOS Safari.
- Set default permission to 'denied' when Notification is undefined.
- Added early return in notification flow when Notifications API is unavailable.
- Wrapped Notification.permission, requestPermission(), and new Notification(...) with typeof checks.
- Updated SecureNotificationManager and app.jsx to degrade gracefully.
- Verified build passes and chat loads correctly on iOS without notifications.
2025-10-19 20:51:44 -04:00
lockbitchat 4e7f5867b5 feat: implement automatic key generation on channel creation
- Remove manual "Create secure keys" button requirement
- Add automatic key generation trigger when "Create channel" is clicked
- Implement loading state with spinner animation during key generation
- Add isGeneratingKeys state management across components
- Pass handleCreateOffer function as prop to EnhancedConnectionSetup
- Update UI to show "Generating secure keys..." message during process
- Ensure proper state cleanup on disconnect and data clearing
- Improve user experience by eliminating extra click step in channel creation flow
2025-10-19 15:23:02 -04:00
lockbitchat 5ddfd1f5b3 fix(ios): prevent chat crash when Notifications API is unavailable on iPhones
- Guarded all Notification API usage to avoid ReferenceError on iOS Safari.
- Set default permission to 'denied' when Notification is undefined.
- Added early return in notification flow when Notifications API is unavailable.
- Wrapped Notification.permission, requestPermission(), and new Notification(...) with typeof checks.
- Updated SecureNotificationManager and app.jsx to degrade gracefully.
- Verified build passes and chat loads correctly on iOS without notifications.
2025-10-17 03:49:33 -04:00
lockbitchat 1acbc12a92 update version to v4.4.18 2025-10-15 20:23:50 -04:00
lockbitchat 3ed8766fc9 v4.3.120 update 2025-10-15 20:15:41 -04:00
lockbitchat b087adfecc feat: implement secure browser notifications system
- Added SecureNotificationManager with cross-browser support (Chrome, Firefox, Safari, Edge)
- Integrated WebRTC message notifications with tab visibility detection
- Implemented XSS protection, URL validation, and rate limiting
- Notifications shown only when chat tab is inactive
- Enforced HTTPS and user gesture requirements
2025-10-15 19:58:28 -04:00
lockbitchat 5b5cc67fdc Push to Codeberg 2025-10-15 04:55:22 -04:00
lockbitchat 93e4b7ad19 test 2025-10-15 04:51:48 -04:00
lockbitchat fce18d27a5 Push to PrivacyGuides 2025-10-15 04:41:05 -04:00
lockbitchat 03e06f59dc Push to Codeberg 2025-10-15 04:30:18 -04:00
lockbitchat 5df6cb0d16 test ssh 2025-10-15 04:25:49 -04:00
lockbitchat 3730f46a08 test remote 2025-10-15 04:23:51 -04:00
lockbitchat abd6f9a7f5 Test mirror 2025-10-15 04:21:20 -04:00
lockbitchat 99469e5bd5 Remove experimental Bluetooth key transfer module
- Deleted BluetoothKeyTransfer.js and related classes
- Removed BluetoothKeyTransfer.jsx UI component
- Cleaned up Bluetooth imports from app-boot.js and bootstrap-modules.js
- Removed Bluetooth buttons and handlers from main app
- Eliminated all Bluetooth functionality due to Web Bluetooth API limitations
- Browsers cannot create GATT servers or advertise devices
- Reduced bundle size by ~78KB
- Application now focuses on supported browser technologies (QR codes, manual key exchange, WebRTC)
2025-10-15 04:13:14 -04:00
lockbitchat 2306b32482 Remove experimental Bluetooth key transfer module
- Deleted BluetoothKeyTransfer.js and related classes
- Removed BluetoothKeyTransfer.jsx UI component
- Cleaned up Bluetooth imports from app-boot.js and bootstrap-modules.js
- Removed Bluetooth buttons and handlers from main app
- Eliminated all Bluetooth functionality due to Web Bluetooth API limitations
- Browsers cannot create GATT servers or advertise devices
- Reduced bundle size by ~78KB
- Application now focuses on supported browser technologies (QR codes, manual key exchange, WebRTC)
2025-10-15 01:47:28 -04:00
lockbitchat cbb6a8fa31 Remove experimental Bluetooth key transfer module
- Deleted BluetoothKeyTransfer.js and related classes
- Removed BluetoothKeyTransfer.jsx UI component
- Cleaned up Bluetooth imports from app-boot.js and bootstrap-modules.js
- Removed Bluetooth buttons and handlers from main app
- Eliminated all Bluetooth functionality due to Web Bluetooth API limitations
- Browsers cannot create GATT servers or advertise devices
- Reduced bundle size by ~78KB
- Application now focuses on supported browser technologies (QR codes, manual key exchange, WebRTC)
2025-10-15 01:46:54 -04:00
lockbitchat 4be6fc55f5 Remove experimental Bluetooth key transfer module
- Deleted BluetoothKeyTransfer.js and related classes
- Removed BluetoothKeyTransfer.jsx UI component
- Cleaned up Bluetooth imports from app-boot.js and bootstrap-modules.js
- Removed Bluetooth buttons and handlers from main app
- Eliminated all Bluetooth functionality due to Web Bluetooth API limitations
- Browsers cannot create GATT servers or advertise devices
- Reduced bundle size by ~78KB
- Application now focuses on supported browser technologies (QR codes, manual key exchange, WebRTC)
2025-10-15 01:46:36 -04:00
lockbitchat cb62d8eb3b fix: resolve ReferenceError issues and improve disconnect functionality
- Add missing sessionTimeLeft state variable to fix ReferenceError in handleDisconnect
- Fix ReferenceError in handleCreateAnswer by correcting variable scope (e.target.value -> answerInput)
- Add type checking for answerData.trim() to prevent TypeError in shouldPreserveAnswerData
- Standardize data types by replacing null assignments with empty strings for offerData/answerData
- Improve handleDisconnect function with proper error handling and comprehensive state cleanup
- Add try-catch wrapper around disconnect operations for better error handling
- Ensure complete metadata removal and connection termination on disconnect
- Fix Bluetooth transfer modal opening with proper role-based event dispatching
2025-10-14 22:51:48 -04:00
lockbitchat ea8cf47118 Table update 2025-10-13 11:54:05 -04:00
lockbitchat d292c84829 session bug fix 2025-10-13 11:13:11 -04:00
lockbitchat 45de8ce285 refactor: implement minimal PWA caching strategy
- Cache only essential PWA assets (manifest, icons, core scripts)
- Use Network First for all other requests
- Remove aggressive caching of UI components and styles
- Preserve PWA installation while minimizing cache footprint
2025-10-13 01:38:41 -04:00
lockbitchat 7af8f528ff refactor: implement minimal PWA caching strategy
- Cache only essential PWA assets (manifest, icons, core scripts)
- Use Network First for all other requests
- Remove aggressive caching of UI components and styles
- Preserve PWA installation while minimizing cache footprint
2025-10-13 01:35:32 -04:00
lockbitchat d24d578321 feat: Add comprehensive iOS PWA splash screen support
- Add complete splash screen configuration for all iOS devices
- Support iPhone 17 Pro Max through iPhone 6 series
- Support all iPad models with landscape/portrait orientations
- Use proper media queries and generated splash images
- Fix iOS splash screen caching and display issues
2025-10-09 20:49:04 -04:00
lockbitchat dd9f13ab59 Add Browser Extension block 2025-10-09 05:09:17 -04:00
lockbitchat 2e5b078bc2 UX/UI updates and bug fixes 2025-10-08 17:23:54 -04:00
lockbitchat 75fa1cd27d Qr generator create bug fix 2025-10-08 01:24:04 -04:00
lockbitchat 60db79d2ae v4.3.120 update 2025-10-08 00:17:53 -04:00
74 changed files with 3538 additions and 1929 deletions
+32
View File
@@ -0,0 +1,32 @@
name: "CodeQL Analysis"
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
- cron: '0 3 * * *'
permissions:
security-events: write
jobs:
analyze:
name: Analyze CodeQL
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript,typescript
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
+29
View File
@@ -0,0 +1,29 @@
name: Mirror to Codeberg
on:
push:
branches:
- main
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.CODEBERG_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan codeberg.org >> ~/.ssh/known_hosts
- name: Push to Codeberg
run: |
git remote remove codeberg || true
git remote add codeberg git@codeberg.org:SecureBitChat/securebit-chat.git
git push codeberg main --force
@@ -0,0 +1,25 @@
name: Mirror to PrivacyGuides
on:
push:
branches:
- main
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Push to PrivacyGuides (HTTPS)
env:
PRIVACYGUIDES_TOKEN: ${{ secrets.PRIVACYGUIDES_TOKEN }}
run: |
git remote remove privacyguides || true
git remote add privacyguides https://SecureBitChat:${PRIVACYGUIDES_TOKEN}@code.privacyguides.dev/SecureBitChat/securebit-chat.git
git push privacyguides main --force
#New
+187 -514
View File
@@ -1,167 +1,99 @@
# SecureBit.chat v4.2.12 - ECDH + DTLS + SAS
# SecureBit.chat v4.4.18
<div align="center">
![SecureBit.chat Logo](logo/favicon.ico)
**The world's first P2P messenger with ECDH + DTLS + SAS security and military-grade cryptography**
**World's first P2P messenger with ECDH + DTLS + SAS security and military-grade cryptography**
[![Latest Release](https://img.shields.io/github/v/release/SecureBitChat/securebit-chat?style=for-the-badge&logo=github&color=orange)](https://github.com/SecureBitChat/securebit-chat/releases/latest)
[![Live Demo](https://img.shields.io/badge/🌐_Live_Demo-Try_Now-success?style=for-the-badge)](https://securebitchat.github.io/securebit-chat/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
[![Security: ECDH+DTLS+SAS](https://img.shields.io/badge/Security-ECDH%2BDTLS%2BSAS-red.svg?style=for-the-badge)]()
[🚀 Try Now](https://securebitchat.github.io/securebit-chat/) • [📖 Documentation](#-quick-start) • [🔒 Security](#-security) • [🤝 Contribute](#-contributing)
</div>
---
## ✨ What's New in v4.2.12 - ECDH + DTLS + SAS
## 🎯 Overview
### 🛡️ Revolutionary ECDH + DTLS + SAS Security System
* **Complete PAKE removal** - Eliminated libsodium dependency and PAKE-based authentication
* **ECDH key exchange** - Elliptic Curve Diffie-Hellman for secure key establishment
* **DTLS fingerprint verification** - Transport layer security validation using WebRTC certificates
* **SAS (Short Authentication String)** - 7-digit verification code for MITM attack prevention
* **Single code generation** - SAS generated once on Offer side and shared with Answer side
* **Mutual verification** - Both users must confirm the same SAS code to establish connection
* **Enhanced MITM protection** - Multi-layer defense against man-in-the-middle attacks
* **Real-time verification** - Immediate feedback on connection security status
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.
### 🔒 ASN.1 Full Structure Validation (BREAKING CHANGE)
* **Complete ASN.1 DER parser** for comprehensive key structure verification
* **OID validation** for algorithms and curves (P-256/P-384 only)
* **EC point format verification** (uncompressed format 0x04)
* **SPKI structure validation** with 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** from P-384 to P-256 for compatibility
* **High-risk vulnerability fix** where keys with valid headers but modified data could be accepted
### Key Features
### 🔐 Enhanced Key Security
* **Full structural validation** according to PKCS standards
* **Complete rewrite** of `validateKeyStructure()` method
* **Enhanced validation** for all key import/export operations
* **Military-grade key verification** exceeding previous standards
### 🔒 Comprehensive Connection Security Overhaul
* **Advanced mutex framework** with 15-second timeout protection
* **Race condition prevention** through atomic key generation
* **Multi-stage validation pipeline** with automatic rollback
* **Enhanced MITM protection** with unique encryption key fingerprints
* **Session ID anti-hijacking** with mutual authentication challenges
* **Package integrity validation** for all connection operations
### 🔐 Secure Key Storage System
* **WeakMap-based isolation** for all cryptographic keys
* **Private key storage** replacing public key properties
* **Secure access methods** with validation and rotation
* **Emergency key wipe** capabilities for threat response
* **Key security monitoring** with lifetime limits enforcement
* **Backward compatibility** maintained through getters/setters
### 🛡️ Production-Ready Security Logging
* **Environment-aware logging** (production vs development)
* **Data sanitization** preventing sensitive information leaks
* **Rate limiting** and automatic memory cleanup
* **Secure debugging** without exposing encryption keys
* **Privacy protection** while maintaining useful diagnostics
### 📱 Progressive Web App (PWA)
* **Install directly** on mobile and desktop devices
* **Offline mode support** with session persistence
* **Improved performance** through smart caching and service workers
* **Native app experience** without app store requirements
### 📂 Secure File Transfer
* **End-to-end encrypted** file transfers over pure P2P WebRTC channels
* **File chunking** with individual encryption per block
* **Hash validation** for every chunk to prevent tampering or MITM attacks
* **Automatic recovery** for lost packets and interrupted transfers
* **AES-GCM 256-bit + ECDH P-384** encryption for files
* **SHA-384 checksums** for integrity enforcement
### 🔍 Enhanced Security Testing
* **Comprehensive data leakage testing** of chat sessions
* **Verified MITM and replay attack resistance**
* **Enhanced memory cleanup algorithms** for session termination
* **Isolated file streams** separated from chat channels
- 🔐 **19-Layer Military Security** - ECDH + DTLS + SAS verification
- 🌐 **Pure P2P Architecture** - No servers, truly decentralized
- 📱 **Progressive Web App** - Install like a native app
- 📂 **Secure File Transfer** - End-to-end encrypted P2P file sharing
- 🔔 **Smart Notifications** - Browser alerts only when away
- 🎭 **Complete Anonymity** - Zero data collection, no registration
---
## 🚀 Try It Now
## ✨ What's New in v4.4.18
### 🌐 [Live Demo — SecureBit.chat](https://securebitchat.github.io/securebit-chat/)
### 🔔 Secure Browser Notifications
- Smart delivery when user is away from chat tab
- Cross-browser compatibility (Chrome, Firefox, Safari, Edge)
- Page Visibility API integration with proper tab focus detection
- XSS protection with text sanitization and URL validation
- Rate limiting and spam protection
- Automatic cleanup and memory management
*No installation required — works directly in your browser with military-grade encryption.*
### 🧹 Code Cleanup & Architecture
- Removed session management logic for simplified architecture
- Eliminated experimental Bluetooth module
- Cleaned debug logging from production code
- Removed test functions from production build
- Enhanced error handling for production stability
**New:** Install as PWA for native app experience on mobile and desktop!
### 🛡️ 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
---
## What Makes SecureBit.chat Unique
## 🏆 Why SecureBit.chat?
### 🏆 Industry Leader
### Security Comparison
* **Dominates in 11/15 security categories** vs Signal, Threema, Session
* **First messenger** with enhanced ECDH + DTLS + SAS security
* **Military-grade cryptography** exceeding government standards
* **Zero servers** — truly decentralized P2P architecture
* **PWA technology** — install like native apps without app stores
| Feature | **SecureBit.chat** | Signal | Threema | Session |
|---------|-------------------|--------|---------|---------|
| Architecture | 🏆 Pure P2P WebRTC | ❌ Centralized | ❌ Centralized | ⚠️ Onion network |
| File Transfer | 🏆 P2P encrypted | ✅ Via servers | ✅ Via servers | ✅ Via servers |
| PWA Support | 🏆 Full PWA | ❌ None | ❌ None | ❌ None |
| Registration | 🏆 Anonymous | ❌ Phone required | ✅ ID generated | ✅ Random ID |
| Traffic Obfuscation | 🏆 Advanced | ❌ None | ❌ None | ✅ Onion routing |
| Data Storage | 🏆 Zero storage | ⚠️ Local database | ⚠️ Local + backup | ⚠️ Local database |
| ASN.1 Validation | 🏆 Complete | ⚠️ Basic | ⚠️ Basic | ⚠️ Basic |
**Legend:** 🏆 Category Leader • ✅ Excellent • ⚠️ Partial/Limited • ❌ Not Available
### 🔐 15-Layer Military Security
### 19-Layer Military Security
1. **WebRTC DTLS** — Transport encryption
2. **ECDH P-384** — Perfect forward secrecy
3. **AES-GCM 256** — Authenticated encryption
4. **ECDSA P-384** — Message integrity
5. **Replay protection** — Timestamp validation
6. **Key rotation** — Every 5 minutes/100 messages
7. **MITM verification** — Out-of-band codes
8. **Traffic obfuscation** — Pattern masking
9. **Metadata protection** — Zero leakage
10. **Memory protection** — No persistent storage
11. **Hardware security** — Non-extractable keys
12. **Session isolation** — Complete cleanup
13. **Mutex framework** — Race condition protection
14. **Secure key storage** WeakMap isolation
15. **Production logging** — Data sanitization
16. **ASN.1 validation** — Complete key structure verification
17. **OID validation** — Algorithm and curve verification
18. **EC point validation** — Format and structure verification
### 🎭 Advanced Privacy
* **Complete anonymity** — no registration required
* **Zero data collection** — messages only in browser memory
* **Traffic analysis resistance** — fake traffic generation
* **Censorship resistance** — no servers to block
* **Instant anonymous channels** — connect in seconds
* **Secure file transfers** — encrypted P2P file sharing
---
## 🛡️ Security Comparison
| Feature | **SecureBit.chat** | Signal | Threema | Session |
| --------------------------- | ----------------------------- | ---------------------------- | --------------------- | ---------------------- |
| **Architecture** | 🏆 Pure P2P WebRTC | ❌ Centralized servers | ❌ Centralized servers | ⚠️ Onion network |
| **Payment Integration** | ❌ None | ❌ None | ❌ None | ❌ None |
| **File Transfer** | 🏆 P2P encrypted + chunked | ✅ Encrypted via servers | ✅ Encrypted via servers | ✅ Encrypted via servers |
| **PWA Support** | 🏆 Full PWA installation | ❌ None | ❌ None | ❌ None |
| **Registration** | 🏆 Anonymous | ❌ Phone required | ✅ ID generated | ✅ Random ID |
| **Traffic Obfuscation** | 🏆 Advanced fake traffic | ❌ None | ❌ None | ✅ Onion routing |
| **Censorship Resistance** | 🏆 Hard to block | ⚠️ Blocked in some countries | ⚠️ May be blocked | ✅ Onion routing |
| **Data Storage** | 🏆 Zero storage | ⚠️ Local database | ⚠️ Local + backup | ⚠️ Local database |
| **Economic Model** | ✅ Open-source | ⚠️ Donations dependent | ✅ Onetime purchase | ⚠️ Donations dependent |
| **Metadata Protection** | 🏆 Full encryption | ⚠️ Sealed Sender (partial) | ⚠️ Minimal metadata | ✅ Onion routing |
| **Key Security** | 🏆 Nonextractable + hardware | ✅ Secure storage | ✅ Local storage | ✅ Secure storage |
| **Perfect Forward Secrecy** | 🏆 Auto rotation (5 min) | ✅ Double Ratchet | ⚠️ Partial (groups) | ✅ Session Ratchet |
| **Open Source** | 🏆 100% + auditable | ✅ Fully open | ⚠️ Only clients | ✅ Fully open |
| **ASN.1 Validation** | 🏆 Complete structure verification | ⚠️ Basic validation | ⚠️ Basic validation | ⚠️ Basic validation |
**Legend:** 🏆 Category Leader | ✅ Excellent | ⚠️ Partial/Limited | ❌ Not Available
1. WebRTC DTLS transport encryption
2. ECDH P-384 perfect forward secrecy
3. AES-GCM 256 authenticated encryption
4. ECDSA P-384 message integrity
5. Replay protection with timestamp validation
6. Automatic key rotation (every 5 min/100 messages)
7. MITM verification with out-of-band codes
8. Traffic obfuscation and pattern masking
9. Complete metadata protection
10. Memory protection with no persistent storage
11. Hardware security with non-extractable keys
12. Session isolation and complete cleanup
13. Mutex framework for race condition protection
14. Secure key storage with WeakMap isolation
15. Production logging with data sanitization
16. ASN.1 complete key structure verification
17. OID validation for algorithms and curves
18. EC point format and structure verification
19. Smart notifications with XSS protection
---
@@ -169,21 +101,21 @@
### Option 1: Use Online (Recommended)
1. **Visit:** [https://securebitchat.github.io/securebit-chat/](https://securebitchat.github.io/securebit-chat/)
2. **Install PWA:** Click "Install" button for native app experience
3. **Choose:** *Create Channel* or *Join Channel*
4. **Complete:** Secure key exchange with verification
5. **Verify:** Security codes and start a secure chat
6. **Communicate:** With militarygrade encryption + secure file transfers
1. Visit [securebitchat.github.io/securebit-chat](https://securebitchat.github.io/securebit-chat/)
2. Install PWA by clicking "Install" button for native app experience
3. Choose "Create Channel" or "Join Channel"
4. Complete secure key exchange with verification
5. Verify security codes and start chatting
6. Communicate with military-grade encryption
### Option 2: SelfHost
### Option 2: Self-Host
```bash
# Clone repository
git clone https://github.com/SecureBitChat/securebit-chat.git
cd securebit-chat
# Serve locally (choose one method)
# Serve locally
python -m http.server 8000 # Python
npx serve . # Node.js
php -S localhost:8000 # PHP
@@ -197,32 +129,15 @@ open http://localhost:8000
## 📂 Secure File Transfer
### Features
- **P2P Direct Transfer** - No servers, direct WebRTC channels
- **Military-Grade Encryption** - AES-GCM 256-bit + ECDH P-384
- **Chunk-Level Security** - Individual encryption per file chunk
- **Hash Validation** - SHA-384 checksums prevent tampering
- **Automatic Recovery** - Retry mechanisms for interruptions
- **Stream Isolation** - Separate channels from chat messages
* **P2P Direct Transfer** — No servers involved, direct WebRTC channels
* **Military-Grade Encryption** — AES-GCM 256-bit + ECDH P-384
* **Chunk-Level Security** — Each file chunk individually encrypted
* **Hash Validation** — SHA-384 checksums prevent tampering
* **Automatic Recovery** — Retry mechanisms for interrupted transfers
* **Stream Isolation** — Separate channels from chat messages
### Supported File Types
* **Documents:** PDF, DOC, TXT, MD
* **Images:** JPG, PNG, GIF, WEBP
* **Archives:** ZIP, RAR, 7Z
* **Media:** MP3, MP4, AVI (size limits apply)
* **General:** Any file type up to size limits
### Security Guarantees
* End-to-end encryption with perfect forward secrecy
* MITM attack prevention through hash validation
* Zero server storage — files transfer directly P2P
* Complete cleanup after transfer completion
---
### Supported Files
Documents (PDF, DOC, TXT), Images (JPG, PNG, GIF), Archives (ZIP, RAR), Media (MP3, MP4), and any file type up to size limits.
---
@@ -231,426 +146,182 @@ open http://localhost:8000
### Cryptographic Stack
```
📂 File Transfer Layer: AES-GCM 256-bit + SHA-384 + Chunking
🔐 Application Layer: AES-GCM 256-bit + ECDSA P-384
🔑 Key Exchange: ECDH P-384 (Perfect Forward Secrecy)
🛡️ Transport Layer: WebRTC DTLS 1.2
🌐 Network Layer: P2P WebRTC Data Channels
📱 PWA Layer: Service Workers + Cache API
🔒 ASN.1 Layer: Complete DER parsing and validation
📂 File Transfer: AES-GCM 256-bit + SHA-384 + Chunking
🔐 Application: AES-GCM 256-bit + ECDSA P-384
🔑 Key Exchange: ECDH P-384 (Perfect Forward Secrecy)
🛡️ Transport: WebRTC DTLS 1.2
🌐 Network: P2P WebRTC Data Channels
📱 PWA: Service Workers + Cache API
🔒 Validation: Complete ASN.1 DER parsing
```
### Security Standards
* NIST SP 80056A — ECDH Key Agreement
* NIST SP 800186 — Elliptic Curve Cryptography
* RFC 6090 — Fundamental ECC Algorithms
* RFC 8446 — TLS 1.3 for WebRTC
* RFC 3874 — SHA-384 Hash Algorithm
* RFC 5280 — X.509 Certificate Structure
* RFC 5480 — Elliptic Curve Subject Public Key Information
### Standards Compliance
- NIST SP 800-56A (ECDH Key Agreement)
- NIST SP 800-186 (Elliptic Curve Cryptography)
- RFC 8446 (TLS 1.3 for WebRTC)
- RFC 5280 (X.509 Certificate Structure)
- RFC 5480 (EC Subject Public Key Information)
### Browser Requirements
* Modern browser with WebRTC support (Chrome 60+, Firefox 60+, Safari 12+)
* HTTPS connection (required for WebRTC and PWA)
* JavaScript enabled
* Service Worker support for PWA features
Modern browser with WebRTC support (Chrome 60+, Firefox 60+, Safari 12+), HTTPS connection, JavaScript enabled, Service Worker support for PWA.
---
## 🗺️ Development Roadmap
## 🗺️ Roadmap
**Current:** v4.02.442 — ASN.1 Validation & Enhanced Security Edition
**Current: v4.4.18** - Browser Notifications & Code Cleanup
* Complete ASN.1 DER parser for key structure validation
* Enhanced key security with OID and EC point verification
* Breaking changes for improved security standards
* Full PKCS compliance for all cryptographic operations
**Next Releases:**
**Previous:** v4.01.441 — PWA & File Transfer Edition ✅
- **v4.5 (Q2 2025)** - Mobile & Desktop Apps
- Native mobile applications (iOS/Android)
- Electron desktop application
- Push notifications and cross-device sync
* Progressive Web App installation
* Secure P2P file transfer system
* Enhanced security testing and MITM protection
* Improved memory cleanup algorithms
- **v5.0 (Q4 2025)** - Quantum-Resistant Edition
- CRYSTALS-Kyber post-quantum key exchange
- SPHINCS+ post-quantum signatures
- Hybrid classical + post-quantum schemes
**Next Releases**
- **v5.5 (Q2 2026)** - Group Communications
- P2P group chats (up to 8 participants)
- Mesh networking topology
- Anonymous group administration
### v4.5 (Q2 2025) — Mobile & Desktop Apps
* Native mobile applications (iOS/Android)
* Electron desktop application
* Push notifications
* Crossdevice synchronization
* Enhanced PWA features
### v5.0 (Q4 2025) — QuantumResistant Edition
* CRYSTALSKyber postquantum key exchange
* SPHINCS+ postquantum signatures
* Hybrid classical + postquantum schemes
* Quantumsafe migration path
### v5.5 (Q2 2026) — Group Communications
* P2P group chats (up to 8 participants)
* Mesh networking topology
* Anonymous group administration
* Group file sharing
### v6.0 (2027) — Decentralized Network
* DHTbased peer discovery
* Builtin onion routing
* Decentralized identity system
* Node incentive mechanisms
- **v6.0 (2027)** - Decentralized Network
- DHT-based peer discovery
- Built-in onion routing
- Decentralized identity system
---
## 🧪 Development
## 💻 Development
### Project Structure
```
securebit-chat/
├── index.html # Main application
├── manifest.json # PWA manifest
├── sw.js # Service worker
├── browserconfig.xml # Browser configuration for PWA
├── index.html # Main application
├── manifest.json # PWA manifest
├── sw.js # Service worker
├── src/
│ ├── components/ui/ # React UI components
│ ├── DownloadApps.js # PWA download/install component
│ │ ── FileTransfer.js # File transfer UI component
│ └── ... # Other UI components
│ ├── crypto/ # Cryptographic utilities
│ └── ASN1Validator.js # Complete ASN.1 DER parser
│ ├── network/ # WebRTC P2P manager
── session/ # Payment session manager
│ ├── transfer/ # File transfer system
│ │ └── EnhancedSecureFileTransfer.js # Secure P2P file transfer
│ ├── pwa/ # PWA management
│ │ ├── install-prompt.js # PWA installation prompts
│ │ ├── offline-manager.js # Offline mode management
│ │ └── pwa-manager.js # PWA lifecycle management
│ └── styles/ # CSS styling
│ ├── pwa.css # PWA-specific styles
│ └── ... # Other stylesheets
├── logo/ # Wallet logos and icons
├── docs/ # Documentation
└── README.md # This file
│ ├── components/ui/ # React UI components
│ ├── crypto/ # Cryptographic utilities
│ │ ── ASN1Validator.js # ASN.1 DER parser
├── network/ # WebRTC P2P manager
│ ├── notifications/ # Browser notifications
├── transfer/ # File transfer system
│ ├── pwa/ # PWA management
── styles/ # CSS styling
├── logo/ # Icons and logos
└── docs/ # Documentation
```
### Technology Stack
* **Frontend:** Pure JavaScript + React (via CDN)
* **PWA:** Service Workers + Cache API + Web App Manifest + Install Prompts
* **Cryptography:** Web Crypto API + custom ECDH/ECDSA + ASN.1 DER parser
* **Network:** WebRTC P2P Data Channels
* **File Transfer:** Enhanced secure P2P streaming with chunked encryption
* **Payments:** Lightning Network via WebLN
* **Offline Support:** Smart caching with offline-manager
* **Styling:** TailwindCSS + custom CSS + PWA-specific styles
### Development Setup
### Build Workflow
```bash
# Clone repository
git clone https://github.com/SecureBitChat/securebit-chat.git
cd securebit-chat
# CSS changes (Tailwind)
npm run build:css
# No build process required — pure clientside
# Just serve the files over HTTPS
# JavaScript/JSX changes
npm run build:js
# For development
python -m http.server 8000
# Full rebuild (recommended)
npm run build
# For production
# Deploy to any static hosting (GitHub Pages, Netlify, etc.)
# Development with live server
npm run dev
```
**Important:** Always rebuild after changes. Source files are in `src/`, generated files in `assets/` and `dist/`. Never edit generated files directly.
### Technology Stack
- **Frontend:** Pure JavaScript + React (via CDN)
- **PWA:** Service Workers + Cache API + Web App Manifest
- **Cryptography:** Web Crypto API + custom ECDH/ECDSA + ASN.1 parser
- **Network:** WebRTC P2P Data Channels
- **Notifications:** Browser Notifications API + Page Visibility API
- **File Transfer:** Enhanced secure P2P streaming with chunked encryption
- **Styling:** TailwindCSS + custom CSS
---
## 🛡️ Security
### Security Audit Status
* ✅ Internal cryptographic review completed
* ✅ P2P protocol security analysis completed
* ✅ File transfer security validation completed
* ✅ MITM and replay attack resistance verified
* ✅ ASN.1 validation and key structure verification completed
* 🔄 Professional security audit planned Q3 2025
* 🔄 Postquantum cryptography review for v5.0
### Audit Status
- ✅ Internal cryptographic review completed
- ✅ P2P protocol security analysis completed
- ✅ File transfer security validation completed
- ✅ ASN.1 validation and key verification completed
- 🔄 Professional security audit planned Q3 2025
### Vulnerability Reporting
Contact: **SecureBitChat@proton.me**
See **SECURITY.md** for detailed security policy and reporting instructions.
Contact: **[SecureBitChat@proton.me](mailto:SecureBitChat@proton.me)**
See **SECURITY.md** for detailed security policy.
### Security Features
* Perfect Forward Secrecy — Past messages and files secure even if keys compromised
* Outofband verification — Prevents maninthemiddle attacks
* Traffic obfuscation — Defeats network analysis
* Memory protection — No persistent storage of sensitive data
* Session isolation — Complete cleanup between sessions
* File integrity — SHA-384 hash validation prevents tampering
* Chunked encryption — Individual encryption per file block
* **ASN.1 validation** — Complete key structure verification according to PKCS standards
* **OID validation** — Algorithm and curve verification for cryptographic operations
* **EC point validation** — Format and structure verification for elliptic curve keys
- Perfect Forward Secrecy for messages and files
- Out-of-band verification prevents MITM attacks
- Traffic obfuscation defeats network analysis
- Memory protection with no persistent storage
- Complete ASN.1 key structure validation
- File integrity with SHA-384 hash validation
---
## 📊 Performance
### Benchmarks
* Connection setup: < 3 seconds
* Message latency: < 100 ms (P2P direct)
* File transfer speed: Up to 5 MB/s per connection
* Throughput: Up to 1 MB/s per connection
* Memory usage: < 50 MB for active session
* Battery impact: Minimal (optimized WebRTC)
* PWA install size: < 2 MB
* **Key validation time:** < 10 ms (ASN.1 parsing)
### Scalability
* Concurrent connections: Limited by device capabilities
* Message size: Up to 2000 characters
* File size: Up to 100 MB per file
* File types: All formats supported
* Group size: Up to 8 participants (v5.5)
---
## 📄 License
MIT License — see **LICENSE** file for details.
### Open Source Commitment
* 100% open source — full transparency
* MIT license — maximum freedom
* No telemetry — zero data collection
* Communitydriven — contributions welcome
- **Connection setup:** < 3 seconds
- **Message latency:** < 100 ms (P2P direct)
- **File transfer speed:** Up to 5 MB/s
- **Memory usage:** < 50 MB active session
- **PWA install size:** < 2 MB
- **Key validation:** < 10 ms (ASN.1 parsing)
---
## 🤝 Contributing
We welcome contributions from the community!
### How to Contribute
We welcome contributions! Here's how:
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/amazing-feature`
3. Commit your changes: `git commit -m "Add amazing feature"`
4. Push to the branch: `git push origin feature/amazing-feature`
5. Open a Pull Request
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
### Contribution Areas
* 🔐 Cryptography — Security improvements and audits
* 🌐 Network — P2P optimization and reliability
* 📂 File Transfer — EnhancedSecureFileTransfer improvements
* 📱 PWA — Install prompts, offline management, and PWA lifecycle
* 🎨 UI/UX — Interface improvements, FileTransfer and DownloadApps components
* 📚 Documentation — Guides, tutorials, translations
* **🔒 ASN.1 Validation** — Enhanced key structure verification and parsing
### Development Guidelines
* Follow existing code style
* Add tests for new features
* Update documentation
* Respect securityfirst principles
* Test PWA functionality across devices
* **Validate all cryptographic operations** with enhanced ASN.1 parsing
🔐 Cryptography • 🌐 Network • 🔔 Notifications • 📂 File Transfer • 📱 PWA • 🎨 UI/UX • 📚 Documentation • 🔒 ASN.1 Validation
---
## 📞 Contact & Support
### Official Channels
* Email: **[SecureBitChat@proton.me](mailto:SecureBitChat@proton.me)**
* GitHub: **Issues & Discussions**
* Security: **[SecureBitChat@proton.me](mailto:SecureBitChat@proton.me)**
### Community
* Discussions: GitHub Discussions for feature requests
* Issues: Bug reports and technical support
* Wiki: Documentation and guides
- **Email:** SecureBitChat@proton.me
- **GitHub:** Issues & Discussions
- **Security:** SecureBitChat@proton.me
---
## ⚠️ Important Disclaimers
### Security Notice
While SecureBit.chat implements military-grade cryptography and follows security best practices, no communication system is 100% secure. Users should:
* Always verify security codes out-of-band
* Keep devices and browsers updated
* Be aware of endpoint security risks
* **File transfers are protected with the same military-grade cryptography as chat messages**
* **All cryptographic keys now undergo complete ASN.1 structure validation**
While SecureBit.chat implements military-grade cryptography, no system is 100% secure. Always verify security codes out-of-band and keep devices updated.
### Legal Notice
This software is provided "as is" for educational and research purposes. Users are responsible for compliance with local laws and regulations regarding:
* Cryptographic software usage
* Private communications
* File sharing and transfer
This software is provided "as is" for educational and research purposes. Users are responsible for compliance with local laws regarding cryptographic software and private communications.
### Privacy Statement
SecureBit.chat:
* Collects zero data - no analytics, tracking, or telemetry
* Stores nothing - all data exists only in browser memory
* Requires no registration - completely anonymous usage
* Uses no servers - direct P2P connections only
* **Files are transferred directly P2P with zero server storage**
SecureBit.chat collects zero data, stores nothing, requires no registration, and uses no servers. All data exists only in browser memory with direct P2P connections.
---
## 🎯 Why Choose SecureBit.chat?
## 📄 License
### For Privacy Advocates
MIT License - see **LICENSE** file for details.
* True zero-knowledge architecture
* Military-grade encryption standards
* Complete anonymity and untraceability
* Resistance to censorship and surveillance
* **Secure P2P file sharing without servers**
* **Complete ASN.1 validation for cryptographic keys**
### For Mobile Users
* **Progressive Web App installation**
* **Offline mode support**
* **Native app experience without app stores**
* **Works on all modern mobile devices**
### For Developers
* 100% open source transparency
* Modern cryptographic standards
* Clean, auditable codebase
* Extensible modular architecture
* **PWA best practices implementation**
* **Complete ASN.1 DER parser for key validation**
### For Everyone
* **Install like native apps**
* **Works offline with session persistence**
* Works on all modern devices
* Intuitive user interface
* Professional security standards
* **Secure file transfers included**
* **Enhanced key security with ASN.1 validation**
---
## 🔧 Development Workflow
### Making Changes and Recompiling
When you make changes to the source code, you need to recompile the assets. Here's the proper workflow:
#### 1. **CSS Changes** (Tailwind classes, styles)
```bash
# Rebuild only CSS
npm run build:css
# Or watch for changes during development
npm run watch
```
#### 2. **JavaScript/JSX Changes** (React components, logic)
```bash
# Rebuild only JavaScript
npm run build:js
# Or rebuild everything
npm run build
```
#### 3. **Full Rebuild** (recommended after major changes)
```bash
# Complete rebuild of all assets
npm run build
```
#### 4. **Development with Live Server**
```bash
# Build and start development server
npm run dev
# Or use custom server
npm run serve
```
### File Structure After Build
```
├── assets/
│ ├── tailwind.css # ← Generated from src/styles/tw-input.css
│ ├── fontawesome/ # ← Local Font Awesome assets
│ └── fonts/ # ← Local Google Fonts
├── dist/
│ ├── app.js # ← Generated from src/app.jsx
│ ├── app-boot.js # ← Generated from src/scripts/app-boot.js
│ └── qr-local.js # ← Generated from src/scripts/qr-local.js
└── src/ # ← Source files (edit these)
├── app.jsx
├── scripts/
├── styles/
└── components/
```
### Important Notes
- **Always rebuild after changes** to see them in the browser
- **CSS changes** require `npm run build:css`
- **JS/JSX changes** require `npm run build:js`
- **Source files** are in `src/` directory
- **Generated files** are in `assets/` and `dist/` directories
- **Never edit** files in `assets/` or `dist/` directly
### Troubleshooting Build Issues
#### CSS not updating?
```bash
# Clear cache and rebuild
rm assets/tailwind.css
npm run build:css
```
#### JavaScript errors?
```bash
# Check for syntax errors in source files
npm run build:js
```
#### All changes not showing?
```bash
# Hard refresh browser (Ctrl+F5) or clear browser cache
# Then rebuild everything
npm run build
```
100% open source with full transparency, no telemetry, and zero data collection.
---
@@ -664,6 +335,8 @@ npm run build
---
**Latest Release: v4.02.442** — ASN.1 Validation & Enhanced Security
**Latest Release: v4.4.18** - Browser Notifications & Code Cleanup
</div>
[🚀 Try Now](https://securebitchat.github.io/securebit-chat/) • [⭐ Star on GitHub](https://github.com/SecureBitChat/securebit-chat)
</div>
+20
View File
@@ -0,0 +1,20 @@
# Notification Sound Asset
#
# This file should contain a short, pleasant notification sound in MP3 format.
# Recommended specifications:
# - Duration: 1-2 seconds
# - Format: MP3, 44.1kHz, 128kbps
# - Volume: Moderate (not too loud)
# - License: Ensure proper licensing for commercial use
#
# You can create this using:
# 1. Text-to-speech generators
# 2. Audio editing software
# 3. Free notification sound libraries
# 4. AI-generated sounds
#
# Example sources:
# - Freesound.org (CC licensed)
# - Zapsplat.com
# - Adobe Audition
# - Audacity (free)
+1
View File
@@ -0,0 +1 @@
+1 -1
View File
File diff suppressed because one or more lines are too long
+1002 -433
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
File diff suppressed because one or more lines are too long
Vendored
+295 -202
View File
@@ -267,11 +267,16 @@ var EnhancedConnectionSetup = ({
toggleQrManualMode,
nextQrFrame,
prevQrFrame,
markAnswerCreated
markAnswerCreated,
notificationIntegrationRef,
isGeneratingKeys,
handleCreateOffer
}) => {
const [mode, setMode] = React.useState("select");
const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);
const resetToSelect = () => {
setMode("select");
setIsGeneratingKeys(false);
onClearData();
};
const handleVerificationConfirm = () => {
@@ -280,6 +285,76 @@ var EnhancedConnectionSetup = ({
const handleVerificationReject = () => {
onVerifyConnection(false);
};
const requestNotificationPermissionOnInteraction = async () => {
if (notificationPermissionRequested) {
return;
}
try {
if (!("Notification" in window)) {
return;
}
if (!window.isSecureContext && window.location.protocol !== "https:" && window.location.hostname !== "localhost") {
return;
}
const currentPermission = typeof Notification !== "undefined" && Notification ? Notification.permission : "denied";
if (currentPermission === "default" && typeof Notification !== "undefined" && Notification) {
const permission = await Notification.requestPermission();
if (permission === "granted") {
try {
if (window.NotificationIntegration && webrtcManagerRef.current) {
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
await integration.init();
notificationIntegrationRef.current = integration;
}
} catch (error) {
}
setTimeout(() => {
try {
const welcomeNotification = new Notification("SecureBit Chat", {
body: "Notifications enabled! You will receive alerts for new messages.",
icon: "/logo/icon-192x192.png",
tag: "welcome-notification"
});
welcomeNotification.onclick = () => {
welcomeNotification.close();
};
setTimeout(() => {
welcomeNotification.close();
}, 5e3);
} catch (error) {
}
}, 1e3);
}
} else if (currentPermission === "granted") {
try {
if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
await integration.init();
notificationIntegrationRef.current = integration;
}
} catch (error) {
}
setTimeout(() => {
try {
const testNotification = new Notification("SecureBit Chat", {
body: "Notifications are working! You will receive alerts for new messages.",
icon: "/logo/icon-192x192.png",
tag: "test-notification"
});
testNotification.onclick = () => {
testNotification.close();
};
setTimeout(() => {
testNotification.close();
}, 5e3);
} catch (error) {
}
}, 1e3);
}
setNotificationPermissionRequested(true);
} catch (error) {
}
};
if (showVerification) {
return React.createElement("div", {
className: "min-h-[calc(100vh-104px)] flex items-center justify-center p-4"
@@ -327,7 +402,15 @@ var EnhancedConnectionSetup = ({
// Create Connection
React.createElement("div", {
key: "create",
onClick: () => setMode("create"),
onClick: () => {
requestNotificationPermissionOnInteraction();
setMode("create");
setTimeout(() => {
if (webrtcManagerRef.current) {
handleCreateOffer();
}
}, 100);
},
className: "card-minimal rounded-xl p-6 cursor-pointer group flex-1 create"
}, [
React.createElement("div", {
@@ -399,7 +482,10 @@ var EnhancedConnectionSetup = ({
// Join Connection
React.createElement("div", {
key: "join",
onClick: () => setMode("join"),
onClick: () => {
requestNotificationPermissionOnInteraction();
setMode("join");
},
className: "card-minimal rounded-xl p-6 cursor-pointer group flex-1 join"
}, [
React.createElement("div", {
@@ -510,16 +596,15 @@ var EnhancedConnectionSetup = ({
key: "description",
className: "text-secondary text-sm mb-4"
}, "Creating cryptographically strong keys and codes to protect against attacks"),
!showOfferStep && React.createElement("button", {
key: "create-btn",
onClick: onCreateOffer,
disabled: connectionStatus === "connecting",
className: `w-full btn-primary text-white py-3 px-4 rounded-lg font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed`
!showOfferStep && isGeneratingKeys && React.createElement("div", {
key: "loading-state",
className: "w-full py-3 px-4 rounded-lg font-medium transition-all duration-200 bg-blue-500/10 border border-blue-500/20 text-blue-400 flex items-center justify-center"
}, [
React.createElement("i", {
className: "fas fa-shield-alt mr-2"
key: "spinner",
className: "fas fa-spinner fa-spin mr-2"
}),
"Create secure keys"
"Generating secure keys..."
]),
showOfferStep && React.createElement("div", {
key: "offer-result",
@@ -622,58 +707,6 @@ var EnhancedConnectionSetup = ({
])
])
]),
// Step 2 - Session Type Selection
// showOfferStep && React.createElement('div', {
// key: 'step2',
// className: "card-minimal rounded-xl p-6"
// }, [
// React.createElement('div', {
// key: 'step-header',
// className: "flex items-center mb-4"
// }, [
// React.createElement('div', {
// key: 'number',
// className: "w-8 h-8 bg-green-500 text-white rounded-lg flex items-center justify-center font-semibold text-sm mr-3"
// }, '2'),
// React.createElement('h3', {
// key: 'title',
// className: "text-lg font-medium text-primary"
// }, "Select session type")
// ]),
// React.createElement('p', {
// key: 'description',
// className: "text-secondary text-sm mb-4"
// }, "Choose a session plan or use limited demo mode for testing."),
// React.createElement(SessionTypeSelector, {
// key: 'session-selector',
// onSelectType: (sessionType) => {
// // Save the selected session type
// setSelectedSessionType(sessionType);
// console.log('🎯 Session type selected:', sessionType);
// // FIX: For demo sessions, we immediately call automatic activation
// if (sessionType === 'demo') {
// console.log('🎮 Demo session selected, scheduling automatic activation...');
// // Delay activation for 2 seconds to stabilize
// setTimeout(() => {
// if (sessionManager) {
// console.log('🚀 Triggering demo session activation from selection...');
// handleDemoVerification();
// }
// }, 2000);
// }
// // Open a modal payment window
// if (typeof window.showPaymentModal === 'function') {
// window.showPaymentModal(sessionType);
// } else {
// // Fallback - show session information
// console.log('Selected session type:', sessionType);
// }
// },
// onCancel: resetToSelect,
// sessionManager: window.sessionManager
// })
// ]),
// Step 3 - Waiting for response
showOfferStep && React.createElement("div", {
key: "step2",
className: "card-minimal rounded-xl p-6"
@@ -714,9 +747,9 @@ var EnhancedConnectionSetup = ({
React.createElement("textarea", {
key: "input",
value: answerInput,
onChange: (e2) => {
setAnswerInput(e2.target.value);
if (e2.target.value.trim().length > 0) {
onChange: (e) => {
setAnswerInput(e.target.value);
if (e.target.value.trim().length > 0) {
if (typeof markAnswerCreated === "function") {
markAnswerCreated();
}
@@ -792,9 +825,9 @@ var EnhancedConnectionSetup = ({
React.createElement("textarea", {
key: "input",
value: offerInput,
onChange: (e2) => {
setOfferInput(e2.target.value);
if (e2.target.value.trim().length > 0) {
onChange: (e) => {
setOfferInput(e.target.value);
if (e.target.value.trim().length > 0) {
if (typeof markAnswerCreated === "function") {
markAnswerCreated();
}
@@ -1085,9 +1118,9 @@ var EnhancedChatInterface = ({
setShowScrollButton(false);
}
};
const handleKeyPress = (e2) => {
if (e2.key === "Enter" && !e2.shiftKey) {
e2.preventDefault();
const handleKeyPress = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
onSendMessage();
}
};
@@ -1272,7 +1305,7 @@ var EnhancedChatInterface = ({
[
React.createElement("textarea", {
value: messageInput,
onChange: (e2) => setMessageInput(e2.target.value),
onChange: (e) => setMessageInput(e.target.value),
onKeyDown: handleKeyPress,
placeholder: "Enter message to encrypt...",
rows: 2,
@@ -1332,12 +1365,13 @@ var EnhancedSecureP2PChat = () => {
const [qrCodeUrl, setQrCodeUrl] = React.useState("");
const [showQRScanner, setShowQRScanner] = React.useState(false);
const [showQRScannerModal, setShowQRScannerModal] = React.useState(false);
const [isGeneratingKeys, setIsGeneratingKeys2] = React.useState(false);
const [isVerified, setIsVerified] = React.useState(false);
const [securityLevel, setSecurityLevel] = React.useState(null);
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
const [localVerificationConfirmed, setLocalVerificationConfirmed] = React.useState(false);
const [remoteVerificationConfirmed, setRemoteVerificationConfirmed] = React.useState(false);
const [bothVerificationsConfirmed, setBothVerificationsConfirmed] = React.useState(false);
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
const [pendingSession, setPendingSession] = React.useState(null);
const [connectionState, setConnectionState] = React.useState({
status: "disconnected",
@@ -1359,7 +1393,7 @@ var EnhancedSecureP2PChat = () => {
const now = Date.now();
const answerAge = now - (connectionState.answerCreatedAt || 0);
const maxPreserveTime = 3e5;
const hasAnswerData = answerData && answerData.trim().length > 0 || answerInput && answerInput.trim().length > 0;
const hasAnswerData = answerData && typeof answerData === "string" && answerData.trim().length > 0 || answerInput && answerInput.trim().length > 0;
const hasAnswerQR = qrCodeUrl && qrCodeUrl.trim().length > 0;
const shouldPreserve = connectionState.hasActiveAnswer && answerAge < maxPreserveTime && !connectionState.isUserInitiatedDisconnect || hasAnswerData && answerAge < maxPreserveTime && !connectionState.isUserInitiatedDisconnect || hasAnswerQR && answerAge < maxPreserveTime && !connectionState.isUserInitiatedDisconnect;
return shouldPreserve;
@@ -1373,8 +1407,8 @@ var EnhancedSecureP2PChat = () => {
React.useEffect(() => {
window.forceCleanup = () => {
handleClearData();
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
if (webrtcManagerRef2.current) {
webrtcManagerRef2.current.disconnect();
}
};
window.clearLogs = () => {
@@ -1387,8 +1421,9 @@ var EnhancedSecureP2PChat = () => {
delete window.clearLogs;
};
}, []);
const webrtcManagerRef = React.useRef(null);
window.webrtcManagerRef = webrtcManagerRef;
const webrtcManagerRef2 = React.useRef(null);
const notificationIntegrationRef = React.useRef(null);
window.webrtcManagerRef = webrtcManagerRef2;
const addMessageWithAutoScroll = React.useCallback((message, type) => {
const newMessage = {
message,
@@ -1429,7 +1464,7 @@ var EnhancedSecureP2PChat = () => {
}
window.isUpdatingSecurity = true;
try {
if (webrtcManagerRef.current) {
if (webrtcManagerRef2.current) {
setSecurityLevel({
level: "MAXIMUM",
score: 100,
@@ -1440,7 +1475,7 @@ var EnhancedSecureP2PChat = () => {
isRealData: true
});
if (window.DEBUG_MODE) {
const currentLevel = webrtcManagerRef.current.ecdhKeyPair && webrtcManagerRef.current.ecdsaKeyPair ? await webrtcManagerRef.current.calculateSecurityLevel() : {
const currentLevel = webrtcManagerRef2.current.ecdhKeyPair && webrtcManagerRef2.current.ecdsaKeyPair ? await webrtcManagerRef2.current.calculateSecurityLevel() : {
level: "MAXIMUM",
score: 100,
sessionType: "premium",
@@ -1463,12 +1498,6 @@ var EnhancedSecureP2PChat = () => {
}, 2e3);
}
}, []);
React.useEffect(() => {
const timer = setInterval(() => {
setSessionTimeLeft(0);
}, 1e3);
return () => clearInterval(timer);
}, []);
const chatMessagesRef = React.useRef(null);
const scrollToBottom = createScrollToBottomFunction(chatMessagesRef);
React.useEffect(() => {
@@ -1479,7 +1508,7 @@ var EnhancedSecureP2PChat = () => {
}
}, [messages]);
React.useEffect(() => {
if (webrtcManagerRef.current) {
if (webrtcManagerRef2.current) {
console.log("\u26A0\uFE0F WebRTC Manager already initialized, skipping...");
return;
}
@@ -1554,8 +1583,8 @@ var EnhancedSecureP2PChat = () => {
setLocalVerificationConfirmed(false);
setRemoteVerificationConfirmed(false);
setBothVerificationsConfirmed(false);
setOfferData(null);
setAnswerData(null);
setOfferData("");
setAnswerData("");
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
@@ -1563,12 +1592,11 @@ var EnhancedSecureP2PChat = () => {
setKeyFingerprint("");
setVerificationCode("");
setSecurityLevel(null);
setSessionTimeLeft(0);
setTimeout(() => {
setConnectionStatus("disconnected");
setShowVerification(false);
setOfferData(null);
setAnswerData(null);
setOfferData("");
setAnswerData("");
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
@@ -1588,8 +1616,8 @@ var EnhancedSecureP2PChat = () => {
setLocalVerificationConfirmed(false);
setRemoteVerificationConfirmed(false);
setBothVerificationsConfirmed(false);
setOfferData(null);
setAnswerData(null);
setOfferData("");
setAnswerData("");
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
@@ -1642,7 +1670,7 @@ var EnhancedSecureP2PChat = () => {
if (typeof console.clear === "function") {
console.clear();
}
webrtcManagerRef.current = new EnhancedSecureWebRTCManager(
webrtcManagerRef2.current = new EnhancedSecureWebRTCManager(
handleMessage,
handleStatusChange,
handleKeyExchange,
@@ -1650,12 +1678,22 @@ var EnhancedSecureP2PChat = () => {
handleAnswerError,
handleVerificationStateChange
);
handleMessage(" SecureBit.chat Enhanced Security Edition v4.3.120 - 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");
if (typeof Notification !== "undefined" && Notification && Notification.permission === "granted" && window.NotificationIntegration && !notificationIntegrationRef.current) {
try {
const integration = new window.NotificationIntegration(webrtcManagerRef2.current);
integration.init().then(() => {
notificationIntegrationRef.current = integration;
}).catch((error) => {
});
} catch (error) {
}
}
handleMessage(" SecureBit.chat Enhanced Security Edition v4.4.18 - 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) {
if (webrtcManagerRef.current && webrtcManagerRef.current.isConnected()) {
if (webrtcManagerRef2.current && webrtcManagerRef2.current.isConnected()) {
try {
webrtcManagerRef.current.sendSystemMessage({
webrtcManagerRef2.current.sendSystemMessage({
type: "peer_disconnect",
reason: "user_disconnect",
timestamp: Date.now()
@@ -1663,12 +1701,12 @@ var EnhancedSecureP2PChat = () => {
} catch (error) {
}
setTimeout(() => {
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
if (webrtcManagerRef2.current) {
webrtcManagerRef2.current.disconnect();
}
}, 100);
} else if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
} else if (webrtcManagerRef2.current) {
webrtcManagerRef2.current.disconnect();
}
} else if (isTabSwitching) {
event.preventDefault();
@@ -1696,8 +1734,8 @@ var EnhancedSecureP2PChat = () => {
}
};
document.addEventListener("visibilitychange", handleVisibilityChange);
if (webrtcManagerRef.current) {
webrtcManagerRef.current.setFileTransferCallbacks(
if (webrtcManagerRef2.current) {
webrtcManagerRef2.current.setFileTransferCallbacks(
// Progress callback
(progress) => {
console.log("File progress:", progress);
@@ -1720,9 +1758,9 @@ var EnhancedSecureP2PChat = () => {
a.download = fileData.fileName;
a.click();
setTimeout(() => fileData.revokeObjectURL(url), 15e3);
} catch (e2) {
console.error("Download failed:", e2);
addMessageWithAutoScroll(` File upload error: ${String(e2?.message || e2)}`, "system");
} catch (e) {
console.error("Download failed:", e);
addMessageWithAutoScroll(` File upload error: ${String(e?.message || e)}`, "system");
}
}
}, "Download")
@@ -1749,9 +1787,9 @@ var EnhancedSecureP2PChat = () => {
clearTimeout(tabSwitchTimeout);
tabSwitchTimeout = null;
}
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
webrtcManagerRef.current = null;
if (webrtcManagerRef2.current) {
webrtcManagerRef2.current.disconnect();
webrtcManagerRef2.current = null;
}
};
}, []);
@@ -1838,8 +1876,8 @@ var EnhancedSecureP2PChat = () => {
const QR_SIZE = isDesktop ? 720 : 512;
const url = await (window.generateQRCode ? window.generateQRCode(current, { errorCorrectionLevel: "M", margin: 2, size: QR_SIZE }) : Promise.resolve(""));
if (url) setQrCodeUrl(url);
} catch (e2) {
console.warn("Animated QR render error (current):", e2);
} catch (e) {
console.warn("Animated QR render error (current):", e);
}
setQrFrameIndex((qrAnimationRef.current?.idx || 0) % (qrAnimationRef.current?.chunks?.length || 1) + 1);
};
@@ -1945,8 +1983,8 @@ var EnhancedSecureP2PChat = () => {
setQrFrameIndex(1);
return;
}
} catch (e2) {
console.warn("Binary QR generation failed, falling back to compressed:", e2?.message || e2);
} catch (e) {
console.warn("Binary QR generation failed, falling back to compressed:", e?.message || e);
}
}
if (typeof window.generateCompressedQRCode === "function") {
@@ -1969,8 +2007,8 @@ var EnhancedSecureP2PChat = () => {
setQrFrameIndex(1);
return;
}
} catch (e2) {
console.warn("Compressed QR generation failed, falling back to plain:", e2?.message || e2);
} catch (e) {
console.warn("Compressed QR generation failed, falling back to plain:", e?.message || e);
}
}
const payload = typeof data === "string" ? data : JSON.stringify(data);
@@ -2164,8 +2202,8 @@ var EnhancedSecureP2PChat = () => {
qrChunksBufferRef.current = { id: null, total: 0, seen: /* @__PURE__ */ new Set(), items: [] };
setShowQRScannerModal(false);
return Promise.resolve(true);
} catch (e2) {
console.warn("Binary chunks reconstruction failed:", e2);
} catch (e) {
console.warn("Binary chunks reconstruction failed:", e);
return Promise.resolve(false);
}
}
@@ -2223,8 +2261,8 @@ var EnhancedSecureP2PChat = () => {
qrChunksBufferRef.current = { id: null, total: 0, seen: /* @__PURE__ */ new Set(), items: [] };
setShowQRScannerModal(false);
return Promise.resolve(true);
} catch (e2) {
console.warn("Binary chunks reconstruction failed:", e2);
} catch (e) {
console.warn("Binary chunks reconstruction failed:", e);
return Promise.resolve(false);
}
}
@@ -2283,8 +2321,8 @@ var EnhancedSecureP2PChat = () => {
qrChunksBufferRef.current = { id: null, total: 0, seen: /* @__PURE__ */ new Set(), items: [] };
setShowQRScannerModal(false);
return Promise.resolve(true);
} catch (e2) {
console.warn("RAW multi-frame reconstruction failed:", e2);
} catch (e) {
console.warn("RAW multi-frame reconstruction failed:", e);
return Promise.resolve(false);
}
} else if (hdr.rt === "bin") {
@@ -2311,8 +2349,8 @@ var EnhancedSecureP2PChat = () => {
qrChunksBufferRef.current = { id: null, total: 0, seen: /* @__PURE__ */ new Set(), items: [] };
setShowQRScannerModal(false);
return Promise.resolve(true);
} catch (e2) {
console.warn("BIN multi-frame reconstruction failed:", e2);
} catch (e) {
console.warn("BIN multi-frame reconstruction failed:", e);
return Promise.resolve(false);
}
} else if (window.receiveAndProcess) {
@@ -2334,8 +2372,8 @@ var EnhancedSecureP2PChat = () => {
setShowQRScannerModal(false);
return Promise.resolve(true);
}
} catch (e2) {
console.warn("COSE multi-chunk processing failed:", e2);
} catch (e) {
console.warn("COSE multi-chunk processing failed:", e);
}
return Promise.resolve(false);
} else {
@@ -2417,11 +2455,12 @@ var EnhancedSecureP2PChat = () => {
};
const handleCreateOffer = async () => {
try {
setIsGeneratingKeys2(true);
setOfferData("");
setShowOfferStep(false);
setShowQRCode(false);
setQrCodeUrl("");
const offer = await webrtcManagerRef.current.createSecureOffer();
const offer = await webrtcManagerRef2.current.createSecureOffer();
setOfferData(offer);
setShowOfferStep(true);
const offerString = typeof offer === "object" ? JSON.stringify(offer) : offer;
@@ -2429,12 +2468,15 @@ var EnhancedSecureP2PChat = () => {
if (typeof window.encodeBinaryToPrefixed === "function") {
const bin = window.encodeBinaryToPrefixed(offerString);
const TARGET_CHUNKS = 4;
let FRAME_MAX = Math.max(200, Math.floor(bin.length / TARGET_CHUNKS));
let total = TARGET_CHUNKS;
let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));
if (FRAME_MAX <= 0) FRAME_MAX = 200;
let total = Math.ceil(bin.length / FRAME_MAX);
if (total < 2) {
total = 2;
FRAME_MAX = Math.ceil(bin.length / 2) || 1;
if (bin.length <= FRAME_MAX) {
total = 1;
FRAME_MAX = bin.length;
} else {
FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);
total = TARGET_CHUNKS;
}
const id = `bin_${Date.now()}_${Math.random().toString(36).slice(2)}`;
const chunks = [];
@@ -2472,8 +2514,8 @@ var EnhancedSecureP2PChat = () => {
} catch {
}
}
} catch (e2) {
console.warn("Offer QR generation failed:", e2);
} catch (e) {
console.warn("Offer QR generation failed:", e);
}
const existingMessages = messages.filter(
(m) => m.type === "system" && (m.message.includes("Secure invitation created") || m.message.includes("Send the encrypted code"))
@@ -2502,6 +2544,8 @@ var EnhancedSecureP2PChat = () => {
id: Date.now(),
timestamp: Date.now()
}]);
} finally {
setIsGeneratingKeys2(false);
}
};
const handleCreateAnswer = async () => {
@@ -2524,14 +2568,24 @@ var EnhancedSecureP2PChat = () => {
}]);
let offer;
try {
console.log("DEBUG: Processing offer input:", offerInput.trim().substring(0, 100) + "...");
console.log("DEBUG: decodeAnyPayload available:", typeof window.decodeAnyPayload === "function");
console.log("DEBUG: decompressIfNeeded available:", typeof window.decompressIfNeeded === "function");
if (typeof window.decodeAnyPayload === "function") {
console.log("DEBUG: Using decodeAnyPayload...");
const any = window.decodeAnyPayload(offerInput.trim());
console.log("DEBUG: decodeAnyPayload result type:", typeof any);
console.log("DEBUG: decodeAnyPayload result:", any);
offer = typeof any === "string" ? JSON.parse(any) : any;
} else {
console.log("DEBUG: Using decompressIfNeeded...");
const rawText = typeof window.decompressIfNeeded === "function" ? window.decompressIfNeeded(offerInput.trim()) : offerInput.trim();
console.log("DEBUG: decompressIfNeeded result:", rawText.substring(0, 100) + "...");
offer = JSON.parse(rawText);
}
console.log("DEBUG: Final offer:", offer);
} catch (parseError) {
console.error("DEBUG: Parse error:", parseError);
throw new Error(`Invalid invitation format: ${parseError.message}`);
}
if (!offer || typeof offer !== "object") {
@@ -2541,7 +2595,7 @@ var EnhancedSecureP2PChat = () => {
if (!isValidOfferType) {
throw new Error("Invalid invitation type. Expected offer or enhanced_secure_offer");
}
const answer = await webrtcManagerRef.current.createSecureAnswer(offer);
const answer = await webrtcManagerRef2.current.createSecureAnswer(offer);
setAnswerData(answer);
setShowAnswerStep(true);
const answerString = typeof answer === "object" ? JSON.stringify(answer) : answer;
@@ -2549,12 +2603,15 @@ var EnhancedSecureP2PChat = () => {
if (typeof window.encodeBinaryToPrefixed === "function") {
const bin = window.encodeBinaryToPrefixed(answerString);
const TARGET_CHUNKS = 4;
let FRAME_MAX = Math.max(200, Math.floor(bin.length / TARGET_CHUNKS));
let total = TARGET_CHUNKS;
let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));
if (FRAME_MAX <= 0) FRAME_MAX = 200;
let total = Math.ceil(bin.length / FRAME_MAX);
if (total < 2) {
total = 2;
FRAME_MAX = Math.ceil(bin.length / 2) || 1;
if (bin.length <= FRAME_MAX) {
total = 1;
FRAME_MAX = bin.length;
} else {
FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);
total = TARGET_CHUNKS;
}
const id = `ans_${Date.now()}_${Math.random().toString(36).slice(2)}`;
const chunks = [];
@@ -2592,10 +2649,10 @@ var EnhancedSecureP2PChat = () => {
} catch {
}
}
} catch (e2) {
console.warn("Answer QR generation failed:", e2);
} catch (e) {
console.warn("Answer QR generation failed:", e);
}
if (e.target.value.trim().length > 0) {
if (answerInput.trim().length > 0) {
if (typeof markAnswerCreated === "function") {
markAnswerCreated();
}
@@ -2676,7 +2733,7 @@ var EnhancedSecureP2PChat = () => {
if (!answerType || answerType !== "answer" && answerType !== "enhanced_secure_answer") {
throw new Error("Invalid response type. Expected answer or enhanced_secure_answer");
}
await webrtcManagerRef.current.handleSecureAnswer(answer);
await webrtcManagerRef2.current.handleSecureAnswer(answer);
if (pendingSession) {
setPendingSession(null);
setMessages((prev) => [...prev, {
@@ -2762,10 +2819,37 @@ var EnhancedSecureP2PChat = () => {
setConnectionStatus("failed");
}
};
const handleVerifyConnection = (isValid) => {
const handleVerifyConnection = async (isValid) => {
if (isValid) {
webrtcManagerRef.current.confirmVerification();
webrtcManagerRef2.current.confirmVerification();
setLocalVerificationConfirmed(true);
try {
if (window.NotificationIntegration && webrtcManagerRef2.current && !notificationIntegrationRef.current) {
const integration = new window.NotificationIntegration(webrtcManagerRef2.current);
await integration.init();
notificationIntegrationRef.current = integration;
const status = integration.getStatus();
if (status.permission === "granted") {
setMessages((prev) => [...prev, {
message: "\u2713 Notifications enabled - you will receive alerts when the tab is inactive",
type: "system",
id: Date.now(),
timestamp: Date.now()
}]);
} else {
setMessages((prev) => [...prev, {
message: "\u2139 Notifications disabled - you can enable them using the button on the main page",
type: "system",
id: Date.now(),
timestamp: Date.now()
}]);
}
} else if (notificationIntegrationRef.current) {
} else {
}
} catch (error) {
console.warn("Failed to initialize notifications:", error);
}
} else {
setMessages((prev) => [...prev, {
message: " Verification rejected. The connection is unsafe! Session reset..",
@@ -2779,8 +2863,8 @@ var EnhancedSecureP2PChat = () => {
setShowVerification(false);
setVerificationCode("");
setConnectionStatus("disconnected");
setOfferData(null);
setAnswerData(null);
setOfferData("");
setAnswerData("");
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
@@ -2799,15 +2883,15 @@ var EnhancedSecureP2PChat = () => {
if (!messageInput.trim()) {
return;
}
if (!webrtcManagerRef.current) {
if (!webrtcManagerRef2.current) {
return;
}
if (!webrtcManagerRef.current.isConnected()) {
if (!webrtcManagerRef2.current.isConnected()) {
return;
}
try {
addMessageWithAutoScroll(messageInput.trim(), "sent");
await webrtcManagerRef.current.sendMessage(messageInput);
await webrtcManagerRef2.current.sendMessage(messageInput);
setMessageInput("");
} catch (error) {
const msg = String(error?.message || error);
@@ -2822,6 +2906,7 @@ var EnhancedSecureP2PChat = () => {
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
setIsGeneratingKeys2(false);
if (!shouldPreserveAnswerData()) {
setShowAnswerStep(false);
}
@@ -2851,53 +2936,59 @@ var EnhancedSecureP2PChat = () => {
document.dispatchEvent(new CustomEvent("peer-disconnect"));
};
const handleDisconnect = () => {
setSessionTimeLeft(0);
updateConnectionState({
status: "disconnected",
isUserInitiatedDisconnect: true
});
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
}
setKeyFingerprint("");
setVerificationCode("");
setSecurityLevel(null);
setIsVerified(false);
setShowVerification(false);
setConnectionStatus("disconnected");
setLocalVerificationConfirmed(false);
setRemoteVerificationConfirmed(false);
setBothVerificationsConfirmed(false);
setConnectionStatus("disconnected");
setShowVerification(false);
setOfferData(null);
setAnswerData(null);
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
setShowAnswerStep(false);
setKeyFingerprint("");
setVerificationCode("");
setSecurityLevel(null);
setIsVerified(false);
setMessages([]);
if (typeof console.clear === "function") {
console.clear();
}
document.dispatchEvent(new CustomEvent("peer-disconnect"));
document.dispatchEvent(new CustomEvent("disconnected"));
document.dispatchEvent(new CustomEvent("session-cleanup", {
detail: {
timestamp: Date.now(),
reason: "manual_disconnect"
}
}));
setTimeout(() => {
try {
setSessionTimeLeft(0);
}, 500);
handleClearData();
setTimeout(() => {
}, 1e3);
updateConnectionState({
status: "disconnected",
isUserInitiatedDisconnect: true
});
if (webrtcManagerRef2.current) {
webrtcManagerRef2.current.disconnect();
}
if (notificationIntegrationRef.current) {
notificationIntegrationRef.current.cleanup();
notificationIntegrationRef.current = null;
}
setKeyFingerprint("");
setVerificationCode("");
setSecurityLevel(null);
setIsVerified(false);
setShowVerification(false);
setConnectionStatus("disconnected");
setLocalVerificationConfirmed(false);
setRemoteVerificationConfirmed(false);
setBothVerificationsConfirmed(false);
setOfferData("");
setAnswerData("");
setOfferInput("");
setAnswerInput("");
setShowOfferStep(false);
setShowAnswerStep(false);
setIsGeneratingKeys2(false);
setShowQRCode(false);
setQrCodeUrl("");
setShowQRScanner(false);
setShowQRScannerModal(false);
setMessages([]);
if (typeof console.clear === "function") {
console.clear();
}
document.dispatchEvent(new CustomEvent("peer-disconnect"));
document.dispatchEvent(new CustomEvent("disconnected"));
document.dispatchEvent(new CustomEvent("session-cleanup", {
detail: {
timestamp: Date.now(),
reason: "manual_disconnect"
}
}));
handleClearData();
setTimeout(() => {
setSessionTimeLeft(0);
}, 500);
console.log("Disconnect completed successfully");
} catch (error) {
console.error("Error during disconnect:", error);
}
};
const handleSessionActivated = (session) => {
let message;
@@ -3007,7 +3098,7 @@ var EnhancedSecureP2PChat = () => {
return React.createElement("div", {
className: "minimal-bg min-h-screen"
}, [
React.createElement(EnhancedMinimalHeader, {
window.EnhancedMinimalHeader && React.createElement(window.EnhancedMinimalHeader, {
key: "header",
status: connectionStatus,
fingerprint: keyFingerprint,
@@ -3016,8 +3107,7 @@ var EnhancedSecureP2PChat = () => {
isConnected: isConnectedAndVerified,
securityLevel,
// sessionManager removed - all features enabled by default
sessionTimeLeft,
webrtcManager: webrtcManagerRef.current
webrtcManager: webrtcManagerRef2.current
}),
React.createElement(
"main",
@@ -3037,7 +3127,7 @@ var EnhancedSecureP2PChat = () => {
isVerified,
chatMessagesRef,
scrollToBottom,
webrtcManager: webrtcManagerRef.current
webrtcManager: webrtcManagerRef2.current
});
})() : React.createElement(EnhancedConnectionSetup, {
onCreateOffer: handleCreateOffer,
@@ -3074,7 +3164,10 @@ var EnhancedSecureP2PChat = () => {
nextQrFrame,
prevQrFrame,
// PAKE passwords removed - using SAS verification instead
markAnswerCreated
markAnswerCreated,
notificationIntegrationRef,
isGeneratingKeys,
handleCreateOffer
})
),
// QR Scanner Modal
+3 -3
View File
File diff suppressed because one or more lines are too long
+64 -19
View File
@@ -5,15 +5,17 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' ;
script-src 'self';
style-src 'self';
font-src 'self' ;
connect-src 'self' https: ;
img-src 'self' data:;
manifest-src 'self';
worker-src 'self';
font-src 'self';
connect-src 'self' https: wss: ws:;
img-src 'self' data: https:;
media-src 'none';
object-src 'none';
frame-src 'none';
frame-ancestors 'none';
worker-src 'self';
manifest-src 'self';
form-action 'self';
upgrade-insecure-requests;
report-uri /csp-report;
@@ -36,15 +38,58 @@
<meta name="msapplication-TileColor" content="#ff6b35">
<meta name="msapplication-config" content="/browserconfig.xml">
<!-- iOS Splash Screens -->
<link rel="apple-touch-startup-image" href="./logo/splash/launch-640x1136.png"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="./logo/splash/launch-750x1334.png"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="./logo/splash/launch-1125x2436.png"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="./logo/splash/launch-1242x2688.png"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<!-- iOS Splash Screens - Полная конфигурация с генератора -->
<!-- Landscape -->
<link rel="apple-touch-startup-image" media="screen and (device-width: 440px) and (device-height: 956px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_17_Pro_Max__iPhone_16_Pro_Max_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 402px) and (device-height: 874px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_17_Pro__iPhone_17__iPhone_16_Pro_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_16_Plus__iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 420px) and (device-height: 912px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_Air_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_16__iPhone_15_Pro__iPhone_15__iPhone_14_Pro_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_16e__iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_11__iPhone_XR_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png">
<!-- iPad Landscape -->
<link rel="apple-touch-startup-image" media="screen and (device-width: 1032px) and (device-height: 1376px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/13__iPad_Pro_M4_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/12.9__iPad_Pro_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1210px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/11__iPad_Pro_M4_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/11__iPad_Pro__10.5__iPad_Pro_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/10.9__iPad_Air_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/10.5__iPad_Air_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/10.2__iPad_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/8.3__iPad_Mini_landscape.png">
<!-- Portrait -->
<link rel="apple-touch-startup-image" media="screen and (device-width: 440px) and (device-height: 956px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_17_Pro_Max__iPhone_16_Pro_Max_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 402px) and (device-height: 874px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_17_Pro__iPhone_17__iPhone_16_Pro_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_16_Plus__iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 420px) and (device-height: 912px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_Air_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_16__iPhone_15_Pro__iPhone_15__iPhone_14_Pro_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_16e__iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_11__iPhone_XR_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png">
<!-- iPad Portrait -->
<link rel="apple-touch-startup-image" media="screen and (device-width: 1032px) and (device-height: 1376px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/13__iPad_Pro_M4_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/12.9__iPad_Pro_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1210px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/11__iPad_Pro_M4_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/11__iPad_Pro__10.5__iPad_Pro_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/10.9__iPad_Air_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/10.5__iPad_Air_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/10.2__iPad_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png">
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="./logo/icon-180x180.png">
@@ -67,12 +112,11 @@
<meta name="theme-color" content="#ff6b35">
<meta name="msapplication-navbutton-color" content="#ff6b35">
<!-- Security Headers for PWA -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' ; style-src 'self' ; font-src 'self' ; connect-src 'self' https: wss: ws:; img-src 'self' data: https:; media-src 'none'; object-src 'none'; frame-src 'none'; worker-src 'self';">
<!-- Security Headers for PWA - CSP is already defined above -->
<!-- GitHub Pages SEO -->
<meta name="description" content="SecureBit.chat v4.3.120 — P2P messenger with ECDH + DTLS + SAS security and 18-layer military-grade cryptography">
<meta name="description" content="SecureBit.chat v4.4.18 — P2P messenger with ECDH + DTLS + SAS security and 18-layer military-grade cryptography">
<meta name="keywords" content="P2P messenger, ECDH, DTLS, SAS, encryption, WebRTC, privacy, ASN.1 validation, military-grade security, 18-layer defense, MITM protection, PFS">
<meta name="author" content="Volodymyr">
<link rel="canonical" href="https://github.com/SecureBitChat/securebit-chat/">
@@ -108,12 +152,13 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="dist/app.js?v=1757383304"></script>
<script type="module" src="dist/app-boot.js?v=1757383304"></script>
<script type="module" src="dist/app.js?v=1757383304"></script>
<script src="src/scripts/pwa-register.js"></script>
<script src="./src/pwa/install-prompt.js" type="module"></script>
<script src="./src/pwa/pwa-manager.js" type="module"></script>
<script src="./src/scripts/pwa-offline-test.js"></script>
<link rel="stylesheet" href="./src/styles/pwa.css">
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "SecureBit.chat v4.3.120 - ECDH + DTLS + SAS",
"name": "SecureBit.chat v4.4.18 - ECDH + DTLS + SAS",
"short_name": "SecureBit",
"description": "P2P messenger with ECDH + DTLS + SAS security, military-grade cryptography and Lightning Network payments",
"start_url": "./",
+285 -120
View File
@@ -280,12 +280,17 @@
toggleQrManualMode,
nextQrFrame,
prevQrFrame,
markAnswerCreated
markAnswerCreated,
notificationIntegrationRef,
isGeneratingKeys,
handleCreateOffer
}) => {
const [mode, setMode] = React.useState('select');
const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);
const resetToSelect = () => {
setMode('select');
setIsGeneratingKeys(false);
onClearData();
};
@@ -296,6 +301,112 @@
const handleVerificationReject = () => {
onVerifyConnection(false);
};
// Request notification permission on first user interaction
const requestNotificationPermissionOnInteraction = async () => {
if (notificationPermissionRequested) {
return; // Already requested
}
try {
// Check if Notification API is supported
if (!('Notification' in window)) {
return;
}
// Check if we're in a secure context
if (!window.isSecureContext && window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {
return;
}
// Check current permission status
const currentPermission = (typeof Notification !== 'undefined' && Notification)
? Notification.permission
: 'denied';
// Only request if permission is default (not granted or denied)
if (currentPermission === 'default' && typeof Notification !== 'undefined' && Notification) {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
// Initialize notification integration immediately
try {
if (window.NotificationIntegration && webrtcManagerRef.current) {
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
await integration.init();
// Store reference for cleanup
notificationIntegrationRef.current = integration;
}
} catch (error) {
// Handle error silently
}
// Send welcome notification
setTimeout(() => {
try {
const welcomeNotification = new Notification('SecureBit Chat', {
body: 'Notifications enabled! You will receive alerts for new messages.',
icon: '/logo/icon-192x192.png',
tag: 'welcome-notification'
});
welcomeNotification.onclick = () => {
welcomeNotification.close();
};
setTimeout(() => {
welcomeNotification.close();
}, 5000);
} catch (error) {
// Handle error silently
}
}, 1000);
}
} else if (currentPermission === 'granted') {
// Initialize notification integration immediately
try {
if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
await integration.init();
// Store reference for cleanup
notificationIntegrationRef.current = integration;
}
} catch (error) {
// Handle error silently
}
// Test notification to confirm it works
setTimeout(() => {
try {
const testNotification = new Notification('SecureBit Chat', {
body: 'Notifications are working! You will receive alerts for new messages.',
icon: '/logo/icon-192x192.png',
tag: 'test-notification'
});
testNotification.onclick = () => {
testNotification.close();
};
setTimeout(() => {
testNotification.close();
}, 5000);
} catch (error) {
// Handle error silently
}
}, 1000);
}
setNotificationPermissionRequested(true);
} catch (error) {
// Handle error silently
}
};
if (showVerification) {
return React.createElement('div', {
@@ -346,7 +457,16 @@
// Create Connection
React.createElement('div', {
key: 'create',
onClick: () => setMode('create'),
onClick: () => {
requestNotificationPermissionOnInteraction();
setMode('create');
// Автоматически запускаем генерацию ключей
setTimeout(() => {
if (webrtcManagerRef.current) {
handleCreateOffer();
}
}, 100);
},
className: "card-minimal rounded-xl p-6 cursor-pointer group flex-1 create"
}, [
React.createElement('div', {
@@ -418,7 +538,10 @@
// Join Connection
React.createElement('div', {
key: 'join',
onClick: () => setMode('join'),
onClick: () => {
requestNotificationPermissionOnInteraction();
setMode('join');
},
className: "card-minimal rounded-xl p-6 cursor-pointer group flex-1 join"
}, [
React.createElement('div', {
@@ -515,7 +638,7 @@
className: "text-xl font-semibold text-primary mb-2"
}, 'Creating a secure channel')
]),
// Step 1
!showAnswerStep && React.createElement('div', {
key: 'step1',
@@ -538,16 +661,15 @@
key: 'description',
className: "text-secondary text-sm mb-4"
}, "Creating cryptographically strong keys and codes to protect against attacks"),
!showOfferStep && React.createElement('button', {
key: 'create-btn',
onClick: onCreateOffer,
disabled: connectionStatus === 'connecting',
className: `w-full btn-primary text-white py-3 px-4 rounded-lg font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed`
!showOfferStep && isGeneratingKeys && React.createElement('div', {
key: 'loading-state',
className: "w-full py-3 px-4 rounded-lg font-medium transition-all duration-200 bg-blue-500/10 border border-blue-500/20 text-blue-400 flex items-center justify-center"
}, [
React.createElement('i', {
className: 'fas fa-shield-alt mr-2'
key: 'spinner',
className: 'fas fa-spinner fa-spin mr-2'
}),
'Create secure keys'
'Generating secure keys...'
]),
showOfferStep && React.createElement('div', {
@@ -591,7 +713,7 @@
} catch { return typeof offerData === 'object' ? JSON.stringify(offerData) : (offerData || ''); }
})(),
className: "flex-1 px-3 py-2 bg-orange-500/10 hover:bg-orange-500/20 text-orange-400 border border-orange-500/20 rounded text-sm font-medium"
}, 'Copy invitation code')
}, 'Copy invitation code'),
]),
showQRCode && qrCodeUrl && React.createElement('div', {
key: 'qr-container',
@@ -653,63 +775,8 @@
])
])
])
]),
]),
// Step 2 - Session Type Selection
// showOfferStep && React.createElement('div', {
// key: 'step2',
// className: "card-minimal rounded-xl p-6"
// }, [
// React.createElement('div', {
// key: 'step-header',
// className: "flex items-center mb-4"
// }, [
// React.createElement('div', {
// key: 'number',
// className: "w-8 h-8 bg-green-500 text-white rounded-lg flex items-center justify-center font-semibold text-sm mr-3"
// }, '2'),
// React.createElement('h3', {
// key: 'title',
// className: "text-lg font-medium text-primary"
// }, "Select session type")
// ]),
// React.createElement('p', {
// key: 'description',
// className: "text-secondary text-sm mb-4"
// }, "Choose a session plan or use limited demo mode for testing."),
// React.createElement(SessionTypeSelector, {
// key: 'session-selector',
// onSelectType: (sessionType) => {
// // Save the selected session type
// setSelectedSessionType(sessionType);
// console.log('🎯 Session type selected:', sessionType);
// // FIX: For demo sessions, we immediately call automatic activation
// if (sessionType === 'demo') {
// console.log('🎮 Demo session selected, scheduling automatic activation...');
// // Delay activation for 2 seconds to stabilize
// setTimeout(() => {
// if (sessionManager) {
// console.log('🚀 Triggering demo session activation from selection...');
// handleDemoVerification();
// }
// }, 2000);
// }
// // Open a modal payment window
// if (typeof window.showPaymentModal === 'function') {
// window.showPaymentModal(sessionType);
// } else {
// // Fallback - show session information
// console.log('Selected session type:', sessionType);
// }
// },
// onCancel: resetToSelect,
// sessionManager: window.sessionManager
// })
// ]),
// Step 3 - Waiting for response
showOfferStep && React.createElement('div', {
key: 'step2',
className: "card-minimal rounded-xl p-6"
@@ -745,7 +812,7 @@
className: 'fas fa-qrcode mr-2'
}),
'Scan QR Code'
])
]),
]),
React.createElement('textarea', {
key: 'input',
@@ -1078,6 +1145,7 @@
};
};
const EnhancedChatInterface = ({
messages,
messageInput,
@@ -1410,8 +1478,12 @@
const [qrCodeUrl, setQrCodeUrl] = React.useState('');
const [showQRScanner, setShowQRScanner] = React.useState(false);
const [showQRScannerModal, setShowQRScannerModal] = React.useState(false);
const [isGeneratingKeys, setIsGeneratingKeys] = React.useState(false);
const [isVerified, setIsVerified] = React.useState(false);
const [securityLevel, setSecurityLevel] = React.useState(null);
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
// Mutual verification states
const [localVerificationConfirmed, setLocalVerificationConfirmed] = React.useState(false);
@@ -1421,7 +1493,6 @@
// PAKE password states removed - using SAS verification instead
// Session state - all security features enabled by default
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
const [pendingSession, setPendingSession] = React.useState(null);
// All security features are enabled by default - no payment required
@@ -1459,7 +1530,7 @@
const maxPreserveTime = 300000;
const hasAnswerData = (answerData && answerData.trim().length > 0) ||
const hasAnswerData = (answerData && typeof answerData === 'string' && answerData.trim().length > 0) ||
(answerInput && answerInput.trim().length > 0);
const hasAnswerQR = qrCodeUrl && qrCodeUrl.trim().length > 0;
@@ -1506,6 +1577,7 @@
}, []);
const webrtcManagerRef = React.useRef(null);
const notificationIntegrationRef = React.useRef(null);
// Expose for modules/UI that run outside this closure (e.g., inline handlers)
// Safe because it's a ref object and we maintain it centrally here
window.webrtcManagerRef = webrtcManagerRef;
@@ -1597,14 +1669,7 @@
}
}, []);
// Session time ticker - unlimited sessions
React.useEffect(() => {
const timer = setInterval(() => {
// Sessions are unlimited - no time restrictions
setSessionTimeLeft(0);
}, 1000);
return () => clearInterval(timer);
}, []);
// Session time ticker removed - sessions are unlimited
// Sessions are unlimited - no expiration handler needed
@@ -1720,8 +1785,8 @@
setBothVerificationsConfirmed(false);
// Clear connection data
setOfferData(null);
setAnswerData(null);
setOfferData('');
setAnswerData('');
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
@@ -1730,16 +1795,14 @@
setVerificationCode('');
setSecurityLevel(null);
// Reset session and timer
setSessionTimeLeft(0);
// Return to main page after a short delay
setTimeout(() => {
setConnectionStatus('disconnected');
setShowVerification(false);
setOfferData(null);
setAnswerData(null);
setOfferData('');
setAnswerData('');
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
@@ -1767,8 +1830,8 @@
setBothVerificationsConfirmed(false);
// Clear connection data
setOfferData(null);
setAnswerData(null);
setOfferData('');
setAnswerData('');
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
@@ -1847,7 +1910,21 @@
handleVerificationStateChange
);
handleMessage(' SecureBit.chat Enhanced Security Edition v4.3.120 - 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');
// Initialize notification integration if permission was already granted
if (typeof Notification !== 'undefined' && Notification && Notification.permission === 'granted' && window.NotificationIntegration && !notificationIntegrationRef.current) {
try {
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
integration.init().then(() => {
notificationIntegrationRef.current = integration;
}).catch((error) => {
// Handle error silently
});
} catch (error) {
// Handle error silently
}
}
handleMessage(' SecureBit.chat Enhanced Security Edition v4.4.18 - 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) {
@@ -2723,11 +2800,14 @@
return true;
}
};
const handleCreateOffer = async () => {
try {
// All security features are enabled by default
setIsGeneratingKeys(true);
setOfferData('');
setShowOfferStep(false);
setShowQRCode(false);
@@ -2746,10 +2826,19 @@
const bin = window.encodeBinaryToPrefixed(offerString);
// Force chunking into 4 parts - split binary data directly
const TARGET_CHUNKS = 4;
let FRAME_MAX = Math.max(200, Math.floor(bin.length / TARGET_CHUNKS));
let total = TARGET_CHUNKS;
let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));
if (FRAME_MAX <= 0) FRAME_MAX = 200;
let total = Math.ceil(bin.length / FRAME_MAX);
if (total < 2) { total = 2; FRAME_MAX = Math.ceil(bin.length / 2) || 1; }
// Ensure we don't exceed TARGET_CHUNKS
if (bin.length <= FRAME_MAX) {
total = 1;
FRAME_MAX = bin.length;
} else {
// Recalculate to ensure exactly TARGET_CHUNKS parts
FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);
total = TARGET_CHUNKS;
}
const id = `bin_${Date.now()}_${Math.random().toString(36).slice(2)}`;
const chunks = [];
@@ -2822,6 +2911,8 @@
id: Date.now(),
timestamp: Date.now()
}]);
} finally {
setIsGeneratingKeys(false);
}
};
@@ -2848,15 +2939,26 @@
let offer;
try {
console.log('DEBUG: Processing offer input:', offerInput.trim().substring(0, 100) + '...');
console.log('DEBUG: decodeAnyPayload available:', typeof window.decodeAnyPayload === 'function');
console.log('DEBUG: decompressIfNeeded available:', typeof window.decompressIfNeeded === 'function');
// Prefer binary decode first, then gzip JSON
if (typeof window.decodeAnyPayload === 'function') {
console.log('DEBUG: Using decodeAnyPayload...');
const any = window.decodeAnyPayload(offerInput.trim());
console.log('DEBUG: decodeAnyPayload result type:', typeof any);
console.log('DEBUG: decodeAnyPayload result:', any);
offer = (typeof any === 'string') ? JSON.parse(any) : any;
} else {
console.log('DEBUG: Using decompressIfNeeded...');
const rawText = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(offerInput.trim()) : offerInput.trim();
console.log('DEBUG: decompressIfNeeded result:', rawText.substring(0, 100) + '...');
offer = JSON.parse(rawText);
}
console.log('DEBUG: Final offer:', offer);
} catch (parseError) {
console.error('DEBUG: Parse error:', parseError);
throw new Error(`Invalid invitation format: ${parseError.message}`);
}
@@ -2883,10 +2985,19 @@
const bin = window.encodeBinaryToPrefixed(answerString);
// Force chunking into 4 parts - split binary data directly
const TARGET_CHUNKS = 4;
let FRAME_MAX = Math.max(200, Math.floor(bin.length / TARGET_CHUNKS));
let total = TARGET_CHUNKS;
let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));
if (FRAME_MAX <= 0) FRAME_MAX = 200;
let total = Math.ceil(bin.length / FRAME_MAX);
if (total < 2) { total = 2; FRAME_MAX = Math.ceil(bin.length / 2) || 1; }
// Ensure we don't exceed TARGET_CHUNKS
if (bin.length <= FRAME_MAX) {
total = 1;
FRAME_MAX = bin.length;
} else {
// Recalculate to ensure exactly TARGET_CHUNKS parts
FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);
total = TARGET_CHUNKS;
}
const id = `ans_${Date.now()}_${Math.random().toString(36).slice(2)}`;
const chunks = [];
@@ -2925,7 +3036,7 @@
}
// Mark answer as created for state management
if (e.target.value.trim().length > 0) {
if (answerInput.trim().length > 0) {
if (typeof markAnswerCreated === 'function') {
markAnswerCreated();
}
@@ -3124,11 +3235,47 @@
}
};
const handleVerifyConnection = (isValid) => {
const handleVerifyConnection = async (isValid) => {
if (isValid) {
webrtcManagerRef.current.confirmVerification();
// Mark local verification as confirmed
setLocalVerificationConfirmed(true);
// Initialize notification integration if permission was granted
try {
if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
await integration.init();
// Store reference for cleanup
notificationIntegrationRef.current = integration;
// Check if permission was already granted
const status = integration.getStatus();
if (status.permission === 'granted') {
setMessages(prev => [...prev, {
message: '✓ Notifications enabled - you will receive alerts when the tab is inactive',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
} else {
setMessages(prev => [...prev, {
message: ' Notifications disabled - you can enable them using the button on the main page',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
}
} else if (notificationIntegrationRef.current) {
} else {
// Handle error silently
}
} catch (error) {
console.warn('Failed to initialize notifications:', error);
// Don't show error to user, notifications are optional
}
} else {
setMessages(prev => [...prev, {
message: ' Verification rejected. The connection is unsafe! Session reset..',
@@ -3146,8 +3293,8 @@
// Reset UI to initial state
setConnectionStatus('disconnected');
setOfferData(null);
setAnswerData(null);
setOfferData('');
setAnswerData('');
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
@@ -3202,6 +3349,7 @@
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
setIsGeneratingKeys(false);
if (!shouldPreserveAnswerData()) {
setShowAnswerStep(false);
@@ -3246,6 +3394,7 @@
};
const handleDisconnect = () => {
try {
setSessionTimeLeft(0);
// Mark as user-initiated disconnect
@@ -3254,11 +3403,18 @@
isUserInitiatedDisconnect: true
});
// Cleanup session state
// Cleanup WebRTC connection
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
}
// Cleanup notification integration
if (notificationIntegrationRef.current) {
notificationIntegrationRef.current.cleanup();
notificationIntegrationRef.current = null;
}
// Clear all connection-related states
setKeyFingerprint('');
setVerificationCode('');
setSecurityLevel(null);
@@ -3271,45 +3427,50 @@
setRemoteVerificationConfirmed(false);
setBothVerificationsConfirmed(false);
// Reset UI to initial state (user-initiated disconnect always clears data)
setConnectionStatus('disconnected');
setShowVerification(false);
setOfferData(null);
setAnswerData(null);
// Reset UI to initial state
setOfferData('');
setAnswerData('');
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
setShowAnswerStep(false);
setKeyFingerprint('');
setVerificationCode('');
setSecurityLevel(null);
setIsVerified(false);
setIsGeneratingKeys(false);
setShowQRCode(false);
setQrCodeUrl('');
setShowQRScanner(false);
setShowQRScannerModal(false);
// Clear messages
setMessages([]);
// Clear console
if (typeof console.clear === 'function') {
console.clear();
}
// Dispatch disconnect events
document.dispatchEvent(new CustomEvent('peer-disconnect'));
document.dispatchEvent(new CustomEvent('disconnected'));
// Dispatch session cleanup event
document.dispatchEvent(new CustomEvent('session-cleanup', {
detail: {
timestamp: Date.now(),
reason: 'manual_disconnect'
}
}));
// Clear data and reset session timer
handleClearData();
setTimeout(() => {
setSessionTimeLeft(0);
}, 500);
handleClearData();
setTimeout(() => {
// Session manager removed - all features enabled by default
}, 1000);
console.log('Disconnect completed successfully');
} catch (error) {
console.error('Error during disconnect:', error);
}
};
const handleSessionActivated = (session) => {
@@ -3444,7 +3605,7 @@
return React.createElement('div', {
className: "minimal-bg min-h-screen"
}, [
React.createElement(EnhancedMinimalHeader, {
window.EnhancedMinimalHeader && React.createElement(window.EnhancedMinimalHeader, {
key: 'header',
status: connectionStatus,
fingerprint: keyFingerprint,
@@ -3453,7 +3614,6 @@
isConnected: isConnectedAndVerified,
securityLevel: securityLevel,
// sessionManager removed - all features enabled by default
sessionTimeLeft: sessionTimeLeft,
webrtcManager: webrtcManagerRef.current
}),
@@ -3512,7 +3672,10 @@
nextQrFrame: nextQrFrame,
prevQrFrame: prevQrFrame,
// PAKE passwords removed - using SAS verification instead
markAnswerCreated: markAnswerCreated
markAnswerCreated: markAnswerCreated,
notificationIntegrationRef: notificationIntegrationRef,
isGeneratingKeys: isGeneratingKeys,
handleCreateOffer: handleCreateOffer
})
),
@@ -3590,7 +3753,8 @@
])
])
])
])
]),
]);
};
@@ -3621,6 +3785,7 @@
if (!window.initializeApp) {
window.initializeApp = initializeApp;
}
}
};
// Render Enhanced Application
ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));
-14
View File
@@ -92,13 +92,6 @@
session: { status: "check", detail: "Random session ID" },
},
{
name: "Payment Integration",
lockbit: { status: "trophy", detail: "Lightning Network satoshis per session + WebLN" },
signal: { status: "times", detail: "No payment system" },
threema: { status: "times", detail: "No payment system" },
session: { status: "times", detail: "No payment system" },
},
{
name: "Metadata Protection",
lockbit: { status: "trophy", detail: "Full metadata encryption + traffic obfuscation" },
signal: { status: "warning", detail: "Sealed Sender (partial)" },
@@ -127,13 +120,6 @@
session: { status: "warning", detail: "Basic key verification" },
},
{
name: "Economic Model",
lockbit: { status: "trophy", detail: "Sustainable pay-per-session model" },
signal: { status: "warning", detail: "Donations and grants dependency" },
threema: { status: "check", detail: "One-time app purchase" },
session: { status: "warning", detail: "Donations dependency" },
},
{
name: "Censorship Resistance",
lockbit: { status: "trophy", detail: "Impossible to block P2P + no servers to target" },
signal: { status: "warning", detail: "Blocked in authoritarian countries" },
+19 -7
View File
@@ -1,28 +1,35 @@
const DownloadApps = () => {
const apps = [
{ id: 'web', name: 'Web App', subtitle: 'Browser Version', icon: 'fas fa-globe', platform: 'Web', isActive: true, url: 'https://securebit.chat/', color: 'green' },
{ id: 'windows', name: 'Windows', subtitle: 'Desktop App', icon: 'fab fa-windows', platform: 'Desktop', isActive: true, url: 'https://securebit.chat/download/windows/SecureBit%20Chat%20Setup%204.1.222.exe', color: 'blue' },
{ id: 'macos', name: 'macOS', subtitle: 'Desktop App', icon: 'fab fa-safari', platform: 'Desktop', isActive: false, url: '#', color: 'gray' },
{ id: 'windows', name: 'Windows', subtitle: 'Desktop App', icon: 'fab fa-windows', platform: 'Desktop', isActive: false, url: 'https://securebit.chat/download/windows/SecureBit%20Chat%20Setup%204.1.222.exe', color: 'blue' },
{ id: 'macos', name: 'macOS', subtitle: 'Desktop App', icon: 'fab fa-safari', platform: 'Desktop', isActive: false, url: '#', color: 'gray' },
{ id: 'linux', name: 'Linux', subtitle: 'Desktop App', icon: 'fab fa-linux', platform: 'Desktop', isActive: false, url: '#', color: 'orange' },
{ id: 'ios', name: 'iOS', subtitle: 'iPhone & iPad', icon: 'fab fa-apple', platform: 'Mobile', isActive: false, url: 'https://apps.apple.com/app/securebit-chat/', color: 'white' },
{ id: 'android', name: 'Android', subtitle: 'Google Play', icon: 'fab fa-android', platform: 'Mobile', isActive: false, url: 'https://play.google.com/store/apps/details?id=com.securebit.chat', color: 'green' }
{ id: 'android', name: 'Android', subtitle: 'Google Play', icon: 'fab fa-android', platform: 'Mobile', isActive: false, url: 'https://play.google.com/store/apps/details?id=com.securebit.chat', color: 'green' },
{ id: 'chrome', name: 'Chrome', subtitle: 'Browser Extension', icon: 'fab fa-chrome', platform: 'Browser', isActive: false, url: '#', color: 'yellow' },
{ id: 'edge', name: 'Edge', subtitle: 'Browser Extension', icon: 'fab fa-edge', platform: 'Browser', isActive: false, url: '#', color: 'blue' },
{ id: 'opera', name: 'Opera', subtitle: 'Browser Extension', icon: 'fab fa-opera', platform: 'Browser', isActive: false, url: '#', color: 'red' },
{ id: 'firefox', name: 'Firefox', subtitle: 'Browser Extension', icon: 'fab fa-firefox-browser', platform: 'Browser', isActive: false, url: '#', color: 'orange' },
];
const handleDownload = (app) => {
if (app.isActive) window.open(app.url, '_blank');
};
const desktopApps = apps.filter(a => a.platform !== 'Mobile');
const desktopApps = apps.filter(a => a.platform === 'Desktop' || a.platform === 'Web');
const mobileApps = apps.filter(a => a.platform === 'Mobile');
const browserApps = apps.filter(a => a.platform === 'Browser');
const cardSize = "w-28 h-28";
const cardSize = "w-28 h-28";
const colorClasses = {
green: 'text-green-500',
blue: 'text-blue-500',
blueios: 'text-blue-600',
gray: 'text-gray-500',
orange: 'text-orange-500',
red: 'text-red-500',
white: 'text-white',
yellow: 'text-yellow-400',
};
const renderAppCard = (app) => (
@@ -65,8 +72,13 @@ const DownloadApps = () => {
),
// Mobile Apps
React.createElement('div', { key: 'mobile-row', className: "flex justify-center gap-6" },
React.createElement('div', { key: 'mobile-row', className: "flex justify-center gap-6 mb-6" },
mobileApps.map(renderAppCard)
),
// Browser Extensions
React.createElement('div', { key: 'browser-row', className: "flex justify-center gap-6" },
browserApps.map(renderAppCard)
)
]);
};
+7 -18
View File
@@ -5,15 +5,14 @@ const EnhancedMinimalHeader = ({
onDisconnect,
isConnected,
securityLevel,
sessionManager,
sessionTimeLeft,
webrtcManager
}) => {
const [currentTimeLeft, setCurrentTimeLeft] = React.useState(sessionTimeLeft || 0);
const [hasActiveSession, setHasActiveSession] = React.useState(false);
const [sessionType, setSessionType] = React.useState('unknown');
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0);
// Added local session state to remove references errors after session timer removal
const [hasActiveSession, setHasActiveSession] = React.useState(false);
const [currentTimeLeft, setCurrentTimeLeft] = React.useState(0);
const [sessionType, setSessionType] = React.useState('unknown');
// ============================================
// FIXED SECURITY UPDATE LOGIC
@@ -159,7 +158,7 @@ const EnhancedMinimalHeader = ({
setHasActiveSession(true);
setCurrentTimeLeft(0);
setSessionType('premium'); // All features enabled
}, [sessionTimeLeft]);
}, []);
React.useEffect(() => {
const handleForceUpdate = (event) => {
@@ -268,8 +267,7 @@ const EnhancedMinimalHeader = ({
details: 'Security verification not available',
isRealData: false,
passedChecks: 0,
totalChecks: 0,
sessionType: 'unknown'
totalChecks: 0
};
console.log('Using fallback security data:', securityData);
}
@@ -277,7 +275,6 @@ const EnhancedMinimalHeader = ({
// Detailed information about the REAL security check
let message = `REAL-TIME SECURITY VERIFICATION\n\n`;
message += `Security Level: ${securityData.level} (${securityData.score}%)\n`;
message += `Session Type: ${securityData.sessionType || 'premium'}\n`;
message += `Verification Time: ${new Date(securityData.timestamp).toLocaleTimeString()}\n`;
message += `Data Source: ${securityData.isRealData ? 'Real Cryptographic Tests' : 'Simulated Data'}\n\n`;
@@ -465,7 +462,6 @@ const EnhancedMinimalHeader = ({
const config = getStatusConfig();
const displaySecurityLevel = isConnected ? (realSecurityLevel || securityLevel) : null;
const shouldShowTimer = hasActiveSession && currentTimeLeft > 0 && window.SessionTimer;
// ============================================
// DATA RELIABILITY INDICATOR
@@ -561,7 +557,7 @@ const EnhancedMinimalHeader = ({
React.createElement('p', {
key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block'
}, 'End-to-end freedom v4.3.120')
}, 'End-to-end freedom v4.4.18')
])
]),
@@ -570,13 +566,6 @@ const EnhancedMinimalHeader = ({
key: 'status-section',
className: 'flex items-center space-x-2 sm:space-x-3'
}, [
// Session Timer - all features enabled by default
shouldShowTimer && React.createElement(window.SessionTimer, {
key: 'session-timer',
timeLeft: currentTimeLeft,
sessionType: sessionType,
onDisconnect: onDisconnect
}),
displaySecurityLevel && React.createElement('div', {
key: 'security-level',
+1 -1
View File
@@ -75,7 +75,7 @@ function Roadmap() {
// current and future phases
{
version: "v4.3.120",
version: "v4.4.18",
title: "Enhanced Security Edition",
status: "current",
date: "Now",
-334
View File
@@ -1,334 +0,0 @@
// SessionTimer Component - v4.3.120 - ECDH + DTLS + SAS
const SessionTimer = ({ timeLeft, sessionType, sessionManager, onDisconnect }) => {
const [currentTime, setCurrentTime] = React.useState(timeLeft || 0);
const [showExpiredMessage, setShowExpiredMessage] = React.useState(false);
const [initialized, setInitialized] = React.useState(false);
const [connectionBroken, setConnectionBroken] = React.useState(false);
const [loggedHidden, setLoggedHidden] = React.useState(false);
React.useEffect(() => {
if (connectionBroken) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer initialization skipped - connection broken');
setLoggedHidden(true);
}
return;
}
let initialTime = 0;
if (sessionManager?.hasActiveSession()) {
initialTime = sessionManager.getTimeLeft();
} else if (timeLeft && timeLeft > 0) {
initialTime = timeLeft;
}
if (initialTime <= 0) {
setCurrentTime(0);
setInitialized(false);
setLoggedHidden(true);
return;
}
if (connectionBroken) {
setCurrentTime(0);
setInitialized(false);
setLoggedHidden(true);
return;
}
setCurrentTime(initialTime);
setInitialized(true);
setLoggedHidden(false);
}, [sessionManager, connectionBroken]);
React.useEffect(() => {
if (connectionBroken) {
if (!loggedHidden) {
setLoggedHidden(true);
}
return;
}
if (timeLeft && timeLeft > 0) {
setCurrentTime(timeLeft);
}
setLoggedHidden(false);
}, [timeLeft, connectionBroken]);
React.useEffect(() => {
if (!initialized) {
return;
}
if (connectionBroken) {
if (!loggedHidden) {
setLoggedHidden(true);
}
return;
}
if (!currentTime || currentTime <= 0 || !sessionManager) {
return;
}
const interval = setInterval(() => {
if (connectionBroken) {
setCurrentTime(0);
clearInterval(interval);
return;
}
if (sessionManager?.hasActiveSession()) {
const newTime = sessionManager.getTimeLeft();
setCurrentTime(newTime);
if (window.DEBUG_MODE && Math.floor(Date.now() / 30000) !== Math.floor((Date.now() - 1000) / 30000)) {
console.log('⏱️ Timer tick:', Math.floor(newTime / 1000) + 's');
}
if (newTime <= 0) {
setShowExpiredMessage(true);
setTimeout(() => setShowExpiredMessage(false), 5000);
clearInterval(interval);
}
} else {
setCurrentTime(0);
clearInterval(interval);
}
}, 1000);
return () => {
clearInterval(interval);
};
}, [initialized, currentTime, sessionManager, connectionBroken]);
React.useEffect(() => {
const handleSessionTimerUpdate = (event) => {
if (connectionBroken) {
return;
}
if (event.detail.timeLeft && event.detail.timeLeft > 0) {
setCurrentTime(event.detail.timeLeft);
}
};
const handleForceHeaderUpdate = (event) => {
if (connectionBroken) {
return;
}
if (sessionManager && sessionManager.hasActiveSession()) {
const newTime = sessionManager.getTimeLeft();
setCurrentTime(newTime);
} else {
setCurrentTime(event.detail.timeLeft);
}
};
const handlePeerDisconnect = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setLoggedHidden(false);
};
const handleNewConnection = (event) => {
setConnectionBroken(false);
setLoggedHidden(false);
};
const handleConnectionCleaned = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
const handleSessionReset = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
const handleSessionCleanup = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
const handleDisconnected = (event) => {
setConnectionBroken(true);
setCurrentTime(0);
setShowExpiredMessage(false);
setInitialized(false);
setLoggedHidden(false);
};
document.addEventListener('session-timer-update', handleSessionTimerUpdate);
document.addEventListener('force-header-update', handleForceHeaderUpdate);
document.addEventListener('peer-disconnect', handlePeerDisconnect);
document.addEventListener('new-connection', handleNewConnection);
document.addEventListener('connection-cleaned', handleConnectionCleaned);
document.addEventListener('session-reset', handleSessionReset);
document.addEventListener('session-cleanup', handleSessionCleanup);
document.addEventListener('disconnected', handleDisconnected);
return () => {
document.removeEventListener('session-timer-update', handleSessionTimerUpdate);
document.removeEventListener('force-header-update', handleForceHeaderUpdate);
document.removeEventListener('peer-disconnect', handlePeerDisconnect);
document.removeEventListener('new-connection', handleNewConnection);
document.removeEventListener('connection-cleaned', handleConnectionCleaned);
document.removeEventListener('session-reset', handleSessionReset);
document.removeEventListener('session-cleanup', handleSessionCleanup);
document.removeEventListener('disconnected', handleDisconnected);
};
}, [sessionManager]);
if (showExpiredMessage) {
return React.createElement('div', {
className: 'session-timer expired flex items-center space-x-2 px-3 py-1.5 rounded-lg animate-pulse',
style: { background: 'linear-gradient(135deg, rgba(239, 68, 68, 0.2) 0%, rgba(220, 38, 38, 0.2) 100%)' }
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-exclamation-triangle text-red-400'
}),
React.createElement('span', {
key: 'message',
className: 'text-red-400 text-sm font-medium'
}, 'Session Expired!')
]);
}
if (!sessionManager) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - no sessionManager');
setLoggedHidden(true);
}
return null;
}
if (connectionBroken) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - connection broken');
setLoggedHidden(true);
}
return null;
}
if (!currentTime || currentTime <= 0) {
if (!loggedHidden) {
console.log('⏱️ SessionTimer hidden - no time left, currentTime:', currentTime);
setLoggedHidden(true);
}
return null;
}
if (loggedHidden) {
setLoggedHidden(false);
}
const totalMinutes = Math.floor(currentTime / (60 * 1000));
const totalSeconds = Math.floor(currentTime / 1000);
const isDemo = sessionType === 'demo';
const isWarning = isDemo ? totalMinutes <= 2 : totalMinutes <= 10;
const isCritical = isDemo ? totalSeconds <= 60 : totalMinutes <= 5;
const formatTime = (ms) => {
const hours = Math.floor(ms / (60 * 60 * 1000));
const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000));
const seconds = Math.floor((ms % (60 * 1000)) / 1000);
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
};
const getTimerStyle = () => {
const totalDuration = sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000;
const timeProgress = (totalDuration - currentTime) / totalDuration;
let backgroundColor, textColor, iconColor, iconClass, shouldPulse;
if (timeProgress <= 0.33) {
backgroundColor = 'linear-gradient(135deg, rgba(34, 197, 94, 0.15) 0%, rgba(22, 163, 74, 0.15) 100%)';
textColor = 'text-green-400';
iconColor = 'text-green-400';
iconClass = 'fas fa-clock';
shouldPulse = false;
} else if (timeProgress <= 0.66) {
backgroundColor = 'linear-gradient(135deg, rgba(234, 179, 8, 0.15) 0%, rgba(202, 138, 4, 0.15) 100%)';
textColor = 'text-yellow-400';
iconColor = 'text-yellow-400';
iconClass = 'fas fa-clock';
shouldPulse = false;
} else {
backgroundColor = 'linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(220, 38, 38, 0.15) 100%)';
textColor = 'text-red-400';
iconColor = 'text-red-400';
iconClass = 'fas fa-exclamation-triangle';
shouldPulse = true;
}
return { backgroundColor, textColor, iconColor, iconClass, shouldPulse };
};
const timerStyle = getTimerStyle();
const handleTimerClick = () => {
if (onDisconnect && typeof onDisconnect === 'function') {
onDisconnect();
}
};
return React.createElement('div', {
className: `session-timer flex items-center space-x-2 px-3 py-1.5 rounded-lg transition-all duration-500 cursor-pointer hover:opacity-80 ${
isDemo ? 'demo-session' : ''
} ${timerStyle.shouldPulse ? 'animate-pulse' : ''}`,
style: { background: timerStyle.backgroundColor },
onClick: handleTimerClick,
title: 'Click to disconnect and clear session'
}, [
React.createElement('i', {
key: 'icon',
className: `${timerStyle.iconClass} ${timerStyle.iconColor}`
}),
React.createElement('span', {
key: 'time',
className: `text-sm font-mono font-semibold ${timerStyle.textColor}`
}, formatTime(currentTime)),
React.createElement('div', {
key: 'progress',
className: 'ml-2 w-16 h-1 bg-gray-700 rounded-full overflow-hidden'
}, [
React.createElement('div', {
key: 'progress-bar',
className: `${timerStyle.textColor.replace('text-', 'bg-')} h-full rounded-full transition-all duration-500`,
style: {
width: `${Math.max(0, Math.min(100, (currentTime / (sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000)) * 100))}%`
}
})
])
]);
};
window.SessionTimer = SessionTimer;
window.updateSessionTimer = (newTimeLeft, newSessionType) => {
document.dispatchEvent(new CustomEvent('session-timer-update', {
detail: { timeLeft: newTimeLeft, sessionType: newSessionType }
}));
};
+123 -20
View File
@@ -224,13 +224,18 @@ class EnhancedSecureCryptoUtils {
// Generate secure password for data exchange
static generateSecurePassword() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
const charCount = chars.length;
const length = 32;
const randomValues = new Uint32Array(length);
crypto.getRandomValues(randomValues);
let password = '';
// Use rejection sampling to avoid bias
for (let i = 0; i < length; i++) {
password += chars[randomValues[i] % chars.length];
let randomValue;
do {
randomValue = crypto.getRandomValues(new Uint32Array(1))[0];
} while (randomValue >= 4294967296 - (4294967296 % charCount)); // Reject biased values
password += chars[randomValue % charCount];
}
return password;
}
@@ -1204,12 +1209,22 @@ class EnhancedSecureCryptoUtils {
// Verify ECDSA signature (P-384 or P-256)
static async verifySignature(publicKey, signature, data) {
try {
console.log('DEBUG: verifySignature called with:', {
publicKey: publicKey,
signature: signature,
data: data
});
const encoder = new TextEncoder();
const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;
const signatureBuffer = new Uint8Array(signature);
console.log('DEBUG: verifySignature dataBuffer:', dataBuffer);
console.log('DEBUG: verifySignature signatureBuffer:', signatureBuffer);
// Try SHA-384 first, fallback to SHA-256
try {
console.log('DEBUG: Trying SHA-384 verification...');
const isValid = await crypto.subtle.verify(
{
name: 'ECDSA',
@@ -1220,6 +1235,8 @@ class EnhancedSecureCryptoUtils {
dataBuffer
);
console.log('DEBUG: SHA-384 verification result:', isValid);
EnhancedSecureCryptoUtils.secureLog.log('info', 'Signature verification completed (SHA-384)', {
isValid,
dataSize: dataBuffer.length
@@ -1227,8 +1244,10 @@ class EnhancedSecureCryptoUtils {
return isValid;
} catch (sha384Error) {
console.log('DEBUG: SHA-384 verification failed, trying SHA-256:', sha384Error);
EnhancedSecureCryptoUtils.secureLog.log('warn', 'SHA-384 verification failed, trying SHA-256', { error: sha384Error.message });
console.log('DEBUG: Trying SHA-256 verification...');
const isValid = await crypto.subtle.verify(
{
name: 'ECDSA',
@@ -1239,6 +1258,8 @@ class EnhancedSecureCryptoUtils {
dataBuffer
);
console.log('DEBUG: SHA-256 verification result:', isValid);
EnhancedSecureCryptoUtils.secureLog.log('info', 'Signature verification completed (SHA-256 fallback)', {
isValid,
dataSize: dataBuffer.length
@@ -1583,6 +1604,12 @@ class EnhancedSecureCryptoUtils {
// Import and verify signed public key
static async importSignedPublicKey(signedPackage, verifyingKey, expectedKeyType = 'ECDH') {
try {
console.log('DEBUG: importSignedPublicKey called with:', {
signedPackage: signedPackage,
verifyingKey: verifyingKey,
expectedKeyType: expectedKeyType
});
// Validate package structure
if (!signedPackage || typeof signedPackage !== 'object') {
throw new Error('Invalid signed package format');
@@ -1609,7 +1636,11 @@ class EnhancedSecureCryptoUtils {
// Verify signature
const packageCopy = { keyType, keyData, timestamp, version };
const packageString = JSON.stringify(packageCopy);
console.log('DEBUG: Web version package string for verification:', packageString);
console.log('DEBUG: Web version signature to verify:', signature);
console.log('DEBUG: Web version verifying key:', verifyingKey);
const isValidSignature = await EnhancedSecureCryptoUtils.verifySignature(verifyingKey, signature, packageString);
console.log('DEBUG: Web version signature verification result:', isValidSignature);
if (!isValidSignature) {
throw new Error('Invalid signature on key package - possible MITM attack');
@@ -2241,9 +2272,9 @@ class EnhancedSecureCryptoUtils {
throw new Error('Challenge mismatch - possible replay attack');
}
// Check response time (max 5 minutes)
// Check response time (max 30 minutes for better UX)
const responseAge = Date.now() - proof.responseTimestamp;
if (responseAge > 300000) {
if (responseAge > 1800000) {
throw new Error('Proof response expired');
}
@@ -2296,11 +2327,19 @@ class EnhancedSecureCryptoUtils {
// Generate verification code for out-of-band authentication
static generateVerificationCode() {
const chars = '0123456789ABCDEF';
const charCount = chars.length;
let result = '';
const values = crypto.getRandomValues(new Uint8Array(6));
// Use rejection sampling to avoid bias
for (let i = 0; i < 6; i++) {
result += chars[values[i] % chars.length];
let randomByte;
do {
randomByte = crypto.getRandomValues(new Uint8Array(1))[0];
} while (randomByte >= 256 - (256 % charCount)); // Reject biased values
result += chars[randomByte % charCount];
}
return result.match(/.{1,2}/g).join('-');
}
@@ -2435,7 +2474,7 @@ class EnhancedSecureCryptoUtils {
}
const messageAge = Date.now() - metadata.timestamp;
if (messageAge > 300000) {
if (messageAge > 1800000) { // 30 minutes for better UX
throw new Error('Message expired (older than 5 minutes)');
}
@@ -2484,22 +2523,86 @@ class EnhancedSecureCryptoUtils {
}
}
// Enhanced input sanitization
// Enhanced input sanitization with iterative processing to handle edge cases
static sanitizeMessage(message) {
if (typeof message !== 'string') {
throw new Error('Message must be a string');
}
return message
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/javascript:/gi, '')
.replace(/data:/gi, '')
.replace(/vbscript:/gi, '')
.replace(/onload\s*=/gi, '')
.replace(/onerror\s*=/gi, '')
.replace(/onclick\s*=/gi, '')
.trim()
.substring(0, 2000); // Increased limit
// Helper function to apply replacement until stable
function replaceUntilStable(str, pattern, replacement = '') {
let previous;
do {
previous = str;
str = str.replace(pattern, replacement);
} while (str !== previous);
return str;
}
// Define all dangerous patterns that need to be removed
const dangerousPatterns = [
// Script tags with various formats
/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi,
/<script\b[^>]*>[\s\S]*?<\/script\s+[^>]*>/gi,
/<script\b[^>]*>[\s\S]*$/gi,
// Other dangerous tags
/<iframe\b[^>]*>[\s\S]*?<\/iframe\s*>/gi,
/<object\b[^>]*>[\s\S]*?<\/object\s*>/gi,
/<embed\b[^>]*>/gi,
/<applet\b[^>]*>[\s\S]*?<\/applet\s*>/gi,
/<style\b[^>]*>[\s\S]*?<\/style\s*>/gi,
// Dangerous protocols
/javascript\s*:/gi,
/data\s*:/gi,
/vbscript\s*:/gi,
// Event handlers
/on\w+\s*=/gi,
// HTML comments
/<!--[\s\S]*?-->/g,
// Link and meta tags with javascript
/<link\b[^>]*javascript[^>]*>/gi,
/<meta\b[^>]*javascript[^>]*>/gi,
// Any remaining script-like content
/<[^>]*script[^>]*>/gi,
/<[^>]*on\w+\s*=[^>]*>/gi
];
// Iterative sanitization to handle edge cases
let sanitized = message;
let previousLength;
let iterations = 0;
const maxIterations = 10; // Prevent infinite loops
do {
previousLength = sanitized.length;
// Apply all dangerous patterns with stable replacement
for (const pattern of dangerousPatterns) {
sanitized = replaceUntilStable(sanitized, pattern);
}
// Additional cleanup for edge cases - each applied until stable
sanitized = replaceUntilStable(sanitized, /<[^>]*>/g);
sanitized = replaceUntilStable(sanitized, /^\w+:/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*["'][^"']*["']/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*[^>\s]+/gi);
// Single character removal is inherently safe
sanitized = sanitized.replace(/[<>]/g, '').trim();
iterations++;
} while (sanitized.length !== previousLength && iterations < maxIterations);
// Final security pass with stable replacements
sanitized = replaceUntilStable(sanitized, /<[^>]*>/g);
sanitized = replaceUntilStable(sanitized, /^\w+:/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*["'][^"']*["']/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*[^>\s]+/gi);
// Final single character cleanup
sanitized = sanitized.replace(/[<>]/g, '').trim();
return sanitized.substring(0, 2000); // Limit length
}
// Generate cryptographically secure salt (64 bytes for enhanced security)
+161 -108
View File
@@ -167,8 +167,6 @@ class EnhancedSecureWebRTCManager {
} : null;
};
this._secureLog('info', '🔒 Enhanced WebRTC Manager initialized with secure API');
this.currentSessionType = null;
this.currentSecurityLevel = 'basic';
this.sessionConstraints = null;
this.peerConnection = null;
this.dataChannel = null;
@@ -406,12 +404,13 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// 3. Fake Traffic Generation
this.fakeTrafficConfig = {
enabled: this._config.fakeTraffic.enabled,
minInterval: this._config.fakeTraffic.minInterval,
maxInterval: this._config.fakeTraffic.maxInterval,
minSize: this._config.fakeTraffic.minSize,
maxSize: this._config.fakeTraffic.maxSize,
patterns: this._config.fakeTraffic.patterns
enabled: this._config.fakeTraffic?.enabled || false,
minInterval: this._config.fakeTraffic?.minInterval || 15000,
maxInterval: this._config.fakeTraffic?.maxInterval || 30000,
minSize: this._config.fakeTraffic?.minSize || 64,
maxSize: this._config.fakeTraffic?.maxSize || 1024,
patterns: this._config.fakeTraffic?.patterns || ['heartbeat', 'status', 'ping'],
randomDecoyIntervals: this._config.fakeTraffic?.randomDecoyIntervals || true
};
this.fakeTrafficTimer = null;
this.lastFakeTraffic = 0;
@@ -1771,8 +1770,20 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Malicious pattern detection
this._maliciousPatterns = [
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, // Script tags
/javascript:/gi, // JavaScript protocol
// Enhanced script tag detection that handles edge cases
/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, // Standard </script>
/<script\b[^>]*>[\s\S]*?<\/script\s+[^>]*>/gi, // </script with attributes>
/<script\b[^>]*>[\s\S]*$/gi, // Malformed script tags without closing
// Additional dangerous tags
/<iframe\b[^>]*>[\s\S]*?<\/iframe\s*>/gi, // iframe tags
/<object\b[^>]*>[\s\S]*?<\/object\s*>/gi, // object tags
/<embed\b[^>]*>/gi, // embed tags
/<applet\b[^>]*>[\s\S]*?<\/applet\s*>/gi, // applet tags
/<style\b[^>]*>[\s\S]*?<\/style\s*>/gi, // style tags
// Dangerous protocols
/javascript\s*:/gi, // JavaScript protocol
/data\s*:/gi, // Data protocol
/vbscript\s*:/gi, // VBScript protocol
/data:text\/html/gi, // Data URLs with HTML
/on\w+\s*=/gi, // Event handlers
/eval\s*\(/gi, // eval() calls
@@ -2311,7 +2322,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Create simple getSecurityStatus method
secureAPI.getSecurityStatus = () => ({
securityLevel: this.currentSecurityLevel || 'basic',
securityLevel: 'maximum',
stage: 'initialized',
activeFeaturesCount: Object.values(this.securityFeatures || {}).filter(Boolean).length
});
@@ -3451,7 +3462,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Validate timestamp (prevent very old messages)
const now = Date.now();
const messageAge = now - aad.timestamp;
if (messageAge > 300000) { // 5 minutes
if (messageAge > 1800000) { // 30 minutes for better UX
throw new Error('AAD timestamp too old - possible replay attack');
}
@@ -3622,7 +3633,14 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
const dv = new DataView(bits);
const n = (dv.getUint32(0) ^ dv.getUint32(4)) >>> 0;
const sasCode = String(n % 10_000_000).padStart(7, '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');
this._secureLog('info', 'SAS code computed successfully', {
@@ -4463,32 +4481,69 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
}
}
// Generate fingerprint mask for anti-fingerprinting with enhanced randomization
generateFingerprintMask() {
// Enhanced randomization to prevent side-channel attacks
const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
const mask = {
timingOffset: cryptoRandom[0] % 1000 + cryptoRandom[1] % 500, // 0-1500ms
sizeVariation: (cryptoRandom[2] % 50 + 75) / 100, // 0.75 to 1.25
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), // Increased size
headerVariations: [
'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature',
'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private'
],
noiseIntensity: cryptoRandom[3] % 100 + 50, // 50-150%
sizeMultiplier: (cryptoRandom[4] % 50 + 75) / 100, // 0.75-1.25
timingVariation: cryptoRandom[5] % 1000 + 100 // 100-1100ms
};
return mask;
}
// Helper function: unbiased integer in [min, max]
getSafeRandomInt(min, max) {
if (!Number.isInteger(min) || !Number.isInteger(max)) {
throw new Error('getSafeRandomInt requires integer min and max');
}
if (min >= max) {
throw new Error('min must be less than max');
}
const range = max - min + 1;
const bitsNeeded = Math.ceil(Math.log2(range));
const bytesNeeded = Math.ceil(bitsNeeded / 8);
const mask = (1 << bitsNeeded) - 1;
let randomValue;
do {
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
randomValue = 0;
for (let i = 0; i < bytesNeeded; i++) {
randomValue = (randomValue * 256) + randomBytes[i];
}
randomValue = randomValue & mask;
} while (randomValue >= range);
return min + randomValue;
}
getSafeRandomFloat(minFloat, maxFloat, steps = 1000) {
if (typeof minFloat !== 'number' || typeof maxFloat !== 'number') {
throw new Error('getSafeRandomFloat requires numeric min and max');
}
if (minFloat >= maxFloat) {
throw new Error('minFloat must be less than maxFloat');
}
const randomIndex = this.getSafeRandomInt(0, steps);
const step = (maxFloat - minFloat) / steps;
return minFloat + (randomIndex * step);
}
generateFingerprintMask() {
const mask = {
timingOffset: this.getSafeRandomInt(0, 1500),
sizeVariation: this.getSafeRandomFloat(0.75, 1.25, 1000),
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
headerVariations: [
'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature',
'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private'
],
noiseIntensity: this.getSafeRandomInt(50, 150),
sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25, 1000),
timingVariation: this.getSafeRandomInt(100, 1100)
};
return mask;
}
// Security configuration - all features enabled by default
configureSecurityForSession(sessionType, securityLevel) {
this._secureLog('info', `🔧 Configuring security for ${sessionType} session (${securityLevel} level)`);
this.currentSessionType = sessionType;
this.currentSecurityLevel = securityLevel;
configureSecurityForSession() {
this._secureLog('info', '🔧 Configuring security - all features enabled by default');
// All security features are enabled by default - no payment required
this.sessionConstraints = {};
@@ -4499,7 +4554,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this.applySessionConstraints();
this._secureLog('info', `✅ Security configured for ${sessionType} - all features enabled`, { constraints: this.sessionConstraints });
this._secureLog('info', '✅ Security configured - all features enabled', { constraints: this.sessionConstraints });
if (!this._validateCryptographicSecurity()) {
this._secureLog('error', '🚨 CRITICAL: Cryptographic security validation failed after session configuration');
@@ -4507,7 +4562,6 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
if (this.onStatusChange) {
this.onStatusChange('security_breach', {
type: 'crypto_security_failure',
sessionType: sessionType,
message: 'Cryptographic security validation failed after session configuration'
});
}
@@ -4644,27 +4698,21 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Security Level Notification
notifySecurityLevel() {
// Avoid duplicate notifications for the same security level
if (this.lastSecurityLevelNotification === this.currentSecurityLevel) {
// Avoid duplicate notifications
if (this.lastSecurityLevelNotification === 'maximum') {
return; // prevent duplication
}
this.lastSecurityLevelNotification = this.currentSecurityLevel;
this.lastSecurityLevelNotification = 'maximum';
const levelMessages = {
'basic': '🔒 Basic Security Active - Demo session with essential protection',
'enhanced': '🔐 Enhanced Security Active - Paid session with advanced protection',
'maximum': '🛡️ Maximum Security Active - Premium session with complete protection'
};
const message = levelMessages[this.currentSecurityLevel] || levelMessages['basic'];
const message = '🛡️ Maximum Security Active - All features enabled';
if (this.onMessage) {
this.deliverMessageToUI(message, 'system');
}
// Showing details of functions for paid sessions
if (this.currentSecurityLevel !== 'basic' && this.onMessage) {
// Showing details of active features
if (this.onMessage) {
const activeFeatures = Object.entries(this.securityFeatures)
.filter(([key, value]) => value === true)
.map(([key]) => key.replace('has', '').replace(/([A-Z])/g, ' $1').trim().toLowerCase())
@@ -4926,8 +4974,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// FIX: Increase intervals to reduce load
const nextInterval = this.fakeTrafficConfig.randomDecoyIntervals ?
Math.random() * (this.fakeTrafficConfig.maxInterval - this.fakeTrafficConfig.minInterval) +
this.fakeTrafficConfig.minInterval :
this.getUnbiasedRandomInRange(this.fakeTrafficConfig.minInterval, Math.min(this.fakeTrafficConfig.maxInterval, 60000)) : // Cap at 60 seconds
this.fakeTrafficConfig.minInterval;
// Minimum interval 15 seconds for stability
@@ -4943,7 +4990,10 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
};
// Start fake traffic generation with longer initial delay
const initialDelay = Math.random() * this.fakeTrafficConfig.maxInterval + EnhancedSecureWebRTCManager.TIMEOUTS.DECOY_INITIAL_DELAY; // Add 5 seconds minimum
// Use a reasonable range for initial delay (5-30 seconds)
const minDelay = EnhancedSecureWebRTCManager.TIMEOUTS.DECOY_INITIAL_DELAY;
const maxDelay = Math.min(this.fakeTrafficConfig.maxInterval, 30000); // Cap at 30 seconds
const initialDelay = this.getUnbiasedRandomInRange(minDelay, maxDelay);
this.fakeTrafficTimer = setTimeout(sendFakeMessage, initialDelay);
}
@@ -4955,13 +5005,10 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
}
generateFakeMessage() {
const pattern = this.fakeTrafficConfig.patterns[
Math.floor(Math.random() * this.fakeTrafficConfig.patterns.length)
];
const patternIndex = this.getUnbiasedRandomInRange(0, this.fakeTrafficConfig.patterns.length - 1);
const pattern = this.fakeTrafficConfig.patterns[patternIndex];
const size = Math.floor(Math.random() *
(this.fakeTrafficConfig.maxSize - this.fakeTrafficConfig.minSize + 1)) +
this.fakeTrafficConfig.minSize;
const size = this.getUnbiasedRandomInRange(this.fakeTrafficConfig.minSize, this.fakeTrafficConfig.maxSize);
const fakeData = crypto.getRandomValues(new Uint8Array(size));
@@ -5562,7 +5609,7 @@ async processOrderedPackets() {
addNoise(data) {
const dataArray = new Uint8Array(data);
const noiseSize = Math.floor(Math.random() * 32) + 8; // 8-40 bytes
const noiseSize = this.getUnbiasedRandomInRange(8, 40); // 8-40 bytes
const noise = crypto.getRandomValues(new Uint8Array(noiseSize));
const result = new Uint8Array(dataArray.length + noiseSize);
@@ -5607,12 +5654,12 @@ async processOrderedPackets() {
addRandomHeaders(data) {
const dataArray = new Uint8Array(data);
const headerCount = Math.floor(Math.random() * 3) + 1; // 1-3 headers
const headerCount = this.getUnbiasedRandomInRange(1, 3); // 1-3 headers
let totalHeaderSize = 0;
// Calculate total header size
for (let i = 0; i < headerCount; i++) {
totalHeaderSize += 4 + Math.floor(Math.random() * 16) + 4; // size + data + checksum
totalHeaderSize += 4 + this.getUnbiasedRandomInRange(0, 15) + 4; // size + data + checksum
}
const result = new Uint8Array(totalHeaderSize + dataArray.length);
@@ -5620,10 +5667,21 @@ async processOrderedPackets() {
// Add random headers
for (let i = 0; i < headerCount; i++) {
const headerName = this.fingerprintMask.headerVariations[
Math.floor(Math.random() * this.fingerprintMask.headerVariations.length)
];
const headerData = crypto.getRandomValues(new Uint8Array(Math.floor(Math.random() * 16) + 4));
// Generate unbiased random index for header selection
let headerIndex;
do {
headerIndex = crypto.getRandomValues(new Uint8Array(1))[0];
} while (headerIndex >= 256 - (256 % this.fingerprintMask.headerVariations.length));
const headerName = this.fingerprintMask.headerVariations[headerIndex % this.fingerprintMask.headerVariations.length];
// Generate unbiased random size for header data (4-19 bytes)
let headerSize;
do {
headerSize = crypto.getRandomValues(new Uint8Array(1))[0];
} while (headerSize >= 256 - (256 % 16));
const headerData = crypto.getRandomValues(new Uint8Array((headerSize % 16) + 4));
// Header structure: [size:4][name:4][data:variable][checksum:4]
const headerView = new DataView(result.buffer, offset);
@@ -6520,11 +6578,10 @@ async processMessage(data) {
if (this.sessionConstraints?.hasAntiFingerprinting) {
this.securityFeatures.hasAntiFingerprinting = true;
this.antiFingerprintingConfig.enabled = true;
if (this.currentSecurityLevel === 'enhanced') {
this.antiFingerprintingConfig.randomizeSizes = false;
this.antiFingerprintingConfig.maskPatterns = false;
this.antiFingerprintingConfig.useRandomHeaders = false;
}
// Enable full anti-fingerprinting features
this.antiFingerprintingConfig.randomizeSizes = true;
this.antiFingerprintingConfig.maskPatterns = true;
this.antiFingerprintingConfig.useRandomHeaders = true;
}
this.notifySecurityUpgrade(2);
@@ -6535,10 +6592,7 @@ async processMessage(data) {
// Method to enable Stage 3 features (traffic obfuscation)
enableStage3Security() {
if (this.currentSecurityLevel !== 'maximum') {
this._secureLog('info', '🔒 Stage 3 features only available for premium sessions');
return;
}
this._secureLog('info', '🔒 Enabling Stage 3 features (traffic obfuscation)');
if (this.sessionConstraints?.hasMessageChunking) {
this.securityFeatures.hasMessageChunking = true;
@@ -6559,10 +6613,7 @@ async processMessage(data) {
// Method for enabling Stage 4 functions (maximum safety)
enableStage4Security() {
if (this.currentSecurityLevel !== 'maximum') {
this._secureLog('info', '🔒 Stage 4 features only available for premium sessions');
return;
}
this._secureLog('info', '🔒 Enabling Stage 4 features (maximum safety)');
if (this.sessionConstraints?.hasDecoyChannels && this.isConnected() && this.isVerified) {
this.securityFeatures.hasDecoyChannels = true;
@@ -6603,14 +6654,11 @@ async processMessage(data) {
.filter(([key, value]) => value === true)
.map(([key]) => key);
const stage = this.currentSecurityLevel === 'basic' ? 1 :
this.currentSecurityLevel === 'enhanced' ? 2 :
this.currentSecurityLevel === 'maximum' ? 4 : 1;
const stage = 4; // Maximum security stage
return {
stage: stage,
sessionType: this.currentSessionType,
securityLevel: this.currentSecurityLevel,
securityLevel: 'maximum',
activeFeatures: activeFeatures,
totalFeatures: Object.keys(this.securityFeatures).length,
activeFeaturesCount: activeFeatures.length,
@@ -6730,12 +6778,7 @@ async processMessage(data) {
// Method for automatic feature enablement with stability check
async autoEnableSecurityFeatures() {
if (this.currentSessionType === 'demo') {
this._secureLog('info', 'Demo session - keeping basic security only');
await this.calculateAndReportSecurityLevel();
this.notifySecurityUpgrade(1);
return;
}
this._secureLog('info', 'Starting graduated security activation - all features enabled');
const checkStability = () => {
const isStable = this.isConnected() &&
@@ -6746,18 +6789,15 @@ async processMessage(data) {
return isStable;
};
this._secureLog('info', ` ${this.currentSessionType} session - starting graduated security activation`);
await this.calculateAndReportSecurityLevel();
this.notifySecurityUpgrade(1);
if (this.currentSecurityLevel === 'enhanced' || this.currentSecurityLevel === 'maximum') {
// Enable all security stages progressively
setTimeout(async () => {
if (checkStability()) {
this.enableStage2Security();
await this.calculateAndReportSecurityLevel();
// For maximum sessions, turn on Stage 3 and 4
if (this.currentSecurityLevel === 'maximum') {
setTimeout(async () => {
if (checkStability()) {
this.enableStage3Security();
@@ -6771,10 +6811,8 @@ async processMessage(data) {
}, 20000);
}
}, 15000);
}
}
}, 10000);
}
}
// ============================================
@@ -9581,9 +9619,9 @@ async processMessage(data) {
throw new Error('Missing required security fields in offer data possible MITM attack');
}
// Replay attack protection (window reduced to 5 minutes)
// Replay attack protection (extended to 30 minutes for better UX)
const offerAge = Date.now() - timestamp;
const MAX_OFFER_AGE = 300000; // 5 minutes instead of 1 hour
const MAX_OFFER_AGE = 1800000; // 30 minutes for better user experience
if (offerAge > MAX_OFFER_AGE) {
this._secureLog('error', 'Offer data is too old - possible replay attack', {
@@ -11116,6 +11154,10 @@ async processMessage(data) {
throw new Error('Connection lost during message preparation');
}
// Note: master key session is managed by SecureMasterKeyManager
// Do not gate here on _isUnlocked to avoid false blocking
// Session timers are handled inside the master key manager on key access
// Validate keys inside critical section
if (!this.encryptionKey || !this.macKey || !this.metadataKey) {
throw new Error('Encryption keys not initialized');
@@ -11172,7 +11214,19 @@ async processMessage(data) {
operationId: operationId,
errorType: error.constructor.name
});
throw error;
// Improved user-facing error messages (English)
if (error.message.includes('Session expired')) {
throw new Error('Session expired. Please enter your password to unlock.');
} else if (error.message.includes('Encryption keys not initialized')) {
throw new Error('Session expired due to inactivity. Please reconnect to the chat.');
} else if (error.message.includes('Connection lost')) {
throw new Error('Connection lost. Please check your Internet connection.');
} else if (error.message.includes('Rate limit exceeded')) {
throw new Error('Message rate limit exceeded. Please wait before sending another message.');
} else {
throw error;
}
}
}, 2000); // Reduced timeout for crypto operations
}
@@ -11528,7 +11582,7 @@ async processMessage(data) {
if (error.message.includes('Connection not ready')) {
throw new Error('Connection not ready for file transfer. Check connection status.');
} else if (error.message.includes('Encryption keys not initialized')) {
throw new Error('Encryption keys not initialized. Try reconnecting.');
throw new Error('Session expired due to inactivity. Please reconnect to the chat.');
} else if (error.message.includes('Transfer timeout')) {
throw new Error('File transfer timeout. Check connection and try again.');
} else {
@@ -11644,11 +11698,10 @@ async processMessage(data) {
// Update session state
this.currentSession = sessionData;
this.sessionManager = sessionData.sessionManager;
// FIX: More lenient checks for activation
const hasKeys = !!(this.encryptionKey && this.macKey);
const hasSession = !!(this.sessionManager && (this.sessionManager.hasActiveSession?.() || sessionData.sessionId));
const hasSession = !!(sessionData.sessionId);
// Force connection status if there is an active session
if (hasSession) {
@@ -12508,7 +12561,7 @@ class SecureKeyStorage {
// Additional info
connectionId: this.connectionId,
keyFingerprint: this.keyFingerprint,
currentSecurityLevel: this.currentSecurityLevel,
currentSecurityLevel: 'maximum',
timestamp: Date.now()
};
@@ -13056,8 +13109,8 @@ class SecureMasterKeyManager {
this._lastActivity = null;
// Configuration
this._sessionTimeoutMs = 15 * 60 * 1000; // 15 minutes
this._inactivityTimeoutMs = 5 * 60 * 1000; // 5 minutes
this._sessionTimeoutMs = 60 * 60 * 1000; // 60 minutes (увеличено с 15 минут)
this._inactivityTimeoutMs = 30 * 60 * 1000; // 30 minutes (увеличено с 5 минут)
// PBKDF2 parameters
this._pbkdf2Iterations = 100000; // 100k iterations
@@ -13072,8 +13125,8 @@ class SecureMasterKeyManager {
this._onSessionExpired = null;
this._onUnlocked = null;
// Setup event listeners
this._setupEventListeners();
// Setup event listeners (disabled for better UX - no auto-disconnect)
// this._setupEventListeners();
}
/**
@@ -0,0 +1,279 @@
/**
* Notification Integration Module for SecureBit WebRTC Chat
* Integrates secure notifications with existing WebRTC architecture
*
* @version 1.0.0
* @author SecureBit Team
* @license MIT
*/
import { SecureChatNotificationManager } from './SecureNotificationManager.js';
class NotificationIntegration {
constructor(webrtcManager) {
this.webrtcManager = webrtcManager;
this.notificationManager = new SecureChatNotificationManager({
maxQueueSize: 10,
rateLimitMs: 1000, // Reduced from 2000ms to 1000ms
trustedOrigins: [
window.location.origin,
// Add other trusted origins for CDN icons
]
});
this.isInitialized = false;
this.originalOnMessage = null;
this.originalOnStatusChange = null;
this.processedMessages = new Set(); // Track processed messages to avoid duplicates
}
/**
* Initialize notification integration
* @returns {Promise<boolean>} Initialization success
*/
async init() {
try {
if (this.isInitialized) {
return true;
}
// Store original callbacks
this.originalOnMessage = this.webrtcManager.onMessage;
this.originalOnStatusChange = this.webrtcManager.onStatusChange;
// Wrap the original onMessage callback
this.webrtcManager.onMessage = (message, type) => {
this.handleIncomingMessage(message, type);
// Call original callback if it exists
if (this.originalOnMessage) {
this.originalOnMessage(message, type);
}
};
// Wrap the original onStatusChange callback
this.webrtcManager.onStatusChange = (status) => {
this.handleStatusChange(status);
// Call original callback if it exists
if (this.originalOnStatusChange) {
this.originalOnStatusChange(status);
}
};
// Also hook into the deliverMessageToUI method if it exists
if (this.webrtcManager.deliverMessageToUI) {
this.originalDeliverMessageToUI = this.webrtcManager.deliverMessageToUI.bind(this.webrtcManager);
this.webrtcManager.deliverMessageToUI = (message, type) => {
this.handleIncomingMessage(message, type);
this.originalDeliverMessageToUI(message, type);
};
}
this.isInitialized = true;
return true;
} catch (error) {
return false;
}
}
/**
* Handle incoming messages and trigger notifications
* @param {*} message - Message content
* @param {string} type - Message type
* @private
*/
handleIncomingMessage(message, type) {
try {
// Create a unique key for this message to avoid duplicates
const messageKey = `${type}:${typeof message === 'string' ? message : JSON.stringify(message)}`;
// Skip if we've already processed this message
if (this.processedMessages.has(messageKey)) {
return;
}
// Mark message as processed
this.processedMessages.add(messageKey);
// Clean up old processed messages (keep only last 100)
if (this.processedMessages.size > 100) {
const messagesArray = Array.from(this.processedMessages);
this.processedMessages.clear();
messagesArray.slice(-50).forEach(msg => this.processedMessages.add(msg));
}
// Only process chat messages, not system messages
if (type === 'system' || type === 'file-transfer' || type === 'heartbeat') {
return;
}
// Extract message information
const messageInfo = this.extractMessageInfo(message, type);
if (!messageInfo) {
return;
}
// Send notification
const notificationResult = this.notificationManager.notify(
messageInfo.senderName,
messageInfo.text,
{
icon: messageInfo.senderAvatar,
senderId: messageInfo.senderId,
onClick: (senderId) => {
this.focusChatWindow();
}
}
);
} catch (error) {
// Handle error silently
}
}
/**
* Handle status changes
* @param {string} status - Connection status
* @private
*/
handleStatusChange(status) {
try {
// Clear notifications when connection is lost
if (status === 'disconnected' || status === 'failed') {
this.notificationManager.clearNotificationQueue();
this.notificationManager.resetUnreadCount();
}
} catch (error) {
// Handle error silently
}
}
/**
* Extract message information for notifications
* @param {*} message - Message content
* @param {string} type - Message type
* @returns {Object|null} Extracted message info or null
* @private
*/
extractMessageInfo(message, type) {
try {
let messageData = message;
// Handle different message formats
if (typeof message === 'string') {
try {
messageData = JSON.parse(message);
} catch (e) {
// Plain text message
return {
senderName: 'Peer',
text: message,
senderId: 'peer',
senderAvatar: null
};
}
}
// Handle structured message data
if (typeof messageData === 'object' && messageData !== null) {
return {
senderName: messageData.senderName || messageData.name || 'Peer',
text: messageData.text || messageData.message || messageData.content || '',
senderId: messageData.senderId || messageData.id || 'peer',
senderAvatar: messageData.senderAvatar || messageData.avatar || null
};
}
return null;
} catch (error) {
return null;
}
}
/**
* Focus chat window when notification is clicked
* @private
*/
focusChatWindow() {
try {
window.focus();
// Scroll to bottom of messages if container exists
const messagesContainer = document.getElementById('messages');
if (messagesContainer) {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
} catch (error) {
// Handle error silently
}
}
/**
* Request notification permission
* @returns {Promise<boolean>} Permission granted status
*/
async requestPermission() {
try {
return await this.notificationManager.requestPermission();
} catch (error) {
return false;
}
}
/**
* Get notification status
* @returns {Object} Notification status
*/
getStatus() {
return this.notificationManager.getStatus();
}
/**
* Clear all notifications
*/
clearNotifications() {
this.notificationManager.clearNotificationQueue();
this.notificationManager.resetUnreadCount();
}
/**
* Cleanup integration
*/
cleanup() {
try {
if (this.isInitialized) {
// Restore original callbacks
if (this.originalOnMessage) {
this.webrtcManager.onMessage = this.originalOnMessage;
}
if (this.originalOnStatusChange) {
this.webrtcManager.onStatusChange = this.originalOnStatusChange;
}
if (this.originalDeliverMessageToUI) {
this.webrtcManager.deliverMessageToUI = this.originalDeliverMessageToUI;
}
// Clear notifications
this.clearNotifications();
this.isInitialized = false;
}
} catch (error) {
// Handle error silently
}
}
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = { NotificationIntegration };
}
// Global export for browser usage
if (typeof window !== 'undefined') {
window.NotificationIntegration = NotificationIntegration;
}
@@ -0,0 +1,613 @@
/**
* Secure and Reliable Notification Manager for P2P WebRTC Chat
* Follows best practices: OWASP, MDN, Chrome DevRel
*
* @version 1.0.0
* @author SecureBit Team
* @license MIT
*/
class SecureChatNotificationManager {
constructor(config = {}) {
// Safely read Notification permission (iOS Safari may not define Notification)
this.permission = (typeof Notification !== 'undefined' && Notification && typeof Notification.permission === 'string')
? Notification.permission
: 'denied';
this.isTabActive = this.checkTabActive(); // Initialize with proper check
this.unreadCount = 0;
this.originalTitle = document.title;
this.notificationQueue = [];
this.maxQueueSize = config.maxQueueSize || 5;
this.rateLimitMs = config.rateLimitMs || 2000; // Spam protection
this.lastNotificationTime = 0;
this.trustedOrigins = config.trustedOrigins || [];
// Secure context flag
this.isSecureContext = window.isSecureContext;
// Cross-browser compatibility for Page Visibility API
this.hidden = this.getHiddenProperty();
this.visibilityChange = this.getVisibilityChangeEvent();
this.initVisibilityTracking();
this.initSecurityChecks();
}
/**
* Initialize security checks and validation
* @private
*/
initSecurityChecks() {
// Security checks are performed silently
}
/**
* Get hidden property name for cross-browser compatibility
* @returns {string} Hidden property name
* @private
*/
getHiddenProperty() {
if (typeof document.hidden !== "undefined") {
return "hidden";
} else if (typeof document.msHidden !== "undefined") {
return "msHidden";
} else if (typeof document.webkitHidden !== "undefined") {
return "webkitHidden";
}
return "hidden"; // fallback
}
/**
* Get visibility change event name for cross-browser compatibility
* @returns {string} Visibility change event name
* @private
*/
getVisibilityChangeEvent() {
if (typeof document.hidden !== "undefined") {
return "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
return "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
return "webkitvisibilitychange";
}
return "visibilitychange"; // fallback
}
/**
* Check if tab is currently active using multiple methods
* @returns {boolean} True if tab is active
* @private
*/
checkTabActive() {
// Primary method: Page Visibility API
if (this.hidden && typeof document[this.hidden] !== "undefined") {
return !document[this.hidden];
}
// Fallback method: document.hasFocus()
if (typeof document.hasFocus === "function") {
return document.hasFocus();
}
// Ultimate fallback: assume active
return true;
}
/**
* Initialize page visibility tracking (Page Visibility API)
* @private
*/
initVisibilityTracking() {
// Primary method: Page Visibility API with cross-browser support
if (typeof document.addEventListener !== "undefined" && typeof document[this.hidden] !== "undefined") {
document.addEventListener(this.visibilityChange, () => {
this.isTabActive = this.checkTabActive();
if (this.isTabActive) {
this.resetUnreadCount();
this.clearNotificationQueue();
}
});
}
// Fallback method: Window focus/blur events
window.addEventListener('focus', () => {
this.isTabActive = this.checkTabActive();
if (this.isTabActive) {
this.resetUnreadCount();
}
});
window.addEventListener('blur', () => {
this.isTabActive = this.checkTabActive();
});
// Page unload cleanup
window.addEventListener('beforeunload', () => {
this.clearNotificationQueue();
});
}
/**
* Request notification permission (BEST PRACTICE: Only call in response to user action)
* Never call on page load!
* @returns {Promise<boolean>} Permission granted status
*/
async requestPermission() {
// Secure context check
if (!this.isSecureContext || !('Notification' in window)) {
return false;
}
if (this.permission === 'granted') {
return true;
}
if (this.permission === 'denied') {
return false;
}
try {
this.permission = await Notification.requestPermission();
return this.permission === 'granted';
} catch (error) {
return false;
}
}
/**
* Update page title with unread count
* @private
*/
updateTitle() {
if (this.unreadCount > 0) {
document.title = `(${this.unreadCount}) ${this.originalTitle}`;
} else {
document.title = this.originalTitle;
}
}
/**
* XSS Protection: Sanitize input text
* @param {string} text - Text to sanitize
* @returns {string} Sanitized text
* @private
*/
sanitizeText(text) {
if (typeof text !== 'string') {
return '';
}
// Remove HTML tags and potentially dangerous characters
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
.substring(0, 500); // Length limit
}
/**
* Validate icon URL (XSS protection)
* @param {string} url - URL to validate
* @returns {string|null} Validated URL or null
* @private
*/
validateIconUrl(url) {
if (!url) return null;
try {
const parsedUrl = new URL(url, window.location.origin);
// Only allow HTTPS and data URLs
if (parsedUrl.protocol === 'https:' || parsedUrl.protocol === 'data:') {
// Check trusted origins if specified
if (this.trustedOrigins.length > 0) {
const isTrusted = this.trustedOrigins.some(origin =>
parsedUrl.origin === origin
);
return isTrusted ? parsedUrl.href : null;
}
return parsedUrl.href;
}
return null;
} catch (error) {
return null;
}
}
/**
* Rate limiting for spam protection
* @returns {boolean} Rate limit check passed
* @private
*/
checkRateLimit() {
const now = Date.now();
if (now - this.lastNotificationTime < this.rateLimitMs) {
return false;
}
this.lastNotificationTime = now;
return true;
}
/**
* Send secure notification
* @param {string} senderName - Name of message sender
* @param {string} message - Message content
* @param {Object} options - Notification options
* @returns {Notification|null} Created notification or null
*/
notify(senderName, message, options = {}) {
// Abort if Notifications API is not available (e.g., iOS Safari)
if (typeof Notification === 'undefined') {
return null;
}
// Update tab active state before checking
this.isTabActive = this.checkTabActive();
// Only show if tab is NOT active (user is on another tab or minimized)
if (this.isTabActive) {
return null;
}
// Permission check
if (this.permission !== 'granted') {
return null;
}
// Rate limiting
if (!this.checkRateLimit()) {
return null;
}
// Data sanitization (XSS Protection)
const safeSenderName = this.sanitizeText(senderName || 'Unknown');
const safeMessage = this.sanitizeText(message || '');
const safeIcon = this.validateIconUrl(options.icon) || '/logo/icon-192x192.png';
// Queue overflow protection
if (this.notificationQueue.length >= this.maxQueueSize) {
this.clearNotificationQueue();
}
try {
const notification = new Notification(
`${safeSenderName}`,
{
body: safeMessage.substring(0, 200), // Length limit
icon: safeIcon,
badge: safeIcon,
tag: `chat-${options.senderId || 'unknown'}`, // Grouping
requireInteraction: false, // Don't block user
silent: options.silent || false,
// Vibrate only for mobile and if supported
vibrate: navigator.vibrate ? [200, 100, 200] : undefined,
// Safe metadata
data: {
senderId: this.sanitizeText(options.senderId),
timestamp: Date.now(),
// Don't include sensitive data!
}
}
);
// Increment counter
this.unreadCount++;
this.updateTitle();
// Add to queue for management
this.notificationQueue.push(notification);
// Safe click handler
notification.onclick = (event) => {
event.preventDefault(); // Prevent default behavior
window.focus();
notification.close();
// Safe callback
if (typeof options.onClick === 'function') {
try {
options.onClick(options.senderId);
} catch (error) {
console.error('[Notifications] Error in onClick handler:', error);
}
}
};
// Error handler
notification.onerror = (event) => {
console.error('[Notifications] Error showing notification:', event);
};
// Auto-close after reasonable time
const autoCloseTimeout = Math.min(options.autoClose || 5000, 10000);
setTimeout(() => {
notification.close();
this.removeFromQueue(notification);
}, autoCloseTimeout);
return notification;
} catch (error) {
console.error('[Notifications] Failed to create notification:', error);
return null;
}
}
/**
* Remove notification from queue
* @param {Notification} notification - Notification to remove
* @private
*/
removeFromQueue(notification) {
const index = this.notificationQueue.indexOf(notification);
if (index > -1) {
this.notificationQueue.splice(index, 1);
}
}
/**
* Clear all notifications
*/
clearNotificationQueue() {
this.notificationQueue.forEach(notification => {
try {
notification.close();
} catch (error) {
// Ignore errors when closing
}
});
this.notificationQueue = [];
}
/**
* Reset unread counter
*/
resetUnreadCount() {
this.unreadCount = 0;
this.updateTitle();
}
/**
* Get current status
* @returns {Object} Current notification status
*/
getStatus() {
return {
permission: this.permission,
isTabActive: this.isTabActive,
unreadCount: this.unreadCount,
isSecureContext: this.isSecureContext,
queueSize: this.notificationQueue.length
};
}
}
/**
* Secure integration with WebRTC
*/
class SecureP2PChat {
constructor() {
this.notificationManager = new SecureChatNotificationManager({
maxQueueSize: 5,
rateLimitMs: 2000,
trustedOrigins: [
window.location.origin,
// Add other trusted origins for CDN icons
]
});
this.dataChannel = null;
this.peerConnection = null;
this.remotePeerName = 'Peer';
this.messageHistory = [];
this.maxHistorySize = 100;
}
/**
* Initialize when user connects
*/
async init() {
// Initialize notification manager silently
}
/**
* Method for manual permission request (called on click)
* @returns {Promise<boolean>} Permission granted status
*/
async enableNotifications() {
const granted = await this.notificationManager.requestPermission();
return granted;
}
/**
* Setup DataChannel with security checks
* @param {RTCDataChannel} dataChannel - WebRTC data channel
*/
setupDataChannel(dataChannel) {
if (!dataChannel) {
console.error('[Chat] Invalid DataChannel');
return;
}
this.dataChannel = dataChannel;
// Setup handlers
this.dataChannel.onmessage = (event) => {
this.handleIncomingMessage(event.data);
};
this.dataChannel.onerror = (error) => {
// Handle error silently
};
}
/**
* XSS Protection: Validate incoming messages
* @param {string|Object} data - Message data
* @returns {Object|null} Validated message or null
* @private
*/
validateMessage(data) {
try {
const message = typeof data === 'string' ? JSON.parse(data) : data;
// Check message structure
if (!message || typeof message !== 'object') {
throw new Error('Invalid message structure');
}
// Check required fields
if (!message.text || typeof message.text !== 'string') {
throw new Error('Invalid message text');
}
// Message length limit (DoS protection)
if (message.text.length > 10000) {
throw new Error('Message too long');
}
return {
text: message.text,
senderName: message.senderName || 'Unknown',
senderId: message.senderId || 'unknown',
timestamp: message.timestamp || Date.now(),
senderAvatar: message.senderAvatar || null
};
} catch (error) {
console.error('[Chat] Message validation failed:', error);
return null;
}
}
/**
* Secure handling of incoming messages
* @param {string|Object} data - Message data
* @private
*/
handleIncomingMessage(data) {
const message = this.validateMessage(data);
if (!message) {
return;
}
// Save to history (with limit)
this.messageHistory.push(message);
if (this.messageHistory.length > this.maxHistorySize) {
this.messageHistory.shift();
}
// Display in UI (with sanitization)
this.displayMessage(message);
// Send notification only if tab is inactive
this.notificationManager.notify(
message.senderName,
message.text,
{
icon: message.senderAvatar,
senderId: message.senderId,
onClick: (senderId) => {
this.scrollToLatestMessage();
}
}
);
// Optional: sound (with check)
if (!this.notificationManager.isTabActive) {
this.playNotificationSound();
}
}
/**
* XSS Protection: Safe message display
* @param {Object} message - Message to display
* @private
*/
displayMessage(message) {
const container = document.getElementById('messages');
if (!container) {
return;
}
const messageEl = document.createElement('div');
messageEl.className = 'message';
// Use textContent to prevent XSS
const nameEl = document.createElement('strong');
nameEl.textContent = message.senderName + ': ';
const textEl = document.createElement('span');
textEl.textContent = message.text;
const timeEl = document.createElement('small');
timeEl.textContent = new Date(message.timestamp).toLocaleTimeString();
messageEl.appendChild(nameEl);
messageEl.appendChild(textEl);
messageEl.appendChild(document.createElement('br'));
messageEl.appendChild(timeEl);
container.appendChild(messageEl);
this.scrollToLatestMessage();
}
/**
* Safe sound playback
* @private
*/
playNotificationSound() {
try {
// Use only local audio files
const audio = new Audio('/assets/audio/notification.mp3');
audio.volume = 0.3; // Moderate volume
// Error handling
audio.play().catch(error => {
// Handle audio error silently
});
} catch (error) {
// Handle audio creation error silently
}
}
/**
* Scroll to latest message
* @private
*/
scrollToLatestMessage() {
const container = document.getElementById('messages');
if (container) {
container.scrollTop = container.scrollHeight;
}
}
/**
* Get status
* @returns {Object} Current chat status
*/
getStatus() {
return {
notifications: this.notificationManager.getStatus(),
messageCount: this.messageHistory.length,
connected: this.dataChannel?.readyState === 'open'
};
}
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = { SecureChatNotificationManager, SecureP2PChat };
}
// Global export for browser usage
if (typeof window !== 'undefined') {
window.SecureChatNotificationManager = SecureChatNotificationManager;
window.SecureP2PChat = SecureP2PChat;
}
+1 -1
View File
@@ -96,7 +96,7 @@ class PWAInstallPrompt {
setupEventListeners() {
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
// Don't prevent default - let browser show its own banner
this.deferredPrompt = event;
if (this.checkInstallationStatus()) {
+2 -1
View File
@@ -1,9 +1,9 @@
import { EnhancedSecureCryptoUtils } from '../crypto/EnhancedSecureCryptoUtils.js';
import { EnhancedSecureWebRTCManager } from '../network/EnhancedSecureWebRTCManager.js';
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
import { NotificationIntegration } from '../notifications/NotificationIntegration.js';
// Import UI components (side-effect: they attach themselves to window.*)
import '../components/ui/SessionTimer.jsx';
import '../components/ui/Header.jsx';
import '../components/ui/DownloadApps.jsx';
import '../components/ui/UniqueFeatureSlider.jsx';
@@ -17,6 +17,7 @@ import '../components/ui/FileTransfer.jsx';
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
window.NotificationIntegration = NotificationIntegration;
// Mount application once DOM and modules are ready
const start = () => {
+25 -27
View File
@@ -1,47 +1,45 @@
// Temporary bootstrap that still uses eval for JSX components fetched as text.
// Next step is to replace this with proper ESM imports of prebuilt JS.
// Bootstrap that loads modules using dynamic ESM imports.
// This approach is CSP-compliant and doesn't use eval().
(async () => {
try {
const timestamp = Date.now();
const [cryptoModule, webrtcModule, paymentModule, fileTransferModule] = await Promise.all([
const [cryptoModule, webrtcModule, fileTransferModule, notificationModule, notificationTestModule, notificationGestureTestModule] = await Promise.all([
import(`../crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`),
import(`../network/EnhancedSecureWebRTCManager.js?v=${timestamp}`),
import(`../session/PayPerSessionManager.js?v=${timestamp}`),
import(`../transfer/EnhancedSecureFileTransfer.js?v=${timestamp}`),
import(`../notifications/NotificationIntegration.js?v=${timestamp}`),
import(`../notifications/NotificationTest.js?v=${timestamp}`),
import(`../notifications/NotificationGestureTest.js?v=${timestamp}`),
]);
const { EnhancedSecureCryptoUtils } = cryptoModule;
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
const { EnhancedSecureWebRTCManager } = webrtcModule;
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
const { PayPerSessionManager } = paymentModule;
window.PayPerSessionManager = PayPerSessionManager;
const { EnhancedSecureFileTransfer } = fileTransferModule;
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
const { NotificationIntegration } = notificationModule;
window.NotificationIntegration = NotificationIntegration;
const { NotificationTest } = notificationTestModule;
window.NotificationTest = NotificationTest;
const { NotificationGestureTest } = notificationGestureTestModule;
window.NotificationGestureTest = NotificationGestureTest;
async function loadReactComponent(path) {
const response = await fetch(`${path}?v=${timestamp}`);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
const code = await response.text();
// eslint-disable-next-line no-eval
eval(code);
}
await Promise.all([
loadReactComponent('../components/ui/SessionTimer.jsx'),
loadReactComponent('../components/ui/Header.jsx'),
loadReactComponent('../components/ui/SessionTypeSelector.jsx'),
loadReactComponent('../components/ui/LightningPayment.jsx'),
loadReactComponent('../components/ui/PaymentModal.jsx'),
loadReactComponent('../components/ui/DownloadApps.jsx'),
loadReactComponent('../components/ui/ComparisonTable.jsx'),
loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'),
loadReactComponent('../components/ui/SecurityFeatures.jsx'),
loadReactComponent('../components/ui/Testimonials.jsx'),
loadReactComponent('../components/ui/Roadmap.jsx'),
loadReactComponent('../components/ui/FileTransfer.jsx'),
// Load React components using dynamic imports instead of eval
const componentModules = await Promise.all([
import(`../components/ui/Header.jsx?v=${timestamp}`),
import(`../components/ui/DownloadApps.jsx?v=${timestamp}`),
import(`../components/ui/ComparisonTable.jsx?v=${timestamp}`),
import(`../components/ui/UniqueFeatureSlider.jsx?v=${timestamp}`),
import(`../components/ui/SecurityFeatures.jsx?v=${timestamp}`),
import(`../components/ui/Testimonials.jsx?v=${timestamp}`),
import(`../components/ui/Roadmap.jsx?v=${timestamp}`),
import(`../components/ui/FileTransfer.jsx?v=${timestamp}`),
]);
// Components are automatically registered on window by their respective modules
console.log('✅ All React components loaded successfully');
if (typeof window.initializeApp === 'function') {
window.initializeApp();
} else {
+4 -21
View File
@@ -1,27 +1,13 @@
window.forceUpdateHeader = (timeLeft, sessionType) => {
window.forceUpdateHeader = () => {
const event = new CustomEvent('force-header-update', {
detail: { timeLeft, sessionType, timestamp: Date.now() },
detail: { timestamp: Date.now() },
});
document.dispatchEvent(event);
if (window.sessionManager && window.sessionManager.forceUpdateTimer) {
window.sessionManager.forceUpdateTimer();
}
};
window.updateSessionTimer = (timeLeft, sessionType) => {
document.dispatchEvent(
new CustomEvent('session-timer-update', {
detail: { timeLeft, sessionType },
}),
);
};
document.addEventListener('session-activated', (event) => {
if (window.updateSessionTimer) {
window.updateSessionTimer(event.detail.timeLeft, event.detail.sessionType);
}
if (window.forceUpdateHeader) {
window.forceUpdateHeader(event.detail.timeLeft, event.detail.sessionType);
window.forceUpdateHeader();
}
if (window.webrtcManager && window.webrtcManager.handleSessionActivation) {
if (window.DEBUG_MODE) {
@@ -29,9 +15,6 @@ document.addEventListener('session-activated', (event) => {
}
window.webrtcManager.handleSessionActivation({
sessionId: event.detail.sessionId,
sessionType: event.detail.sessionType,
timeLeft: event.detail.timeLeft,
isDemo: event.detail.isDemo,
sessionManager: window.sessionManager,
});
}
@@ -65,7 +48,7 @@ window.showUpdateNotification = function showUpdateNotification() {
<i class="fas fa-download text-lg"></i>
<div class="flex-1">
<div class="font-medium">Update Available</div>
<div class="text-sm opacity-90">SecureBit.chat v4.3.120 - ECDH + DTLS + SAS is ready</div>
<div class="text-sm opacity-90">SecureBit.chat v4.4.18 - ECDH + DTLS + SAS is ready</div>
</div>
<button data-action="reload" class="bg-white/20 hover:bg-white/30 px-3 py-1 rounded text-sm font-medium transition-colors">
Update
+270
View File
@@ -0,0 +1,270 @@
// PWA Offline Test Script for SecureBit.chat
// Enhanced Security Edition v4.3.120
// Tests offline functionality and cache status
class PWAOfflineTester {
constructor() {
this.testResults = [];
this.isRunning = false;
}
async runTests() {
if (this.isRunning) {
console.warn('⚠️ Tests already running');
return;
}
this.isRunning = true;
this.testResults = [];
console.log('🧪 Starting PWA Offline Tests...');
try {
await this.testServiceWorkerRegistration();
await this.testCacheStatus();
await this.testOfflineResources();
await this.testOnlineResources();
this.showTestResults();
} catch (error) {
console.error('❌ Test failed:', error);
this.addTestResult('Test Suite', false, `Test suite failed: ${error.message}`);
} finally {
this.isRunning = false;
}
}
async testServiceWorkerRegistration() {
try {
if ('serviceWorker' in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
this.addTestResult('Service Worker Registration', true, 'Service worker is registered');
} else {
this.addTestResult('Service Worker Registration', false, 'No service worker found');
}
} else {
this.addTestResult('Service Worker Support', false, 'Service worker not supported');
}
} catch (error) {
this.addTestResult('Service Worker Registration', false, `Error: ${error.message}`);
}
}
async testCacheStatus() {
try {
if ('caches' in window) {
const cacheNames = await caches.keys();
const totalCached = 0;
for (const cacheName of cacheNames) {
const cache = await caches.open(cacheName);
const keys = await cache.keys();
totalCached += keys.length;
}
if (totalCached > 0) {
this.addTestResult('Cache Status', true, `${totalCached} resources cached`);
} else {
this.addTestResult('Cache Status', false, 'No resources cached');
}
} else {
this.addTestResult('Cache API Support', false, 'Cache API not supported');
}
} catch (error) {
this.addTestResult('Cache Status', false, `Error: ${error.message}`);
}
}
async testOfflineResources() {
const criticalResources = [
'/',
'/index.html',
'/manifest.json',
'/dist/app.js',
'/dist/app-boot.js',
'/libs/react/react.production.min.js',
'/libs/react-dom/react-dom.production.min.js',
'/assets/tailwind.css'
];
let cachedCount = 0;
for (const resource of criticalResources) {
try {
const cached = await caches.match(resource);
if (cached) {
cachedCount++;
}
} catch (error) {
console.warn(`⚠️ Failed to check cache for ${resource}:`, error);
}
}
const success = cachedCount >= criticalResources.length * 0.8; // 80% success rate
this.addTestResult('Critical Resources Cached', success,
`${cachedCount}/${criticalResources.length} critical resources cached`);
}
async testOnlineResources() {
try {
// Test if we can fetch a simple resource
const response = await fetch('/favicon.ico', {
method: 'HEAD',
cache: 'no-cache'
});
if (response.ok) {
this.addTestResult('Network Connectivity', true, 'Network is accessible');
} else {
this.addTestResult('Network Connectivity', false, `Network error: ${response.status}`);
}
} catch (error) {
this.addTestResult('Network Connectivity', false, `Network error: ${error.message}`);
}
}
addTestResult(testName, passed, message) {
this.testResults.push({
name: testName,
passed,
message,
timestamp: new Date().toISOString()
});
console.log(`${passed ? '✅' : '❌'} ${testName}: ${message}`);
}
showTestResults() {
const passedTests = this.testResults.filter(test => test.passed).length;
const totalTests = this.testResults.length;
const successRate = Math.round((passedTests / totalTests) * 100);
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 backdrop-blur-sm';
modal.innerHTML = `
<div class="bg-gray-800 rounded-xl p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
<div class="flex items-center mb-6">
<div class="w-12 h-12 bg-blue-500/10 rounded-full flex items-center justify-center mr-4">
<i class="fas fa-vial text-blue-400 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-white">PWA Offline Test Results</h3>
</div>
<div class="mb-6 p-4 rounded-lg ${successRate >= 80 ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}">
<div class="flex items-center justify-between">
<div>
<div class="font-medium text-white">Overall Score</div>
<div class="text-sm text-gray-300">${passedTests}/${totalTests} tests passed</div>
</div>
<div class="text-2xl font-bold ${successRate >= 80 ? 'text-green-400' : 'text-red-400'}">
${successRate}%
</div>
</div>
</div>
<div class="space-y-3">
${this.testResults.map(test => `
<div class="flex items-center justify-between p-3 rounded-lg ${test.passed ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}">
<div class="flex items-center space-x-3">
<i class="fas ${test.passed ? 'fa-check-circle text-green-400' : 'fa-times-circle text-red-400'}"></i>
<div>
<div class="font-medium text-white">${test.name}</div>
<div class="text-sm text-gray-300">${test.message}</div>
</div>
</div>
</div>
`).join('')}
</div>
<div class="mt-6 space-y-3">
<div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4">
<h4 class="font-medium text-blue-300 mb-2">Recommendations:</h4>
<ul class="text-sm text-blue-200 space-y-1">
${this.getRecommendations().map(rec => `<li>• ${rec}</li>`).join('')}
</ul>
</div>
<div class="flex space-x-3">
<button onclick="window.pwaOfflineTester.runTests(); this.parentElement.parentElement.parentElement.remove();"
class="flex-1 bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg font-medium transition-colors">
Run Tests Again
</button>
<button onclick="this.parentElement.parentElement.remove()"
class="flex-1 bg-gray-600 hover:bg-gray-500 text-white py-3 px-4 rounded-lg font-medium transition-colors">
Close
</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
}
getRecommendations() {
const recommendations = [];
const failedTests = this.testResults.filter(test => !test.passed);
if (failedTests.some(test => test.name.includes('Service Worker'))) {
recommendations.push('Ensure service worker is properly registered and active');
}
if (failedTests.some(test => test.name.includes('Cache'))) {
recommendations.push('Check cache configuration and ensure resources are being cached');
}
if (failedTests.some(test => test.name.includes('Network'))) {
recommendations.push('Verify network connectivity and server availability');
}
if (recommendations.length === 0) {
recommendations.push('All tests passed! Your PWA offline functionality is working correctly.');
}
return recommendations;
}
// Public API
getTestResults() {
return this.testResults;
}
clearResults() {
this.testResults = [];
}
}
// Singleton pattern
let instance = null;
const PWAOfflineTesterAPI = {
getInstance() {
if (!instance) {
instance = new PWAOfflineTester();
}
return instance;
},
runTests() {
return this.getInstance().runTests();
}
};
// Export for module use
if (typeof module !== 'undefined' && module.exports) {
module.exports = PWAOfflineTesterAPI;
} else if (typeof window !== 'undefined' && !window.PWAOfflineTester) {
window.PWAOfflineTester = PWAOfflineTesterAPI;
}
// Auto-initialize
if (typeof window !== 'undefined' && !window.pwaOfflineTester) {
window.pwaOfflineTester = PWAOfflineTesterAPI.getInstance();
// Add global function for easy access
window.testPWAOffline = () => {
window.pwaOfflineTester.runTests();
};
}
-29
View File
@@ -349,35 +349,6 @@ button i {
margin-right: 0.5rem;
}
/* Pay-per-session UI - Обновленный трехцветный таймер */
.session-timer {
padding: 8px 16px;
border-radius: 8px;
font-weight: 600;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
transition: all 0.5s ease;
}
.session-timer:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
/* Анимация пульсации для красной зоны */
@keyframes timer-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.session-timer.animate-pulse {
animation: timer-pulse 2s ease-in-out infinite;
}
/* Lightning button */
.lightning-button {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
+84 -51
View File
@@ -1,34 +1,33 @@
// SecureBit.chat Service Worker
// Enhanced Security Edition v4.3.120 - ECDH + DTLS + SAS
// Conservative PWA Edition v4.4.18 - Minimal Caching Strategy
const CACHE_NAME = 'securebit-v4.3.120';
const STATIC_CACHE = 'securebit-static-v4.3.120';
const DYNAMIC_CACHE = 'securebit-dynamic-v4.3.120';
const CACHE_NAME = 'securebit-pwa-v4.4.18';
const STATIC_CACHE = 'securebit-pwa-static-v4.4.18';
const DYNAMIC_CACHE = 'securebit-pwa-dynamic-v4.4.18';
// Files to cache for offline functionality (excluding external CDNs that may have CORS issues)
// Essential files for PWA offline functionality
const STATIC_ASSETS = [
'/',
'/index.html',
'/manifest.json',
'/src/crypto/EnhancedSecureCryptoUtils.js',
'/src/network/EnhancedSecureWebRTCManager.js',
'/src/session/PayPerSessionManager.js',
'/src/components/ui/SessionTimer.jsx',
'/src/components/ui/Header.jsx',
'/src/components/ui/PasswordModal.jsx',
'/src/components/ui/SessionTypeSelector.jsx',
'/src/components/ui/PaymentModal.jsx',
'/src/components/ui/DownloadApps.jsx',
'/src/components/ui/ComparisonTable.jsx',
'/src/components/ui/UniqueFeatureSlider.jsx',
'/src/components/ui/SecurityFeatures.jsx',
'/src/components/ui/Testimonials.jsx',
'/src/components/ui/Roadmap.jsx',
'/src/styles/main.css',
'/src/styles/animations.css',
'/src/styles/components.css',
// Core PWA files only
'/dist/app.js',
'/dist/app-boot.js',
// Essential styles for PWA
'/src/styles/pwa.css',
'/logo/favicon.ico'
// PWA icons (required for install)
'/logo/icon-192x192.png',
'/logo/icon-512x512.png',
'/logo/favicon.ico',
// PWA components only
'/src/pwa/pwa-manager.js',
'/src/pwa/install-prompt.js',
'/src/scripts/pwa-register.js',
'/src/scripts/pwa-offline-test.js'
];
// Sensitive files that should never be cached
@@ -43,21 +42,22 @@ const SENSITIVE_PATTERNS = [
// Network first patterns (always try network first)
const NETWORK_FIRST_PATTERNS = [
/\.js$/,
/\.jsx$/,
/\/src\//,
/api/
/\/api\//,
/\/session\//,
/\/payment\//,
/\/verification\//,
/preimage/,
/auth/
];
// Cache first patterns (static assets)
// Cache first patterns (only essential PWA assets)
const CACHE_FIRST_PATTERNS = [
/\.css$/,
/\.png$/,
/\.jpg$/,
/\.svg$/,
/\.ico$/,
/fonts/,
/logo/
/manifest\.json$/,
/logo\/icon-.*\.png$/,
/logo\/favicon\.ico$/,
/src\/styles\/pwa\.css$/,
/src\/pwa\/.*\.js$/,
/src\/scripts\/pwa-.*\.js$/
];
self.addEventListener('message', (event) => {
@@ -160,23 +160,23 @@ self.addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});
// Smart request handling with security considerations
// Conservative request handling - only cache PWA essentials
async function handleRequest(request) {
const url = new URL(request.url);
try {
// Strategy 1: Cache First (for static assets)
// Strategy 1: Cache First (only for essential PWA assets)
if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
return await cacheFirst(request);
}
// Strategy 2: Network First (for dynamic content and security-critical files)
// Strategy 2: Network First (for all other requests)
if (NETWORK_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
return await networkFirst(request);
}
// Strategy 3: Stale While Revalidate (for main pages)
return await staleWhileRevalidate(request);
// Strategy 3: Network First for everything else (no aggressive caching)
return await networkFirst(request);
} catch (error) {
console.error('❌ Request handling failed:', error);
@@ -254,29 +254,39 @@ async function staleWhileRevalidate(request) {
return cachedResponse || networkResponsePromise || handleOffline(request);
}
// Offline fallback
// Offline fallback - minimal caching for PWA only
async function handleOffline(request) {
const url = new URL(request.url);
// For navigation requests, return cached index.html
if (request.destination === 'document') {
const cachedIndex = await caches.match('/');
if (request.destination === 'document' || request.mode === 'navigate') {
const cachedIndex = await caches.match('/index.html');
if (cachedIndex) {
return cachedIndex;
}
// Fallback to root if index.html not found
const cachedRoot = await caches.match('/');
if (cachedRoot) {
return cachedRoot;
}
}
// For images, return a placeholder or cached version
if (request.destination === 'image') {
return new Response(
'<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><rect width="200" height="200" fill="#1a1a1a"/><text x="100" y="100" text-anchor="middle" fill="#666" font-size="14">Offline</text></svg>',
{ headers: { 'Content-Type': 'image/svg+xml' } }
);
// For PWA assets, try to return cached version
if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
const cachedAsset = await caches.match(request);
if (cachedAsset) {
return cachedAsset;
}
}
// Return a generic offline response
// Return a generic offline response for everything else
return new Response(
JSON.stringify({ error: 'Offline', message: 'Network unavailable' }),
JSON.stringify({
error: 'Offline',
message: 'Network unavailable - PWA offline mode',
url: url.pathname
}),
{
status: 503,
statusText: 'Service Unavailable',
@@ -293,6 +303,29 @@ self.addEventListener('sync', (event) => {
}
});
async function retryFailedRequests() {
try {
// Get all cached requests that failed
const cache = await caches.open(DYNAMIC_CACHE);
const requests = await cache.keys();
for (const request of requests) {
try {
// Try to fetch the request again
const response = await fetch(request);
if (response.ok) {
// Update cache with successful response
await cache.put(request, response);
}
} catch (error) {
console.warn('⚠️ Retry failed for:', request.url, error.message);
}
}
} catch (error) {
console.error('❌ Failed to retry requests:', error);
}
}
// Notification click handler
View File