From 3741d8a46053b4fef07877bb35d1bd92d37db9d5 Mon Sep 17 00:00:00 2001
From: shuaiplus <2327005759@qq.com>
Date: Wed, 28 May 2025 01:54:13 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9C=89=E4=BA=86workers=E5=B0=B1=E5=88=A0?=
=?UTF-8?q?=E6=8E=89node.js=E7=89=88=E5=90=8E=E7=AB=AF=E5=90=A7=E3=80=82?=
=?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=95=B4=E4=BD=93=E4=BB=A3=E7=A0=81=EF=BC=8C?=
=?UTF-8?q?=E5=87=86=E5=A4=87=E5=8F=91=E5=B8=83=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.github/workflows/docker-build.yml | 55 ---
Dockerfile | 124 ------
LICENSE | 15 +
README.md | 284 ++++++--------
README.zh-CN.md | 205 ----------
README_EN.md | 155 ++++++++
server/package-lock.json | 76 ----
server/package.json | 13 -
server/server.js | 586 -----------------------------
wrangler.toml | 2 +-
10 files changed, 285 insertions(+), 1230 deletions(-)
delete mode 100644 .github/workflows/docker-build.yml
delete mode 100644 Dockerfile
create mode 100644 LICENSE
delete mode 100644 README.zh-CN.md
create mode 100644 README_EN.md
delete mode 100644 server/package-lock.json
delete mode 100644 server/package.json
delete mode 100644 server/server.js
diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
deleted file mode 100644
index 250fd12..0000000
--- a/.github/workflows/docker-build.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: Build and Push Docker Image
-
-on:
- push:
- branches: [ main, master ]
- tags: [ 'v*' ]
- pull_request:
- branches: [ main, master ]
-
-env:
- REGISTRY: ghcr.io
- IMAGE_NAME: ${{ github.repository }}
-
-jobs:
- build:
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Log in to Container Registry
- uses: docker/login-action@v3
- with:
- registry: ${{ env.REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Extract metadata
- id: meta
- uses: docker/metadata-action@v5
- with:
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- tags: |
- type=ref,event=branch
- type=ref,event=pr
- type=semver,pattern={{version}}
- type=semver,pattern={{major}}.{{minor}}
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- - name: Build and push Docker image
- uses: docker/build-push-action@v5
- with:
- context: .
- platforms: linux/amd64
- push: true
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 5b295d7..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,124 +0,0 @@
-# Use a Node.js 18 Alpine base image for the backend builder
-# 使用 Node.js 18 Alpine 基础镜像作为后端构建器
-FROM node:18-alpine AS backend-builder
-
-WORKDIR /app
-
-# Copy package.json first to leverage Docker's caching
-# 首先复制 package.json 以利用 Docker 的缓存
-COPY server/package.json ./server/
-# Install production dependencies
-# 安装生产依赖
-RUN cd server && npm install --production --no-package-lock --no-audit
-
-# Copy only the server.js file
-# 仅复制 server.js 文件
-COPY server/server.js ./server/
-
-# Frontend build stage
-# 前端构建阶段
-FROM node:18-alpine AS frontend-builder
-
-WORKDIR /app
-
-# Copy frontend dependencies and configuration files
-# 复制前端依赖和配置文件
-COPY package.json package-lock.json* ./
-# Install dependencies
-# 安装依赖
-RUN npm ci --no-audit || npm install --no-audit
-
-# Copy source code and configuration files
-# 复制源代码和配置文件
-COPY vite.config.js ./
-COPY client/ ./client/
-
-# Build the frontend
-# 构建前端
-RUN npm run build:docker
-
-# Second stage: Minimal image
-# 第二阶段:极小镜像
-FROM docker.1ms.run/alpine:3.16
-
-# Install minimal Node.js and Nginx
-# 安装最小化版本的 Node.js 和 Nginx
-RUN apk add --no-cache nodejs nginx && \
- mkdir -p /app/server /app/client /run/nginx && \
- # Clean up apk cache
- # 清理 apk 缓存
- rm -rf /var/cache/apk/*
-
-# Copy server files and static files
-# 复制服务器文件和静态文件
-COPY --from=backend-builder /app/server/node_modules /app/server/node_modules
-COPY --from=backend-builder /app/server/*.js /app/server/
-# Copy built frontend files from the frontend build stage
-# 从前端构建阶段复制构建好的文件,而不是复制 dist 目录
-COPY --from=frontend-builder /app/dist/ /app/client/
-
-# Optimized Nginx configuration
-# 优化的 Nginx 配置
-RUN cat > /etc/nginx/nginx.conf <<'EOF'
-worker_processes 1;
-worker_rlimit_nofile 512;
-events {
- worker_connections 128;
- multi_accept off;
-}
-http {
- include mime.types;
- default_type application/octet-stream;
-
- # Optimization settings
- # 优化设置
- sendfile on;
- tcp_nopush on;
- tcp_nodelay on;
- keepalive_timeout 15;
- types_hash_max_size 1024;
- client_max_body_size 1M;
- client_body_buffer_size 128k;
-
- # Disable access logs to reduce I/O
- # 禁用访问日志以减少 I/O
- access_log off;
- error_log /dev/null;
-
- # Disable unnecessary features
- # 禁用不需要的功能
- server_tokens off;
-
- server {
- listen 80;
- server_name localhost; # Serve static files
- # 提供静态文件服务
- location / {
- root /app/client;
- index index.html;
- try_files $uri $uri/ /index.html;
- }
-
- # Proxy WebSocket requests to Node.js backend
- # 将 WebSocket 请求代理到 Node.js 后端
- location /ws {
- proxy_pass http://127.0.0.1:8088;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- proxy_set_header Host $host;
- }
- }
-}
-EOF
-
-EXPOSE 80
-
-# Set low memory environment variables and remove unsupported options
-# 设置低内存环境变量,去除不支持的选项
-ENV NODE_OPTIONS="--max-old-space-size=64" \
- NODE_ENV="production"
-
-# Run in the foreground and combine commands to reduce the number of processes
-# 使用前台运行并合并命令减少进程数
-CMD ["sh", "-c", "node --expose-gc --unhandled-rejections=strict /app/server/server.js & nginx -g 'daemon off;'"]
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d056ba3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2024, NodeCrypt
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
index d18be82..1d13c74 100644
--- a/README.md
+++ b/README.md
@@ -1,209 +1,153 @@
# NodeCrypt
-A modern real-time chat system with **true end-to-end encryption**, ensuring that neither the server nor any potential man-in-the-middle can access your conversation content.
+🌐 **[English README](README_EN.md)**
-[中文文档](./README.zh-CN.md)
-
-## 一键部署到 Cloudflare Workers
+本项目支持一键部署到cloudflare
[](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeCrypt)
-## Core Concept
+## 📝 项目简介
-NodeCrypt implements a secure communication channel where:
+NodeCrypt 是一个真正的端到端加密聊天系统,实现完全的零知识架构。整个系统设计确保服务器、网络中间人、甚至系统管理员都无法获取任何明文消息内容。所有加密和解密操作都在客户端本地进行,服务器仅作为加密数据的盲中继。
-1. **Only the intended recipients can decrypt messages**
-2. **The server merely relays encrypted data without access to content**
-3. **All cryptographic operations happen client-side**
-4. **No encryption keys are ever transmitted in plain text**
+### 系统架构
+- **前端**:ES6+ 模块化 JavaScript,无框架依赖
+- **后端**:Cloudflare Workers + Durable Objects
+- **通信**:WebSocket 实时双向通信
+- **构建**:Vite 现代化构建工具
-### Security Architecture
+## 🔐 零知识架构设计
-```
- Client A Server Client B
- | | |
- |-- Generate Key Pair -->| |
- | |---- Key Exchange ---->|
- |<---------------------- Secure Channel -------->|
- | | |
- |-- Encrypted Message -->|-- Encrypted Message ->|
- | | (Cannot Read) |
-```
+### 核心原则
+- **服务器盲转**:服务器永远无法解密消息内容,仅负责加密数据中转
+- **无数据库存储**:系统不使用任何持久化存储,所有数据仅在内存中临时存在
+- **端到端加密**:消息从发送方到接收方全程加密,中间任何节点都无法解密
+- **前向安全性**:即使密钥泄露,也无法解密历史消息,因为根本就没有历史消息
+- **匿名通信**:用户无需注册真实身份,支持临时匿名聊天
-## Technology Stack
+### 隐私保护机制
-### Frontend
-- **Framework**: Native JavaScript (ES6+ modules)
-- **Build Tool**: Vite
-- **Encryption**: Custom NodeCrypt.js implementation
-- **UI Design**: Responsive interface with modern design
+- **实时成员提醒**:房间在线列表完全透明,内任何人加入或离开都会实时通知所有成员,
+- **无历史消息**:新加入的用户无法看到任何历史聊天记录
+- **私聊加密**:点击用户头像可发起端到端加密的私密对话,房间内其他成员完全无法看到私聊内容
-### Backend
-- **Runtime**: Node.js
-- **Communication**: WebSocket protocol
-- **Encryption**: RSA + AES-256-CBC hybrid encryption
-- **Deployment**: Docker + Nginx
+### 房间密码机制
-### Core Encryption Mechanism
-- **Key Exchange**: Elliptic Curve Diffie-Hellman (ECDH)
-- **Symmetric Encryption**: AES-256-CBC
-- **Asymmetric Encryption**: RSA (server authentication)
-- **End-to-End**: Direct encrypted communication between clients
+房间密码作为**密钥派生因子**参与端到端加密:`最终共享密钥 = ECDH_共享密钥 XOR SHA256(房间密码)`
-## Encryption Workflow
+- **密码错误隔离**:不同密码的房间无法解密彼此的消息
+- **服务器盲区**:服务器永远无法获知房间密码
-1. **Initial Setup**:
- - Each client generates a unique elliptic curve key pair (public/private)
- - Server authenticates itself using RSA signatures
+### 三层安全体系
-2. **Secure Channel Establishment**:
- - Client A and Client B exchange public keys via the server
- - Both clients independently compute the same shared secret using ECDH
- - This shared secret is never transmitted over the network
+#### 第一层:RSA-2048 服务器身份验证
+- 服务器启动时生成临时 RSA-2048 密钥对,每24小时自动轮换
+- 客户端连接时验证服务器公钥,防止中间人攻击
+- 私钥仅在服务器内存中存在,从不持久化存储
-3. **Message Encryption**:
- - Messages are encrypted with AES-256-CBC using the shared secret
- - Each message includes a unique initialization vector
- - Message authenticity is verified with HMAC
+#### 第二层:ECDH-P384 密钥协商
+- 每个客户端生成独立的椭圆曲线密钥对(P-384曲线)
+- 通过椭圆曲线 Diffie-Hellman 密钥交换协议建立共享密钥
+- 每个客户端与服务器之间拥有独立的加密通道
-4. **Private Messaging**:
- - Unique shared keys are established between each pair of users
- - Messages are specifically encrypted for the intended recipient only
- - Server cannot determine message content, only routing information
+#### 第三层:混合对称加密
+- **服务器通信**:使用 AES-256-CBC 加密客户端与服务器间的控制消息
+- **客户端通信**:使用 ChaCha20 加密客户端之间的实际聊天内容
+- 每条消息使用独立的初始化向量(IV)和随机数(Nonce)
-## Key Features
+## 🔄 完整加密流程详解
-### 🔒 Security
-- **True End-to-End Encryption**: Only intended recipients can decrypt messages
-- **Zero-Knowledge Server**: Server only handles encrypted data packets
-- **Perfect Forward Secrecy**: New session keys for each conversation
-- **Man-in-the-Middle Protection**: RSA server authentication
-- **Secure Key Exchange**: ECDH for secure shared key generation
-- **XSS Protection**: Content sanitization and HTML escaping
+```mermaid
+sequenceDiagram
+ participant C as 客户端
+ participant S as 服务器
+ participant O as 其他客户端
-### 💬 Chat Functionality
-- Multi-room support (join multiple chat rooms simultaneously)
-- Private chat mode (click user avatar to start private chat)
-- Image sharing and preview (with drag and zoom support)
-- Emoji support
-- System notification messages
-
-### 🎨 User Experience
-- **Auto Avatar Generation**: SVG avatars based on username
-- **Responsive Design**: Works perfectly on desktop and mobile
-- **Adjustable Layout**: Resizable sidebar
-- **Smart Input**: Auto-height adjustment, multi-line support
-- **Unread Notifications**: Badge showing unread count
-
-### 📱 Mobile Optimization
-- Touch-friendly UI interactions
-- Mobile-specific sidebar with overlay
-- Adaptive layout and font sizes
-- Prevents page drag and zoom on touch devices
-
-## Implementation Details
-
-### Encryption Class (NodeCrypt.js)
-
-The core encryption logic is implemented in the `NodeCrypt.js` file:
-
-```javascript
-// Sample code showing the encryption process (simplified)
-class NodeCrypt {
- constructor() {
- // Initialize elliptic curve for ECDH
- this.clientEc = new elliptic('curve25519');
+ Note over C,S: 阶段1: 服务器身份验证 (RSA-2048)
+ C->>S: WebSocket连接
+ S->>C: RSA-2048公钥
- // Generate client keypair
- this.clientKeys = this.clientEc.genKeyPair();
- this.clientPublic = this.clientKeys.getPublic('hex');
- this.clientPrivate = this.clientKeys.getPrivate('hex');
- }
-
- // Establish shared secret with another client
- establishSharedSecret(otherClientPublic) {
- const sharedSecret = this.clientKeys.derive(
- this.clientEc.keyFromPublic(otherClientPublic, 'hex').getPublic()
- );
- return sha256(sharedSecret.toString(16));
- }
-
- // Encrypt message for specific recipient
- encryptClientMessage(message, sharedSecret) {
- const iv = crypto.randomBytes(16);
- const cipher = new ModeOfOperation.cbc(sharedSecret, iv);
- const encrypted = cipher.encrypt(message);
- return { iv: iv.toString('hex'), data: encrypted.toString('hex') };
- }
-
- // Decrypt message from specific sender
- decryptClientMessage(encryptedMessage, sharedSecret) {
- const iv = Buffer.from(encryptedMessage.iv, 'hex');
- const decipher = new ModeOfOperation.cbc(sharedSecret, iv);
- const decrypted = decipher.decrypt(Buffer.from(encryptedMessage.data, 'hex'));
- return decrypted;
- }
-}
+ Note over C,S: 阶段2: 客户端-服务器密钥交换 (P-384 ECDH)
+ C->>S: P-384 ECDH公钥
+ S->>C: P-384公钥 + RSA签名
+ Note over C: 验证RSA签名并派生AES-256密钥
+ Note over S: 从P-384 ECDH派生AES-256密钥
+
+ Note over C,S: 阶段3: 房间认证
+ C->>S: 加入请求 (房间哈希,AES-256加密)
+ Note over S: 将客户端添加到房间/频道
+ S->>C: 成员列表 (其他客户端ID,加密)
+ Note over C,O: 阶段4: 客户端间密钥交换 (Curve25519)
+ Note over C: 为每个成员生成Curve25519密钥对
+ C->>S: Curve25519公钥包 (AES-256加密)
+ S->>O: 转发客户端C的公钥
+ O->>S: 返回其他客户端的Curve25519公钥
+ S->>C: 转发其他客户端的公钥
+
+ Note over C,O: 阶段5: 密码增强密钥派生
+ Note over C: 客户端密钥 = ECDH_Curve25519(自己私钥, 对方公钥) XOR SHA256(密码)
+ Note over O: 客户端密钥 = ECDH_Curve25519(自己私钥, 对方公钥) XOR SHA256(密码)
+
+ Note over C,O: 阶段6: 身份验证
+ C->>S: 用户名 (用客户端密钥ChaCha20加密)
+ S->>O: 转发加密用户名
+ O->>S: 用户名 (用客户端密钥ChaCha20加密)
+ S->>C: 转发加密用户名
+ Note over C,O: 双方客户端现在验证彼此身份 Note over C,O: 阶段7: 安全消息传输 (双层加密)
+ Note over C: 1. ChaCha20加密(消息内容)
2. AES-256加密(传输层包装)
+ C->>S: 双层加密消息
+ Note over S: 解密AES-256传输层
提取ChaCha20加密数据
无法解密消息内容
+ S->>O: 转发ChaCha20加密数据
+ Note over O: 解密AES-256传输层
ChaCha20解密获得消息内容
```
-### Client-to-Client Secure Channel
-When two clients want to communicate securely:
+## 🛠️ 技术实现
-1. Client A sends its public key to the server
-2. Server relays Client A's public key to Client B
-3. Client B computes shared secret using Client A's public key and its own private key
-4. Client B sends its public key to Client A (via server)
-5. Client A computes the same shared secret using Client B's public key and its own private key
-6. Both clients now have identical shared secrets without ever transmitting the secret itself
+- **Web Cryptography API**:浏览器原生加密实现,提供硬件加速
+- **elliptic.js**:椭圆曲线密码学库,实现 Curve25519 和 P-384
+- **aes-js**:纯 JavaScript AES 实现,支持多种模式
+- **js-chacha20**:ChaCha20 流加密算法的 JavaScript 实现
+- **js-sha256**:SHA-256 哈希算法实现
-### Message Flow with Encryption
+## 🚀 部署说明
-### 2. Chat Display (chat.js)
-- Message rendering and type handling
-- Image preview modal
-- Input field placeholder and auto-height
-- Paste plain text processing
+### 本地开发
+克隆项目并安装依赖后,使用 `npm run dev` 启动开发服务器。
-### 3. User Interface (ui.js)
-- User list rendering
-- Mobile UI adaptation
-- Login modal management
-- Settings panel interaction
+使用 `npm run deploy` 部署到 Cloudflare Workers。
-### 4. Utility Libraries
-- **util.dom.js**: DOM operation wrappers
-- **util.string.js**: String handling and HTML escaping
-- **util.avatar.js**: Avatar generation
-- **util.image.js**: Image processing
-- **util.emoji.js**: Emoji picker
-- **util.settings.js**: Settings management
+## 🔬 安全验证
-## Deployment
+### 加密过程验证
+用户可通过浏览器开发者工具观察完整的加密解密过程,验证消息在传输过程中确实处于加密状态。
-### Docker Deployment
-- **Dockerfile**: Container configuration
-- **start.sh**: Startup script (dynamic RSA key pair generation)
-- **Nginx**: Static file serving and WebSocket proxy
+### 网络流量分析
+使用网络抓包工具可以验证所有 WebSocket 传输的数据都是不可读的加密内容。
-## Project Highlights
+### 代码安全审计
+所有加密相关代码完全开源,使用标准密码学算法,欢迎安全研究者进行独立审计。
-1. **Security**: True end-to-end encryption where even the server cannot decrypt message content
-2. **User Experience**: Modern UI with smooth interactions
-3. **Modularity**: Clean separation of concerns with well-organized code modules
-4. **Responsiveness**: Fully functional on all device sizes
-5. **Extensibility**: Clear module separation, easy to extend functionality
+## ⚠️ 安全建议
-## Getting Started
+- **使用强房间密码**:房间密码直接影响端到端加密强度,建议使用复杂密码
+- **密码保密性**:房间密码一旦泄露,该房间所有通信内容都可能被解密
+- **使用最新版本的现代浏览器**:确保密码学API的安全性和性能
-```bash
-# Development mode
-npm run dev
+## 🤝 安全贡献
-# Production build
-npm run build
+欢迎安全研究者报告漏洞和进行安全审计。严重安全问题将在24小时内修复。
-# Docker deployment
-docker build -t nodecrypt .
-docker run -p 80:80 nodecrypt
-```
\ No newline at end of file
+## 📄 开源协议
+
+本项目采用 ISC 开源协议。
+
+## ⚠️ 免责声明
+
+本项目仅供学习和技术研究使用,不得用于任何违法犯罪活动。使用者应遵守所在国家和地区的相关法律法规。项目作者不承担因使用本软件而产生的任何法律责任。请在合法合规的前提下使用本项目。
+
+---
+
+**NodeCrypt** - 真正的端到端加密通信 🔐
+
+*"在数字时代,加密是保护隐私的最后一道防线"*
\ No newline at end of file
diff --git a/README.zh-CN.md b/README.zh-CN.md
deleted file mode 100644
index b093ac6..0000000
--- a/README.zh-CN.md
+++ /dev/null
@@ -1,205 +0,0 @@
-# NodeCrypt
-
-一个具备**真正端到端加密**功能的现代化实时聊天系统,确保服务器和任何潜在的中间人都无法获取您的对话内容。
-
-[English Documentation](./README.md)
-
-## 核心理念
-
-NodeCrypt实现了一个安全的通信渠道,其中:
-
-1. **只有预期的接收者能够解密消息**
-2. **服务器仅转发加密数据,无法访问内容**
-3. **所有加密操作都在客户端进行**
-4. **加密密钥永远不会以明文形式传输**
-
-### 安全架构
-
-```
- 客户端A 服务器 客户端B
- | | |
- |-- 生成密钥对 --------->| |
- | |---- 密钥交换 -------->|
- |<---------------------- 安全通道 -------------->|
- | | |
- |-- 加密消息 ----------->|-- 加密消息 ---------->|
- | | (无法读取) |
-```
-
-## 技术架构
-
-### 前端技术栈
-- **框架**: 原生 JavaScript (ES6+模块化)
-- **构建工具**: Vite
-- **加密库**: 自研 NodeCrypt.js 实现
-- **UI设计**: 现代化响应式界面
-
-### 后端技术栈
-- **运行时**: Node.js
-- **通信协议**: WebSocket
-- **加密**: RSA + AES-256-CBC 混合加密
-- **部署**: Docker + Nginx
-
-### 核心加密机制
-- **密钥交换**: 椭圆曲线 Diffie-Hellman (ECDH)
-- **对称加密**: AES-256-CBC
-- **非对称加密**: RSA (服务器身份验证)
-- **端到端**: 客户端间直接加密通信
-
-## 加密工作流程
-
-1. **初始设置**:
- - 每个客户端生成唯一的椭圆曲线密钥对(公钥/私钥)
- - 服务器使用RSA签名进行身份验证
-
-2. **安全通道建立**:
- - 客户端A和客户端B通过服务器交换公钥
- - 两个客户端独立计算出相同的共享密钥,使用ECDH算法
- - 这个共享密钥从不在网络上传输
-
-3. **消息加密**:
- - 消息使用共享密钥通过AES-256-CBC进行加密
- - 每条消息包含唯一的初始化向量(IV)
- - 消息真实性通过HMAC验证
-
-4. **私密通信**:
- - 每对用户之间建立唯一的共享密钥
- - 消息专门为预期接收者加密
- - 服务器只能确定消息的路由信息,无法获知消息内容
-
-## 主要功能特性
-
-### 🔒 安全特性
-- **真正的端到端加密**: 只有预期接收者可以解密消息
-- **零知识服务器**: 服务器只处理加密数据包
-- **完美前向保密**: 每次对话使用新的会话密钥
-- **中间人攻击防护**: RSA服务器身份验证
-- **安全密钥交换**: ECDH用于安全共享密钥生成
-- **XSS攻击防护**: 内容净化和HTML转义
-
-### 💬 聊天功能
-- 多房间支持(可同时加入多个聊天室)
-- 私聊模式(点击用户头像进入私聊)
-- 图片发送和预览(支持拖拽缩放)
-- 表情符号支持
-- 系统通知消息
-
-### 🎨 用户体验
-- **自动头像生成**: 基于用户名的 SVG 头像
-- **响应式设计**: 完美适配桌面和移动端
-- **可调整布局**: 侧边栏宽度可调
-- **智能输入框**: 自动高度调整,支持多行输入
-- **未读消息提醒**: 红色徽章显示未读数量
-
-### 📱 移动端优化
-- 触摸友好的 UI 交互
-- 移动端专用侧边栏和遮罩
-- 自适应布局和字体大小
-- 防止页面拖动和缩放
-
-## 实现细节
-
-### 加密类 (NodeCrypt.js)
-
-核心加密逻辑在`NodeCrypt.js`文件中实现:
-
-```javascript
-// 展示加密过程的示例代码(简化版)
-class NodeCrypt {
- constructor() {
- // 初始化用于ECDH的椭圆曲线
- this.clientEc = new elliptic('curve25519');
-
- // 生成客户端密钥对
- this.clientKeys = this.clientEc.genKeyPair();
- this.clientPublic = this.clientKeys.getPublic('hex');
- this.clientPrivate = this.clientKeys.getPrivate('hex');
- }
-
- // 与另一个客户端建立共享密钥
- establishSharedSecret(otherClientPublic) {
- const sharedSecret = this.clientKeys.derive(
- this.clientEc.keyFromPublic(otherClientPublic, 'hex').getPublic()
- );
- return sha256(sharedSecret.toString(16));
- }
-
- // 为特定接收者加密消息
- encryptClientMessage(message, sharedSecret) {
- const iv = crypto.randomBytes(16);
- const cipher = new ModeOfOperation.cbc(sharedSecret, iv);
- const encrypted = cipher.encrypt(message);
- return { iv: iv.toString('hex'), data: encrypted.toString('hex') };
- }
-
- // 解密来自特定发送者的消息
- decryptClientMessage(encryptedMessage, sharedSecret) {
- const iv = Buffer.from(encryptedMessage.iv, 'hex');
- const decipher = new ModeOfOperation.cbc(sharedSecret, iv);
- const decrypted = decipher.decrypt(Buffer.from(encryptedMessage.data, 'hex'));
- return decrypted;
- }
-}
-```
-
-### 客户端之间的安全通道
-
-当两个客户端想要安全通信时:
-
-1. 客户端A将其公钥发送给服务器
-2. 服务器将客户端A的公钥转发给客户端B
-3. 客户端B使用客户端A的公钥和自己的私钥计算共享密钥
-4. 客户端B将其公钥发送给客户端A(通过服务器)
-5. 客户端A使用客户端B的公钥和自己的私钥计算出相同的共享密钥
-6. 两个客户端现在拥有相同的共享密钥,而无需在网络上传输密钥本身
-
-### 加密消息流程
-
-### 2. 聊天显示 (chat.js)
-- 消息渲染和类型处理
-- 图片预览模态框
-- 输入框占位符和自动增高
-- 粘贴纯文本处理
-
-### 3. 用户界面 (ui.js)
-- 用户列表渲染
-- 移动端 UI 适配
-- 登录模态框管理
-- 设置面板交互
-
-### 4. 工具库
-- **util.dom.js**: DOM 操作封装
-- **util.string.js**: 字符串处理和 HTML 转义
-- **util.avatar.js**: 头像生成
-- **util.image.js**: 图片处理
-- **util.emoji.js**: 表情选择器
-- **util.settings.js**: 设置管理
-
-## 部署方案
-
-### Docker 部署
-- **Dockerfile**: 容器化配置
-- **start.sh**: 启动脚本(动态生成 RSA 密钥对)
-- **Nginx**: 静态文件服务和 WebSocket 代理
-
-## 项目亮点
-
-1. **安全性**: 实现了真正的端到端加密,即使服务器也无法解密消息内容
-2. **用户体验**: 现代化 UI 设计,流畅的交互体验
-3. **技术先进性**: 使用现代 JavaScript 特性,模块化架构
-4. **跨平台**: 完美支持桌面和移动端
-5. **可扩展性**: 清晰的模块划分,易于功能扩展
-
-## 启动方式
-
-```bash
-# 开发模式
-npm run dev
-
-# 生产构建
-npm run build
-
-# Docker 部署
-docker build -t nodecrypt .
-docker run -p 80:80 nodecrypt
-```
diff --git a/README_EN.md b/README_EN.md
new file mode 100644
index 0000000..b59835f
--- /dev/null
+++ b/README_EN.md
@@ -0,0 +1,155 @@
+# NodeCrypt
+
+🌐 **[中文版 README](README.md)**
+
+One-click deployment to Cloudflare supported
+
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeCrypt)
+
+## 📝 Project Introduction
+
+NodeCrypt is a truly end-to-end encrypted chat system that implements a complete zero-knowledge architecture. The entire system design ensures that servers, network intermediaries, and even system administrators cannot access any plaintext message content. All encryption and decryption operations are performed locally on the client side, with the server serving only as a blind relay for encrypted data.
+
+### System Architecture
+- **Frontend**: ES6+ modular JavaScript, no framework dependencies
+- **Backend**: Cloudflare Workers + Durable Objects
+- **Communication**: Real-time bidirectional WebSocket communication
+- **Build**: Vite modern build tool
+
+## 🔐 Zero-Knowledge Architecture Design
+
+### Core Principles
+- **Server Blind Relay**: The server can never decrypt message content, only responsible for encrypted data relay
+- **No Database Storage**: The system does not use any persistent storage; all data exists only temporarily in memory
+- **End-to-End Encryption**: Messages are encrypted from sender to receiver throughout the entire process; no intermediate node can decrypt them
+- **Forward Secrecy**: Even if keys are compromised, historical messages cannot be decrypted because there are no historical messages at all
+- **Anonymous Communication**: Users do not need to register real identities; supports temporary anonymous chat
+
+### Privacy Protection Mechanisms
+
+- **Real-time Member Notifications**: The room online list is completely transparent; any member joining or leaving will notify all members in real-time
+- **No Historical Messages**: Newly joined users cannot see any historical chat records
+- **Private Chat Encryption**: Clicking on a user's avatar can initiate end-to-end encrypted private conversations that are completely invisible to other room members
+
+### Room Password Mechanism
+
+Room passwords serve as **key derivation factors** in end-to-end encryption: `Final Shared Key = ECDH_Shared_Key XOR SHA256(Room Password)`
+
+- **Password Error Isolation**: Rooms with different passwords cannot decrypt each other's messages
+- **Server Blind Spot**: The server can never know the room password
+
+### Three-Layer Security System
+
+#### Layer 1: RSA-2048 Server Identity Authentication
+- Server generates temporary RSA-2048 key pairs on startup, automatically rotated every 24 hours
+- Client verifies server public key on connection to prevent man-in-the-middle attacks
+- Private keys exist only in server memory and are never persistently stored
+
+#### Layer 2: ECDH-P384 Key Agreement
+- Each client generates independent elliptic curve key pairs (P-384 curve)
+- Establishes shared keys through Elliptic Curve Diffie-Hellman key exchange protocol
+- Each client has an independent encrypted channel with the server
+
+#### Layer 3: Hybrid Symmetric Encryption
+- **Server Communication**: Uses AES-256-CBC to encrypt control messages between client and server
+- **Client Communication**: Uses ChaCha20 to encrypt actual chat content between clients
+- Each message uses independent initialization vectors (IV) and nonces
+
+## 🔄 Complete Encryption Process
+
+```mermaid
+sequenceDiagram
+ participant C as Client
+ participant S as Server
+ participant O as Other Clients
+
+ Note over C,S: Phase 1: Server Identity Authentication (RSA-2048)
+ C->>S: WebSocket Connection
+ S->>C: RSA-2048 Public Key
+
+ Note over C,S: Phase 2: Client-Server Key Exchange (P-384 ECDH)
+ C->>S: P-384 ECDH Public Key
+ S->>C: P-384 Public Key + RSA Signature
+ Note over C: Verify RSA signature and derive AES-256 key
+ Note over S: Derive AES-256 key from P-384 ECDH
+
+ Note over C,S: Phase 3: Room Authentication
+ C->>S: Join Request (Room Hash, AES-256 encrypted)
+ Note over S: Add client to room/channel
+ S->>C: Member List (Other client IDs, encrypted)
+
+ Note over C,O: Phase 4: Inter-Client Key Exchange (Curve25519)
+ Note over C: Generate Curve25519 key pair for each member
+ C->>S: Curve25519 Public Key Bundle (AES-256 encrypted)
+ S->>O: Forward Client C's public key
+ O->>S: Return other clients' Curve25519 public keys
+ S->>C: Forward other clients' public keys
+
+ Note over C,O: Phase 5: Password-Enhanced Key Derivation
+ Note over C: Client Key = ECDH_Curve25519(own private key, other's public key) XOR SHA256(password)
+ Note over O: Client Key = ECDH_Curve25519(own private key, other's public key) XOR SHA256(password)
+
+ Note over C,O: Phase 6: Identity Authentication
+ C->>S: Username (ChaCha20 encrypted with client key)
+ S->>O: Forward encrypted username
+ O->>S: Username (ChaCha20 encrypted with client key)
+ S->>C: Forward encrypted username
+ Note over C,O: Both clients now verify each other's identity
+
+ Note over C,O: Phase 7: Secure Message Transmission (Double-layer encryption)
+ Note over C: 1. ChaCha20 encrypt(message content)
2. AES-256 encrypt(transport layer wrapper)
+ C->>S: Double-layer encrypted message
+ Note over S: Decrypt AES-256 transport layer
Extract ChaCha20 encrypted data
Cannot decrypt message content
+ S->>O: Forward ChaCha20 encrypted data
+ Note over O: Decrypt AES-256 transport layer
ChaCha20 decrypt to get message content
+```
+
+## 🛠️ Technical Implementation
+
+- **Web Cryptography API**: Native browser encryption implementation with hardware acceleration
+- **elliptic.js**: Elliptic curve cryptography library implementing Curve25519 and P-384
+- **aes-js**: Pure JavaScript AES implementation supporting multiple modes
+- **js-chacha20**: JavaScript implementation of ChaCha20 stream cipher
+- **js-sha256**: SHA-256 hash algorithm implementation
+
+## 🚀 Deployment Instructions
+
+### Local Development
+After cloning the project and installing dependencies, use `npm run dev` to start the development server.
+
+Use `npm run deploy` to deploy to Cloudflare Workers.
+
+## 🔬 Security Verification
+
+### Encryption Process Verification
+Users can observe the complete encryption and decryption process through browser developer tools to verify that messages are indeed encrypted during transmission.
+
+### Network Traffic Analysis
+Network packet capture tools can verify that all WebSocket transmitted data is unreadable encrypted content.
+
+### Code Security Audit
+All encryption-related code is completely open source, using standard cryptographic algorithms. Security researchers are welcome to conduct independent audits.
+
+## ⚠️ Security Recommendations
+
+- **Use Strong Room Passwords**: Room passwords directly affect end-to-end encryption strength; complex passwords are recommended
+- **Password Confidentiality**: If a room password is leaked, all communication content in that room may be decrypted
+- **Use Latest Modern Browsers**: Ensure security and performance of cryptographic APIs
+
+## 🤝 Security Contributions
+
+Security researchers are welcome to report vulnerabilities and conduct security audits. Critical security issues will be fixed within 24 hours.
+
+## 📄 Open Source License
+
+This project uses the ISC open source license.
+
+## ⚠️ Disclaimer
+
+This project is for educational and technical research purposes only and must not be used for any illegal or criminal activities. Users should comply with the relevant laws and regulations of their country and region. The project author assumes no legal responsibility for any consequences arising from the use of this software. Please use this project legally and compliantly.
+
+---
+
+**NodeCrypt** - True End-to-End Encrypted Communication 🔐
+
+*"In the digital age, encryption is the last line of defense for privacy"*
diff --git a/server/package-lock.json b/server/package-lock.json
deleted file mode 100644
index b0530a5..0000000
--- a/server/package-lock.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
- "name": "nodecrypt-server",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "nodecrypt-server",
- "version": "1.0.0",
- "license": "ISC",
- "dependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2",
- "ws": "^7.3.1"
- }
- },
- "node_modules/bufferutil": {
- "version": "4.0.9",
- "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
- "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
- "node_modules/node-gyp-build": {
- "version": "4.8.4",
- "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
- "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
- "license": "MIT",
- "bin": {
- "node-gyp-build": "bin.js",
- "node-gyp-build-optional": "optional.js",
- "node-gyp-build-test": "build-test.js"
- }
- },
- "node_modules/utf-8-validate": {
- "version": "5.0.10",
- "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
- "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
- "node_modules/ws": {
- "version": "7.5.10",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
- "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8.3.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- }
- }
-}
diff --git a/server/package.json b/server/package.json
deleted file mode 100644
index 32f6380..0000000
--- a/server/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "nodecrypt-server",
- "version": "1.0.0",
- "description": "NodeCrypt Server",
- "main": "server.js",
- "author": "Zoltan Hajdu",
- "license": "ISC",
- "dependencies": {
- "ws": "^7.3.1",
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- }
-}
diff --git a/server/server.js b/server/server.js
deleted file mode 100644
index 90a981e..0000000
--- a/server/server.js
+++ /dev/null
@@ -1,586 +0,0 @@
-#!/usr/bin/env nodejs
-
-'use strict';
-
-const crypto = require('crypto');
-const ws = require('ws');
-
-// Generate a new RSA key pair
-// 生成一个新的 RSA 密钥对
-const generateRSAKeyPair = () => {
- try {
- console.log('Generating new RSA keypair...');
- const {
- publicKey,
- privateKey
- } = crypto.generateKeyPairSync('rsa', {
- modulusLength: 2048,
- publicKeyEncoding: {
- type: 'spki',
- format: 'der'
- },
- privateKeyEncoding: {
- type: 'pkcs8',
- format: 'der'
- }
- });
-
- return {
- rsaPublic: Buffer.from(publicKey).toString('base64'),
- rsaPrivate: crypto.createPrivateKey({
- key: privateKey,
- format: 'der',
- type: 'pkcs8'
- })
- };
- } catch (error) {
- console.error('Error generating RSA key pair:', error);
- process.exit(1);
- }
-};
-
-const keyPair = generateRSAKeyPair();
-console.log('RSA key pair generated successfully');
-
-const config = {
- rsaPrivate: keyPair.rsaPrivate,
- rsaPublic: keyPair.rsaPublic,
- wsHost: '127.0.0.1',
- wsPort: 8088,
- seenTimeout: 60000,
- debug: false
-};
-
-
-const wss = new ws.Server({
- host: config.wsHost,
- port: config.wsPort,
- perMessageDeflate: false
-});
-
-console.log('server started', config.wsHost, config.wsPort);
-
-
-var clients = {};
-var channels = {};
-
-// WebSocket server connection event handler
-// WebSocket 服务器连接事件处理程序
-wss.on('connection', (connection) => {
-
- if (
- !connection
- ) {
- return;
- }
-
-
- const seenThreshold = (getTime() - config.seenTimeout);
-
- for (
- const clientId in clients
- ) {
- if (
- clients[clientId].seen < seenThreshold
- ) {
- try {
- logEvent('connection-seen', clientId, 'debug');
- clients[clientId].connection.terminate();
- } catch (error) {
- logEvent('connection-seen', error, 'error');
- }
- }
- }
-
-
- const clientId = generateClientId();
-
- if (
- !clientId ||
- clients[clientId]
- ) {
- closeConnection(connection);
- return;
- }
-
- logEvent('connection', clientId, 'debug');
-
-
- clients[clientId] = {
- connection: connection,
- seen: getTime(),
- key: null,
- channel: null
- };
-
- try {
- logEvent('sending-public-key', clientId, 'debug');
- sendMessage(connection, JSON.stringify({
- type: 'server-key',
- key: config.rsaPublic
- }));
- } catch (error) {
- logEvent('sending-public-key', error, 'error');
- }
-
-
-
- connection.on('message', (message) => {
-
- if (
- !isString(message) ||
- !clients[clientId]
- ) {
- return;
- }
-
- clients[clientId].seen = getTime();
-
- if (
- message === 'ping'
- ) {
- sendMessage(connection, 'pong');
- return;
- }
-
- logEvent('message', [clientId, message], 'debug');
-
- if (
- !clients[clientId].shared &&
- message.length < 2048
- ) {
-
-
- try {
-
- const keys = crypto.createECDH('secp384r1');
-
- keys.generateKeys();
-
- const publicKey = keys.getPublicKey();
- const signature = crypto.sign('sha256', publicKey, {
- key: config.rsaPrivate,
- padding: crypto.constants.RSA_PKCS1_PADDING,
- dsaEncoding: 'ieee-p1363'
- });
-
- clients[clientId].shared = keys.computeSecret(message, 'hex', null).slice(8, 40);
-
- sendMessage(connection, publicKey.toString('hex') + '|' + signature.toString('base64'));
-
- } catch (error) {
- logEvent('message-key', [clientId, error], 'error');
- closeConnection(connection);
- }
-
- return;
-
- }
-
- if (
- clients[clientId].shared &&
- message.length <= (8 * 1024 * 1024)
- ) {
-
- processEncryptedMessage(clientId, message);
-
- }
-
- });
-
-
-
- connection.on('close', (event) => {
-
- logEvent('close', [clientId, event], 'debug');
-
-
- const channel = clients[clientId].channel;
-
- if (
- channel &&
- channels[channel]
- ) {
-
- channels[channel].splice(channels[channel].indexOf(clientId), 1);
-
- if (
- channels[channel].length === 0
- ) {
-
-
- delete(channels[channel]);
-
- } else {
-
-
- try {
-
- const members = channels[channel];
-
- for (
- const member of members
- ) {
-
- const client = clients[member];
-
- if (
- isClientInChannel(client, channel)
- ) {
- sendMessage(client.connection, encryptMessage({
- a: 'l',
- p: members.filter((value) => {
- return (
- value !== member ?
- true :
- false
- );
- })
- }, client.shared));
- }
-
- }
-
- } catch (error) {
- logEvent('close-list', [clientId, error], 'error');
- }
-
- }
-
- }
-
-
- if (
- clients[clientId]
- ) {
- delete(clients[clientId]);
- }
-
- });
-
-});
-
-// Process encrypted messages
-// 处理加密消息
-const processEncryptedMessage = (clientId, message) => {
- let decrypted = null;
-
- try {
- decrypted = decryptMessage(message, clients[clientId].shared);
-
- logEvent('message-decrypted', [clientId, decrypted], 'debug');
-
- if (
- !isObject(decrypted) ||
- !isString(decrypted.a)
- ) {
- return;
- }
-
- const action = decrypted.a;
-
- if (action === 'j') {
- handleJoinChannel(clientId, decrypted);
- } else if (action === 'c') {
- handleClientMessage(clientId, decrypted);
- } else if (action === 'w') {
- handleChannelMessage(clientId, decrypted);
- }
-
- } catch (error) {
- logEvent('process-encrypted-message', [clientId, error], 'error');
- } finally {
- decrypted = null;
- }
-};
-
-// Handle channel join requests
-// 处理加入频道请求
-const handleJoinChannel = (clientId, decrypted) => {
- if (
- !isString(decrypted.p) ||
- clients[clientId].channel
- ) {
- return;
- }
-
- try {
- const channel = decrypted.p;
-
- clients[clientId].channel = channel;
-
- if (!channels[channel]) {
- channels[channel] = [clientId];
- } else {
- channels[channel].push(clientId);
- }
-
- broadcastMemberList(channel);
-
- } catch (error) {
- logEvent('message-join', [clientId, error], 'error');
- }
-};
-
-// Handle client messages
-// 处理客户端消息
-const handleClientMessage = (clientId, decrypted) => {
- if (
- !isString(decrypted.p) ||
- !isString(decrypted.c) ||
- !clients[clientId].channel
- ) {
- return;
- }
-
- try {
- const channel = clients[clientId].channel;
- const targetClient = clients[decrypted.c];
-
- if (isClientInChannel(targetClient, channel)) {
- const messageObj = {
- a: 'c',
- p: decrypted.p,
- c: clientId
- };
-
- const encrypted = encryptMessage(messageObj, targetClient.shared);
- sendMessage(targetClient.connection, encrypted);
-
- messageObj.p = null;
- }
-
- } catch (error) {
- logEvent('message-client', [clientId, error], 'error');
- }
-};
-
-// Handle channel messages
-// 处理频道消息
-const handleChannelMessage = (clientId, decrypted) => {
- if (
- !isObject(decrypted.p) ||
- !clients[clientId].channel
- ) {
- return;
- }
-
- try {
- const channel = clients[clientId].channel;
-
- for (const member in decrypted.p) {
- const targetClient = clients[member];
-
- if (
- isString(decrypted.p[member]) &&
- isClientInChannel(targetClient, channel)
- ) {
- const messageObj = {
- a: 'c',
- p: decrypted.p[member],
- c: clientId
- };
-
- const encrypted = encryptMessage(messageObj, targetClient.shared);
- sendMessage(targetClient.connection, encrypted);
-
- messageObj.p = null;
- }
- }
-
- } catch (error) {
- logEvent('message-channel', [clientId, error], 'error');
- }
-};
-
-// Broadcast member list to channel
-// 向频道广播成员列表
-const broadcastMemberList = (channel) => {
- try {
- const members = channels[channel];
-
- for (const member of members) {
- const client = clients[member];
-
- if (isClientInChannel(client, channel)) {
- const filteredMembers = members.filter(value => value !== member);
-
- const listObj = {
- a: 'l',
- p: filteredMembers
- };
-
- const encrypted = encryptMessage(listObj, client.shared);
- sendMessage(client.connection, encrypted);
-
- listObj.p = null;
- }
- }
- } catch (error) {
- logEvent('broadcast-member-list', error, 'error');
- }
-};
-
-
-
-// Log events with timestamps and levels
-// 记录带时间戳和级别的事件
-const logEvent = (source, message, level) => {
- if (
- level !== 'debug' ||
- config.debug
- ) {
-
- const date = new Date(),
- dateString = date.getFullYear() + '-' +
- ('0' + (date.getMonth() + 1)).slice(-2) + '-' +
- ('0' + date.getDate()).slice(-2) + ' ' +
- ('0' + date.getHours()).slice(-2) + ':' +
- ('0' + date.getMinutes()).slice(-2) + ':' +
- ('0' + date.getSeconds()).slice(-2);
-
- console.log('[' + dateString + ']', (level ? level.toUpperCase() : 'INFO'), source + (message ? ':' : ''), (message ? message : ''));
-
- }
-};
-
-
-const generateClientId = () => {
- try {
- return (crypto.randomBytes(8).toString('hex'));
- } catch (error) {
- logEvent('generateClientId', error, 'error');
- return (null);
- }
-};
-
-
-const closeConnection = (connection) => {
- try {
- connection.close();
- } catch (error) {
- logEvent('closeConnection', error, 'error');
- }
-};
-
-
-const isClientInChannel = (client, channel) => {
- return (
- client &&
- client.connection &&
- client.shared &&
- client.channel &&
- client.channel === channel ?
- true :
- false
- );
-};
-
-
-const sendMessage = (connection, message) => {
- try {
- if (
- connection.readyState &&
- connection.readyState === ws.OPEN
- ) {
- connection.send(message);
- }
- } catch (error) {
- logEvent('sendMessage', error, 'error');
- }
-};
-
-
-const encryptMessage = (message, key) => {
-
- let encrypted = '';
-
- try {
-
- const messageBuffer = Buffer.from(JSON.stringify(message), 'utf8');
-
- const paddedBuffer = (messageBuffer.length % 16) !== 0 ?
- Buffer.concat([messageBuffer, Buffer.alloc(16 - (messageBuffer.length % 16))]) :
- messageBuffer;
-
- const iv = crypto.randomBytes(16);
- const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
- cipher.setAutoPadding(false);
-
- encrypted = iv.toString('base64') + '|' + cipher.update(paddedBuffer, '', 'base64') + cipher.final('base64');
-
- } catch (error) {
- logEvent('encryptMessage', error, 'error');
- }
-
- return (encrypted);
-
-};
-
-
-const decryptMessage = (message, key) => {
-
- let decrypted = {};
-
- try {
-
- const parts = message.split('|');
- const decipher = crypto.createDecipheriv(
- 'aes-256-cbc',
- key,
- Buffer.from(parts[0], 'base64')
- );
-
- decipher.setAutoPadding(false);
-
- const decryptedText = decipher.update(parts[1], 'base64', 'utf8') + decipher.final('utf8');
- decrypted = JSON.parse(decryptedText.replace(/\0+$/, ''));
-
- } catch (error) {
- logEvent('decryptMessage', error, 'error');
- }
-
- return (decrypted);
-
-};
-
-
-const getTime = () => {
- return (new Date().getTime());
-};
-
-
-const isString = (value) => {
- return (
- value &&
- Object.prototype.toString.call(value) === '[object String]' ?
- true :
- false
- );
-};
-
-
-const isArray = (value) => {
- return (
- value &&
- Object.prototype.toString.call(value) === '[object Array]' ?
- true :
- false
- );
-};
-
-
-const isObject = (value) => {
- return (
- value &&
- Object.prototype.toString.call(value) === '[object Object]' ?
- true :
- false
- );
-};
-
-setInterval(() => {
- if (global.gc) {
- global.gc();
- }
-}, 30000);
\ No newline at end of file
diff --git a/wrangler.toml b/wrangler.toml
index 632f37c..d48cf91 100644
--- a/wrangler.toml
+++ b/wrangler.toml
@@ -1,4 +1,4 @@
-name = "nodecrypt-worker"
+name = "nodecrypt"
main = "worker/index.js"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]