release: prepare v4.8.5 security hardening release
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## v4.8.5 — Security hardening release
|
||||
|
||||
This release consolidates several months of security, privacy, and lifecycle hardening work by the SecureBit.chat team.
|
||||
|
||||
### Security
|
||||
|
||||
- Added mandatory interactive SAS verification; passive click-through confirmation is no longer sufficient.
|
||||
- Made SAS computation deterministic across peers using shared session material.
|
||||
- Enforced protocol version `4.1` mismatch handling for incompatible clients.
|
||||
- Added TURN relay-only privacy mode and explicit warnings when TURN is unavailable.
|
||||
- Encrypted sensitive IndexedDB metadata and added safe lazy migration for legacy plaintext records.
|
||||
- Added mandatory consent gating for every incoming file transfer.
|
||||
- Replaced broad file acceptance with an explicit file-type allowlist and spoofing checks.
|
||||
- Sanitized every incoming decrypted chat message before UI delivery.
|
||||
|
||||
### Reliability and resource lifecycle
|
||||
|
||||
- Consolidated disconnect behavior into one canonical cleanup path.
|
||||
- Added cleanup for tracked timers, deferred retries, peer-disconnect scheduling, and fake/decoy traffic.
|
||||
- Rejected pending sender consent promises immediately during cleanup.
|
||||
- Bounded retained received-file buffers and added graceful handling for expired download handles.
|
||||
- Cleared React file-transfer UI state and detached live callbacks on unmount.
|
||||
- Improved reconnect hygiene and stale-session cleanup behavior.
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Pinned dependency versions.
|
||||
- Applied safe transitive patch/minor updates.
|
||||
- Verified a clean `npm audit` result.
|
||||
- Expanded regression coverage for SAS, file consent, sanitization, privacy mode, metadata encryption, cleanup, and callback lifecycle behavior.
|
||||
@@ -1,590 +1,121 @@
|
||||
# SecureBit.chat v4.7.56
|
||||
# SecureBit.chat v4.8.5
|
||||
|
||||
<div align="center">
|
||||
SecureBit.chat is a browser-based peer-to-peer chat application built on WebRTC and Web Crypto APIs. It is designed for direct encrypted communication, explicit peer verification, and a small operational footprint without account registration or server-side message storage.
|
||||
|
||||

|
||||
## Security model
|
||||
|
||||
**World's first P2P messenger with ECDH + DTLS + SAS security and military-grade cryptography**
|
||||
SecureBit.chat uses:
|
||||
|
||||
[](https://github.com/SecureBitChat/securebit-chat/releases/latest)
|
||||
[](https://securebit.chat/)
|
||||
[](https://github.com/SecureBitChat/securebit-desktop)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
- ECDH key agreement with derived session keys
|
||||
- DTLS-protected WebRTC transport
|
||||
- deterministic Short Authentication String (SAS) verification
|
||||
- end-to-end encrypted chat payloads
|
||||
- replay protection and session-state cleanup
|
||||
- encrypted local key metadata in IndexedDB
|
||||
|
||||
[Try Web Version](https://securebit.chat/) • [🖥️ Download Desktop Apps](https://github.com/SecureBitChat/securebit-desktop) • [📖 Documentation](#-quick-start) • [🔒 Security](#-security)
|
||||
A session is not treated as verified until both peers complete the interactive SAS flow. Each user must compare the displayed code with the peer through an out-of-band channel and enter the matching code manually. Three failed SAS attempts terminate the session.
|
||||
|
||||
</div>
|
||||
## Highlights in v4.8.5
|
||||
|
||||
---
|
||||
## 🔐 Shared Security Core
|
||||
This release consolidates several months of security hardening work by the project team:
|
||||
|
||||
SecureBitChat uses a shared Rust-based cryptographic core:
|
||||
https://github.com/SecureBitChat/securebit-core
|
||||
- mandatory interactive SAS verification instead of passive click-through confirmation
|
||||
- deterministic SAS computation from shared session material
|
||||
- protocol version `4.1` negotiation with mismatch rejection
|
||||
- optional TURN relay-only privacy mode with clear warnings when TURN is unavailable
|
||||
- encrypted IndexedDB metadata with lazy migration from legacy plaintext records
|
||||
- explicit file-transfer consent before any receive buffers are allocated
|
||||
- strict file-type allowlist using both MIME type and extension checks
|
||||
- incoming decrypted message sanitization before UI delivery
|
||||
- improved disconnect, timer, file-transfer, and React UI cleanup behavior
|
||||
- pinned dependency versions and a clean `npm audit` baseline
|
||||
|
||||
This core is used across all platforms (web, desktop, mobile) as a single source of truth for all security-critical logic.
|
||||
## Quick start
|
||||
|
||||
Community review is welcome. Bug reports and security feedback can be submitted via GitHub Issues.
|
||||
|
||||
## Now Available: Desktop Applications!
|
||||
|
||||
**SecureBit Chat native desktop apps are now available for Windows, macOS, and Linux!**
|
||||
|
||||
[](https://github.com/SecureBitChat/securebit-desktop)
|
||||
|
||||
### Get Desktop Apps
|
||||
- **Windows 10/11** - NSIS Installer (x64)
|
||||
- **macOS 11+** - Universal App (Intel + Apple Silicon)
|
||||
- **Linux** - AppImage (Universal, amd64)
|
||||
|
||||
**Status:** Public Beta v0.1.0 Available
|
||||
**Technology:** Built with Tauri v2 (Rust + Web Technologies)
|
||||
**Coming Q1 2026:** Windows Store, Mac App Store, Snap Store
|
||||
|
||||
**[Download Desktop Apps →](https://github.com/SecureBitChat/securebit-desktop/releases/latest)**
|
||||
|
||||
---
|
||||
|
||||
## Shared Security Core
|
||||
|
||||
**All SecureBit applications share the same open-source cryptographic core:**
|
||||
|
||||
[](https://github.com/SecureBitChat/securebit-core)
|
||||
|
||||
### Platform Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Applications │
|
||||
├──────────────────┬──────────────────┬──────────────────────┤
|
||||
│ Web Version │ Desktop Apps │ Mobile (Coming) │
|
||||
│ (This Repo) │ (Tauri v2) │ (Q1 2026) │
|
||||
│ Browser PWA │ Windows/Mac/ │ iOS/Android │
|
||||
│ v4.7.56 │ Linux │ Native Apps │
|
||||
│ │ v0.1.0 Beta │ │
|
||||
└────────┬─────────┴────────┬─────────┴──────────┬───────────┘
|
||||
│ │ │
|
||||
└──────────────────┼────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────┐
|
||||
│ securebit-core (Open Source) │
|
||||
│ • All Cryptographic Operations │
|
||||
│ • P2P Protocol Implementation │
|
||||
│ • End-to-End Encryption │
|
||||
│ • Key Exchange & Verification │
|
||||
│ • ASN.1 Structure Validation │
|
||||
│ License: Apache 2.0 │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Why This Architecture?
|
||||
|
||||
- **100% of cryptography is open source** - Audit at [securebit-core](https://github.com/SecureBitChat/securebit-core)
|
||||
- **Single source of truth** - Same security across all platforms
|
||||
- **Full transparency** - Security-critical code is publicly auditable
|
||||
- **Community reviewed** - Bug reports and security feedback welcome
|
||||
- **Memory-safe core** - Rust implementation prevents entire classes of vulnerabilities
|
||||
- **Cross-platform consistency** - Identical security guarantees on all platforms
|
||||
|
||||
**Core Repository:** https://github.com/SecureBitChat/securebit-core
|
||||
**License:** Apache License 2.0
|
||||
**Language:** Rust (memory-safe, zero-cost abstractions)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
SecureBit.chat is a revolutionary peer-to-peer messenger that prioritizes your privacy with military-grade encryption. No servers, no registration, no data collection - just pure, secure communication.
|
||||
|
||||
### Platform Availability
|
||||
|
||||
| Platform | Status | Version | Link |
|
||||
|----------|--------|---------|------|
|
||||
| **Web Browser** | Production | v4.7.56 | [Launch Web App](https://securebitchat.github.io/securebit-chat/) |
|
||||
| **Windows Desktop** | Beta | v0.1.0 | [Download](https://github.com/SecureBitChat/securebit-desktop/releases/latest) |
|
||||
| **macOS Desktop** | Beta | v0.1.0 | [Download](https://github.com/SecureBitChat/securebit-desktop/releases/latest) |
|
||||
| **Linux Desktop** | Beta | v0.1.0 | [Download](https://github.com/SecureBitChat/securebit-desktop/releases/latest) |
|
||||
| **iOS Mobile** | 🔄 In Development | - | Coming Q1 2026 |
|
||||
| **Android Mobile** | 🔄 In Development | - | Coming Q1 2026 |
|
||||
|
||||
### Key Features
|
||||
|
||||
- **18-Layer Military Security** - ECDH + DTLS + SAS triple-layer verification
|
||||
- **Pure P2P Architecture** - No servers, truly decentralized
|
||||
- **Progressive Web App** - Install like a native app on any device
|
||||
- **Native Desktop Apps** - Windows, macOS, Linux (Tauri v2)
|
||||
- **Native Mobile Apps** - iOS (Swift/SwiftUI), Android (Kotlin/Jetpack Compose) - Coming Q1 2026
|
||||
- **Secure File Transfer** - End-to-end encrypted P2P file sharing
|
||||
- **Smart Notifications** - Browser and desktop alerts
|
||||
- **Complete Anonymity** - Zero data collection, no registration
|
||||
- **Open Source Security** - Cryptographic core is fully auditable
|
||||
- **ASN.1 Validation** - Complete key structure verification
|
||||
- **Perfect Forward Secrecy** - Automatic key rotation
|
||||
|
||||
---
|
||||
|
||||
## ✨ What's New in v4.7.56
|
||||
|
||||
### Desktop Edition Release
|
||||
|
||||
- **Native Desktop Applications** - Windows, macOS, and Linux support
|
||||
- **Tauri v2 Framework** - Lightweight, secure, and performant
|
||||
- **System Integration** - Native notifications, system tray, auto-start
|
||||
- **Offline Support** - Works without internet connection
|
||||
- **Multi-window Support** - Multiple conversation windows
|
||||
- **Improved Performance** - Native code execution for crypto operations
|
||||
|
||||
### Bug Fixes & Improvements
|
||||
|
||||
- **Fix:** Prevent encryption key loss and IndexedDB connection errors
|
||||
- **Fix:** Disable timer-based key rotation for Double Ratchet mode
|
||||
- **Fix:** Auto-reinitialize encryption keys when missing but ECDH available
|
||||
- **Fix:** Preserve active keys during periodic cleanup in ratchet sessions
|
||||
- **Fix:** IndexedDB "database closing" errors with connection checking
|
||||
- **Improvement:** Individual transactions per queue item to prevent race conditions
|
||||
- **Improvement:** Enhanced message text wrapping in chat interface
|
||||
|
||||
### Security Enhancements
|
||||
|
||||
- **ECDH + DTLS + SAS System** - Triple-layer security verification
|
||||
- **ASN.1 Full Structure Validation** - Complete key structure verification
|
||||
- **Enhanced MITM Protection** - Multi-layer defense system
|
||||
- **Secure Key Storage** - WeakMap-based isolation
|
||||
- **Production-Ready Logging** - Data sanitization and privacy protection
|
||||
- **HKDF Key Derivation** - RFC 5869 compliant key separation and derivation
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Web Version (Browser)
|
||||
|
||||
1. **Visit** [https://securebit.chat/](https://securebit.chat/)
|
||||
2. **Share your link** or enter your peer's link
|
||||
3. **Start chatting** - No registration required!
|
||||
|
||||
**Install as PWA:**
|
||||
- Click the install prompt in your browser
|
||||
- Or use browser menu: "Install SecureBit.chat"
|
||||
|
||||
### Desktop Version (Native Apps)
|
||||
|
||||
1. **Download** installer from [securebit-desktop releases](https://github.com/SecureBitChat/securebit-desktop/releases/latest)
|
||||
2. **Install** on Windows, macOS, or Linux
|
||||
3. **Launch** and start secure communication
|
||||
|
||||
**Platform-specific instructions:**
|
||||
- **Windows:** Run `.exe` installer, follow setup wizard
|
||||
- **macOS:** Open `.zip`, drag `SecureBit Chat.app` to Applications
|
||||
- **Linux:** Make AppImage executable: `chmod +x SecureBit.Chat_*.AppImage`, then run
|
||||
|
||||
### Features Comparison
|
||||
|
||||
| Feature | Web Version | Desktop Apps |
|
||||
|---------|-------------|--------------|
|
||||
| **P2P Encryption** | ✅ | ✅ |
|
||||
| **File Sharing** | ✅ | ✅ |
|
||||
| **Voice/Video Calls** | ✅ | ✅ |
|
||||
| **Screen Sharing** | ✅ | ✅ |
|
||||
| **System Notifications** | ✅ (Browser) | ✅ (Native) |
|
||||
| **Offline Mode** | ❌ | ✅ |
|
||||
| **Auto-start** | ❌ | ✅ |
|
||||
| **System Tray** | ❌ | ✅ |
|
||||
| **Multi-window** | ❌ | ✅ |
|
||||
| **Background Operation** | ❌ | ✅ |
|
||||
| **Lower Resource Usage** | ❌ | ✅ (Tauri) |
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ Roadmap
|
||||
|
||||
**Current: v4.7.56** - Desktop Edition Available
|
||||
|
||||
### Released Versions
|
||||
|
||||
- **v4.5** - Enhanced Security Edition
|
||||
- ECDH + DTLS + SAS triple-layer security
|
||||
- 18-layer military-grade cryptography
|
||||
- Complete ASN.1 validation
|
||||
- Perfect Forward Secrecy
|
||||
|
||||
- **v4.7** - Desktop Edition (Current)
|
||||
- Native desktop applications (Windows, macOS, Linux)
|
||||
- Built with Tauri v2
|
||||
- System tray integration and native notifications
|
||||
- Offline support and multi-window
|
||||
|
||||
- **v0.1.0** - Desktop Apps Beta
|
||||
- Initial desktop release
|
||||
- Windows, macOS, Linux support
|
||||
|
||||
### Upcoming Releases
|
||||
|
||||
- **v5.0 (Q1 2026)** - Mobile Edition
|
||||
- Native iOS app (Swift/SwiftUI)
|
||||
- Native Android app (Kotlin/Jetpack Compose)
|
||||
- PWA support for mobile browsers
|
||||
- Real-time push notifications
|
||||
- Battery optimization
|
||||
- Biometric authentication
|
||||
|
||||
- **v5.5 (Q2 2026)** - Quantum-Resistant Edition
|
||||
- CRYSTALS-Kyber post-quantum key exchange
|
||||
- SPHINCS+ post-quantum signatures
|
||||
- Hybrid classical + post-quantum schemes
|
||||
- Quantum-safe key exchange
|
||||
- Migration of existing sessions
|
||||
|
||||
- **v6.0 (Q4 2026)** - Group Communications
|
||||
- P2P group connections up to 8 participants
|
||||
- Mesh networking for groups
|
||||
- Signal Double Ratchet for groups
|
||||
- Anonymous groups without metadata
|
||||
- Ephemeral groups (disappear after session)
|
||||
|
||||
- **v6.5 (2027)** - Decentralized Network
|
||||
- DHT for peer discovery
|
||||
- Built-in onion routing
|
||||
- Tokenomics and node incentives
|
||||
- Governance via DAO
|
||||
- Self-healing network
|
||||
|
||||
- **v7.0 (2028+)** - AI Privacy Assistant
|
||||
- Local AI threat analysis
|
||||
- Automatic MITM detection
|
||||
- Adaptive cryptography
|
||||
- Zero-knowledge machine learning
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Open Source Cryptographic Core
|
||||
|
||||
**All security-critical code is open source and auditable:**
|
||||
|
||||
- **Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
|
||||
- **License:** Apache License 2.0
|
||||
- **Language:** Rust (memory-safe, prevents entire vulnerability classes)
|
||||
- **Auditable:** 100% of cryptographic operations
|
||||
- **Standards:** RFC 5869 (HKDF), NIST SP 800-56A (ECDH), RFC 8446 (DTLS)
|
||||
|
||||
### Security Features
|
||||
|
||||
#### Triple-Layer Verification
|
||||
1. **ECDH (Elliptic Curve Diffie-Hellman)** - P-384 curve key exchange
|
||||
2. **DTLS (Datagram Transport Layer Security)** - WebRTC transport security with fingerprint verification
|
||||
3. **SAS (Short Authentication String)** - Visual MITM detection and verification
|
||||
|
||||
#### Cryptographic Primitives
|
||||
- **Key Exchange:** ECDH P-384 (NIST curve)
|
||||
- **Signatures:** ECDSA P-384
|
||||
- **Encryption:** AES-256-GCM
|
||||
- **Key Derivation:** HKDF-SHA-256 (RFC 5869)
|
||||
- **Authentication:** HMAC-SHA-256
|
||||
- **Hashing:** SHA-256, SHA-384
|
||||
|
||||
#### Protocol Security
|
||||
- Perfect Forward Secrecy (PFS)
|
||||
- End-to-End Encryption (E2EE)
|
||||
- Zero-Knowledge Architecture
|
||||
- Replay Protection
|
||||
- Metadata Protection
|
||||
- ASN.1 Structure Validation
|
||||
- OID and EC Point Verification
|
||||
- SPKI Structure Validation
|
||||
|
||||
#### Security Architecture
|
||||
- **18-Layer Defense System** - Multiple independent security layers
|
||||
- **MITM Attack Prevention** - Triple verification prevents man-in-the-middle attacks
|
||||
- **Key Isolation** - WeakMap-based secure key storage
|
||||
- **Secure Memory Management** - Automatic secure deletion of sensitive data
|
||||
- **Production Logging** - Sanitized logs prevent information leakage
|
||||
|
||||
### What We DON'T Collect
|
||||
- No personal information
|
||||
- No phone numbers or emails
|
||||
- No contact lists on servers
|
||||
- No message content or metadata
|
||||
- No telemetry or analytics
|
||||
- No usage statistics
|
||||
- No IP addresses logged
|
||||
- No device fingerprints
|
||||
- No location data
|
||||
|
||||
### Security Audit
|
||||
|
||||
Want to audit our security? Check these repositories:
|
||||
|
||||
1. **[securebit-core](https://github.com/SecureBitChat/securebit-core)** - All cryptographic operations (Rust)
|
||||
2. **[securebit-chat](https://github.com/SecureBitChat/securebit-chat)** - Web UI implementation (this repo, JavaScript/React)
|
||||
|
||||
**Report Security Issues:** SecureBitChat@proton.me
|
||||
**PGP Key:** Available on request for encrypted security reports
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js** 18+
|
||||
- **npm** or **yarn**
|
||||
- **Git**
|
||||
|
||||
### Installation
|
||||
### Run locally
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/SecureBitChat/securebit-chat.git
|
||||
cd securebit-chat
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Run development server
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build for production
|
||||
npm run build
|
||||
|
||||
# Build CSS only
|
||||
npm run build:css
|
||||
|
||||
# Build JavaScript only
|
||||
npm run build:js
|
||||
|
||||
# Preview production build (requires Python)
|
||||
python -m http.server 8000
|
||||
```
|
||||
|
||||
### Development Scripts
|
||||
|
||||
```bash
|
||||
# Development server with hot reload
|
||||
npm run dev
|
||||
|
||||
# Watch CSS changes
|
||||
npm run watch
|
||||
|
||||
# Build everything
|
||||
npm run build
|
||||
|
||||
# Serve built files
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
Then open the local server URL in two browser windows or profiles.
|
||||
|
||||
```
|
||||
securebit-chat/
|
||||
├── src/
|
||||
│ ├── components/ # React components
|
||||
│ │ ├── ui/ # UI components (Header, Roadmap, etc.)
|
||||
│ │ └── QRScanner.jsx # QR code scanner
|
||||
│ ├── crypto/ # Cryptography utilities
|
||||
│ │ └── EnhancedSecureCryptoUtils.js
|
||||
│ ├── network/ # WebRTC P2P logic
|
||||
│ │ └── EnhancedSecureWebRTCManager.js
|
||||
│ ├── transfer/ # File transfer
|
||||
│ │ └── EnhancedSecureFileTransfer.js
|
||||
│ ├── notifications/ # Notification system
|
||||
│ ├── pwa/ # PWA functionality
|
||||
│ ├── scripts/ # Bootstrap and initialization
|
||||
│ └── styles/ # CSS stylesheets
|
||||
├── dist/ # Built files (generated)
|
||||
├── assets/ # Static assets
|
||||
├── public/ # Public files
|
||||
└── docs/ # Documentation
|
||||
### Establish a session
|
||||
|
||||
1. Create an offer in the first browser.
|
||||
2. Transfer the offer to the peer and create an answer.
|
||||
3. Return the answer to the first browser.
|
||||
4. Compare the SAS code out of band.
|
||||
5. Enter the matching SAS code on both sides.
|
||||
6. Begin chatting only after both peers are verified.
|
||||
|
||||
## Configuration
|
||||
|
||||
### TURN / privacy mode
|
||||
|
||||
Direct WebRTC connections may expose IP addresses to peers. SecureBit.chat supports a relay-only privacy mode:
|
||||
|
||||
- default mode keeps normal WebRTC behavior and existing STUN support
|
||||
- relay-only mode sets `iceTransportPolicy: "relay"`
|
||||
- relay-only mode requires a configured TURN server
|
||||
- STUN alone does not hide IP addresses
|
||||
- public TURN credentials are not bundled or hardcoded
|
||||
|
||||
Configure ICE servers at deployment time and enable relay-only mode only when a TURN service is available. See [`doc/CONFIGURATION.md`](doc/CONFIGURATION.md).
|
||||
|
||||
### File transfer policy
|
||||
|
||||
Incoming file transfers require explicit user consent. Before the consent prompt appears, metadata is validated and dangerous names are rejected. Safe accepted categories are:
|
||||
|
||||
- common raster images
|
||||
- PDF
|
||||
- plain text
|
||||
- ZIP archives
|
||||
|
||||
Executable, scriptable, and high-risk formats are rejected, including `.exe`, `.bat`, `.cmd`, `.sh`, `.js`, `.msi`, `.dmg`, `.app`, `.jar`, `.scr`, `.ps1`, `.vbs`, `.html`, and `.svg`. MIME type and filename extension must agree.
|
||||
|
||||
## Development
|
||||
|
||||
### Requirements
|
||||
|
||||
- Node.js 18+
|
||||
- npm
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm test
|
||||
npm audit
|
||||
npm run build
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Contributing to Core
|
||||
### Project layout
|
||||
|
||||
Want to improve security? Contribute to the cryptographic core:
|
||||
- **Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
|
||||
- **Focus:** Cryptography, protocol implementation, security features
|
||||
- **Language:** Rust
|
||||
```text
|
||||
src/network/ WebRTC connection and session lifecycle
|
||||
src/transfer/ secure file-transfer implementation
|
||||
src/crypto/ cryptographic utilities
|
||||
src/components React UI components
|
||||
doc/ technical documentation
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
## Related Projects
|
||||
- [`SECURITY.md`](SECURITY.md)
|
||||
- [`doc/CONFIGURATION.md`](doc/CONFIGURATION.md)
|
||||
- [`doc/CRYPTOGRAPHY.md`](doc/CRYPTOGRAPHY.md)
|
||||
- [`doc/SECURITY-ARCHITECTURE.md`](doc/SECURITY-ARCHITECTURE.md)
|
||||
- [`doc/API.md`](doc/API.md)
|
||||
- [`CHANGELOG.md`](CHANGELOG.md)
|
||||
|
||||
### Official SecureBit Ecosystem
|
||||
## Responsible use
|
||||
|
||||
| Project | Description | Status | License |
|
||||
|---------|-------------|--------|---------|
|
||||
| **[securebit-core](https://github.com/SecureBitChat/securebit-core)** | Cryptographic kernel (Rust) | ✅ Production | Apache 2.0 |
|
||||
| **[securebit-chat](https://github.com/SecureBitChat/securebit-chat)** | Web application (this repo) | ✅ Production v4.7.56 | MIT |
|
||||
| **[securebit-desktop](https://github.com/SecureBitChat/securebit-desktop)** | Desktop apps (Windows/Mac/Linux) | ✅ Beta v0.1.0 | Proprietary* |
|
||||
| **securebit-mobile** | Mobile apps (iOS/Android) | 🔄 Coming Q1 2026 | TBD |
|
||||
SecureBit.chat is intended for lawful, ethical use. See [`RESPONSIBLE_USE.md`](RESPONSIBLE_USE.md) and [`SECURITY_DISCLAIMER.md`](SECURITY_DISCLAIMER.md).
|
||||
|
||||
*\* Desktop apps are free for personal and commercial use. Only the UI layer is proprietary - all cryptography is open source in securebit-core.*
|
||||
## License
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- **Frontend:** React, Tailwind CSS
|
||||
- **Build:** esbuild, Tailwind CLI
|
||||
- **P2P:** WebRTC
|
||||
- **Crypto Core:** Rust (securebit-core)
|
||||
- **Desktop:** Tauri v2
|
||||
- **Mobile (Future):** Swift/SwiftUI (iOS), Kotlin/Jetpack Compose (Android)
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! Here's how:
|
||||
|
||||
### Contributing to Web Version (This Repo)
|
||||
|
||||
1. **Fork** the repository
|
||||
2. **Create** feature branch: `git checkout -b feature/amazing-feature`
|
||||
3. **Commit** changes: `git commit -m "Add amazing feature"`
|
||||
4. **Push** to branch: `git push origin feature/amazing-feature`
|
||||
5. **Open** Pull Request
|
||||
|
||||
### Contributing to Cryptographic Core
|
||||
|
||||
Want to improve security? Contribute to the core:
|
||||
- **Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
|
||||
- **Focus:** Cryptography, protocol implementation, security features
|
||||
- **Language:** Rust
|
||||
|
||||
### Contributing to Desktop Apps
|
||||
|
||||
- **Repository:** [securebit-desktop](https://github.com/SecureBitChat/securebit-desktop)
|
||||
- **Focus:** UI/UX improvements, platform-specific features
|
||||
- **Technology:** Tauri v2, Rust, TypeScript
|
||||
|
||||
### Other Ways to Help
|
||||
|
||||
- **Report bugs** - Open issues on GitHub
|
||||
- **Security research** - Email SecureBitChat@proton.me
|
||||
- **Improve documentation** - Help others understand the project
|
||||
- **Star the repositories** - Support visibility and development
|
||||
- **Spread the word** - Share with privacy advocates
|
||||
- **Provide feedback** - Help shape the future of SecureBit
|
||||
|
||||
**If you support our mission - please star the repos!**
|
||||
- [⭐ Star securebit-chat (Web)](https://github.com/SecureBitChat/securebit-chat)
|
||||
- [⭐ Star securebit-core (Crypto)](https://github.com/SecureBitChat/securebit-core)
|
||||
- [⭐ Star securebit-desktop (Apps)](https://github.com/SecureBitChat/securebit-desktop)
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
### This Repository (Web Version)
|
||||
**License:** MIT License
|
||||
|
||||
### Cryptographic Core
|
||||
**License:** Apache License 2.0
|
||||
**Repository:** [securebit-core](https://github.com/SecureBitChat/securebit-core)
|
||||
|
||||
### Desktop Applications
|
||||
**License:** Proprietary (Free for personal & commercial use)
|
||||
**Repository:** [securebit-desktop](https://github.com/SecureBitChat/securebit-desktop)
|
||||
|
||||
*Note: Desktop apps are free to use. Only the UI layer is proprietary - all cryptography is open source.*
|
||||
|
||||
---
|
||||
|
||||
## Community & Support
|
||||
|
||||
### Get Help
|
||||
- **Documentation:** Check README and [core docs](https://github.com/SecureBitChat/securebit-core)**
|
||||
- **Discussions:** [GitHub Discussions](https://github.com/SecureBitChat/securebit-chat/discussions)
|
||||
- **Issues:** [Report bugs on GitHub](https://github.com/SecureBitChat/securebit-chat/issues)
|
||||
- **Email:** SecureBitChat@proton.me
|
||||
|
||||
### Contact
|
||||
- **Security Issues:** SecureBitChat@proton.me (encrypted preferred)
|
||||
- **Business Inquiries:** hello@securebit.chat
|
||||
- **Twitter/X:** [@SecureBitChat](https://twitter.com/SecureBitChat)
|
||||
- **Website:** https://securebit.chat (coming soon)
|
||||
|
||||
### Community Guidelines
|
||||
|
||||
- Be respectful and constructive
|
||||
- Focus on privacy and security
|
||||
- Help others learn and contribute
|
||||
- Report security issues responsibly
|
||||
- Follow the code of conduct
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
### Built With
|
||||
- **React** - UI framework
|
||||
- **Tailwind CSS** - Styling
|
||||
- **esbuild** - Build tool
|
||||
- **WebRTC** - P2P communication
|
||||
- **IndexedDB** - Local storage
|
||||
- **Rust** - Cryptographic core
|
||||
- **Tauri v2** - Desktop framework
|
||||
|
||||
### Special Thanks
|
||||
- **Rust Crypto Team** - Cryptographic primitives and standards
|
||||
- **WebRTC Community** - P2P technology and standards
|
||||
- **Tauri Team** - Desktop framework development
|
||||
- **Security Researchers** - Audits, feedback, and improvements
|
||||
- **Contributors** - Code, docs, testing, and support
|
||||
- **Privacy Advocates** - Inspiration and mission support
|
||||
|
||||
### Standards & Specifications
|
||||
- **RFC 5869** - HKDF key derivation
|
||||
- **NIST SP 800-56A** - ECDH key agreement
|
||||
- **RFC 8446** - DTLS 1.3
|
||||
- **RFC 7748** - Elliptic curves for security
|
||||
- **X.509** - ASN.1 certificate structure
|
||||
|
||||
---
|
||||
|
||||
## Project Status
|
||||
|
||||
### Active Development
|
||||
- **Web Version** - Stable (v4.7.56), receiving bug fixes and improvements
|
||||
- **Desktop Apps** - Public beta (v0.1.0), active development
|
||||
- **Cryptographic Core** - Stable, production-ready
|
||||
- **Mobile Apps** - In development (Q1 2026)
|
||||
|
||||
### Community
|
||||
- **GitHub Stars** - [Help us grow!](https://github.com/SecureBitChat/securebit-chat)
|
||||
- **Contributors** - [See all contributors](https://github.com/SecureBitChat/securebit-chat/graphs/contributors)
|
||||
- **Issues** - [Open issues](https://github.com/SecureBitChat/securebit-chat/issues)
|
||||
- **Pull Requests** - [Contribute](https://github.com/SecureBitChat/securebit-chat/pulls)
|
||||
- **Discussions** - [Join the conversation](https://github.com/SecureBitChat/securebit-chat/discussions)
|
||||
|
||||
### Metrics
|
||||
- **Downloads** - Desktop apps available for all platforms
|
||||
- **Security** - 18-layer military-grade protection
|
||||
- **Platforms** - Web, Windows, macOS, Linux (Mobile coming Q1 2026)
|
||||
- **License** - Open source core, free desktop apps
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**SecureBit.chat Security Team**
|
||||
|
||||
*Committed to protecting your privacy with military-grade security*
|
||||
|
||||
---
|
||||
|
||||
**Latest Release: v4.7.56** - Desktop Edition Available
|
||||
**Desktop Apps: v0.1.0** - Public Beta Available
|
||||
**Mobile Apps: Coming Q1 2026**
|
||||
|
||||
[🚀 Try Web Version](https://securebit.chat/) • [🖥️ Download Desktop Apps](https://github.com/SecureBitChat/securebit-desktop) • [⭐ Star on GitHub](https://github.com/SecureBitChat/securebit-chat)
|
||||
|
||||
---
|
||||
|
||||
**Made with 🔒 for privacy advocates worldwide**
|
||||
|
||||
Copyright © 2025-2026 SecureBit Team. All rights reserved.
|
||||
|
||||
</div>
|
||||
MIT License. See [`LICENSE`](LICENSE).
|
||||
|
||||
+54
-261
@@ -1,287 +1,80 @@
|
||||
# Security Policy
|
||||
|
||||
## 🛡️ Security Overview
|
||||
## Supported release line
|
||||
|
||||
SecureBit.chat is built with security-first principles and implements **military-grade security** with 18-layer protection system. We take security vulnerabilities seriously and appreciate responsible disclosure from the security community.
|
||||
| Release | Status | Protocol |
|
||||
| --- | --- | --- |
|
||||
| v4.1.x | Supported | 4.1 |
|
||||
| earlier releases | Unsupported | legacy |
|
||||
|
||||
**Current Security Status:** 🔒 **MAXIMUM SECURITY (Stage 5)** - Exceeds government-grade communication standards with complete ASN.1 validation
|
||||
Users should run the current supported release line to receive the latest verification, storage, and file-transfer protections.
|
||||
|
||||
## 🔒 Enhanced Security Features (Stage 5)
|
||||
## Reporting a vulnerability
|
||||
|
||||
### Multi-Layer Cryptographic Implementation
|
||||
- **Key Exchange:** ECDH P-384 (NIST recommended curve) with non-extractable keys
|
||||
- **Primary Encryption:** AES-GCM 256-bit with authenticated encryption
|
||||
- **Nested Encryption:** Additional AES-GCM 256-bit layer for maximum protection
|
||||
- **Metadata Protection:** Separate AES-GCM 256-bit encryption for message metadata
|
||||
- **Digital Signatures:** ECDSA P-384 with SHA-384 for message authenticity and MITM protection
|
||||
- **Perfect Forward Secrecy:** Automatic key rotation every 5 minutes with secure key versioning
|
||||
- **Non-extractable Keys:** All cryptographic keys are hardware-protected and non-exportable
|
||||
- **Enhanced Replay Protection:** Multi-factor protection with sequence numbers, message IDs, and timestamps
|
||||
- **Secure Key Storage:** WeakMap-based isolation preventing direct access to sensitive keys
|
||||
- **Key Security Monitoring:** Automatic validation, rotation, and emergency wipe capabilities
|
||||
- **HKDF Key Derivation:** RFC 5869 compliant key separation with proper salt and info parameters
|
||||
Please report security issues privately before public disclosure.
|
||||
|
||||
### Advanced Traffic Obfuscation
|
||||
- **Packet Padding:** Random padding (64-512 bytes) to hide real message sizes
|
||||
- **Anti-Fingerprinting:** Advanced traffic pattern obfuscation and timing randomization
|
||||
- **Fake Traffic Generation:** Invisible decoy messages for traffic analysis protection
|
||||
- **Message Chunking:** Split messages into random-sized chunks with variable delays
|
||||
- **Packet Reordering Protection:** Sequence-based packet reassembly with timeout handling
|
||||
- **Decoy Channels:** Multiple fake communication channels to confuse attackers
|
||||
- Email: `SecureBitChat@proton.me`
|
||||
- Include: affected version, reproduction steps, impact, and any proof-of-concept material
|
||||
- Avoid publishing exploit details before a coordinated fix is available
|
||||
|
||||
### Enhanced Security Architecture
|
||||
- **Zero-trust Model:** No central servers to compromise
|
||||
- **P2P Direct:** WebRTC encrypted channels with enhanced validation
|
||||
- **No Data Persistence:** Messages exist only in memory, automatic cleanup
|
||||
- **Enhanced Rate Limiting:** 60 messages/minute, 5 connections/5 minutes with cryptographic verification
|
||||
- **Session Security:** 64-byte salts, unique session IDs, and replay attack prevention
|
||||
- **MITM Protection:** Out-of-band verification codes with enhanced validation
|
||||
- **Connection Security Framework:** Advanced mutex system with 15-second timeout protection
|
||||
- **Race Condition Protection:** Atomic key generation and serialized connection operations
|
||||
- **Multi-stage Validation:** Step-by-step validation with automatic rollback on failures
|
||||
- **Production Security Logging:** Environment-aware logging with data sanitization
|
||||
## Current security behavior
|
||||
|
||||
### 🔐 ASN.1 Complete Structure Validation (NEW)
|
||||
- **Complete ASN.1 DER Parser:** Full structural validation of all cryptographic keys
|
||||
- **OID Validation:** Algorithm and curve verification (P-256/P-384 only)
|
||||
- **EC Point Format Verification:** Uncompressed format 0x04 validation
|
||||
- **SPKI Structure Validation:** Element count and type checking
|
||||
- **Key Size Limits:** 50-2000 bytes to prevent DoS attacks
|
||||
- **BIT STRING Validation:** Ensuring unused bits are 0
|
||||
- **Fallback Support:** P-384 to P-256 compatibility
|
||||
- **High-Risk Vulnerability Fix:** Prevents keys with valid headers but modified data
|
||||
### Peer verification
|
||||
|
||||
## 🚨 Supported Versions
|
||||
- SAS verification is mandatory and interactive.
|
||||
- SAS values are derived deterministically from shared session material.
|
||||
- Users must compare the code out of band and enter the matching code manually.
|
||||
- A session becomes verified only after both local and remote confirmations succeed.
|
||||
- Three failed local SAS entries terminate the session.
|
||||
- Protocol version `4.1` rejects incompatible peers instead of silently falling back to older verification behavior.
|
||||
|
||||
| Version | Security Level | Supported |
|
||||
| ------- | -------------- | ------------------ |
|
||||
| 4.02.x | MILITARY-GRADE | ✅ Yes (18 layers)|
|
||||
| 4.01.x | MILITARY-GRADE | ✅ Yes (15 layers)|
|
||||
| 4.0.x | MAXIMUM | ✅ Yes (12 layers)|
|
||||
| 3.x.x | HIGH | ⚠️ Limited |
|
||||
| < 3.0 | BASIC | ❌ No |
|
||||
### Message handling
|
||||
|
||||
**Recommendation:** Upgrade to 4.02.x immediately for complete ASN.1 validation and military-grade security protection.
|
||||
- Chat payloads remain encrypted in transit.
|
||||
- Decrypted incoming chat text is sanitized before it reaches React state or the UI.
|
||||
- Encrypted payload validation remains separate from display sanitization.
|
||||
|
||||
## 📋 Reporting a Vulnerability
|
||||
### File transfer
|
||||
|
||||
### 🔴 Critical Vulnerabilities
|
||||
For **critical security issues** that could compromise user safety:
|
||||
- Incoming transfer metadata is validated before presentation to the user.
|
||||
- Every incoming file requires explicit Accept or Reject consent.
|
||||
- Receive buffers are not allocated before consent.
|
||||
- File names are normalized for display and dangerous names are rejected.
|
||||
- Allowed file types are explicit and validated using both MIME type and extension.
|
||||
- High-risk executable or scriptable types are blocked.
|
||||
- Repeated incoming transfer offers are rate-limited and bounded.
|
||||
|
||||
**DO NOT** create a public GitHub issue.
|
||||
### Local storage
|
||||
|
||||
**Contact us privately:**
|
||||
- 📧 **Email:** security@SecureBit.chat (PGP key below)
|
||||
- 🔒 **Signal:** +[REDACTED] (ask for Signal number via email)
|
||||
- 🔐 **Keybase:** @SecureBitChat
|
||||
- Sensitive IndexedDB metadata is encrypted, including timestamps and session-related fields where feasible.
|
||||
- Only minimum lookup keys remain in plaintext when required.
|
||||
- Legacy plaintext metadata is migrated lazily on read.
|
||||
- Corrupted encrypted metadata fails closed.
|
||||
|
||||
### 🟡 Non-Critical Issues
|
||||
For general security improvements or non-critical findings:
|
||||
- Create a GitHub issue with `[SECURITY]` prefix
|
||||
- Use our security issue template
|
||||
### Network privacy
|
||||
|
||||
## 📝 Vulnerability Disclosure Process
|
||||
- Default mode preserves standard WebRTC connectivity.
|
||||
- Relay-only privacy mode uses TURN by setting `iceTransportPolicy: "relay"`.
|
||||
- STUN-only configurations do not provide IP protection.
|
||||
- If TURN is absent, the UI warns that direct WebRTC may expose IP addresses.
|
||||
|
||||
1. **Report:** Send details to security@SecureBit.chat
|
||||
2. **Acknowledgment:** We'll respond within 24 hours
|
||||
3. **Investigation:** We'll investigate and keep you updated
|
||||
4. **Fix:** We'll develop and test a fix
|
||||
5. **Disclosure:** Public disclosure after fix is deployed
|
||||
6. **Credit:** We'll credit you in our security hall of fame
|
||||
### Lifecycle cleanup
|
||||
|
||||
### Timeline Expectations
|
||||
- **Initial Response:** < 24 hours
|
||||
- **Status Update:** Every 72 hours
|
||||
- **Fix Timeline:** Critical bugs < 7 days, Others < 30 days
|
||||
- Disconnect cleanup closes data channels and peer connections, clears verification state, and wipes session crypto state.
|
||||
- Timers, deferred retries, decoy traffic, pending transfers, and React file-transfer callbacks are cleaned up on shutdown.
|
||||
- Received file buffers are retained only within a bounded window and expired handles fail gracefully.
|
||||
|
||||
## 🏆 Security Hall of Fame
|
||||
## Security verification commands
|
||||
|
||||
We maintain a hall of fame for security researchers who help improve SecureBit.chat:
|
||||
|
||||
<!-- Security researchers will be listed here -->
|
||||
*Be the first to help secure SecureBit.chat!*
|
||||
|
||||
## 🔍 Security Audit History
|
||||
|
||||
### Independent Audits
|
||||
- **Pending:** Professional cryptographic audit (Q2 2025)
|
||||
- **Community:** Ongoing peer review by security researchers
|
||||
|
||||
### Internal Security Measures
|
||||
- **Code Review:** All cryptographic code reviewed by multiple developers
|
||||
- **Security Testing:** Comprehensive 18-layer security test suite
|
||||
- **Dependencies:** Regular security updates for all dependencies
|
||||
- **Vulnerability Testing:** Automated testing for all 18 security layers
|
||||
- **ASN.1 Validation:** Complete structural validation of all cryptographic keys
|
||||
|
||||
## 📊 Security Architecture (Stage 5)
|
||||
|
||||
```
|
||||
19-Layer Security Architecture:
|
||||
├── Layer 1: Enhanced Authentication (ECDSA P-384 + SHA-384)
|
||||
├── Layer 2: Key Exchange (ECDH P-384, non-extractable keys)
|
||||
├── Layer 3: Metadata Protection (AES-256-GCM + 64-byte salt)
|
||||
├── Layer 4: Message Encryption (Enhanced with sequence numbers)
|
||||
├── Layer 5: Nested Encryption (Additional AES-256-GCM layer)
|
||||
├── Layer 6: Packet Padding (64-512 bytes random obfuscation)
|
||||
├── Layer 7: Anti-Fingerprinting (Advanced pattern obfuscation)
|
||||
├── Layer 8: Packet Reordering Protection (Sequence + timeout)
|
||||
├── Layer 9: Message Chunking (Random delays + sizes)
|
||||
├── Layer 10: Fake Traffic Generation (Invisible decoy messages)
|
||||
├── Layer 11: Enhanced Rate Limiting (Cryptographic verification)
|
||||
├── Layer 12: Perfect Forward Secrecy (5-minute key rotation)
|
||||
├── Layer 13: Mutex Framework (Race condition protection)
|
||||
├── Layer 14: Secure Key Storage (WeakMap isolation)
|
||||
├── Layer 15: Production Logging (Data sanitization)
|
||||
├── Layer 16: ASN.1 Validation (Complete key structure verification)
|
||||
├── Layer 17: OID Validation (Algorithm and curve verification)
|
||||
├── Layer 18: EC Point Validation (Format and structure verification)
|
||||
└── Layer 19: HKDF Key Derivation (RFC 5869 compliant key separation)
|
||||
```
|
||||
|
||||
### Security Metrics
|
||||
- **Encryption Strength:** Triple-layer AES-256-GCM
|
||||
- **Key Security:** P-384 ECDH/ECDSA (equivalent to 7680-bit RSA)
|
||||
- **Forward Secrecy:** Complete (automatic key rotation)
|
||||
- **Traffic Analysis Protection:** Maximum (6-layer obfuscation)
|
||||
- **Attack Surface:** Minimal (P2P, no central servers)
|
||||
- **Key Validation:** Complete ASN.1 DER parsing and validation
|
||||
- **Structural Security:** Full PKCS compliance for all operations
|
||||
|
||||
## 🛠️ Security Best Practices for Users
|
||||
|
||||
### For Maximum Security:
|
||||
1. **Verify Authenticity:** Always verify out-of-band codes (enhanced 6-digit format)
|
||||
2. **Use Official Source:** Only use https://SecureBit.chat
|
||||
3. **Keep Updated:** Use version 4.02.x for complete ASN.1 validation
|
||||
4. **Secure Environment:** Use updated browsers on secure devices
|
||||
5. **Monitor Security Status:** Check for "MAXIMUM SECURITY" indicator in chat
|
||||
|
||||
### Security Indicators:
|
||||
- ✅ **Green Shield:** MAXIMUM SECURITY (Stage 5) active
|
||||
- 🟡 **Yellow Shield:** HIGH SECURITY (Stage 3-4)
|
||||
- 🔴 **Red Shield:** Security issues detected
|
||||
|
||||
### Red Flags:
|
||||
- ❌ Verification codes don't match
|
||||
- ❌ Security level below Stage 5
|
||||
- ❌ Unusual connection behavior
|
||||
- ❌ Requests for private keys or seed phrases
|
||||
- ❌ Unofficial domains or mirrors
|
||||
- ❌ Missing security layer notifications
|
||||
|
||||
### Research Ethics
|
||||
- **No Disruption:** Don't interfere with live users
|
||||
- **Responsible Disclosure:** Follow our disclosure timeline
|
||||
- **No Data Harvesting:** Don't collect user communications
|
||||
- **Legal Compliance:** Follow all applicable laws
|
||||
- **Respect Privacy:** Don't attempt to break active encrypted sessions
|
||||
|
||||
## 🔬 Security Research Guidelines
|
||||
|
||||
### Scope
|
||||
**In Scope:**
|
||||
- ✅ Cryptographic implementation flaws in any of the 18 layers
|
||||
- ✅ WebRTC security issues
|
||||
- ✅ Authentication bypass attempts
|
||||
- ✅ Input validation vulnerabilities
|
||||
- ✅ Client-side security issues
|
||||
- ✅ Traffic analysis vulnerabilities
|
||||
- ✅ Perfect Forward Secrecy implementation
|
||||
- ✅ Anti-fingerprinting bypass techniques
|
||||
- ✅ Fake traffic detection methods
|
||||
- ✅ ASN.1 validation bypass attempts
|
||||
- ✅ Key structure manipulation attacks
|
||||
- ✅ OID validation bypass techniques
|
||||
|
||||
**Out of Scope:**
|
||||
- ❌ Social engineering attacks
|
||||
- ❌ Physical attacks on user devices
|
||||
- ❌ DoS attacks on user connections
|
||||
- ❌ Issues requiring physical access
|
||||
- ❌ Browser security vulnerabilities
|
||||
|
||||
## 🔄 Recent Security Updates (Version 4.02)
|
||||
|
||||
### Major Security Enhancements:
|
||||
- ✅ **Implemented 19-layer security architecture**
|
||||
- ✅ **Added complete ASN.1 DER parser for key validation**
|
||||
- ✅ **Enhanced key security with OID and EC point verification**
|
||||
- ✅ **Fixed high-risk vulnerability in key structure validation**
|
||||
- ✅ **Added SPKI structure validation and element checking**
|
||||
- ✅ **Implemented key size limits to prevent DoS attacks**
|
||||
- ✅ **Added BIT STRING validation ensuring unused bits are 0**
|
||||
- ✅ **Enhanced fallback support from P-384 to P-256**
|
||||
- ✅ **Implemented RFC 5869 compliant HKDF key derivation**
|
||||
- ✅ **Enhanced key separation with proper salt and info parameters**
|
||||
|
||||
### Previous Enhancements (Version 4.01):
|
||||
- ✅ **Implemented 15-layer security architecture**
|
||||
- ✅ **Added Perfect Forward Secrecy with automatic key rotation**
|
||||
- ✅ **Enhanced MITM protection with ECDSA signatures**
|
||||
- ✅ **Implemented traffic obfuscation (fake traffic, padding, chunking)**
|
||||
- ✅ **Added anti-fingerprinting protection**
|
||||
- ✅ **Fixed demo session creation vulnerability**
|
||||
- ✅ **Eliminated session replay attacks**
|
||||
- ✅ **Enhanced rate limiting with cryptographic verification**
|
||||
|
||||
### Bug Fixes:
|
||||
- 🔧 **Fixed fake traffic visibility in user interface**
|
||||
- 🔧 **Resolved message processing conflicts**
|
||||
- 🔧 **Improved security layer error handling**
|
||||
- 🔧 **Enhanced session validation**
|
||||
- 🔧 **Complete rewrite of validateKeyStructure() method**
|
||||
|
||||
## 📚 Security Resources
|
||||
|
||||
### Technical Documentation:
|
||||
- [18-Layer Security Architecture](docs/SECURITY-ARCHITECTURE.md)
|
||||
- [Cryptographic Implementation](docs/CRYPTOGRAPHY.md)
|
||||
- [P2P Security Model](docs/P2P-SECURITY.md)
|
||||
- [Lightning Integration Security](docs/LIGHTNING-SECURITY.md)
|
||||
- [Traffic Obfuscation Guide](docs/TRAFFIC-OBFUSCATION.md)
|
||||
- [ASN.1 Validation Guide](docs/ASN1-VALIDATION.md)
|
||||
|
||||
### External Resources:
|
||||
- [WebRTC Security Guide](https://webrtc-security.github.io/)
|
||||
- [Web Crypto API Best Practices](https://www.w3.org/TR/WebCryptoAPI/)
|
||||
- [Lightning Network Security](https://lightning.network/lightning-network-paper.pdf)
|
||||
- [NIST Cryptographic Standards](https://csrc.nist.gov/)
|
||||
- [RFC 5280 - X.509 Certificate Structure](https://tools.ietf.org/html/rfc5280)
|
||||
- [RFC 5480 - Elliptic Curve Subject Public Key Information](https://tools.ietf.org/html/rfc5480)
|
||||
|
||||
### Security Verification:
|
||||
```bash
|
||||
# Verify current security status in browser console:
|
||||
webrtcManager.getSecurityStatus()
|
||||
# Expected: { stage: 5, securityLevel: 'MAXIMUM', activeFeatures: 18 }
|
||||
|
||||
# Verify ASN.1 validation status:
|
||||
cryptoManager.getASN1ValidationStatus()
|
||||
# Expected: { enabled: true, parser: 'DER', validation: 'complete' }
|
||||
npm audit
|
||||
npm test
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 📞 Contact Information
|
||||
## Limitations
|
||||
|
||||
- **Security Team:** security@SecureBit.chat
|
||||
- **General Contact:** lockbitchat@tutanota.com
|
||||
- **GitHub Issues:** https://github.com/SecureBitChat/securebit-chat/issues
|
||||
|
||||
## 🏅 Security Achievements
|
||||
|
||||
SecureBit.chat v4.02 provides:
|
||||
- **🥇 Military-Grade Security:** 19-layer protection system
|
||||
- **🥇 Government-Level Encryption:** Triple AES-256-GCM + P-384 ECDH/ECDSA
|
||||
- **🥇 Perfect Forward Secrecy:** Complete with automatic key rotation
|
||||
- **🥇 Traffic Analysis Protection:** Maximum with 6-layer obfuscation
|
||||
- **🥇 Zero-Trust Architecture:** No central points of failure
|
||||
- **🥇 Complete ASN.1 Validation:** Full structural verification of all cryptographic keys
|
||||
- **🥇 PKCS Compliance:** Complete adherence to cryptographic standards
|
||||
- **🥇 HKDF Key Derivation:** RFC 5869 compliant key separation and derivation
|
||||
|
||||
**Security Rating: MAXIMUM** - Exceeds most government and military communication standards with complete key structure validation.
|
||||
|
||||
---
|
||||
|
||||
*This security policy is reviewed and updated quarterly. Last updated: January 15, 2025*
|
||||
*Security implementation verified and tested as of Version 4.02.442*
|
||||
- A compromised endpoint can still expose plaintext.
|
||||
- WebRTC privacy depends on deployment configuration; TURN must be supplied by the operator.
|
||||
- Users must perform the out-of-band SAS comparison correctly.
|
||||
- Browser security and operating-system security remain part of the threat model.
|
||||
|
||||
+16
-233
@@ -1,244 +1,27 @@
|
||||
# Security Disclaimer and Terms of Use
|
||||
|
||||
## 🔒 SecureBit.chat Enhanced Security Edition v4.02.442
|
||||
SecureBit.chat is provided as open-source software for lawful private communication, research, and education. It is supplied **as is**, without warranties of any kind.
|
||||
|
||||
### Important Legal Notice
|
||||
## User responsibilities
|
||||
|
||||
**READ THIS DISCLAIMER CAREFULLY BEFORE USING SECUREBIT.CHAT SOFTWARE**
|
||||
By using SecureBit.chat, you are responsible for:
|
||||
|
||||
---
|
||||
- complying with applicable laws and organizational policies
|
||||
- securing your devices and browser environment
|
||||
- verifying SAS codes through an out-of-band channel
|
||||
- understanding that endpoint compromise can defeat application-layer protections
|
||||
- configuring TURN correctly when relay-only privacy mode is required
|
||||
|
||||
## 📋 Overview
|
||||
## Security limitations
|
||||
|
||||
SecureBit.chat is an open-source, peer-to-peer encrypted messaging application designed to support **freedom of speech** and **privacy rights**. This software implements military-grade cryptography with complete ASN.1 validation and is provided as-is for educational, research, and legitimate communication purposes.
|
||||
No communication system can guarantee absolute security. SecureBit.chat reduces risk through encrypted transport, mandatory peer verification, explicit file-transfer consent, local metadata protection, and lifecycle cleanup, but it cannot protect against compromised devices, malicious users with physical access, or incorrect operational practices.
|
||||
|
||||
---
|
||||
## Intended use
|
||||
|
||||
## ⚖️ Legal Disclaimer
|
||||
SecureBit.chat is intended for legitimate private communication, journalism, research, education, business confidentiality, and personal privacy. It is not intended to facilitate unlawful activity, abuse, harassment, or harm.
|
||||
|
||||
### Developer Liability
|
||||
## Current release
|
||||
|
||||
**THE DEVELOPER(S) OF SECUREBIT.CHAT ASSUME NO RESPONSIBILITY OR LIABILITY FOR:**
|
||||
|
||||
- Any misuse, illegal use, or criminal activities conducted using this software
|
||||
- Compliance with local, national, or international laws and regulations
|
||||
- Any damages, losses, or consequences resulting from the use of this software
|
||||
- The security or privacy of communications in jurisdictions where encryption is restricted
|
||||
- Any vulnerability, security flaw, or compromise that may occur despite our security measures
|
||||
|
||||
### User Responsibility
|
||||
|
||||
**BY USING SECUREBIT.CHAT, YOU ACKNOWLEDGE AND AGREE THAT:**
|
||||
|
||||
1. **Full Legal Responsibility**: You bear complete and sole responsibility for how you use this software
|
||||
2. **Compliance Obligation**: You must ensure your use complies with all applicable laws in your jurisdiction
|
||||
3. **Risk Acceptance**: You understand and accept all risks associated with using encrypted communication software
|
||||
4. **No Warranty**: This software is provided "AS IS" without any warranties, express or implied
|
||||
|
||||
---
|
||||
|
||||
## 🌍 Jurisdictional Considerations
|
||||
|
||||
### Encryption Laws Vary Globally
|
||||
|
||||
- **Some countries restrict or prohibit** the use of strong encryption
|
||||
- **Export controls** may apply in certain jurisdictions
|
||||
- **Corporate/government networks** may have policies against encrypted communications
|
||||
- **Users must verify** local laws before using this software
|
||||
|
||||
### High-Risk Jurisdictions
|
||||
|
||||
**Exercise extreme caution** in countries with:
|
||||
- Restrictions on encrypted communications
|
||||
- Surveillance laws requiring backdoors
|
||||
- Penalties for using VPN/encryption software
|
||||
- Authoritarian internet controls
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Intended Use Cases
|
||||
|
||||
### ✅ Legitimate Uses (Encouraged)
|
||||
- **Journalism**: Protecting sources and whistleblowers
|
||||
- **Human Rights**: Organizing and advocacy in oppressive regimes
|
||||
- **Business**: Corporate communications requiring confidentiality
|
||||
- **Personal Privacy**: Private communications between individuals
|
||||
- **Research**: Academic study of cryptographic protocols
|
||||
- **Education**: Learning about secure communication systems
|
||||
|
||||
### ❌ Prohibited Uses (Illegal/Unethical)
|
||||
- Any illegal activities under applicable law
|
||||
- Criminal conspiracies or planning illegal acts
|
||||
- Harassment, threats, or abuse of others
|
||||
- Circumventing legitimate law enforcement (where legally required)
|
||||
- Distribution of illegal content
|
||||
- Financial crimes or fraud
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Limitations
|
||||
|
||||
### No Absolute Security
|
||||
|
||||
**UNDERSTAND THAT:**
|
||||
- No cryptographic system is 100% unbreakable
|
||||
- Implementation bugs may exist despite best efforts
|
||||
- Social engineering and endpoint security remain vulnerabilities
|
||||
- Quantum computing may eventually threaten current encryption
|
||||
- Traffic analysis may reveal communication patterns
|
||||
|
||||
### User Security Responsibilities
|
||||
|
||||
**YOU MUST:**
|
||||
- Keep your devices secure and updated
|
||||
- Use strong, unique passwords
|
||||
- Verify security codes through out-of-band channels
|
||||
- Understand the risks of your communication environment
|
||||
- Follow operational security (OPSEC) best practices
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ Freedom of Speech Support
|
||||
|
||||
### Our Mission
|
||||
|
||||
SecureBit.chat is developed to support:
|
||||
- **Article 19** of the Universal Declaration of Human Rights
|
||||
- **Freedom of expression** and **right to privacy**
|
||||
- **Resistance to censorship** and mass surveillance
|
||||
- **Protection of journalists, activists, and dissidents**
|
||||
|
||||
### Ethical Use Commitment
|
||||
|
||||
We believe privacy and free speech are fundamental human rights, but:
|
||||
- These rights come with responsibilities
|
||||
- Freedom of speech does not include freedom from consequences
|
||||
- Users must respect the rights and safety of others
|
||||
- Illegal activity is never justified, regardless of privacy tools used
|
||||
|
||||
---
|
||||
|
||||
## 📊 Technical Security Information
|
||||
|
||||
### Current Implementation (v4.02.442)
|
||||
- **ECDH P-384** key exchange with complete ASN.1 validation
|
||||
- **AES-GCM 256-bit** encryption
|
||||
- **ECDSA P-384** digital signatures with enhanced key verification
|
||||
- **RSA-2048** digital signatures for file metadata
|
||||
- **Perfect Forward Secrecy** with key rotation
|
||||
- **MITM protection** via out-of-band verification
|
||||
- **Zero server architecture** (pure P2P)
|
||||
- **DTLS Race Condition Protection** against October 2024 WebRTC vulnerabilities
|
||||
- **ICE Endpoint Verification** for secure WebRTC connections
|
||||
- **Message Size Validation** with 1MB DoS protection
|
||||
- **Atomic Operations** for race condition prevention
|
||||
- **Secure Memory Management** with advanced wiping techniques
|
||||
- **Symbol-Based Context Isolation** for private instance management
|
||||
- **Rate Limiting System** (10 files/minute) with client identification
|
||||
|
||||
### 🔒 ASN.1 Complete Structure Validation (NEW)
|
||||
- **Complete ASN.1 DER Parser**: Full structural validation of all cryptographic keys
|
||||
- **OID Validation**: Algorithm and curve verification (P-256/P-384 only)
|
||||
- **EC Point Format Verification**: Uncompressed format 0x04 validation
|
||||
- **SPKI Structure Validation**: Element count and type checking
|
||||
- **Key Size Limits**: 50-2000 bytes to prevent DoS attacks
|
||||
- **BIT STRING Validation**: Ensuring unused bits are 0
|
||||
- **Fallback Support**: P-384 to P-256 compatibility
|
||||
- **High-Risk Vulnerability Fix**: Prevents keys with valid headers but modified data
|
||||
|
||||
### Known Limitations
|
||||
- WebRTC fingerprinting possibilities (mitigated by anti-fingerprinting techniques)
|
||||
- Browser-based implementation constraints
|
||||
- Dependency on Web Crypto API security
|
||||
- No protection against compromised endpoints
|
||||
- Traffic analysis potential despite encryption (mitigated by packet padding and noise)
|
||||
- Memory safety depends on JavaScript engine implementation
|
||||
- DTLS protection effectiveness depends on WebRTC implementation
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Future Development
|
||||
|
||||
### Post-Quantum Roadmap
|
||||
- **v5.0**: CRYSTALS-Kyber/Dilithium implementation
|
||||
- **Long-term**: Resistance to quantum cryptanalysis
|
||||
- **Ongoing**: Security audits and improvements
|
||||
|
||||
### Advanced Security Technologies (v4.02.442)
|
||||
- **ASN.1 Validation Framework**: Complete DER parsing and key structure verification
|
||||
- **Enhanced Key Security**: OID and EC point validation for all cryptographic operations
|
||||
- **PKCS Compliance**: Full adherence to cryptographic standards
|
||||
- **Structural Security**: Complete validation of all key components
|
||||
- **Vulnerability Prevention**: High-risk key manipulation attack prevention
|
||||
|
||||
### Previous Advanced Security Technologies (v4.01.441)
|
||||
- **DTLS Protection Framework**: Comprehensive WebRTC security enhancement
|
||||
- **Memory Safety Mechanisms**: Advanced protection against use-after-free vulnerabilities
|
||||
- **Race Condition Prevention**: Atomic operations for critical security sections
|
||||
- **Error Sanitization System**: Secure error handling without information leakage
|
||||
- **Context Isolation**: Symbol-based private instance management
|
||||
- **File Transfer Security**: Cryptographic signatures and metadata validation
|
||||
- **Advanced DoS Protection**: Message size validation and rate limiting
|
||||
|
||||
---
|
||||
|
||||
## 📞 Contact and Reporting
|
||||
|
||||
### Security Issues
|
||||
- **Responsible disclosure**: Email security issues to the development team
|
||||
- **CVE reporting**: We participate in responsible vulnerability disclosure
|
||||
- **Bug bounty**: Consider implementing for critical security findings
|
||||
|
||||
### Legal Concerns
|
||||
- **Law enforcement**: Contact appropriate legal authorities in your jurisdiction
|
||||
- **Abuse reports**: Report illegal use to relevant authorities
|
||||
- **Compliance questions**: Consult with legal counsel
|
||||
|
||||
---
|
||||
|
||||
## 📜 License and Terms
|
||||
|
||||
### Open Source License
|
||||
SecureBit.chat is released under the **MIT License**, providing:
|
||||
- Freedom to use, modify, and distribute
|
||||
- No warranty or liability guarantees
|
||||
- Full source code transparency
|
||||
- Right to audit security implementation
|
||||
|
||||
### Terms Acceptance
|
||||
**By downloading, installing, or using SecureBit.chat, you acknowledge:**
|
||||
|
||||
1. You have read and understood this disclaimer
|
||||
2. You accept full responsibility for your use of the software
|
||||
3. You agree to comply with all applicable laws
|
||||
4. You understand the security limitations and risks
|
||||
5. You will not hold the developers liable for any consequences
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Final Warning
|
||||
|
||||
**SECUREBIT.CHAT IS A POWERFUL TOOL FOR PRIVACY AND FREE SPEECH**
|
||||
|
||||
With great power comes great responsibility. Use this software ethically, legally, and with full understanding of the risks and responsibilities involved.
|
||||
|
||||
**Remember**: The strongest encryption cannot protect against poor operational security, compromised endpoints, or illegal activities that attract law enforcement attention.
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Declaration of Intent
|
||||
|
||||
This software is created to:
|
||||
- **Protect human rights** and fundamental freedoms
|
||||
- **Support legitimate privacy** needs in an increasingly surveilled world
|
||||
- **Advance the field** of secure communications
|
||||
- **Educate users** about cryptography and privacy
|
||||
|
||||
**It is NOT intended to facilitate illegal activities or harm others.**
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: January 15, 2025*
|
||||
*Version: Enhanced Security Edition v4.02.442 - ASN.1 Validated*
|
||||
|
||||
**USE AT YOUR OWN RISK AND RESPONSIBILITY**
|
||||
- Product release: `v4.8.5`
|
||||
- Protocol version: `4.1`
|
||||
- Last updated: May 17, 2026
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+677
-296
File diff suppressed because it is too large
Load Diff
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+114
-14
@@ -30,6 +30,28 @@ var EnhancedCopyButton = ({ text, className = "", children }) => {
|
||||
]);
|
||||
};
|
||||
var VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed, remoteConfirmed, bothConfirmed }) => {
|
||||
const [sasInput, setSasInput] = React.useState("");
|
||||
const [error, setError] = React.useState("");
|
||||
const normalizedExpectedLength = (verificationCode || "").replace(/[-\s]/g, "").length;
|
||||
const normalizedInputLength = sasInput.replace(/[-\s]/g, "").length;
|
||||
const canConfirm = !localConfirmed && normalizedExpectedLength > 0 && normalizedInputLength === normalizedExpectedLength;
|
||||
React.useEffect(() => {
|
||||
setSasInput("");
|
||||
setError("");
|
||||
}, [verificationCode]);
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
setError("");
|
||||
await onConfirm(sasInput);
|
||||
} catch (confirmationError) {
|
||||
setSasInput("");
|
||||
if (confirmationError?.message === "SAS_MAX_ATTEMPTS") {
|
||||
setError("Too many incorrect attempts. Session reset for safety.");
|
||||
} else {
|
||||
setError("Incorrect code. Check it with your peer and try again.");
|
||||
}
|
||||
}
|
||||
};
|
||||
return React.createElement("div", {
|
||||
className: "card-minimal rounded-xl p-6 border-purple-500/20"
|
||||
}, [
|
||||
@@ -57,7 +79,7 @@ var VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed,
|
||||
React.createElement("p", {
|
||||
key: "description",
|
||||
className: "text-secondary text-sm"
|
||||
}, "Verify the security code with your contact via another communication channel (voice, SMS, etc.):"),
|
||||
}, "Compare this code with your peer out-of-band, then type the same code below to unlock the chat."),
|
||||
React.createElement("div", {
|
||||
key: "code-display",
|
||||
className: "text-center"
|
||||
@@ -67,6 +89,36 @@ var VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed,
|
||||
className: "verification-code text-2xl py-4"
|
||||
}, verificationCode)
|
||||
]),
|
||||
React.createElement("div", {
|
||||
key: "sas-input-wrap",
|
||||
className: "space-y-2"
|
||||
}, [
|
||||
React.createElement("label", {
|
||||
key: "sas-label",
|
||||
className: "block text-sm text-secondary"
|
||||
}, "Enter the verified code"),
|
||||
React.createElement("input", {
|
||||
key: "sas-input",
|
||||
type: "text",
|
||||
value: sasInput,
|
||||
onChange: (event) => {
|
||||
setSasInput(event.target.value.toUpperCase());
|
||||
if (error) setError("");
|
||||
},
|
||||
autoFocus: true,
|
||||
autoComplete: "off",
|
||||
spellCheck: false,
|
||||
inputMode: "text",
|
||||
disabled: localConfirmed,
|
||||
placeholder: verificationCode ? "Type code here" : "Waiting for code\u2026",
|
||||
className: "w-full rounded-lg border border-purple-500/30 bg-black/20 px-4 py-3 text-center text-xl tracking-[0.3em] text-primary uppercase focus:border-purple-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60",
|
||||
style: { fontFamily: "monospace", textTransform: "uppercase" }
|
||||
}),
|
||||
error && React.createElement("p", {
|
||||
key: "sas-error",
|
||||
className: "text-sm text-red-400"
|
||||
}, error)
|
||||
]),
|
||||
// Verification status indicators
|
||||
React.createElement("div", {
|
||||
key: "verification-status",
|
||||
@@ -136,14 +188,14 @@ var VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed,
|
||||
}, [
|
||||
React.createElement("button", {
|
||||
key: "confirm",
|
||||
onClick: onConfirm,
|
||||
disabled: localConfirmed,
|
||||
className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${localConfirmed ? "bg-gray-500/20 text-gray-400 cursor-not-allowed" : "btn-verify text-white"}`
|
||||
onClick: handleConfirm,
|
||||
disabled: !canConfirm,
|
||||
className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${!canConfirm ? "bg-gray-500/20 text-gray-400 cursor-not-allowed" : "btn-verify text-white"}`
|
||||
}, [
|
||||
React.createElement("i", {
|
||||
className: `fas ${localConfirmed ? "fa-check-circle" : "fa-check"} mr-2`
|
||||
}),
|
||||
localConfirmed ? "Confirmed" : "The codes match"
|
||||
localConfirmed ? "Confirmed" : "Confirm code"
|
||||
]),
|
||||
React.createElement("button", {
|
||||
key: "reject",
|
||||
@@ -270,7 +322,9 @@ var EnhancedConnectionSetup = ({
|
||||
markAnswerCreated,
|
||||
notificationIntegrationRef,
|
||||
isGeneratingKeys,
|
||||
handleCreateOffer
|
||||
handleCreateOffer,
|
||||
relayOnlyMode,
|
||||
setRelayOnlyMode
|
||||
}) => {
|
||||
const [mode, setMode] = React.useState("select");
|
||||
const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);
|
||||
@@ -279,11 +333,11 @@ var EnhancedConnectionSetup = ({
|
||||
setIsGeneratingKeys(false);
|
||||
onClearData();
|
||||
};
|
||||
const handleVerificationConfirm = () => {
|
||||
onVerifyConnection(true);
|
||||
const handleVerificationConfirm = (userCode) => {
|
||||
return onVerifyConnection(userCode);
|
||||
};
|
||||
const handleVerificationReject = () => {
|
||||
onVerifyConnection(false);
|
||||
onVerifyConnection(null, false);
|
||||
};
|
||||
const requestNotificationPermissionOnInteraction = async () => {
|
||||
if (notificationPermissionRequested) {
|
||||
@@ -395,6 +449,28 @@ var EnhancedConnectionSetup = ({
|
||||
className: "text-secondary max-w-2xl mx-auto"
|
||||
}, "Choose a connection method for a secure channel with ECDH encryption and Perfect Forward Secrecy.")
|
||||
]),
|
||||
React.createElement("label", {
|
||||
key: "privacy-mode",
|
||||
className: "mb-6 mx-auto flex max-w-2xl items-start gap-3 rounded-xl border border-purple-500/20 bg-purple-500/10 p-4 text-left"
|
||||
}, [
|
||||
React.createElement("input", {
|
||||
key: "input",
|
||||
type: "checkbox",
|
||||
checked: relayOnlyMode,
|
||||
onChange: (event) => setRelayOnlyMode(event.target.checked),
|
||||
className: "mt-1"
|
||||
}),
|
||||
React.createElement("span", { key: "copy" }, [
|
||||
React.createElement("span", {
|
||||
key: "title",
|
||||
className: "block text-sm font-medium text-primary"
|
||||
}, "Privacy mode: relay-only WebRTC"),
|
||||
React.createElement("span", {
|
||||
key: "desc",
|
||||
className: "block text-sm text-secondary"
|
||||
}, "Uses TURN relay-only when configured. Without TURN, direct WebRTC may expose IP addresses and relay-only connections cannot start.")
|
||||
])
|
||||
]),
|
||||
React.createElement("div", {
|
||||
key: "options",
|
||||
className: "flex flex-col md:flex-row items-center justify-center gap-6 max-w-3xl mx-auto"
|
||||
@@ -1352,6 +1428,13 @@ var EnhancedChatInterface = ({
|
||||
var EnhancedSecureP2PChat = () => {
|
||||
const [messages, setMessages] = React.useState([]);
|
||||
const [connectionStatus, setConnectionStatus] = React.useState("disconnected");
|
||||
const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => {
|
||||
try {
|
||||
return localStorage.getItem("securebit_relay_only_mode") === "true";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const [messageInput, setMessageInput] = React.useState("");
|
||||
const [offerData, setOfferData] = React.useState("");
|
||||
const [answerData, setAnswerData] = React.useState("");
|
||||
@@ -1501,6 +1584,15 @@ var EnhancedSecureP2PChat = () => {
|
||||
}, []);
|
||||
const chatMessagesRef = React.useRef(null);
|
||||
const scrollToBottom = createScrollToBottomFunction(chatMessagesRef);
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem("securebit_relay_only_mode", String(relayOnlyMode));
|
||||
} catch {
|
||||
}
|
||||
if (webrtcManagerRef2.current?._config?.webrtc) {
|
||||
webrtcManagerRef2.current._config.webrtc.relayOnly = relayOnlyMode;
|
||||
}
|
||||
}, [relayOnlyMode]);
|
||||
React.useEffect(() => {
|
||||
if (messages.length > 0 && chatMessagesRef.current) {
|
||||
scrollToBottom();
|
||||
@@ -1677,7 +1769,13 @@ var EnhancedSecureP2PChat = () => {
|
||||
handleKeyExchange,
|
||||
handleVerificationRequired,
|
||||
handleAnswerError,
|
||||
handleVerificationStateChange
|
||||
handleVerificationStateChange,
|
||||
{
|
||||
webrtc: {
|
||||
relayOnly: relayOnlyMode,
|
||||
iceServers: Array.isArray(window.SECUREBIT_ICE_SERVERS) ? window.SECUREBIT_ICE_SERVERS : void 0
|
||||
}
|
||||
}
|
||||
);
|
||||
if (typeof Notification !== "undefined" && Notification && Notification.permission === "granted" && window.NotificationIntegration && !notificationIntegrationRef.current) {
|
||||
try {
|
||||
@@ -1689,7 +1787,7 @@ var EnhancedSecureP2PChat = () => {
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
handleMessage(" SecureBit.chat Enhanced Security Edition v4.7.56 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.", "system");
|
||||
handleMessage(" SecureBit.chat Enhanced Security Edition v4.8.5 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.", "system");
|
||||
const handleBeforeUnload = (event) => {
|
||||
if (event.type === "beforeunload" && !isTabSwitching) {
|
||||
if (webrtcManagerRef2.current && webrtcManagerRef2.current.isConnected()) {
|
||||
@@ -2809,9 +2907,9 @@ var EnhancedSecureP2PChat = () => {
|
||||
setConnectionStatus("failed");
|
||||
}
|
||||
};
|
||||
const handleVerifyConnection = async (isValid) => {
|
||||
const handleVerifyConnection = async (userCode, isValid = true) => {
|
||||
if (isValid) {
|
||||
webrtcManagerRef2.current.confirmVerification();
|
||||
webrtcManagerRef2.current.confirmVerification(userCode);
|
||||
setLocalVerificationConfirmed(true);
|
||||
try {
|
||||
if (window.NotificationIntegration && webrtcManagerRef2.current && !notificationIntegrationRef.current) {
|
||||
@@ -3157,7 +3255,9 @@ var EnhancedSecureP2PChat = () => {
|
||||
markAnswerCreated,
|
||||
notificationIntegrationRef,
|
||||
isGeneratingKeys,
|
||||
handleCreateOffer
|
||||
handleCreateOffer,
|
||||
relayOnlyMode,
|
||||
setRelayOnlyMode
|
||||
})
|
||||
),
|
||||
// QR Scanner Modal
|
||||
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+27
-1193
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
# Configuration Guide
|
||||
|
||||
## Requirements
|
||||
|
||||
- modern browser with WebRTC and Web Crypto support
|
||||
- Node.js 18+ for local development
|
||||
- TURN service only when relay-only privacy mode is required
|
||||
|
||||
## Local setup
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
npm run serve
|
||||
```
|
||||
|
||||
## ICE server configuration
|
||||
|
||||
SecureBit.chat keeps existing STUN support for ordinary WebRTC connectivity. Deployments that require relay-only privacy must provide their own TURN service credentials through deployment configuration; public TURN credentials are intentionally not bundled.
|
||||
|
||||
### Privacy modes
|
||||
|
||||
| Mode | Behavior | IP privacy |
|
||||
| --- | --- | --- |
|
||||
| default | standard WebRTC candidate gathering | direct candidates may expose IP addresses |
|
||||
| relay-only | `iceTransportPolicy: "relay"` | requires TURN and avoids direct peer candidates when configured correctly |
|
||||
|
||||
### Operational rules
|
||||
|
||||
- STUN is not a privacy substitute for TURN.
|
||||
- Relay-only mode without TURN cannot establish a working relay connection.
|
||||
- The UI warns users when TURN is missing.
|
||||
- Validate TURN deployment with browser WebRTC diagnostics before production rollout.
|
||||
|
||||
## Verification flow
|
||||
|
||||
Protocol `4.1` requires interactive SAS verification:
|
||||
|
||||
1. both peers derive the same SAS from shared session material
|
||||
2. users compare the code out of band
|
||||
3. each user enters the matching code manually
|
||||
4. the chat unlocks only after both confirmations succeed
|
||||
|
||||
Three failed local attempts disconnect the session.
|
||||
|
||||
## File-transfer policy
|
||||
|
||||
Incoming file requests are validated before the consent prompt and require explicit user approval.
|
||||
|
||||
Allowed categories:
|
||||
|
||||
- common raster images
|
||||
- PDF
|
||||
- plain text
|
||||
- ZIP archives
|
||||
|
||||
Blocked examples:
|
||||
|
||||
- `.exe`, `.bat`, `.cmd`, `.sh`, `.js`
|
||||
- `.msi`, `.dmg`, `.app`, `.jar`, `.scr`
|
||||
- `.ps1`, `.vbs`, `.html`, `.svg`
|
||||
|
||||
Both MIME type and extension must be acceptable. Missing or unknown MIME types are treated as unsafe unless explicitly covered by policy.
|
||||
+29
-6113
File diff suppressed because it is too large
Load Diff
+15
-2088
File diff suppressed because it is too large
Load Diff
+42
-763
@@ -1,774 +1,53 @@
|
||||
# SecureBit.chat Security Architecture v4.02.985
|
||||
# Security Architecture
|
||||
|
||||
## 🛡️ Overview
|
||||
## Current baseline
|
||||
|
||||
SecureBit.chat implements a revolutionary **18-layer security architecture** with ECDH + DTLS + SAS authentication that provides military-grade protection for peer-to-peer communications. This document details the technical implementation of our security system, which exceeds most government and enterprise communication standards.
|
||||
| Area | Current behavior |
|
||||
| --- | --- |
|
||||
| Protocol | `4.1` with mismatch rejection |
|
||||
| Peer verification | mandatory manual SAS entry |
|
||||
| Transport | WebRTC over DTLS |
|
||||
| Privacy mode | optional TURN relay-only mode |
|
||||
| Message UI safety | incoming decrypted text sanitized before display |
|
||||
| File transfer | validated metadata, explicit consent, allowlist policy |
|
||||
| Local metadata | encrypted IndexedDB envelopes with migration |
|
||||
| Lifecycle | unified disconnect cleanup and bounded resource retention |
|
||||
|
||||
**Current Implementation:** Stage 5 - Maximum Security
|
||||
**Security Rating:** Maximum (ECDH + DTLS + SAS)
|
||||
**Active Layers:** 18/18
|
||||
**Threat Protection:** Comprehensive (MITM, Traffic Analysis, Replay Attacks, Session Hijacking, Race Conditions, Key Exposure, DTLS Race Conditions, Memory Safety, Use-After-Free, Key Structure Manipulation)
|
||||
## Verification state machine
|
||||
|
||||
---
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
1. [Security Architecture Overview](#security-architecture-overview)
|
||||
2. [Layer-by-Layer Analysis](#layer-by-layer-analysis)
|
||||
3. [Cryptographic Specifications](#cryptographic-specifications)
|
||||
4. [Threat Model](#threat-model)
|
||||
5. [Implementation Details](#implementation-details)
|
||||
6. [Security Verification](#security-verification)
|
||||
7. [Performance Impact](#performance-impact)
|
||||
8. [Compliance Standards](#compliance-standards)
|
||||
9. [ASN.1 Validation Framework](#asn1-validation-framework)
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Security Architecture Overview
|
||||
|
||||
### 19-Layer Defense System
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ APPLICATION LAYER │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Layer 19: HKDF Key Derivation (RFC 5869 Compliant) │
|
||||
│ Layer 18: EC Point Validation (Format & Structure) │
|
||||
│ Layer 17: OID Validation (Algorithm & Curve Verification) │
|
||||
│ Layer 16: ASN.1 Validation (Complete Key Structure) │
|
||||
│ Layer 15: Production Security Logging (Data Sanitization) │
|
||||
│ Layer 14: Secure Key Storage (WeakMap Isolation) │
|
||||
│ Layer 13: Mutex Framework (Race Condition Protection) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ CRYPTOGRAPHIC LAYER │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Layer 12: Perfect Forward Secrecy (Key Rotation) │
|
||||
│ Layer 11: Enhanced Rate Limiting (DDoS Protection) │
|
||||
│ Layer 10: Fake Traffic Generation (Traffic Analysis) │
|
||||
│ Layer 9: Message Chunking (Timing Analysis Protection) │
|
||||
│ Layer 8: Packet Reordering Protection (Sequence Security) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ OBFUSCATION LAYER │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Layer 7: Anti-Fingerprinting (Pattern Obfuscation) │
|
||||
│ Layer 6: Packet Padding (Size Obfuscation) │
|
||||
│ Layer 5: Nested Encryption (Additional AES-GCM) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ ENCRYPTION LAYER │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Layer 4: Message Encryption (Enhanced AES-GCM) │
|
||||
│ Layer 3: Metadata Protection (Separate AES-GCM) │
|
||||
│ Layer 2: Key Exchange (ECDH P-384) │
|
||||
│ Layer 1: Enhanced Authentication (ECDSA P-384) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ TRANSPORT LAYER │
|
||||
│ (WebRTC/ICE/DTLS) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```text
|
||||
connection established
|
||||
↓
|
||||
shared keys derived
|
||||
↓
|
||||
deterministic SAS displayed
|
||||
↓
|
||||
manual out-of-band comparison
|
||||
↓
|
||||
local input validated
|
||||
↓
|
||||
peer confirmation received
|
||||
↓
|
||||
verified session
|
||||
```
|
||||
|
||||
### Security Progression Stages
|
||||
The verified state is reached only when both local and remote confirmation flags are true.
|
||||
|
||||
| Stage | Layers Active | Security Level | Target Threats |
|
||||
|-------|---------------|----------------|-----------------|
|
||||
| 1 | 1-5 | Basic Enhanced | Basic attacks, MITM |
|
||||
| 2 | 1-7 | Medium | + Traffic analysis |
|
||||
| 3 | 1-9 | High | + Timing attacks |
|
||||
| 4 | 1-12 | High Enhanced | + Advanced persistent threats |
|
||||
| 5 | 1-15 | Military-Grade | + Race conditions, Key exposure |
|
||||
| 6 | 1-19 | Maximum | + DTLS race conditions, Memory safety, Key structure validation, HKDF compliance |
|
||||
## File-transfer architecture
|
||||
|
||||
---
|
||||
1. sender emits metadata
|
||||
2. receiver validates name, size, type, and abuse limits
|
||||
3. receiver sees Accept / Reject prompt
|
||||
4. no receive buffers are allocated before acceptance
|
||||
5. sender transmits chunks only after acceptance
|
||||
6. completed received buffers are retained within a bounded window
|
||||
|
||||
## 🔍 Layer-by-Layer Analysis
|
||||
## Disconnect cleanup
|
||||
|
||||
### Layer 1: Enhanced Authentication (ECDSA P-384)
|
||||
**Purpose:** Cryptographic proof of message authenticity and sender verification
|
||||
The canonical disconnect path clears:
|
||||
|
||||
**Technical Specifications:**
|
||||
- **Algorithm:** ECDSA with P-384 curve
|
||||
- **Hash Function:** SHA-384 (primary), SHA-256 (fallback)
|
||||
- **Key Size:** 384-bit (equivalent to 7680-bit RSA)
|
||||
- **Signature Size:** 96 bytes
|
||||
- **Key Properties:** Non-extractable, hardware-protected
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// Self-signed key package for MITM protection
|
||||
const keyPackage = {
|
||||
keyType: 'ECDSA',
|
||||
keyData: exported384BitKey,
|
||||
timestamp: Date.now(),
|
||||
version: '4.02',
|
||||
signature: ecdsaSignature
|
||||
};
|
||||
```
|
||||
|
||||
### Layer 16: ASN.1 Validation (Complete Key Structure)
|
||||
**Purpose:** Complete structural validation of all cryptographic keys according to PKCS standards
|
||||
|
||||
**Technical Specifications:**
|
||||
- **Parser:** Complete ASN.1 DER parser
|
||||
- **Validation Scope:** Full key structure verification
|
||||
- **Standards:** RFC 5280, RFC 5480, PKCS compliance
|
||||
- **Performance:** < 10ms validation time
|
||||
- **Coverage:** All cryptographic operations
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// Complete ASN.1 DER parsing and validation
|
||||
const validateKeyStructure = (keyData) => {
|
||||
const asn1Parser = new ASN1Validator();
|
||||
const parsed = asn1Parser.parseDER(keyData);
|
||||
|
||||
// Validate complete structure
|
||||
if (!asn1Parser.validateSPKI(parsed)) {
|
||||
throw new Error('Invalid SPKI structure');
|
||||
}
|
||||
|
||||
// Validate OID and curves
|
||||
if (!asn1Parser.validateOID(parsed)) {
|
||||
throw new Error('Invalid algorithm OID');
|
||||
}
|
||||
|
||||
// Validate EC point format
|
||||
if (!asn1Parser.validateECPoint(parsed)) {
|
||||
throw new Error('Invalid EC point format');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
### Layer 17: OID Validation (Algorithm & Curve Verification)
|
||||
**Purpose:** Verification of cryptographic algorithms and elliptic curves
|
||||
|
||||
**Technical Specifications:**
|
||||
- **Supported Curves:** P-256, P-384 only
|
||||
- **Algorithm Validation:** Complete OID verification
|
||||
- **Fallback Support:** P-384 to P-256 compatibility
|
||||
- **Security:** Prevents algorithm substitution attacks
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// OID validation for algorithms and curves
|
||||
const validateOID = (parsed) => {
|
||||
const validOIDs = {
|
||||
'1.2.840.10045.3.1.7': 'P-256', // secp256r1
|
||||
'1.3.132.0.34': 'P-384' // secp384r1
|
||||
};
|
||||
|
||||
const oid = parsed.algorithm.algorithm;
|
||||
if (!validOIDs[oid]) {
|
||||
throw new Error(`Unsupported curve: ${oid}`);
|
||||
}
|
||||
|
||||
return validOIDs[oid];
|
||||
};
|
||||
```
|
||||
|
||||
### Layer 19: HKDF Key Derivation (RFC 5869 Compliant)
|
||||
**Purpose:** RFC 5869 compliant key derivation with proper key separation and cryptographic security
|
||||
|
||||
**Technical Specifications:**
|
||||
- **Standard:** RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function
|
||||
- **Hash Function:** SHA-256 for optimal compatibility and performance
|
||||
- **Salt Security:** 64-byte cryptographically secure salt for each derivation
|
||||
- **Key Separation:** Unique `info` parameters for each derived key type
|
||||
- **Non-Extractable Keys:** Hardware-protected keys for enhanced security
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// HKDF key derivation with proper separation
|
||||
const deriveSharedKeys = async (privateKey, publicKey, salt) => {
|
||||
// Step 1: Pure ECDH derivation
|
||||
const rawKeyMaterial = await crypto.subtle.deriveKey(
|
||||
{ name: 'ECDH', public: publicKey },
|
||||
privateKey,
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
true, // Extractable for HKDF processing
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
|
||||
// Export and import for HKDF
|
||||
const rawKeyData = await crypto.subtle.exportKey('raw', rawKeyMaterial);
|
||||
const rawSharedSecret = await crypto.subtle.importKey(
|
||||
'raw', rawKeyData,
|
||||
{ name: 'HKDF', hash: 'SHA-256' },
|
||||
false, ['deriveKey']
|
||||
);
|
||||
|
||||
// Step 2: Derive specific keys with unique info parameters
|
||||
const messageKey = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'HKDF',
|
||||
hash: 'SHA-256',
|
||||
salt: saltBytes,
|
||||
info: encoder.encode('message-encryption-v4')
|
||||
},
|
||||
rawSharedSecret,
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
false, ['encrypt', 'decrypt']
|
||||
);
|
||||
|
||||
// Additional keys derived with unique info parameters...
|
||||
return { messageKey, macKey, pfsKey, metadataKey, fingerprint };
|
||||
};
|
||||
```
|
||||
|
||||
### Layer 18: EC Point Validation (Format & Structure Verification)
|
||||
**Purpose:** Verification of elliptic curve point format and structure
|
||||
|
||||
**Technical Specifications:**
|
||||
- **Format:** Uncompressed format 0x04 only
|
||||
- **Structure:** Complete point coordinate validation
|
||||
- **Size Limits:** 50-2000 bytes to prevent DoS attacks
|
||||
- **BIT STRING:** Unused bits must be 0
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// EC point format and structure validation
|
||||
const validateECPoint = (parsed) => {
|
||||
const publicKey = parsed.subjectPublicKey;
|
||||
|
||||
// Check format (uncompressed 0x04)
|
||||
if (publicKey[0] !== 0x04) {
|
||||
throw new Error('Only uncompressed EC point format supported');
|
||||
}
|
||||
|
||||
// Validate size limits
|
||||
if (publicKey.length < 50 || publicKey.length > 2000) {
|
||||
throw new Error('Key size outside allowed range (50-2000 bytes)');
|
||||
}
|
||||
|
||||
// Validate BIT STRING unused bits
|
||||
if (parsed.unusedBits !== 0) {
|
||||
throw new Error('BIT STRING unused bits must be 0');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Cryptographic Specifications
|
||||
|
||||
### Algorithm Selection Rationale
|
||||
|
||||
| Component | Algorithm | Key Size | Rationale |
|
||||
|-----------|-----------|----------|-----------|
|
||||
| Key Exchange | ECDH P-384 | 384-bit | NSA Suite B, quantum-resistant timeline |
|
||||
| Signatures | ECDSA P-384 | 384-bit | Matches key exchange, proven security |
|
||||
| Encryption | AES-256-GCM | 256-bit | NIST recommended, authenticated encryption |
|
||||
| Hashing | SHA-384 | 384-bit | Matches curve size, collision resistant |
|
||||
| MAC | HMAC-SHA-384 | 384-bit | Proven security, matches hash function |
|
||||
|
||||
### Security Strengths
|
||||
|
||||
- **ECDH P-384:** Equivalent to 7680-bit RSA
|
||||
- **AES-256:** Quantum computer resistant until 2040+
|
||||
- **SHA-384:** 192-bit security level (collision resistance)
|
||||
- **Combined Security:** Exceeds 256-bit security level
|
||||
|
||||
### Cryptographic Operations Performance
|
||||
|
||||
| Operation | Time (ms) | CPU Usage | Memory Usage |
|
||||
|-----------|-----------|-----------|--------------|
|
||||
| Key Generation | 10-50 | Medium | Low |
|
||||
| ECDH Agreement | 5-15 | Low | Low |
|
||||
| AES Encryption | 0.1-1 | Very Low | Very Low |
|
||||
| ECDSA Signing | 2-8 | Low | Low |
|
||||
| Message Processing | 1-5 | Low | Low |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Threat Model
|
||||
|
||||
### Threat Classifications
|
||||
|
||||
#### **🔴 Critical Threats (Fully Mitigated)**
|
||||
- **Nation-State Attacks:** Advanced persistent threats
|
||||
- **MITM Attacks:** Certificate pinning bypass attempts
|
||||
- **Cryptographic Attacks:** Implementation vulnerabilities
|
||||
- **Traffic Analysis:** Deep packet inspection and metadata analysis
|
||||
|
||||
#### **🟡 High Threats (Substantially Mitigated)**
|
||||
- **Side-Channel Attacks:** Timing and power analysis
|
||||
- **Social Engineering:** User manipulation (partially mitigated)
|
||||
- **Endpoint Compromise:** Device-level attacks
|
||||
- **Quantum Computing:** Future quantum attacks (timeline > 15 years)
|
||||
|
||||
#### **🟢 Medium Threats (Completely Mitigated)**
|
||||
- **Passive Eavesdropping:** Network traffic interception
|
||||
- **Replay Attacks:** Message reuse attempts
|
||||
- **DDoS Attacks:** Service disruption attempts
|
||||
- **Protocol Downgrade:** Forced weak encryption
|
||||
|
||||
### Attack Scenarios and Defenses
|
||||
|
||||
#### Scenario 1: Government Surveillance
|
||||
**Attack:** Comprehensive traffic monitoring and analysis
|
||||
**Defense Layers:** 6, 7, 10, 12 (traffic obfuscation)
|
||||
**Result:** Encrypted traffic indistinguishable from noise
|
||||
|
||||
#### Scenario 2: Corporate Espionage
|
||||
**Attack:** Targeted interception with advanced tools
|
||||
**Defense Layers:** 1, 2, 3, 4, 5 (cryptographic protection)
|
||||
**Result:** Computationally infeasible to decrypt
|
||||
|
||||
#### Scenario 3: ISP-Level Monitoring
|
||||
**Attack:** Deep packet inspection and metadata collection
|
||||
**Defense Layers:** 6, 7, 8, 9, 10 (pattern obfuscation)
|
||||
**Result:** No useful metadata or patterns extractable
|
||||
|
||||
#### Scenario 4: Academic Cryptanalysis
|
||||
**Attack:** Advanced mathematical attacks on crypto
|
||||
**Defense Layers:** 2, 4, 5 (multiple algorithms)
|
||||
**Result:** Multiple independent cryptographic barriers
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Implementation Details
|
||||
|
||||
### Message Flow Through Security Layers
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ User Message │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌─────────▼───────┐ ┌──────────────────┐
|
||||
│ Layer 7: Anti │ │ Outbound Process │
|
||||
│ Fingerprinting │◄───┤ (Sending) │
|
||||
└─────────┬───────┘ └──────────────────┘
|
||||
│
|
||||
┌─────────▼───────┐
|
||||
│ Layer 6: Packet│
|
||||
│ Padding │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌─────────▼───────┐
|
||||
│ Layer 8: Packet│
|
||||
│ Reordering │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌─────────▼───────┐
|
||||
│ Layer 5: Nested│
|
||||
│ Encryption │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌─────────▼───────┐
|
||||
│ Layer 4: Message│
|
||||
│ Encryption │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌─────────▼───────┐
|
||||
│ WebRTC Channel │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Security Layer Configuration
|
||||
|
||||
```javascript
|
||||
// Production configuration for maximum security
|
||||
const securityConfig = {
|
||||
// Stage 4 - Maximum Security
|
||||
features: {
|
||||
hasEncryption: true,
|
||||
hasECDH: true,
|
||||
hasECDSA: true,
|
||||
hasMutualAuth: true,
|
||||
hasMetadataProtection: true,
|
||||
hasEnhancedReplayProtection: true,
|
||||
hasNonExtractableKeys: true,
|
||||
hasRateLimiting: true,
|
||||
hasEnhancedValidation: true,
|
||||
hasPFS: true,
|
||||
hasNestedEncryption: true,
|
||||
hasPacketPadding: true,
|
||||
hasFakeTraffic: true,
|
||||
hasMessageChunking: true,
|
||||
hasDecoyChannels: true,
|
||||
hasPacketReordering: true,
|
||||
hasAntiFingerprinting: true
|
||||
},
|
||||
|
||||
// Performance optimizations
|
||||
performance: {
|
||||
paddingRange: [64, 512], // Reduced for efficiency
|
||||
chunkSize: 2048, // Larger chunks
|
||||
fakeTrafficInterval: [10000, 30000], // Less frequent
|
||||
keyRotation: 300000 // 5 minutes
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Security Verification
|
||||
|
||||
### Automated Testing
|
||||
|
||||
```javascript
|
||||
// Security layer verification
|
||||
async function verifySecurityLayers() {
|
||||
const tests = [
|
||||
verifyEncryption(),
|
||||
verifyECDHKeyExchange(),
|
||||
verifyECDSASignatures(),
|
||||
verifyMutualAuth(),
|
||||
verifyMetadataProtection(),
|
||||
verifyReplayProtection(),
|
||||
verifyNonExtractableKeys(),
|
||||
verifyRateLimiting(),
|
||||
verifyEnhancedValidation(),
|
||||
verifyPFS(),
|
||||
verifyNestedEncryption(),
|
||||
verifyPacketPadding()
|
||||
];
|
||||
|
||||
const results = await Promise.all(tests);
|
||||
return results.every(result => result === true);
|
||||
}
|
||||
```
|
||||
|
||||
### Manual Verification Commands
|
||||
|
||||
```javascript
|
||||
// Check security status
|
||||
webrtcManager.getSecurityStatus()
|
||||
// Expected: { stage: 4, securityLevel: 'MAXIMUM', activeFeatures: 12 }
|
||||
|
||||
// Verify cryptographic implementation
|
||||
webrtcManager.calculateSecurityLevel()
|
||||
// Expected: { level: 'HIGH', score: 80+, verificationResults: {...} }
|
||||
|
||||
// Test fake traffic filtering
|
||||
webrtcManager.checkFakeTrafficStatus()
|
||||
// Expected: { fakeTrafficEnabled: true, timerActive: true }
|
||||
```
|
||||
|
||||
### Security Metrics Dashboard
|
||||
|
||||
| Metric | Target | Current | Status |
|
||||
|--------|---------|---------|---------|
|
||||
| Active Security Layers | 19 | 19 | ✅ |
|
||||
| Encryption Strength | 256-bit | 256-bit | ✅ |
|
||||
| Key Exchange Security | P-384 | P-384 | ✅ |
|
||||
| Forward Secrecy | Complete | Complete | ✅ |
|
||||
| Traffic Obfuscation | Maximum | Maximum | ✅ |
|
||||
| Attack Surface | Minimal | Minimal | ✅ |
|
||||
| HKDF Compliance | RFC 5869 | RFC 5869 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Layer 13: Mutex Framework (Race Condition Protection)
|
||||
|
||||
### Purpose
|
||||
Prevents race conditions during connection establishment and cryptographic operations through advanced mutex coordination system.
|
||||
|
||||
### Technical Implementation
|
||||
- **Custom Mutex System:** `_withMutex('connectionOperation')` with 15-second timeout
|
||||
- **Atomic Operations:** Serialized connection operations to prevent conflicts
|
||||
- **Deadlock Prevention:** Emergency recovery mechanisms with automatic cleanup
|
||||
- **Operation Tracking:** Unique `operationId` for comprehensive diagnostics
|
||||
|
||||
### Security Benefits
|
||||
- **Race Condition Prevention:** Eliminates timing-based attacks during key generation
|
||||
- **Connection Integrity:** Ensures atomic connection establishment
|
||||
- **Error Recovery:** Automatic rollback via `_cleanupFailedOfferCreation()` on failures
|
||||
- **Diagnostic Capability:** Phase tracking for precise error identification
|
||||
|
||||
### Implementation Details
|
||||
```javascript
|
||||
// Mutex-protected connection operations
|
||||
await this._withMutex('connectionOperation', async () => {
|
||||
const operationId = this._generateOperationId();
|
||||
try {
|
||||
await this._generateEncryptionKeys();
|
||||
await this._validateConnectionParameters();
|
||||
await this._establishSecureChannel();
|
||||
} catch (error) {
|
||||
await this._cleanupFailedOfferCreation(operationId);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Layer 14: Secure Key Storage (WeakMap Isolation)
|
||||
|
||||
### Purpose
|
||||
Replaces public key properties with private WeakMap-based storage to prevent unauthorized access and memory exposure.
|
||||
|
||||
### Technical Implementation
|
||||
- **WeakMap Storage:** `_secureKeyStorage` for all cryptographic keys
|
||||
- **Private Access Methods:** `_getSecureKey()`, `_setSecureKey()`, `_initializeSecureKeyStorage()`
|
||||
- **Key Validation:** `_validateKeyValue()` with type and format checking
|
||||
- **Key Rotation:** `_rotateKeys()` with secure key replacement
|
||||
- **Emergency Wipe:** `_emergencyKeyWipe()` for threat response
|
||||
|
||||
### Security Benefits
|
||||
- **Memory Protection:** Keys inaccessible via direct property access or debugger
|
||||
- **Access Control:** Secure getters/setters with validation
|
||||
- **Key Lifetime Management:** Automatic rotation and expiration
|
||||
- **Threat Response:** Immediate key destruction capabilities
|
||||
|
||||
### Implementation Details
|
||||
```javascript
|
||||
// Secure key storage initialization
|
||||
this._initializeSecureKeyStorage();
|
||||
|
||||
// Secure key access
|
||||
const encryptionKey = this._getSecureKey('encryptionKey');
|
||||
this._setSecureKey('encryptionKey', newKey, { validate: true });
|
||||
|
||||
// Emergency key wipe
|
||||
this._emergencyKeyWipe();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Layer 15: Production Security Logging (Data Sanitization)
|
||||
|
||||
### Purpose
|
||||
Implements environment-aware logging system that prevents sensitive data exposure while maintaining useful diagnostics.
|
||||
|
||||
### Technical Implementation
|
||||
- **Environment Detection:** Automatic production vs development mode detection
|
||||
- **Data Sanitization:** `_secureLog()` replacing `console.log` with sanitization
|
||||
- **Log Level Control:** Production (warn+error only), Development (debug+)
|
||||
- **Rate Limiting:** Automatic log spam prevention and cleanup
|
||||
- **Memory Management:** Automatic cleanup for log counters
|
||||
|
||||
### Security Benefits
|
||||
- **Data Protection:** Encryption keys, message content, and tokens are sanitized
|
||||
- **Privacy Preservation:** User privacy maintained in production logs
|
||||
- **Debugging Support:** Safe debugging information without sensitive content
|
||||
- **Compliance:** Meets privacy regulations and security standards
|
||||
|
||||
### Implementation Details
|
||||
```javascript
|
||||
// Secure logging with data sanitization
|
||||
this._secureLog('debug', 'Connection established', {
|
||||
userId: '[REDACTED]',
|
||||
encryptionKey: '[REDACTED]',
|
||||
messageContent: '[REDACTED]'
|
||||
});
|
||||
|
||||
// Environment-aware logging
|
||||
if (this._isProductionMode()) {
|
||||
// Only critical errors and warnings
|
||||
} else {
|
||||
// Full debugging information (sanitized)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Layer 16: Atomic Operations (Race Condition Prevention)
|
||||
|
||||
### Purpose
|
||||
Prevents race conditions in critical security operations through atomic lock-based mechanisms.
|
||||
|
||||
### Technical Implementation
|
||||
- **Lock Management:** Map-based lock system with unique keys
|
||||
- **Atomic Operations:** `withLock()` wrapper for critical sections
|
||||
- **Timeout Protection:** Configurable lock timeouts (default: 5 seconds)
|
||||
- **Automatic Cleanup:** Lock removal after operation completion
|
||||
- **Error Handling:** Graceful fallback on lock failures
|
||||
|
||||
### Security Benefits
|
||||
- **Race Condition Prevention:** Eliminates concurrent access vulnerabilities
|
||||
- **Data Integrity:** Ensures consistent state during operations
|
||||
- **Critical Section Protection:** Secures file transfer and cryptographic operations
|
||||
- **Deadlock Prevention:** Automatic cleanup prevents resource exhaustion
|
||||
|
||||
### Implementation Details
|
||||
```javascript
|
||||
// Atomic operation wrapper
|
||||
return this.atomicOps.withLock(
|
||||
`chunk-${chunkMessage.fileId}`,
|
||||
async () => {
|
||||
// Critical section protected by lock
|
||||
// File chunk processing logic
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Layer 17: DTLS Race Condition Protection (WebRTC Security)
|
||||
|
||||
### Purpose
|
||||
Advanced protection against October 2024 WebRTC DTLS ClientHello race condition vulnerabilities.
|
||||
|
||||
### Technical Implementation
|
||||
- **ICE Endpoint Verification:** Secure validation before DTLS establishment
|
||||
- **ClientHello Validation:** TLS cipher suite and version verification
|
||||
- **Source Authentication:** Cryptographic verification of DTLS packet sources
|
||||
- **Queue Management:** DTLS message queuing during ICE verification
|
||||
- **Timeout Protection:** Configurable verification timeouts
|
||||
|
||||
### Security Benefits
|
||||
- **DTLS Vulnerability Mitigation:** Protects against race condition attacks
|
||||
- **WebRTC Security Enhancement:** Comprehensive transport layer protection
|
||||
- **Endpoint Validation:** Ensures legitimate connection sources
|
||||
- **Protocol Security:** TLS version and cipher suite validation
|
||||
|
||||
### Implementation Details
|
||||
```javascript
|
||||
// DTLS source validation
|
||||
await this.validateDTLSSource(clientHelloData, expectedSource);
|
||||
|
||||
// ICE endpoint verification
|
||||
this.addVerifiedICEEndpoint(endpoint);
|
||||
|
||||
// DTLS message handling
|
||||
await this.handleDTLSClientHello(clientHelloData, sourceEndpoint);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Layer 18: Memory Safety Protection (Use-After-Free)
|
||||
|
||||
### Purpose
|
||||
Advanced memory safety mechanisms to prevent use-after-free vulnerabilities and ensure secure data cleanup.
|
||||
|
||||
### Technical Implementation
|
||||
- **Secure Memory Wiping:** Advanced buffer wiping with zero-filling
|
||||
- **Context Isolation:** Symbol-based private instance management
|
||||
- **Memory Cleanup:** Comprehensive cleanup of sensitive data structures
|
||||
- **Error Handling:** Secure error handling without information leakage
|
||||
- **Garbage Collection:** Optional forced GC for critical operations
|
||||
|
||||
### Security Benefits
|
||||
- **Use-After-Free Prevention:** Eliminates memory safety vulnerabilities
|
||||
- **Data Leakage Prevention:** Secure cleanup of sensitive information
|
||||
- **Context Security:** Isolated instance management prevents tampering
|
||||
- **Error Security:** Sanitized error messages prevent information disclosure
|
||||
|
||||
### Implementation Details
|
||||
```javascript
|
||||
// Secure memory wiping
|
||||
SecureMemoryManager.secureWipe(buffer);
|
||||
|
||||
// Context isolation
|
||||
SecureFileTransferContext.getInstance().setFileTransferSystem(this);
|
||||
|
||||
// Enhanced memory cleanup
|
||||
for (const [key, value] of Object.entries(receivingState)) {
|
||||
if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
|
||||
SecureMemoryManager.secureWipe(value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Performance Impact
|
||||
|
||||
### Latency Analysis
|
||||
|
||||
| Security Layer | Added Latency | Justification |
|
||||
|----------------|---------------|---------------|
|
||||
| Authentication | ~5ms | Necessary for MITM protection |
|
||||
| Key Exchange | ~10ms | One-time cost per session |
|
||||
| Metadata Protection | ~1ms | Minimal overhead |
|
||||
| Message Encryption | ~2ms | Standard AES-GCM performance |
|
||||
| Nested Encryption | ~2ms | Additional security layer |
|
||||
| Packet Padding | ~0.5ms | Simple padding operation |
|
||||
| Anti-Fingerprinting | ~3ms | Pattern obfuscation |
|
||||
| Reordering Protection | ~1ms | Header processing |
|
||||
| Message Chunking | ~50ms | Intentional delay for security |
|
||||
| Fake Traffic | 0ms | Background operation |
|
||||
| Rate Limiting | ~0.1ms | Memory lookup |
|
||||
| PFS Key Rotation | ~10ms | Every 5 minutes |
|
||||
| Mutex Framework | ~2ms | Race condition protection |
|
||||
| Secure Key Storage | ~0.5ms | WeakMap access overhead |
|
||||
| Production Logging | ~1ms | Data sanitization processing |
|
||||
| Atomic Operations | ~2ms | Race condition protection |
|
||||
| DTLS Protection | ~3ms | WebRTC security enhancement |
|
||||
| Memory Safety | ~1ms | Secure cleanup operations |
|
||||
|
||||
**Total Average Latency:** ~84.5ms per message (acceptable for secure communications)
|
||||
|
||||
### Throughput Impact
|
||||
|
||||
- **Without Security:** ~1000 messages/second
|
||||
- **With Stage 4 Security:** ~500 messages/second
|
||||
- **Efficiency:** 50% (excellent for security level provided)
|
||||
|
||||
### Memory Usage
|
||||
|
||||
- **Base Memory:** ~2MB
|
||||
- **Security Layers:** ~3MB additional
|
||||
- **Cryptographic Keys:** ~1MB
|
||||
- **Total:** ~6MB (minimal for modern devices)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Compliance Standards
|
||||
|
||||
### Industry Standards Met
|
||||
|
||||
- ✅ **NIST SP 800-57:** Key management best practices
|
||||
- ✅ **FIPS 140-2 Level 2:** Cryptographic module security
|
||||
- ✅ **NSA Suite B:** Cryptographic algorithms (P-384, AES-256)
|
||||
- ✅ **RFC 7748:** Elliptic curve cryptography standards
|
||||
- ✅ **RFC 5869:** HKDF key derivation
|
||||
- ✅ **RFC 3394:** AES key wrap specifications
|
||||
|
||||
### Regulatory Compliance
|
||||
|
||||
- ✅ **GDPR:** Privacy by design, data minimization
|
||||
- ✅ **CCPA:** California privacy protection
|
||||
- ✅ **HIPAA:** Healthcare data protection (suitable for)
|
||||
- ✅ **SOX:** Financial data protection (suitable for)
|
||||
- ✅ **ITAR:** International traffic in arms regulations (crypto export)
|
||||
|
||||
### Security Certifications (Pending)
|
||||
|
||||
- 🔄 **Common Criteria EAL4+:** Security functionality evaluation
|
||||
- 🔄 **FIPS 140-3:** Next-generation cryptographic validation
|
||||
- 🔄 **ISO 27001:** Information security management
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Future Enhancements
|
||||
|
||||
### Quantum-Resistant Cryptography (2026)
|
||||
- **Post-Quantum Key Exchange:** CRYSTALS-Kyber
|
||||
- **Post-Quantum Signatures:** CRYSTALS-Dilithium
|
||||
- **Hybrid Classical/Quantum:** Dual algorithm support
|
||||
|
||||
### Advanced Traffic Obfuscation (2025)
|
||||
- **AI-Powered Pattern Generation:** Machine learning fake traffic
|
||||
- **Protocol Mimicry:** Disguise as common protocols (HTTP, DNS)
|
||||
- **Adaptive Obfuscation:** Real-time pattern adjustment
|
||||
|
||||
### Enhanced Perfect Forward Secrecy (2025)
|
||||
- **Automatic Key Rotation:** Every 1 minute
|
||||
- **Group Key Management:** Multi-party key rotation
|
||||
- **Quantum Key Distribution:** Hardware-based key generation
|
||||
|
||||
---
|
||||
|
||||
## 📞 Technical Support
|
||||
|
||||
For technical questions about the security architecture:
|
||||
|
||||
- **Security Team:** security@SecureBit.chat
|
||||
- **Technical Documentation:** docs@SecureBit.chat
|
||||
- **GitHub Issues:** [Security Architecture Issues](https://github.com/SecureBitChat/securebit-chat/issues?q=label%3Asecurity-architecture)
|
||||
|
||||
---
|
||||
|
||||
*This document is updated with each major security enhancement. Current version reflects Stage 5 Military-Grade Security implementation with comprehensive connection security overhaul.*
|
||||
|
||||
**Last Updated:** January 15, 2025
|
||||
**Document Version:** 4.1
|
||||
**Security Implementation:** Stage 5 - Military-Grade Security
|
||||
**Review Status:** ✅ Verified and Tested
|
||||
- WebRTC channels and peer connection handles
|
||||
- timers, deferred retries, fake traffic, and decoy traffic
|
||||
- pending transfer state and consent waits
|
||||
- verification state and crypto/PFS state
|
||||
- React file-transfer callbacks and stale UI transfer state
|
||||
|
||||
+4
-4
@@ -147,13 +147,13 @@
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script src="src/utils/updateManager.js"></script>
|
||||
<script type="module" src="src/components/UpdateChecker.jsx"></script>
|
||||
<script type="module" src="dist/qr-local.js?v=1767754446404"></script>
|
||||
<script type="module" src="src/components/QRScanner.js?v=1767754446404"></script>
|
||||
<script type="module" src="dist/qr-local.js?v=1779043608721"></script>
|
||||
<script type="module" src="src/components/QRScanner.js?v=1779043608721"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="dist/app-boot.js?v=1767754446404"></script>
|
||||
<script type="module" src="dist/app.js?v=1767754446404"></script>
|
||||
<script type="module" src="dist/app-boot.js?v=1779043608721"></script>
|
||||
<script type="module" src="dist/app.js?v=1779043608721"></script>
|
||||
|
||||
<script src="src/scripts/pwa-register.js"></script>
|
||||
<script src="./src/pwa/install-prompt.js" type="module"></script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"version": "1767754446404",
|
||||
"buildVersion": "1767754446404",
|
||||
"appVersion": "4.7.56",
|
||||
"buildTime": "2026-01-07T02:54:06.493Z",
|
||||
"buildId": "1767754446404-ebcf2dc",
|
||||
"gitHash": "ebcf2dc",
|
||||
"version": "1779043608721",
|
||||
"buildVersion": "1779043608721",
|
||||
"appVersion": "4.8.5",
|
||||
"buildTime": "2026-05-17T18:46:48.763Z",
|
||||
"buildId": "1779043608721-4b8c882",
|
||||
"gitHash": "4b8c882",
|
||||
"generated": true,
|
||||
"generatedAt": "2026-01-07T02:54:06.494Z"
|
||||
"generatedAt": "2026-05-17T18:46:48.764Z"
|
||||
}
|
||||
Generated
+33
-29
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"name": "securebit-chat",
|
||||
"version": "1.0.0",
|
||||
"version": "4.8.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "securebit-chat",
|
||||
"version": "1.0.0",
|
||||
"version": "4.8.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"cbor-js": "^0.1.0",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"pako": "^2.1.0",
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.4"
|
||||
"base64-js": "1.5.1",
|
||||
"cbor-js": "0.1.0",
|
||||
"html5-qrcode": "2.3.8",
|
||||
"pako": "2.1.0",
|
||||
"qr-scanner": "1.4.2",
|
||||
"qrcode": "1.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.9",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"esbuild": "0.25.9",
|
||||
"tailwindcss": "3.4.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -766,9 +766,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1120,9 +1120,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -1330,13 +1331,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
@@ -1516,9 +1517,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1558,9 +1559,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"version": "8.5.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
|
||||
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2152,9 +2153,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
||||
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
||||
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -2162,6 +2163,9 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "securebit-chat",
|
||||
"version": "4.7.56",
|
||||
"version": "4.8.5",
|
||||
"description": "Secure P2P Communication Application with End-to-End Encryption",
|
||||
"main": "index.html",
|
||||
"scripts": {
|
||||
@@ -11,7 +11,7 @@
|
||||
"dev": "npm run build && python -m http.server 8000",
|
||||
"watch": "npx tailwindcss -i src/styles/tw-input.css -o assets/tailwind.css --watch",
|
||||
"serve": "npx http-server -p 8000",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "node tests/sas-verification.test.mjs && node tests/file-transfer-consent.test.mjs && node tests/incoming-message-sanitization.test.mjs && node tests/file-type-allowlist.test.mjs && node tests/webrtc-privacy-mode.test.mjs && node tests/indexeddb-metadata-encryption.test.mjs && node tests/disconnect-cleanup.test.mjs && node tests/timer-lifecycle.test.mjs && node tests/file-transfer-cleanup.test.mjs && node tests/file-transfer-ui-cleanup.test.mjs && node tests/file-transfer-callback-propagation.test.mjs"
|
||||
},
|
||||
"keywords": [
|
||||
"p2p",
|
||||
@@ -24,15 +24,15 @@
|
||||
"author": "SecureBit Team",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.9",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"esbuild": "0.25.9",
|
||||
"tailwindcss": "3.4.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"cbor-js": "^0.1.0",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"pako": "^2.1.0",
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.4"
|
||||
"base64-js": "1.5.1",
|
||||
"cbor-js": "0.1.0",
|
||||
"html5-qrcode": "2.3.8",
|
||||
"pako": "2.1.0",
|
||||
"qr-scanner": "1.4.2",
|
||||
"qrcode": "1.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
+112
-15
@@ -36,6 +36,31 @@
|
||||
|
||||
// Verification Component
|
||||
const VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed, remoteConfirmed, bothConfirmed }) => {
|
||||
const [sasInput, setSasInput] = React.useState('');
|
||||
const [error, setError] = React.useState('');
|
||||
const normalizedExpectedLength = (verificationCode || '').replace(/[-\s]/g, '').length;
|
||||
const normalizedInputLength = sasInput.replace(/[-\s]/g, '').length;
|
||||
const canConfirm = !localConfirmed && normalizedExpectedLength > 0 && normalizedInputLength === normalizedExpectedLength;
|
||||
|
||||
React.useEffect(() => {
|
||||
setSasInput('');
|
||||
setError('');
|
||||
}, [verificationCode]);
|
||||
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
setError('');
|
||||
await onConfirm(sasInput);
|
||||
} catch (confirmationError) {
|
||||
setSasInput('');
|
||||
if (confirmationError?.message === 'SAS_MAX_ATTEMPTS') {
|
||||
setError('Too many incorrect attempts. Session reset for safety.');
|
||||
} else {
|
||||
setError('Incorrect code. Check it with your peer and try again.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return React.createElement('div', {
|
||||
className: "card-minimal rounded-xl p-6 border-purple-500/20"
|
||||
}, [
|
||||
@@ -63,7 +88,7 @@
|
||||
React.createElement('p', {
|
||||
key: 'description',
|
||||
className: "text-secondary text-sm"
|
||||
}, "Verify the security code with your contact via another communication channel (voice, SMS, etc.):"),
|
||||
}, "Compare this code with your peer out-of-band, then type the same code below to unlock the chat."),
|
||||
React.createElement('div', {
|
||||
key: 'code-display',
|
||||
className: "text-center"
|
||||
@@ -73,6 +98,36 @@
|
||||
className: "verification-code text-2xl py-4"
|
||||
}, verificationCode)
|
||||
]),
|
||||
React.createElement('div', {
|
||||
key: 'sas-input-wrap',
|
||||
className: "space-y-2"
|
||||
}, [
|
||||
React.createElement('label', {
|
||||
key: 'sas-label',
|
||||
className: "block text-sm text-secondary"
|
||||
}, "Enter the verified code"),
|
||||
React.createElement('input', {
|
||||
key: 'sas-input',
|
||||
type: 'text',
|
||||
value: sasInput,
|
||||
onChange: (event) => {
|
||||
setSasInput(event.target.value.toUpperCase());
|
||||
if (error) setError('');
|
||||
},
|
||||
autoFocus: true,
|
||||
autoComplete: 'off',
|
||||
spellCheck: false,
|
||||
inputMode: 'text',
|
||||
disabled: localConfirmed,
|
||||
placeholder: verificationCode ? 'Type code here' : 'Waiting for code…',
|
||||
className: "w-full rounded-lg border border-purple-500/30 bg-black/20 px-4 py-3 text-center text-xl tracking-[0.3em] text-primary uppercase focus:border-purple-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60",
|
||||
style: { fontFamily: 'monospace', textTransform: 'uppercase' }
|
||||
}),
|
||||
error && React.createElement('p', {
|
||||
key: 'sas-error',
|
||||
className: "text-sm text-red-400"
|
||||
}, error)
|
||||
]),
|
||||
// Verification status indicators
|
||||
React.createElement('div', {
|
||||
key: 'verification-status',
|
||||
@@ -142,14 +197,14 @@
|
||||
}, [
|
||||
React.createElement('button', {
|
||||
key: 'confirm',
|
||||
onClick: onConfirm,
|
||||
disabled: localConfirmed,
|
||||
className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${localConfirmed ? 'bg-gray-500/20 text-gray-400 cursor-not-allowed' : 'btn-verify text-white'}`
|
||||
onClick: handleConfirm,
|
||||
disabled: !canConfirm,
|
||||
className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${!canConfirm ? 'bg-gray-500/20 text-gray-400 cursor-not-allowed' : 'btn-verify text-white'}`
|
||||
}, [
|
||||
React.createElement('i', {
|
||||
className: `fas ${localConfirmed ? 'fa-check-circle' : 'fa-check'} mr-2`
|
||||
}),
|
||||
localConfirmed ? 'Confirmed' : 'The codes match'
|
||||
localConfirmed ? 'Confirmed' : 'Confirm code'
|
||||
]),
|
||||
React.createElement('button', {
|
||||
key: 'reject',
|
||||
@@ -283,7 +338,9 @@
|
||||
markAnswerCreated,
|
||||
notificationIntegrationRef,
|
||||
isGeneratingKeys,
|
||||
handleCreateOffer
|
||||
handleCreateOffer,
|
||||
relayOnlyMode,
|
||||
setRelayOnlyMode
|
||||
}) => {
|
||||
const [mode, setMode] = React.useState('select');
|
||||
const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);
|
||||
@@ -294,12 +351,12 @@
|
||||
onClearData();
|
||||
};
|
||||
|
||||
const handleVerificationConfirm = () => {
|
||||
onVerifyConnection(true);
|
||||
const handleVerificationConfirm = (userCode) => {
|
||||
return onVerifyConnection(userCode);
|
||||
};
|
||||
|
||||
const handleVerificationReject = () => {
|
||||
onVerifyConnection(false);
|
||||
onVerifyConnection(null, false);
|
||||
};
|
||||
|
||||
// Request notification permission on first user interaction
|
||||
@@ -449,6 +506,28 @@
|
||||
className: "text-secondary max-w-2xl mx-auto"
|
||||
}, "Choose a connection method for a secure channel with ECDH encryption and Perfect Forward Secrecy.")
|
||||
]),
|
||||
React.createElement('label', {
|
||||
key: 'privacy-mode',
|
||||
className: "mb-6 mx-auto flex max-w-2xl items-start gap-3 rounded-xl border border-purple-500/20 bg-purple-500/10 p-4 text-left"
|
||||
}, [
|
||||
React.createElement('input', {
|
||||
key: 'input',
|
||||
type: 'checkbox',
|
||||
checked: relayOnlyMode,
|
||||
onChange: (event) => setRelayOnlyMode(event.target.checked),
|
||||
className: "mt-1"
|
||||
}),
|
||||
React.createElement('span', { key: 'copy' }, [
|
||||
React.createElement('span', {
|
||||
key: 'title',
|
||||
className: "block text-sm font-medium text-primary"
|
||||
}, 'Privacy mode: relay-only WebRTC'),
|
||||
React.createElement('span', {
|
||||
key: 'desc',
|
||||
className: "block text-sm text-secondary"
|
||||
}, 'Uses TURN relay-only when configured. Without TURN, direct WebRTC may expose IP addresses and relay-only connections cannot start.')
|
||||
])
|
||||
]),
|
||||
|
||||
React.createElement('div', {
|
||||
key: 'options',
|
||||
@@ -1464,6 +1543,9 @@
|
||||
|
||||
const [messages, setMessages] = React.useState([]);
|
||||
const [connectionStatus, setConnectionStatus] = React.useState('disconnected');
|
||||
const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => {
|
||||
try { return localStorage.getItem('securebit_relay_only_mode') === 'true'; } catch { return false; }
|
||||
});
|
||||
|
||||
// Moved scrollToBottom logic to be available globally
|
||||
const [messageInput, setMessageInput] = React.useState('');
|
||||
@@ -1680,6 +1762,13 @@
|
||||
|
||||
// Create scroll function using global helper
|
||||
const scrollToBottom = createScrollToBottomFunction(chatMessagesRef);
|
||||
|
||||
React.useEffect(() => {
|
||||
try { localStorage.setItem('securebit_relay_only_mode', String(relayOnlyMode)); } catch {}
|
||||
if (webrtcManagerRef.current?._config?.webrtc) {
|
||||
webrtcManagerRef.current._config.webrtc.relayOnly = relayOnlyMode;
|
||||
}
|
||||
}, [relayOnlyMode]);
|
||||
|
||||
// Auto-scroll when messages change
|
||||
React.useEffect(() => {
|
||||
@@ -1909,7 +1998,13 @@
|
||||
handleKeyExchange,
|
||||
handleVerificationRequired,
|
||||
handleAnswerError,
|
||||
handleVerificationStateChange
|
||||
handleVerificationStateChange,
|
||||
{
|
||||
webrtc: {
|
||||
relayOnly: relayOnlyMode,
|
||||
iceServers: Array.isArray(window.SECUREBIT_ICE_SERVERS) ? window.SECUREBIT_ICE_SERVERS : undefined
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Initialize notification integration if permission was already granted
|
||||
@@ -1926,7 +2021,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
handleMessage(' SecureBit.chat Enhanced Security Edition v4.7.56 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
|
||||
handleMessage(' SecureBit.chat Enhanced Security Edition v4.8.5 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
|
||||
|
||||
const handleBeforeUnload = (event) => {
|
||||
if (event.type === 'beforeunload' && !isTabSwitching) {
|
||||
@@ -3226,9 +3321,9 @@
|
||||
}
|
||||
};
|
||||
|
||||
const handleVerifyConnection = async (isValid) => {
|
||||
const handleVerifyConnection = async (userCode, isValid = true) => {
|
||||
if (isValid) {
|
||||
webrtcManagerRef.current.confirmVerification();
|
||||
webrtcManagerRef.current.confirmVerification(userCode);
|
||||
// Mark local verification as confirmed
|
||||
setLocalVerificationConfirmed(true);
|
||||
|
||||
@@ -3666,7 +3761,9 @@
|
||||
markAnswerCreated: markAnswerCreated,
|
||||
notificationIntegrationRef: notificationIntegrationRef,
|
||||
isGeneratingKeys: isGeneratingKeys,
|
||||
handleCreateOffer: handleCreateOffer
|
||||
handleCreateOffer: handleCreateOffer,
|
||||
relayOnlyMode: relayOnlyMode,
|
||||
setRelayOnlyMode: setRelayOnlyMode
|
||||
})
|
||||
),
|
||||
|
||||
@@ -3810,4 +3907,4 @@
|
||||
ReactDOM.render(AppWithUpdateChecker, document.getElementById('root'));
|
||||
} else {
|
||||
ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
const [dragOver, setDragOver] = React.useState(false);
|
||||
const [transfers, setTransfers] = React.useState({ sending: [], receiving: [] });
|
||||
const [readyFiles, setReadyFiles] = React.useState([]); // файлы, готовые к скачиванию
|
||||
const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]);
|
||||
const fileInputRef = React.useRef(null);
|
||||
|
||||
// Update transfers periodically
|
||||
@@ -18,6 +19,14 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
return () => clearInterval(interval);
|
||||
}, [isConnected, webrtcManager]);
|
||||
|
||||
// Clear session-local UI state when the connection ends so reconnect starts clean.
|
||||
React.useEffect(() => {
|
||||
if (isConnected) return;
|
||||
setReadyFiles([]);
|
||||
setPendingIncomingFiles([]);
|
||||
setTransfers({ sending: [], receiving: [] });
|
||||
}, [isConnected]);
|
||||
|
||||
// Setup file transfer callbacks - ИСПРАВЛЕНИЕ: НЕ отправляем промежуточные сообщения в чат
|
||||
React.useEffect(() => {
|
||||
if (!webrtcManager) return;
|
||||
@@ -61,8 +70,20 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
|
||||
// ИСПРАВЛЕНИЕ: НЕ дублируем сообщения об ошибках
|
||||
// Уведомления об ошибках уже отправляются в WebRTC менеджере
|
||||
},
|
||||
|
||||
// Incoming file request callback - user consent is mandatory
|
||||
(fileRequest) => {
|
||||
setPendingIncomingFiles(prev => {
|
||||
if (prev.some(file => file.fileId === fileRequest.fileId)) return prev;
|
||||
return [...prev, fileRequest];
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
webrtcManager.setFileTransferCallbacks(null, null, null, null);
|
||||
};
|
||||
}, [webrtcManager]);
|
||||
|
||||
const handleFileSelect = async (files) => {
|
||||
@@ -177,6 +198,19 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleIncomingDecision = async (fileId, accepted) => {
|
||||
try {
|
||||
if (accepted) {
|
||||
await webrtcManager.acceptIncomingFile(fileId);
|
||||
} else {
|
||||
await webrtcManager.rejectIncomingFile(fileId);
|
||||
}
|
||||
} finally {
|
||||
setPendingIncomingFiles(prev => prev.filter(file => file.fileId !== fileId));
|
||||
setTransfers(webrtcManager.getFileTransfers());
|
||||
}
|
||||
};
|
||||
|
||||
if (!isConnected) {
|
||||
return React.createElement('div', {
|
||||
className: "p-4 text-center text-muted"
|
||||
@@ -239,6 +273,45 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
onChange: handleFileInputChange
|
||||
}),
|
||||
|
||||
pendingIncomingFiles.length > 0 && React.createElement('div', {
|
||||
key: 'incoming-consent',
|
||||
className: "mt-4 space-y-2"
|
||||
}, pendingIncomingFiles.map(file => React.createElement('div', {
|
||||
key: file.fileId,
|
||||
className: "rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3"
|
||||
}, [
|
||||
React.createElement('div', {
|
||||
key: 'info',
|
||||
className: "mb-3 flex items-center justify-between gap-3"
|
||||
}, [
|
||||
React.createElement('div', { key: 'text' }, [
|
||||
React.createElement('div', {
|
||||
key: 'title',
|
||||
className: "text-sm font-medium text-primary"
|
||||
}, 'Incoming file request'),
|
||||
React.createElement('div', {
|
||||
key: 'meta',
|
||||
className: "text-xs text-secondary"
|
||||
}, `${file.fileName} · ${formatFileSize(file.fileSize)} · ${file.mimeType}`)
|
||||
])
|
||||
]),
|
||||
React.createElement('div', {
|
||||
key: 'actions',
|
||||
className: "flex gap-2"
|
||||
}, [
|
||||
React.createElement('button', {
|
||||
key: 'accept',
|
||||
onClick: () => handleIncomingDecision(file.fileId, true),
|
||||
className: "rounded-md bg-green-500/20 px-3 py-2 text-sm text-green-300 hover:bg-green-500/30"
|
||||
}, 'Accept'),
|
||||
React.createElement('button', {
|
||||
key: 'reject',
|
||||
onClick: () => handleIncomingDecision(file.fileId, false),
|
||||
className: "rounded-md bg-red-500/20 px-3 py-2 text-sm text-red-300 hover:bg-red-500/30"
|
||||
}, 'Reject')
|
||||
])
|
||||
]))),
|
||||
|
||||
// Active Transfers
|
||||
(transfers.sending.length > 0 || transfers.receiving.length > 0) && React.createElement('div', {
|
||||
key: 'transfers',
|
||||
@@ -366,7 +439,7 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
a.click();
|
||||
rf.revokeObjectURL(url);
|
||||
} catch (e) {
|
||||
alert('Failed to start download: ' + e.message);
|
||||
alert(e.message || 'This file is no longer available for download.');
|
||||
}
|
||||
}
|
||||
}, [
|
||||
@@ -420,4 +493,4 @@ const FileTransferComponent = ({ webrtcManager, isConnected }) => {
|
||||
};
|
||||
|
||||
// Export
|
||||
window.FileTransferComponent = FileTransferComponent;
|
||||
window.FileTransferComponent = FileTransferComponent;
|
||||
|
||||
@@ -539,7 +539,7 @@ const EnhancedMinimalHeader = ({
|
||||
React.createElement('p', {
|
||||
key: 'subtitle',
|
||||
className: 'text-xs sm:text-sm text-muted hidden sm:block'
|
||||
}, 'End-to-end freedom v4.7.56')
|
||||
}, 'End-to-end freedom v4.8.5')
|
||||
])
|
||||
]),
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -256,12 +256,13 @@ class SecureMemoryManager {
|
||||
}
|
||||
|
||||
class EnhancedSecureFileTransfer {
|
||||
constructor(webrtcManager, onProgress, onComplete, onError, onFileReceived) {
|
||||
constructor(webrtcManager, onProgress, onComplete, onError, onFileReceived, onIncomingFileRequest) {
|
||||
this.webrtcManager = webrtcManager;
|
||||
this.onProgress = onProgress;
|
||||
this.onComplete = onComplete;
|
||||
this.onError = onError;
|
||||
this.onFileReceived = onFileReceived;
|
||||
this.onIncomingFileRequest = onIncomingFileRequest;
|
||||
|
||||
// Validate webrtcManager
|
||||
if (!webrtcManager) {
|
||||
@@ -284,87 +285,58 @@ class EnhancedSecureFileTransfer {
|
||||
this.RETRY_ATTEMPTS = 3;
|
||||
|
||||
this.FILE_TYPE_RESTRICTIONS = {
|
||||
documents: {
|
||||
extensions: ['.pdf', '.doc', '.docx', '.txt', '.md', '.rtf', '.odt'],
|
||||
mimeTypes: [
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'text/plain',
|
||||
'text/markdown',
|
||||
'application/rtf',
|
||||
'application/vnd.oasis.opendocument.text'
|
||||
],
|
||||
maxSize: 50 * 1024 * 1024, // 50 MB
|
||||
category: 'Documents',
|
||||
description: 'PDF, DOC, TXT, MD, RTF, ODT'
|
||||
pdf: {
|
||||
extensions: ['.pdf'],
|
||||
mimeTypes: ['application/pdf'],
|
||||
maxSize: 50 * 1024 * 1024,
|
||||
category: 'PDF',
|
||||
description: 'PDF'
|
||||
},
|
||||
|
||||
text: {
|
||||
extensions: ['.txt'],
|
||||
mimeTypes: ['text/plain'],
|
||||
maxSize: 10 * 1024 * 1024,
|
||||
category: 'Plain text',
|
||||
description: 'TXT'
|
||||
},
|
||||
|
||||
images: {
|
||||
extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.ico'],
|
||||
extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.ico'],
|
||||
mimeTypes: [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
'image/bmp',
|
||||
'image/svg+xml',
|
||||
'image/x-icon'
|
||||
],
|
||||
maxSize: 25 * 1024 * 1024, // 25 MB
|
||||
category: 'Images',
|
||||
description: 'JPG, PNG, GIF, WEBP, BMP, SVG, ICO'
|
||||
description: 'JPG, JPEG, PNG, GIF, WEBP, BMP, ICO'
|
||||
},
|
||||
|
||||
archives: {
|
||||
extensions: ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', '.xz'],
|
||||
mimeTypes: [
|
||||
'application/zip',
|
||||
'application/x-rar-compressed',
|
||||
'application/x-7z-compressed',
|
||||
'application/x-tar',
|
||||
'application/gzip',
|
||||
'application/x-bzip2',
|
||||
'application/x-xz'
|
||||
],
|
||||
extensions: ['.zip'],
|
||||
mimeTypes: ['application/zip'],
|
||||
maxSize: 100 * 1024 * 1024, // 100 MB
|
||||
category: 'Archives',
|
||||
description: 'ZIP, RAR, 7Z, TAR, GZ, BZ2, XZ'
|
||||
},
|
||||
|
||||
media: {
|
||||
extensions: ['.mp3', '.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.ogg', '.wav'],
|
||||
mimeTypes: [
|
||||
'audio/mpeg',
|
||||
'video/mp4',
|
||||
'video/x-msvideo',
|
||||
'video/x-matroska',
|
||||
'video/quicktime',
|
||||
'video/x-ms-wmv',
|
||||
'video/x-flv',
|
||||
'video/webm',
|
||||
'audio/ogg',
|
||||
'audio/wav'
|
||||
],
|
||||
maxSize: 100 * 1024 * 1024, // 100 MB
|
||||
category: 'Media',
|
||||
description: 'MP3, MP4, AVI, MKV, MOV, WMV, FLV, WEBM, OGG, WAV'
|
||||
},
|
||||
|
||||
general: {
|
||||
extensions: [],
|
||||
mimeTypes: [],
|
||||
maxSize: 50 * 1024 * 1024, // 50 MB
|
||||
category: 'General',
|
||||
description: 'Any file type up to size limits'
|
||||
description: 'ZIP'
|
||||
}
|
||||
};
|
||||
this.BLOCKED_EXTENSIONS = new Set([
|
||||
'.exe', '.bat', '.cmd', '.sh', '.js', '.msi', '.dmg', '.app',
|
||||
'.jar', '.scr', '.ps1', '.vbs', '.html', '.svg'
|
||||
]);
|
||||
|
||||
// Active transfers tracking
|
||||
this.activeTransfers = new Map(); // fileId -> transfer state
|
||||
this.receivingTransfers = new Map(); // fileId -> receiving state
|
||||
this.pendingIncomingTransfers = new Map(); // fileId -> validated metadata awaiting consent
|
||||
this.transferQueue = []; // Queue for pending transfers
|
||||
this.pendingChunks = new Map();
|
||||
this.incomingOfferLimiter = new RateLimiter(5, 60000);
|
||||
this.MAX_PENDING_INCOMING_TRANSFERS = 3;
|
||||
|
||||
// Session key derivation
|
||||
this.sessionKeys = new Map(); // fileId -> derived session key
|
||||
@@ -373,6 +345,7 @@ class EnhancedSecureFileTransfer {
|
||||
this.processedChunks = new Set(); // Prevent replay attacks
|
||||
this.transferNonces = new Map(); // fileId -> current nonce counter
|
||||
this.receivedFileBuffers = new Map(); // fileId -> { buffer:ArrayBuffer, type:string, name:string, size:number }
|
||||
this.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 3;
|
||||
|
||||
this.setupFileMessageHandlers();
|
||||
|
||||
@@ -386,24 +359,15 @@ class EnhancedSecureFileTransfer {
|
||||
// ============================================
|
||||
|
||||
getFileType(file) {
|
||||
const fileName = file.name.toLowerCase();
|
||||
const fileExtension = fileName.substring(fileName.lastIndexOf('.'));
|
||||
const mimeType = file.type.toLowerCase();
|
||||
const fileName = String(file?.name || '').toLowerCase();
|
||||
const extensionIndex = fileName.lastIndexOf('.');
|
||||
const fileExtension = extensionIndex >= 0 ? fileName.substring(extensionIndex) : '';
|
||||
const mimeType = String(file?.type || '').toLowerCase();
|
||||
|
||||
for (const [typeKey, typeConfig] of Object.entries(this.FILE_TYPE_RESTRICTIONS)) {
|
||||
if (typeKey === 'general') continue; // Пропускаем общий тип
|
||||
|
||||
if (typeConfig.extensions.includes(fileExtension)) {
|
||||
return {
|
||||
type: typeKey,
|
||||
category: typeConfig.category,
|
||||
description: typeConfig.description,
|
||||
maxSize: typeConfig.maxSize,
|
||||
allowed: true
|
||||
};
|
||||
}
|
||||
|
||||
if (typeConfig.mimeTypes.includes(mimeType)) {
|
||||
const extensionAllowed = typeConfig.extensions.includes(fileExtension);
|
||||
const mimeAllowed = typeConfig.mimeTypes.includes(mimeType);
|
||||
if (extensionAllowed && mimeAllowed) {
|
||||
return {
|
||||
type: typeKey,
|
||||
category: typeConfig.category,
|
||||
@@ -414,26 +378,42 @@ class EnhancedSecureFileTransfer {
|
||||
}
|
||||
}
|
||||
|
||||
const generalConfig = this.FILE_TYPE_RESTRICTIONS.general;
|
||||
return {
|
||||
type: 'general',
|
||||
category: generalConfig.category,
|
||||
description: generalConfig.description,
|
||||
maxSize: generalConfig.maxSize,
|
||||
allowed: true
|
||||
type: 'blocked',
|
||||
category: 'Unsupported',
|
||||
description: 'Allowed: JPG, JPEG, PNG, GIF, WEBP, BMP, ICO, PDF, TXT, ZIP',
|
||||
maxSize: this.MAX_FILE_SIZE,
|
||||
allowed: false,
|
||||
extension: fileExtension,
|
||||
mimeType
|
||||
};
|
||||
}
|
||||
|
||||
validateFile(file) {
|
||||
const fileType = this.getFileType(file);
|
||||
const errors = [];
|
||||
const fileName = String(file?.name || '');
|
||||
const lowerName = fileName.toLowerCase();
|
||||
const extensionIndex = lowerName.lastIndexOf('.');
|
||||
const fileExtension = extensionIndex >= 0 ? lowerName.substring(extensionIndex) : '';
|
||||
const mimeType = String(file?.type || '').toLowerCase();
|
||||
|
||||
if (this.BLOCKED_EXTENSIONS.has(fileExtension)) {
|
||||
errors.push(`File rejected: ${fileExtension} files are not allowed for security reasons.`);
|
||||
}
|
||||
|
||||
if (!mimeType) {
|
||||
errors.push('File rejected: missing MIME type is unsafe.');
|
||||
}
|
||||
|
||||
if (file.size > fileType.maxSize) {
|
||||
errors.push(`File size (${this.formatFileSize(file.size)}) exceeds maximum allowed for ${fileType.category} (${this.formatFileSize(fileType.maxSize)})`);
|
||||
}
|
||||
|
||||
if (!fileType.allowed) {
|
||||
errors.push(`File type not allowed. Supported types: ${fileType.description}`);
|
||||
if (mimeType && !this.BLOCKED_EXTENSIONS.has(fileExtension)) {
|
||||
errors.push(`File rejected: extension and MIME type must match an allowed type. Supported types: ${fileType.description}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (file.size > this.MAX_FILE_SIZE) {
|
||||
@@ -449,6 +429,48 @@ class EnhancedSecureFileTransfer {
|
||||
};
|
||||
}
|
||||
|
||||
normalizeDisplayFileName(fileName) {
|
||||
return String(fileName || '')
|
||||
.normalize('NFKC')
|
||||
.replace(/[\u0000-\u001F\u007F]/g, '')
|
||||
.replace(/[\\/]+/g, '_')
|
||||
.trim()
|
||||
.slice(0, 255);
|
||||
}
|
||||
|
||||
validateIncomingMetadata(metadata) {
|
||||
const errors = [];
|
||||
if (!metadata || typeof metadata !== 'object') errors.push('Invalid file transfer metadata');
|
||||
if (!metadata?.fileId || typeof metadata.fileId !== 'string') errors.push('Invalid file id');
|
||||
if (!Number.isSafeInteger(metadata?.fileSize) || metadata.fileSize <= 0) errors.push('Invalid file size');
|
||||
if (!Number.isSafeInteger(metadata?.totalChunks) || metadata.totalChunks <= 0) errors.push('Invalid chunk count');
|
||||
if (!Number.isSafeInteger(metadata?.chunkSize) || metadata.chunkSize <= 0 || metadata.chunkSize > this.CHUNK_SIZE) errors.push('Invalid chunk size');
|
||||
if (!Array.isArray(metadata?.salt) || metadata.salt.length !== 32) errors.push('Invalid salt');
|
||||
|
||||
const rawName = typeof metadata?.fileName === 'string' ? metadata.fileName : '';
|
||||
const displayName = this.normalizeDisplayFileName(rawName);
|
||||
const hasDangerousName =
|
||||
!rawName ||
|
||||
rawName !== rawName.trim() ||
|
||||
/[\u0000-\u001F\u007F]/.test(rawName) ||
|
||||
/[\\/]/.test(rawName) ||
|
||||
rawName === '.' ||
|
||||
rawName === '..' ||
|
||||
displayName.length === 0;
|
||||
if (hasDangerousName) errors.push('Dangerous file name');
|
||||
|
||||
if (errors.length === 0) {
|
||||
const validation = this.validateFile({
|
||||
name: displayName,
|
||||
size: metadata.fileSize,
|
||||
type: metadata.fileType || 'application/octet-stream'
|
||||
});
|
||||
if (!validation.isValid) errors.push(...validation.errors);
|
||||
}
|
||||
|
||||
return { isValid: errors.length === 0, errors, displayName };
|
||||
}
|
||||
|
||||
formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
@@ -461,8 +483,6 @@ class EnhancedSecureFileTransfer {
|
||||
const supportedTypes = {};
|
||||
|
||||
for (const [typeKey, typeConfig] of Object.entries(this.FILE_TYPE_RESTRICTIONS)) {
|
||||
if (typeKey === 'general') continue;
|
||||
|
||||
supportedTypes[typeKey] = {
|
||||
category: typeConfig.category,
|
||||
description: typeConfig.description,
|
||||
@@ -878,10 +898,21 @@ class EnhancedSecureFileTransfer {
|
||||
this.activeTransfers.set(fileId, transferState);
|
||||
this.transferNonces.set(fileId, 0);
|
||||
|
||||
const consentPromise = new Promise((resolve, reject) => {
|
||||
transferState.resolveConsent = resolve;
|
||||
transferState.rejectConsent = reject;
|
||||
transferState.consentTimeout = setTimeout(() => {
|
||||
transferState.consentTimeout = null;
|
||||
reject(new Error('Transfer timeout'));
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
// Send file metadata first
|
||||
await this.sendFileMetadata(transferState);
|
||||
|
||||
// Start chunk transmission
|
||||
// Wait for explicit receiver consent before any chunks are sent.
|
||||
await consentPromise;
|
||||
|
||||
await this.startChunkTransmission(transferState);
|
||||
|
||||
return fileId;
|
||||
@@ -1106,11 +1137,14 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
async handleFileTransferStart(metadata) {
|
||||
try {
|
||||
// Validate metadata
|
||||
if (!metadata.fileId || !metadata.fileName || !metadata.fileSize) {
|
||||
throw new Error('Invalid file transfer metadata');
|
||||
const clientId = this.getClientIdentifier();
|
||||
if (!this.incomingOfferLimiter.isAllowed(clientId)) {
|
||||
throw new Error('Incoming file request rate limit exceeded');
|
||||
}
|
||||
|
||||
const validation = this.validateIncomingMetadata(metadata);
|
||||
if (!validation.isValid) throw new Error(validation.errors.join('. '));
|
||||
|
||||
if (metadata.signature && this.verificationKey) {
|
||||
try {
|
||||
const isValid = await FileMetadataSigner.verifyFileMetadata(
|
||||
@@ -1137,55 +1171,30 @@ class EnhancedSecureFileTransfer {
|
||||
}
|
||||
|
||||
// Check if we already have this transfer
|
||||
if (this.receivingTransfers.has(metadata.fileId)) {
|
||||
if (this.receivingTransfers.has(metadata.fileId) || this.pendingIncomingTransfers.has(metadata.fileId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Derive session key from salt
|
||||
const sessionKey = await this.deriveFileSessionKeyFromSalt(
|
||||
metadata.fileId,
|
||||
metadata.salt
|
||||
);
|
||||
|
||||
// Create receiving transfer state
|
||||
const receivingState = {
|
||||
fileId: metadata.fileId,
|
||||
fileName: metadata.fileName,
|
||||
fileSize: metadata.fileSize,
|
||||
fileType: metadata.fileType || 'application/octet-stream',
|
||||
fileHash: metadata.fileHash,
|
||||
totalChunks: metadata.totalChunks,
|
||||
chunkSize: metadata.chunkSize || this.CHUNK_SIZE,
|
||||
sessionKey: sessionKey,
|
||||
salt: metadata.salt,
|
||||
receivedChunks: new Map(),
|
||||
receivedCount: 0,
|
||||
startTime: Date.now(),
|
||||
lastChunkTime: Date.now(),
|
||||
status: 'receiving'
|
||||
};
|
||||
|
||||
this.receivingTransfers.set(metadata.fileId, receivingState);
|
||||
|
||||
// Send acceptance response
|
||||
const response = {
|
||||
type: 'file_transfer_response',
|
||||
fileId: metadata.fileId,
|
||||
accepted: true,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
await this.sendSecureMessage(response);
|
||||
|
||||
// Process buffered chunks if any
|
||||
if (this.pendingChunks.has(metadata.fileId)) {
|
||||
const bufferedChunks = this.pendingChunks.get(metadata.fileId);
|
||||
|
||||
for (const [chunkIndex, chunkMessage] of bufferedChunks.entries()) {
|
||||
await this.handleFileChunk(chunkMessage);
|
||||
}
|
||||
|
||||
this.pendingChunks.delete(metadata.fileId);
|
||||
if (this.pendingIncomingTransfers.size >= this.MAX_PENDING_INCOMING_TRANSFERS) {
|
||||
throw new Error('Too many pending incoming file requests');
|
||||
}
|
||||
|
||||
const pendingMetadata = {
|
||||
...metadata,
|
||||
fileName: validation.displayName,
|
||||
receivedAt: Date.now()
|
||||
};
|
||||
this.pendingIncomingTransfers.set(metadata.fileId, pendingMetadata);
|
||||
|
||||
if (typeof this.onIncomingFileRequest === 'function') {
|
||||
this.onIncomingFileRequest({
|
||||
fileId: pendingMetadata.fileId,
|
||||
fileName: pendingMetadata.fileName,
|
||||
fileSize: pendingMetadata.fileSize,
|
||||
mimeType: pendingMetadata.fileType || 'application/octet-stream'
|
||||
});
|
||||
} else {
|
||||
await this.rejectIncomingFile(metadata.fileId, 'User consent unavailable');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -1211,13 +1220,8 @@ class EnhancedSecureFileTransfer {
|
||||
try {
|
||||
let receivingState = this.receivingTransfers.get(chunkMessage.fileId);
|
||||
|
||||
// Buffer early chunks if transfer not yet initialized
|
||||
// Never buffer chunks before explicit consent.
|
||||
if (!receivingState) {
|
||||
if (!this.pendingChunks.has(chunkMessage.fileId)) {
|
||||
this.pendingChunks.set(chunkMessage.fileId, new Map());
|
||||
}
|
||||
|
||||
this.pendingChunks.get(chunkMessage.fileId).set(chunkMessage.chunkIndex, chunkMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1352,7 +1356,7 @@ class EnhancedSecureFileTransfer {
|
||||
receivingState.endTime = Date.now();
|
||||
receivingState.status = 'completed';
|
||||
|
||||
this.receivedFileBuffers.set(receivingState.fileId, {
|
||||
this._storeReceivedFileBuffer(receivingState.fileId, {
|
||||
buffer: fileBuffer,
|
||||
type: receivingState.fileType,
|
||||
name: receivingState.fileName,
|
||||
@@ -1360,7 +1364,13 @@ class EnhancedSecureFileTransfer {
|
||||
});
|
||||
|
||||
if (this.onFileReceived) {
|
||||
const getBlob = async () => new Blob([this.receivedFileBuffers.get(receivingState.fileId).buffer], { type: receivingState.fileType });
|
||||
const getBlob = async () => {
|
||||
const blob = await this.getBlob(receivingState.fileId);
|
||||
if (!blob) {
|
||||
throw new Error('This file is no longer available for download.');
|
||||
}
|
||||
return blob;
|
||||
};
|
||||
const getObjectURL = async () => {
|
||||
const blob = await getBlob();
|
||||
return URL.createObjectURL(blob);
|
||||
@@ -1443,8 +1453,18 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
if (response.accepted) {
|
||||
transferState.status = 'accepted';
|
||||
if (transferState.consentTimeout) clearTimeout(transferState.consentTimeout);
|
||||
transferState.consentTimeout = null;
|
||||
transferState.resolveConsent?.();
|
||||
transferState.resolveConsent = null;
|
||||
transferState.rejectConsent = null;
|
||||
} else {
|
||||
transferState.status = 'rejected';
|
||||
if (transferState.consentTimeout) clearTimeout(transferState.consentTimeout);
|
||||
transferState.consentTimeout = null;
|
||||
transferState.rejectConsent?.(new Error(response.error || 'Transfer rejected'));
|
||||
transferState.rejectConsent = null;
|
||||
transferState.resolveConsent = null;
|
||||
|
||||
if (this.onError) {
|
||||
this.onError(`Transfer rejected: ${response.error || 'Unknown reason'}`);
|
||||
@@ -1555,6 +1575,48 @@ class EnhancedSecureFileTransfer {
|
||||
}));
|
||||
}
|
||||
|
||||
getPendingIncomingTransfers() {
|
||||
return Array.from(this.pendingIncomingTransfers.values()).map(transfer => ({
|
||||
fileId: transfer.fileId,
|
||||
fileName: transfer.fileName,
|
||||
fileSize: transfer.fileSize,
|
||||
mimeType: transfer.fileType || 'application/octet-stream',
|
||||
receivedAt: transfer.receivedAt
|
||||
}));
|
||||
}
|
||||
|
||||
async acceptIncomingFile(fileId) {
|
||||
const metadata = this.pendingIncomingTransfers.get(fileId);
|
||||
if (!metadata) return false;
|
||||
const sessionKey = await this.deriveFileSessionKeyFromSalt(fileId, metadata.salt);
|
||||
this.receivingTransfers.set(fileId, {
|
||||
fileId,
|
||||
fileName: metadata.fileName,
|
||||
fileSize: metadata.fileSize,
|
||||
fileType: metadata.fileType || 'application/octet-stream',
|
||||
fileHash: metadata.fileHash,
|
||||
totalChunks: metadata.totalChunks,
|
||||
chunkSize: metadata.chunkSize || this.CHUNK_SIZE,
|
||||
sessionKey,
|
||||
salt: metadata.salt,
|
||||
receivedChunks: new Map(),
|
||||
receivedCount: 0,
|
||||
startTime: Date.now(),
|
||||
lastChunkTime: Date.now(),
|
||||
status: 'receiving'
|
||||
});
|
||||
this.pendingIncomingTransfers.delete(fileId);
|
||||
await this.sendSecureMessage({ type: 'file_transfer_response', fileId, accepted: true, timestamp: Date.now() });
|
||||
return true;
|
||||
}
|
||||
|
||||
async rejectIncomingFile(fileId, error = 'Rejected by user') {
|
||||
if (!this.pendingIncomingTransfers.has(fileId)) return false;
|
||||
this.pendingIncomingTransfers.delete(fileId);
|
||||
await this.sendSecureMessage({ type: 'file_transfer_response', fileId, accepted: false, error, timestamp: Date.now() });
|
||||
return true;
|
||||
}
|
||||
|
||||
cancelTransfer(fileId) {
|
||||
try {
|
||||
if (this.activeTransfers.has(fileId)) {
|
||||
@@ -1573,6 +1635,19 @@ class EnhancedSecureFileTransfer {
|
||||
}
|
||||
|
||||
cleanupTransfer(fileId) {
|
||||
const transferState = this.activeTransfers.get(fileId);
|
||||
if (transferState) {
|
||||
if (transferState.consentTimeout) {
|
||||
clearTimeout(transferState.consentTimeout);
|
||||
transferState.consentTimeout = null;
|
||||
}
|
||||
if (transferState.rejectConsent) {
|
||||
transferState.rejectConsent(new Error('Transfer cancelled during cleanup or disconnect'));
|
||||
transferState.rejectConsent = null;
|
||||
transferState.resolveConsent = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.activeTransfers.delete(fileId);
|
||||
this.sessionKeys.delete(fileId);
|
||||
this.transferNonces.delete(fileId);
|
||||
@@ -1585,6 +1660,28 @@ class EnhancedSecureFileTransfer {
|
||||
}
|
||||
}
|
||||
|
||||
_storeReceivedFileBuffer(fileId, entry) {
|
||||
this.receivedFileBuffers.set(fileId, entry);
|
||||
while (this.receivedFileBuffers.size > this.MAX_RETAINED_RECEIVED_FILE_BUFFERS) {
|
||||
const oldestFileId = this.receivedFileBuffers.keys().next().value;
|
||||
this._discardReceivedFileBuffer(oldestFileId);
|
||||
}
|
||||
}
|
||||
|
||||
_discardReceivedFileBuffer(fileId) {
|
||||
const fileBuffer = this.receivedFileBuffers.get(fileId);
|
||||
if (!fileBuffer) return;
|
||||
try {
|
||||
if (fileBuffer.buffer) {
|
||||
SecureMemoryManager.secureWipe(fileBuffer.buffer);
|
||||
new Uint8Array(fileBuffer.buffer).fill(0);
|
||||
}
|
||||
} catch (_) {
|
||||
// Best-effort wipe; deletion must still proceed.
|
||||
}
|
||||
this.receivedFileBuffers.delete(fileId);
|
||||
}
|
||||
|
||||
// ✅ УЛУЧШЕННАЯ безопасная очистка памяти для предотвращения use-after-free
|
||||
cleanupReceivingTransfer(fileId) {
|
||||
try {
|
||||
@@ -1809,6 +1906,7 @@ class EnhancedSecureFileTransfer {
|
||||
|
||||
// Clear all state
|
||||
this.pendingChunks.clear();
|
||||
this.pendingIncomingTransfers.clear();
|
||||
this.activeTransfers.clear();
|
||||
this.receivingTransfers.clear();
|
||||
this.transferQueue.length = 0;
|
||||
@@ -1816,6 +1914,10 @@ class EnhancedSecureFileTransfer {
|
||||
this.transferNonces.clear();
|
||||
this.processedChunks.clear();
|
||||
|
||||
for (const fileId of Array.from(this.receivedFileBuffers.keys())) {
|
||||
this._discardReceivedFileBuffer(fileId);
|
||||
}
|
||||
|
||||
this.clearKeys();
|
||||
}
|
||||
|
||||
@@ -2026,4 +2128,4 @@ class EnhancedSecureFileTransfer {
|
||||
}
|
||||
}
|
||||
|
||||
export { EnhancedSecureFileTransfer };
|
||||
export { EnhancedSecureFileTransfer };
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
globalThis.window = {
|
||||
EnhancedSecureCryptoUtils: {
|
||||
secureLog: { log() {} }
|
||||
}
|
||||
};
|
||||
globalThis.CustomEvent = class CustomEvent {
|
||||
constructor(type, init) {
|
||||
this.type = type;
|
||||
this.detail = init?.detail;
|
||||
}
|
||||
};
|
||||
const dispatchedEvents = [];
|
||||
globalThis.document = {
|
||||
dispatchEvent(event) {
|
||||
dispatchedEvents.push(event);
|
||||
}
|
||||
};
|
||||
|
||||
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
function closableChannel() {
|
||||
return {
|
||||
readyState: 'open',
|
||||
closed: false,
|
||||
onopen() {},
|
||||
onclose() {},
|
||||
onmessage() {},
|
||||
onerror() {},
|
||||
close() { this.closed = true; }
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
let transferCleanups = 0;
|
||||
const dataChannel = closableChannel();
|
||||
const heartbeatChannel = closableChannel();
|
||||
const decoyChannel = closableChannel();
|
||||
const peerConnection = {
|
||||
closed: false,
|
||||
onconnectionstatechange() {},
|
||||
ondatachannel() {},
|
||||
close() { this.closed = true; }
|
||||
};
|
||||
const timer = setTimeout(() => {}, 10_000);
|
||||
const manager = {
|
||||
intentionalDisconnect: false,
|
||||
fileTransferSystem: { cleanup() { transferCleanups += 1; } },
|
||||
dataChannel,
|
||||
heartbeatChannel,
|
||||
peerConnection,
|
||||
decoyTimers: new Map([['decoy', timer]]),
|
||||
decoyChannels: new Map([['decoy', decoyChannel]]),
|
||||
packetBuffer: new Map([['p', 1]]),
|
||||
chunkQueue: [1],
|
||||
processedMessageIds: new Set(['m']),
|
||||
messageCounter: 4,
|
||||
keyVersions: new Map([['v', 1]]),
|
||||
oldKeys: new Map([['o', 1]]),
|
||||
currentKeyVersion: 3,
|
||||
lastKeyRotation: 1,
|
||||
sequenceNumber: 7,
|
||||
expectedSequenceNumber: 8,
|
||||
replayWindow: new Set([9]),
|
||||
messageQueue: [{ secret: true }],
|
||||
calls: [],
|
||||
_stopAllTimers() { this.calls.push('_stopAllTimers'); },
|
||||
stopHeartbeat() { this.calls.push('stopHeartbeat'); },
|
||||
stopFakeTrafficGeneration() { this.calls.push('stopFakeTrafficGeneration'); },
|
||||
_wipeEphemeralKeys() { this.calls.push('_wipeEphemeralKeys'); },
|
||||
_hardWipeOldKeys() { this.calls.push('_hardWipeOldKeys'); },
|
||||
_secureCleanupCryptographicMaterials() { this.calls.push('_secureCleanupCryptographicMaterials'); },
|
||||
_clearVerificationStates() {
|
||||
this.calls.push('_clearVerificationStates');
|
||||
this.localVerificationConfirmed = false;
|
||||
this.remoteVerificationConfirmed = false;
|
||||
this.bothVerificationsConfirmed = false;
|
||||
this.isVerified = false;
|
||||
this.verificationCode = null;
|
||||
this.pendingSASCode = null;
|
||||
},
|
||||
_secureWipeMemory() { this.calls.push('_secureWipeMemory'); },
|
||||
_forceGarbageCollection() { return Promise.resolve(); },
|
||||
sendDisconnectNotification() { this.calls.push('sendDisconnectNotification'); },
|
||||
onStatusChange(value) { this.status = value; },
|
||||
onKeyExchange(value) { this.keyExchange = value; },
|
||||
onVerificationRequired(value) { this.verificationRequired = value; },
|
||||
_secureLog() {}
|
||||
};
|
||||
|
||||
EnhancedSecureWebRTCManager.prototype.disconnect.call(manager);
|
||||
|
||||
assert.equal(transferCleanups, 1);
|
||||
assert.equal(manager.fileTransferSystem, null);
|
||||
assert.equal(dataChannel.closed, true);
|
||||
assert.equal(heartbeatChannel.closed, true);
|
||||
assert.equal(decoyChannel.closed, true);
|
||||
assert.equal(peerConnection.closed, true);
|
||||
assert.equal(manager.dataChannel, null);
|
||||
assert.equal(manager.heartbeatChannel, null);
|
||||
assert.equal(manager.peerConnection, null);
|
||||
assert.equal(manager.decoyTimers.size, 0);
|
||||
assert.equal(manager.decoyChannels.size, 0);
|
||||
assert.equal(manager.packetBuffer.size, 0);
|
||||
assert.deepEqual(manager.chunkQueue, []);
|
||||
assert.equal(manager.processedMessageIds.size, 0);
|
||||
assert.equal(manager.keyVersions.size, 0);
|
||||
assert.equal(manager.oldKeys.size, 0);
|
||||
assert.equal(manager.replayWindow.size, 0);
|
||||
assert.deepEqual(manager.messageQueue, []);
|
||||
assert.equal(manager.status, 'disconnected');
|
||||
assert.equal(manager.keyExchange, '');
|
||||
assert.equal(manager.verificationRequired, '');
|
||||
assert.ok(manager.calls.includes('_clearVerificationStates'));
|
||||
assert.ok(dispatchedEvents.some(event => event.type === 'peer-disconnect'));
|
||||
assert.ok(dispatchedEvents.some(event => event.type === 'connection-cleaned'));
|
||||
}
|
||||
|
||||
console.log('Disconnect cleanup tests passed');
|
||||
@@ -0,0 +1,31 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
globalThis.window = { EnhancedSecureCryptoUtils: {} };
|
||||
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
{
|
||||
const oldProgress = () => {};
|
||||
const manager = {
|
||||
fileTransferSystem: {
|
||||
onProgress: oldProgress,
|
||||
onFileReceived: oldProgress,
|
||||
onError: oldProgress,
|
||||
onIncomingFileRequest: oldProgress
|
||||
}
|
||||
};
|
||||
|
||||
EnhancedSecureWebRTCManager.prototype.setFileTransferCallbacks.call(
|
||||
manager,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
assert.equal(manager.fileTransferSystem.onProgress, null);
|
||||
assert.equal(manager.fileTransferSystem.onFileReceived, null);
|
||||
assert.equal(manager.fileTransferSystem.onError, null);
|
||||
assert.equal(manager.fileTransferSystem.onIncomingFileRequest, null);
|
||||
}
|
||||
|
||||
console.log('File transfer callback propagation tests passed');
|
||||
@@ -0,0 +1,90 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { EnhancedSecureFileTransfer } from '../src/transfer/EnhancedSecureFileTransfer.js';
|
||||
|
||||
function createSystem() {
|
||||
const manager = {
|
||||
dataChannel: { onmessage: null, send() {}, readyState: 'open' },
|
||||
isVerified: true,
|
||||
fileTransferSystem: null,
|
||||
isConnected: () => true
|
||||
};
|
||||
return new EnhancedSecureFileTransfer(manager);
|
||||
}
|
||||
|
||||
// cleanupTransfer rejects pending sender consent immediately and clears its timeout.
|
||||
{
|
||||
const system = createSystem();
|
||||
let rejectionMessage = null;
|
||||
const timer = setTimeout(() => {}, 10_000);
|
||||
system.activeTransfers.set('file_waiting', {
|
||||
consentTimeout: timer,
|
||||
rejectConsent(error) { rejectionMessage = error.message; },
|
||||
resolveConsent() {}
|
||||
});
|
||||
system.sessionKeys.set('file_waiting', {});
|
||||
system.transferNonces.set('file_waiting', 1);
|
||||
|
||||
system.cleanupTransfer('file_waiting');
|
||||
|
||||
assert.equal(system.activeTransfers.has('file_waiting'), false);
|
||||
assert.equal(system.sessionKeys.has('file_waiting'), false);
|
||||
assert.equal(system.transferNonces.has('file_waiting'), false);
|
||||
assert.equal(rejectionMessage, 'Transfer cancelled during cleanup or disconnect');
|
||||
assert.equal(system.activeTransfers.size, 0);
|
||||
}
|
||||
|
||||
// global cleanup does not leave pending consent promises alive until timeout.
|
||||
{
|
||||
const system = createSystem();
|
||||
let rejected = false;
|
||||
const timer = setTimeout(() => {}, 10_000);
|
||||
system.activeTransfers.set('file_waiting', {
|
||||
consentTimeout: timer,
|
||||
rejectConsent() { rejected = true; },
|
||||
resolveConsent() {}
|
||||
});
|
||||
system.cleanup();
|
||||
assert.equal(rejected, true);
|
||||
assert.equal(system.activeTransfers.size, 0);
|
||||
}
|
||||
|
||||
// receivedFileBuffers is bounded and evicts the oldest retained buffer.
|
||||
{
|
||||
const system = createSystem();
|
||||
system.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 2;
|
||||
system._storeReceivedFileBuffer('a', { buffer: new Uint8Array([1]).buffer });
|
||||
system._storeReceivedFileBuffer('b', { buffer: new Uint8Array([2]).buffer });
|
||||
system._storeReceivedFileBuffer('c', { buffer: new Uint8Array([3]).buffer });
|
||||
assert.equal(system.receivedFileBuffers.size, 2);
|
||||
assert.equal(system.receivedFileBuffers.has('a'), false);
|
||||
assert.equal(system.receivedFileBuffers.has('b'), true);
|
||||
assert.equal(system.receivedFileBuffers.has('c'), true);
|
||||
}
|
||||
|
||||
// Evicted received buffers fail gracefully for old download closures.
|
||||
{
|
||||
const system = createSystem();
|
||||
system.MAX_RETAINED_RECEIVED_FILE_BUFFERS = 1;
|
||||
let fileData = null;
|
||||
system.onFileReceived = data => { fileData = data; };
|
||||
system.calculateFileHashFromData = async () => 'hash';
|
||||
system.sendSecureMessage = async () => {};
|
||||
const receivingState = {
|
||||
fileId: 'old',
|
||||
fileName: 'old.pdf',
|
||||
fileSize: 1,
|
||||
fileType: 'application/pdf',
|
||||
fileHash: 'hash',
|
||||
totalChunks: 1,
|
||||
receivedChunks: new Map([[0, new Uint8Array([1]).buffer]]),
|
||||
startTime: Date.now()
|
||||
};
|
||||
await system.assembleFile(receivingState);
|
||||
system._storeReceivedFileBuffer('new', { buffer: new Uint8Array([2]).buffer });
|
||||
await assert.rejects(
|
||||
() => fileData.getObjectURL(),
|
||||
/no longer available for download/i
|
||||
);
|
||||
}
|
||||
|
||||
console.log('File transfer cleanup tests passed');
|
||||
@@ -0,0 +1,62 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { EnhancedSecureFileTransfer } from '../src/transfer/EnhancedSecureFileTransfer.js';
|
||||
|
||||
function createSystem(onIncomingFileRequest = () => {}) {
|
||||
const manager = {
|
||||
dataChannel: { onmessage: null, send() {}, readyState: 'open' },
|
||||
isVerified: true,
|
||||
fileTransferSystem: null,
|
||||
isConnected: () => true
|
||||
};
|
||||
const system = new EnhancedSecureFileTransfer(manager, null, null, null, null, onIncomingFileRequest);
|
||||
system.sendSecureMessage = async () => {};
|
||||
return system;
|
||||
}
|
||||
|
||||
function validMetadata(overrides = {}) {
|
||||
return {
|
||||
type: 'file_transfer_start',
|
||||
fileId: 'file_1',
|
||||
fileName: 'report.pdf',
|
||||
fileSize: 1024,
|
||||
fileType: 'application/pdf',
|
||||
fileHash: 'abc',
|
||||
totalChunks: 1,
|
||||
chunkSize: 1024,
|
||||
salt: new Array(32).fill(1),
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
// Metadata is validated before a consent prompt is shown.
|
||||
{
|
||||
const system = createSystem();
|
||||
assert.equal(system.validateIncomingMetadata(validMetadata()).isValid, true);
|
||||
assert.equal(system.validateIncomingMetadata(validMetadata({ fileName: '../evil.pdf' })).isValid, false);
|
||||
assert.equal(system.validateIncomingMetadata(validMetadata({ fileSize: 200 * 1024 * 1024 })).isValid, false);
|
||||
}
|
||||
|
||||
// No receiving state or chunk buffers are allocated before explicit acceptance.
|
||||
{
|
||||
let prompted = null;
|
||||
const system = createSystem(request => { prompted = request; });
|
||||
await system.handleFileTransferStart(validMetadata());
|
||||
assert.equal(prompted.fileName, 'report.pdf');
|
||||
assert.equal(system.pendingIncomingTransfers.size, 1);
|
||||
assert.equal(system.receivingTransfers.size, 0);
|
||||
|
||||
await system.handleFileChunk({ fileId: 'file_1', chunkIndex: 0 });
|
||||
assert.equal(system.pendingChunks.size, 0);
|
||||
}
|
||||
|
||||
// Incoming request spam is bounded.
|
||||
{
|
||||
const system = createSystem();
|
||||
for (let index = 0; index < system.MAX_PENDING_INCOMING_TRANSFERS; index += 1) {
|
||||
await system.handleFileTransferStart(validMetadata({ fileId: `file_${index}` }));
|
||||
}
|
||||
await system.handleFileTransferStart(validMetadata({ fileId: 'file_overflow' }));
|
||||
assert.equal(system.pendingIncomingTransfers.size, system.MAX_PENDING_INCOMING_TRANSFERS);
|
||||
}
|
||||
|
||||
console.log('File transfer consent tests passed');
|
||||
@@ -0,0 +1,75 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
|
||||
const effects = [];
|
||||
const setterCalls = [];
|
||||
let stateIndex = 0;
|
||||
const callbackCalls = [];
|
||||
|
||||
const context = {
|
||||
window: {},
|
||||
React: {
|
||||
useState(initialValue) {
|
||||
const index = stateIndex++;
|
||||
return [initialValue, value => setterCalls.push({ index, value })];
|
||||
},
|
||||
useRef(initialValue) {
|
||||
return { current: initialValue };
|
||||
},
|
||||
useEffect(effect) {
|
||||
effects.push(effect);
|
||||
},
|
||||
createElement() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const source = fs.readFileSync(new URL('../src/components/ui/FileTransfer.jsx', import.meta.url), 'utf8');
|
||||
vm.runInNewContext(source, context);
|
||||
|
||||
const manager = {
|
||||
fileTransferSystem: {
|
||||
onProgress: () => {},
|
||||
onFileReceived: () => {},
|
||||
onError: () => {},
|
||||
onIncomingFileRequest: () => {}
|
||||
},
|
||||
setFileTransferCallbacks(...args) {
|
||||
callbackCalls.push(args);
|
||||
this.onFileProgress = args[0];
|
||||
this.onFileReceived = args[1];
|
||||
this.onFileError = args[2];
|
||||
this.onIncomingFileRequest = args[3];
|
||||
if (this.fileTransferSystem) {
|
||||
this.fileTransferSystem.onProgress = args[0];
|
||||
this.fileTransferSystem.onFileReceived = args[1];
|
||||
this.fileTransferSystem.onError = args[2];
|
||||
this.fileTransferSystem.onIncomingFileRequest = args[3];
|
||||
}
|
||||
},
|
||||
getFileTransfers() {
|
||||
return { sending: [], receiving: [] };
|
||||
},
|
||||
isConnected() {
|
||||
return false;
|
||||
},
|
||||
isVerified: false
|
||||
};
|
||||
|
||||
context.window.FileTransferComponent({ webrtcManager: manager, isConnected: false });
|
||||
const cleanups = effects.map(effect => effect()).filter(Boolean);
|
||||
|
||||
assert.ok(setterCalls.some(call => call.index === 2 && Array.isArray(call.value) && call.value.length === 0));
|
||||
assert.ok(setterCalls.some(call => call.index === 3 && Array.isArray(call.value) && call.value.length === 0));
|
||||
assert.ok(setterCalls.some(call => call.index === 1 && call.value.sending.length === 0 && call.value.receiving.length === 0));
|
||||
|
||||
cleanups.forEach(cleanup => cleanup());
|
||||
assert.deepEqual(callbackCalls.at(-1), [null, null, null, null]);
|
||||
assert.equal(manager.fileTransferSystem.onProgress, null);
|
||||
assert.equal(manager.fileTransferSystem.onFileReceived, null);
|
||||
assert.equal(manager.fileTransferSystem.onError, null);
|
||||
assert.equal(manager.fileTransferSystem.onIncomingFileRequest, null);
|
||||
|
||||
console.log('File transfer UI cleanup tests passed');
|
||||
@@ -0,0 +1,41 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { EnhancedSecureFileTransfer } from '../src/transfer/EnhancedSecureFileTransfer.js';
|
||||
|
||||
function createSystem() {
|
||||
const manager = {
|
||||
dataChannel: { onmessage: null, send() {}, readyState: 'open' },
|
||||
isVerified: true,
|
||||
fileTransferSystem: null,
|
||||
isConnected: () => true
|
||||
};
|
||||
return new EnhancedSecureFileTransfer(manager, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function file(name, type, size = 1024) {
|
||||
return { name, type, size };
|
||||
}
|
||||
|
||||
const system = createSystem();
|
||||
|
||||
// Allowed files
|
||||
assert.equal(system.validateFile(file('photo.png', 'image/png')).isValid, true);
|
||||
assert.equal(system.validateFile(file('report.pdf', 'application/pdf')).isValid, true);
|
||||
assert.equal(system.validateFile(file('notes.txt', 'text/plain')).isValid, true);
|
||||
assert.equal(system.validateFile(file('bundle.zip', 'application/zip')).isValid, true);
|
||||
|
||||
// Explicitly blocked extensions
|
||||
for (const name of ['run.exe', 'boot.bat', 'shell.sh', 'payload.js', 'page.html', 'vector.svg']) {
|
||||
assert.equal(system.validateFile(file(name, 'application/octet-stream')).isValid, false, name);
|
||||
}
|
||||
|
||||
// MIME spoofing: safe extension with unsafe MIME and unsafe extension with safe MIME are blocked.
|
||||
assert.equal(system.validateFile(file('photo.png', 'application/x-msdownload')).isValid, false);
|
||||
assert.equal(system.validateFile(file('payload.exe', 'image/png')).isValid, false);
|
||||
|
||||
// Missing MIME is unsafe.
|
||||
assert.equal(system.validateFile(file('photo.png', '')).isValid, false);
|
||||
|
||||
// Uppercase extension bypass is blocked.
|
||||
assert.equal(system.validateFile(file('PAYLOAD.EXE', 'application/octet-stream')).isValid, false);
|
||||
|
||||
console.log('File type allowlist tests passed');
|
||||
@@ -0,0 +1,57 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
globalThis.window = {
|
||||
DEBUG_MODE: true,
|
||||
DEVELOPMENT_MODE: true,
|
||||
webpackHotUpdate: {},
|
||||
location: {
|
||||
hostname: 'localhost',
|
||||
search: '?debug'
|
||||
}
|
||||
};
|
||||
|
||||
const { EnhancedSecureCryptoUtils } = await import('../src/crypto/EnhancedSecureCryptoUtils.js');
|
||||
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
|
||||
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
function createManager() {
|
||||
return {
|
||||
delivered: [],
|
||||
_debugMode: false,
|
||||
_secureLog() {},
|
||||
_sanitizeIncomingChatMessage: EnhancedSecureWebRTCManager.prototype._sanitizeIncomingChatMessage,
|
||||
onMessage(message, type) {
|
||||
this.delivered.push({ message, type });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Normal text survives unchanged.
|
||||
{
|
||||
const manager = createManager();
|
||||
EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, 'hello secure world', 'received');
|
||||
assert.deepEqual(manager.delivered[0], { message: 'hello secure world', type: 'received' });
|
||||
}
|
||||
|
||||
// XSS-like and HTML payloads are sanitized before UI delivery.
|
||||
{
|
||||
const manager = createManager();
|
||||
EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, '<script>alert(1)</script>Hello <b>peer</b>', 'received');
|
||||
assert.deepEqual(manager.delivered[0], { message: 'Hello peer', type: 'received' });
|
||||
}
|
||||
|
||||
// Event-handler and protocol strings are removed before reaching React state.
|
||||
{
|
||||
const manager = createManager();
|
||||
EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, '<img src=x onerror=alert(1)> javascript:alert(1)', 'received');
|
||||
assert.deepEqual(manager.delivered[0], { message: 'alert(1)', type: 'received' });
|
||||
}
|
||||
|
||||
// Outgoing/system messages are not altered by the incoming-message gate.
|
||||
{
|
||||
const manager = createManager();
|
||||
EnhancedSecureWebRTCManager.prototype.deliverMessageToUI.call(manager, '<b>system</b>', 'system');
|
||||
assert.deepEqual(manager.delivered[0], { message: '<b>system</b>', type: 'system' });
|
||||
}
|
||||
|
||||
console.log('Incoming message sanitization tests passed');
|
||||
@@ -0,0 +1,112 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
globalThis.window = { EnhancedSecureCryptoUtils: {} };
|
||||
const { SecureIndexedDBWrapper, SecurePersistentKeyStorage } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
class FakeMasterKeyManager {
|
||||
isUnlocked() { return true; }
|
||||
async unlock() {}
|
||||
async encryptBytes(bytes) {
|
||||
return { encryptedData: Uint8Array.from(bytes, byte => byte ^ 0xaa), iv: new Uint8Array(12).fill(7) };
|
||||
}
|
||||
async decryptBytes(bytes, iv) {
|
||||
if (!iv || iv[0] !== 7) throw new Error('bad iv');
|
||||
return Uint8Array.from(bytes, byte => byte ^ 0xaa);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeDB {
|
||||
constructor(records = []) {
|
||||
this.records = new Map(records.map(record => [record.keyId, record]));
|
||||
}
|
||||
async initialize() {}
|
||||
async listKeys() { return [...this.records.values()]; }
|
||||
async getKeyMetadataRecord(keyId) { return this.records.get(keyId) || null; }
|
||||
async putKeyMetadataRecord(record) { this.records.set(record.keyId, record); }
|
||||
}
|
||||
|
||||
class FakeIndexedDBConnection {
|
||||
constructor() {
|
||||
this.records = new Map();
|
||||
}
|
||||
transaction(storeNames) {
|
||||
const transaction = {
|
||||
objectStore: (name) => ({
|
||||
put: (record) => {
|
||||
this.records.set(name, record);
|
||||
queueMicrotask(() => transaction.oncomplete?.());
|
||||
return {};
|
||||
}
|
||||
}),
|
||||
oncomplete: null,
|
||||
onerror: null
|
||||
};
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
|
||||
// New metadata is encrypted and sensitive fields are not plaintext.
|
||||
{
|
||||
const db = new FakeDB();
|
||||
const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), db);
|
||||
const encrypted = await storage._encryptMetadata({
|
||||
created: 111,
|
||||
lastAccessed: 222,
|
||||
sessionId: 'session-secret',
|
||||
peerId: 'peer-secret'
|
||||
});
|
||||
await db.putKeyMetadataRecord({ keyId: 'k1', ...encrypted });
|
||||
const raw = db.records.get('k1');
|
||||
assert.equal(raw.created, undefined);
|
||||
assert.equal(raw.lastAccessed, undefined);
|
||||
assert.equal(raw.sessionId, undefined);
|
||||
assert.ok(Array.isArray(raw.encryptedMetadata));
|
||||
}
|
||||
|
||||
// Old plaintext metadata can be read and is migrated.
|
||||
{
|
||||
const db = new FakeDB([{ keyId: 'legacy', created: 1, lastAccessed: 2, sessionId: 'old-session' }]);
|
||||
const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), db);
|
||||
const listed = await storage.listStoredKeys();
|
||||
assert.equal(listed[0].sessionId, 'old-session');
|
||||
const migrated = db.records.get('legacy');
|
||||
assert.equal(migrated.sessionId, undefined);
|
||||
assert.ok(migrated.encryptedMetadata);
|
||||
}
|
||||
|
||||
// Corrupted encrypted metadata fails safely and is not exposed.
|
||||
{
|
||||
const db = new FakeDB([{ keyId: 'bad', encryptedMetadata: [1, 2, 3], metadataIv: [0] }]);
|
||||
const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), db);
|
||||
const listed = await storage.listStoredKeys();
|
||||
assert.deepEqual(listed, []);
|
||||
}
|
||||
|
||||
// Plaintext timestamp fields are avoidable in the encrypted envelope.
|
||||
{
|
||||
const storage = new SecurePersistentKeyStorage(new FakeMasterKeyManager(), new FakeDB());
|
||||
const encrypted = await storage._encryptMetadata({ created: 10, lastAccessed: 20, usageCount: 3 });
|
||||
assert.equal('created' in encrypted, false);
|
||||
assert.equal('lastAccessed' in encrypted, false);
|
||||
assert.equal('usageCount' in encrypted, false);
|
||||
}
|
||||
|
||||
// Avoidable timestamps are not left plaintext in new IndexedDB records.
|
||||
{
|
||||
const wrapper = new SecureIndexedDBWrapper();
|
||||
wrapper.db = new FakeIndexedDBConnection();
|
||||
await wrapper.storeEncryptedKey(
|
||||
'k2',
|
||||
new Uint8Array([1]),
|
||||
new Uint8Array([2]),
|
||||
{ name: 'AES-GCM' },
|
||||
['encrypt'],
|
||||
'secret',
|
||||
{ metadataVersion: 1, encryptedMetadata: [3], metadataIv: [4] }
|
||||
);
|
||||
assert.equal('timestamp' in wrapper.db.records.get(wrapper.KEYS_STORE), false);
|
||||
assert.equal('created' in wrapper.db.records.get(wrapper.METADATA_STORE), false);
|
||||
assert.equal('lastAccessed' in wrapper.db.records.get(wrapper.METADATA_STORE), false);
|
||||
}
|
||||
|
||||
console.log('IndexedDB metadata encryption tests passed');
|
||||
@@ -0,0 +1,87 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
let compareCalls = 0;
|
||||
globalThis.window = {
|
||||
EnhancedSecureCryptoUtils: {
|
||||
constantTimeCompare(a, b) {
|
||||
compareCalls += 1;
|
||||
return a === b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
function createFakeManager() {
|
||||
const sent = [];
|
||||
return {
|
||||
sent,
|
||||
verificationCode: 'A1-B2-C3',
|
||||
sasValidationAttempts: 0,
|
||||
localVerificationConfirmed: false,
|
||||
remoteVerificationConfirmed: false,
|
||||
bothVerificationsConfirmed: false,
|
||||
disconnected: false,
|
||||
_validateSASCode: EnhancedSecureWebRTCManager.prototype._validateSASCode,
|
||||
_secureLog() {},
|
||||
deliverMessageToUI() {},
|
||||
disconnect() {
|
||||
this.disconnected = true;
|
||||
},
|
||||
dataChannel: {
|
||||
send(payload) {
|
||||
sent.push(JSON.parse(payload));
|
||||
}
|
||||
},
|
||||
_checkBothVerificationsConfirmed() {},
|
||||
processMessageQueue() {}
|
||||
};
|
||||
}
|
||||
|
||||
// testSASNormalization
|
||||
{
|
||||
const manager = createFakeManager();
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'a1 b2 c3'), true);
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'A1B2C3'), true);
|
||||
}
|
||||
|
||||
// testConstantTimeCompare
|
||||
{
|
||||
const manager = createFakeManager();
|
||||
compareCalls = 0;
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'A1-B2-C3'), true);
|
||||
assert.equal(compareCalls, 1);
|
||||
}
|
||||
|
||||
// testInvalidInputs
|
||||
{
|
||||
const manager = createFakeManager();
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, null), false);
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'A1B2'), false);
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._validateSASCode.call(manager, 'FFFFFF'), false);
|
||||
}
|
||||
|
||||
// three failed attempts disconnect; a correct attempt signals only after validation
|
||||
{
|
||||
const manager = createFakeManager();
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
assert.throws(
|
||||
() => EnhancedSecureWebRTCManager.prototype.confirmVerification.call(manager, 'FFFFFF'),
|
||||
/SAS_MISMATCH/
|
||||
);
|
||||
}
|
||||
assert.equal(manager.disconnected, false);
|
||||
assert.throws(
|
||||
() => EnhancedSecureWebRTCManager.prototype.confirmVerification.call(manager, 'FFFFFF'),
|
||||
/SAS_MAX_ATTEMPTS/
|
||||
);
|
||||
assert.equal(manager.disconnected, true);
|
||||
|
||||
const validManager = createFakeManager();
|
||||
EnhancedSecureWebRTCManager.prototype.confirmVerification.call(validManager, 'a1 b2 c3');
|
||||
assert.equal(validManager.localVerificationConfirmed, true);
|
||||
assert.equal(validManager.sent[0].type, 'verification_confirmed');
|
||||
assert.equal(validManager.sent[0].data.verificationMethod, 'MANUAL_SAS_ENTRY');
|
||||
}
|
||||
|
||||
console.log('SAS verification tests passed');
|
||||
@@ -0,0 +1,124 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
globalThis.window = {
|
||||
EnhancedSecureCryptoUtils: {
|
||||
secureLog: { log() {} }
|
||||
}
|
||||
};
|
||||
globalThis.CustomEvent = class CustomEvent {
|
||||
constructor(type, init) {
|
||||
this.type = type;
|
||||
this.detail = init?.detail;
|
||||
}
|
||||
};
|
||||
globalThis.document = { dispatchEvent() {} };
|
||||
|
||||
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
const realSetTimeout = globalThis.setTimeout;
|
||||
const realClearTimeout = globalThis.clearTimeout;
|
||||
const realSetInterval = globalThis.setInterval;
|
||||
const realClearInterval = globalThis.clearInterval;
|
||||
|
||||
const timers = [];
|
||||
globalThis.setTimeout = (callback, delay) => {
|
||||
const timer = { kind: 'timeout', callback, delay, cleared: false };
|
||||
timers.push(timer);
|
||||
return timer;
|
||||
};
|
||||
globalThis.clearTimeout = (timer) => {
|
||||
if (timer) timer.cleared = true;
|
||||
};
|
||||
globalThis.setInterval = (callback, delay) => {
|
||||
const timer = { kind: 'interval', callback, delay, cleared: false };
|
||||
timers.push(timer);
|
||||
return timer;
|
||||
};
|
||||
globalThis.clearInterval = (timer) => {
|
||||
if (timer) timer.cleared = true;
|
||||
};
|
||||
|
||||
try {
|
||||
// Periodic log cleanup is tracked and cleared with the existing timer system.
|
||||
{
|
||||
const manager = {
|
||||
_activeTimers: new Set(),
|
||||
_startKeySecurityMonitoring() {},
|
||||
_verifyAPIIntegrity() { return true; },
|
||||
_startSecurityMonitoring() {},
|
||||
_cleanupLogs() {},
|
||||
_secureLog() {}
|
||||
};
|
||||
manager._trackActiveTimer = EnhancedSecureWebRTCManager.prototype._trackActiveTimer;
|
||||
EnhancedSecureWebRTCManager.prototype._finalizeSecureInitialization.call(manager);
|
||||
assert.equal(manager._activeTimers.size, 1);
|
||||
const logTimer = manager._logCleanupInterval;
|
||||
EnhancedSecureWebRTCManager.prototype._stopAllTimers.call(manager);
|
||||
assert.equal(logTimer.cleared, true);
|
||||
}
|
||||
|
||||
// Deferred file-transfer retries are tracked, cleared, and cannot re-run after session shutdown.
|
||||
{
|
||||
let initCalls = 0;
|
||||
const manager = {
|
||||
_sessionAlive: true,
|
||||
_activeTimers: new Set(),
|
||||
_fileTransferInitRetryTimers: new Set(),
|
||||
fileTransferSystem: null,
|
||||
dataChannel: { readyState: 'open' },
|
||||
isVerified: false,
|
||||
_secureLog() {},
|
||||
_trackActiveTimer: EnhancedSecureWebRTCManager.prototype._trackActiveTimer,
|
||||
_untrackActiveTimer: EnhancedSecureWebRTCManager.prototype._untrackActiveTimer,
|
||||
_scheduleFileTransferInitRetry: EnhancedSecureWebRTCManager.prototype._scheduleFileTransferInitRetry,
|
||||
initializeFileTransfer() {
|
||||
initCalls += 1;
|
||||
}
|
||||
};
|
||||
EnhancedSecureWebRTCManager.prototype.initializeFileTransfer.call(manager);
|
||||
const retryTimer = [...manager._fileTransferInitRetryTimers][0];
|
||||
manager._sessionAlive = false;
|
||||
EnhancedSecureWebRTCManager.prototype._stopAllTimers.call(manager);
|
||||
if (!retryTimer.cleared) retryTimer.callback();
|
||||
assert.equal(retryTimer.cleared, true);
|
||||
assert.equal(manager._fileTransferInitRetryTimers.size, 0);
|
||||
assert.equal(initCalls, 0);
|
||||
}
|
||||
|
||||
// Repeated peer-disconnect notifications schedule only one delayed cleanup, and cleanup is cancelled on disconnect.
|
||||
{
|
||||
let disconnectCalls = 0;
|
||||
const manager = {
|
||||
_sessionAlive: true,
|
||||
_activeTimers: new Set(),
|
||||
peerDisconnectNotificationSent: false,
|
||||
_peerDisconnectCleanupTimer: null,
|
||||
deliverMessageToUI() {},
|
||||
onStatusChange() {},
|
||||
stopHeartbeat() {},
|
||||
onKeyExchange() {},
|
||||
onVerificationRequired() {},
|
||||
disconnect() { disconnectCalls += 1; },
|
||||
_secureLog() {},
|
||||
_trackActiveTimer: EnhancedSecureWebRTCManager.prototype._trackActiveTimer,
|
||||
_untrackActiveTimer: EnhancedSecureWebRTCManager.prototype._untrackActiveTimer
|
||||
};
|
||||
EnhancedSecureWebRTCManager.prototype.handlePeerDisconnectNotification.call(manager, { reason: 'connection_lost' });
|
||||
EnhancedSecureWebRTCManager.prototype.handlePeerDisconnectNotification.call(manager, { reason: 'connection_lost' });
|
||||
const scheduled = timers.filter(timer => timer.kind === 'timeout' && timer.delay === 2000);
|
||||
assert.equal(scheduled.length, 1);
|
||||
|
||||
manager._sessionAlive = false;
|
||||
EnhancedSecureWebRTCManager.prototype._stopAllTimers.call(manager);
|
||||
if (!scheduled[0].cleared) scheduled[0].callback();
|
||||
assert.equal(scheduled[0].cleared, true);
|
||||
assert.equal(disconnectCalls, 0);
|
||||
}
|
||||
} finally {
|
||||
globalThis.setTimeout = realSetTimeout;
|
||||
globalThis.clearTimeout = realClearTimeout;
|
||||
globalThis.setInterval = realSetInterval;
|
||||
globalThis.clearInterval = realClearInterval;
|
||||
}
|
||||
|
||||
console.log('Timer lifecycle tests passed');
|
||||
@@ -0,0 +1,60 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
globalThis.window = {
|
||||
EnhancedSecureCryptoUtils: {},
|
||||
DEBUG_MODE: true,
|
||||
DEVELOPMENT_MODE: true,
|
||||
location: { hostname: 'localhost', search: '?debug' },
|
||||
webpackHotUpdate: {}
|
||||
};
|
||||
|
||||
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
||||
|
||||
function fake(config = {}) {
|
||||
return {
|
||||
_config: {
|
||||
webrtc: {
|
||||
relayOnly: config.relayOnly ?? false,
|
||||
iceServers: config.iceServers ?? [{ urls: 'stun:stun.example.test:3478' }]
|
||||
}
|
||||
},
|
||||
_ipLeakWarningShown: false,
|
||||
delivered: [],
|
||||
deliverMessageToUI(message, type) {
|
||||
this.delivered.push({ message, type });
|
||||
},
|
||||
_hasTurnServer: EnhancedSecureWebRTCManager.prototype._hasTurnServer
|
||||
};
|
||||
}
|
||||
|
||||
// Default mode preserves current behavior.
|
||||
{
|
||||
const manager = fake();
|
||||
const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
|
||||
assert.equal(config.iceTransportPolicy, undefined);
|
||||
assert.equal(config.iceServers[0].urls, 'stun:stun.example.test:3478');
|
||||
}
|
||||
|
||||
// Privacy mode uses relay-only transport.
|
||||
{
|
||||
const manager = fake({ relayOnly: true, iceServers: [{ urls: 'turn:turn.example.test:3478' }] });
|
||||
const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
|
||||
assert.equal(config.iceTransportPolicy, 'relay');
|
||||
}
|
||||
|
||||
// Missing TURN warns clearly.
|
||||
{
|
||||
const manager = fake();
|
||||
EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
|
||||
assert.match(manager.delivered[0].message, /may expose IP addresses/i);
|
||||
}
|
||||
|
||||
// STUN-only config does not claim IP protection, even with privacy mode selected.
|
||||
{
|
||||
const manager = fake({ relayOnly: true, iceServers: [{ urls: 'stun:stun.example.test:3478' }] });
|
||||
assert.equal(EnhancedSecureWebRTCManager.prototype._hasTurnServer.call(manager), false);
|
||||
EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
|
||||
assert.match(manager.delivered[0].message, /STUN alone does not hide IP addresses/i);
|
||||
}
|
||||
|
||||
console.log('WebRTC privacy mode tests passed');
|
||||
Reference in New Issue
Block a user