'use strict'; const crypto = require('crypto'); const axios = require('axios'); const { validateSession } = require('./email-auth'); // ─── 常量 ─── const HANDSHAKE_EXPIRE_MS = 5 * 60 * 1000; // 5分钟 const HEARTBEAT_INTERVAL = 30 * 1000; // 30秒 const MAX_RETRIES = 3; // ─── 握手状态存储 ─── const pendingHandshakes = new Map(); // token → { agentId, publicKey, createdAt } const activeConnections = new Map(); // connectionId → { agentId, partnerId, heartbeatAt } // ─── 错误代码 ─── const ERRORS = { INVALID_SESSION: { code: 'HS001', message: '无效会话' }, INVALID_AGENT_ID: { code: 'HS002', message: '无效Agent ID' }, HANDSHAKE_EXPIRED: { code: 'HS003', message: '握手过期' }, ENCRYPTION_FAILED: { code: 'HS004', message: '加密失败' }, DECRYPTION_FAILED: { code: 'HS005', message: '解密失败' }, INVALID_SIGNATURE: { code: 'HS006', message: '无效签名' }, CONNECTION_LOST: { code: 'HS007', message: '连接丢失' } }; /** * 生成密钥对 */ function generateKeyPair() { return crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); } /** * 初始化握手 * @param {string} token - 会话token * @param {string} agentId - Agent ID * @returns {{ handshakeId: string, publicKey: string }} */ function initHandshake(token, agentId) { if (!validateSession(token).valid) { throw ERRORS.INVALID_SESSION; } if (!agentId || typeof agentId !== 'string') { throw ERRORS.INVALID_AGENT_ID; } const handshakeId = crypto.randomBytes(16).toString('hex'); const keyPair = generateKeyPair(); pendingHandshakes.set(handshakeId, { agentId, publicKey: keyPair.publicKey, privateKey: keyPair.privateKey, createdAt: Date.now() }); return { handshakeId, publicKey: keyPair.publicKey }; } /** * 完成握手 * @param {string} handshakeId - 握手ID * @param {string} partnerId - 对方Agent ID * @param {string} encryptedData - 加密数据 * @returns {{ connectionId: string, partnerPublicKey: string }} */ function completeHandshake(handshakeId, partnerId, encryptedData) { const handshake = pendingHandshakes.get(handshakeId); if (!handshake) { throw ERRORS.INVALID_SESSION; } if (Date.now() - handshake.createdAt > HANDSHAKE_EXPIRE_MS) { pendingHandshakes.delete(handshakeId); throw ERRORS.HANDSHAKE_EXPIRED; } try { const decrypted = crypto.privateDecrypt( { key: handshake.privateKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }, Buffer.from(encryptedData, 'base64') ).toString('utf8'); const { publicKey: partnerPublicKey } = JSON.parse(decrypted); const connectionId = crypto.randomBytes(16).toString('hex'); activeConnections.set(connectionId, { agentId: handshake.agentId, partnerId, heartbeatAt: Date.now() }); pendingHandshakes.delete(handshakeId); return { connectionId, partnerPublicKey }; } catch (err) { console.error(`[Agent握手] 解密失败: ${err.message}`); throw ERRORS.DECRYPTION_FAILED; } } /** * 发送心跳 * @param {string} connectionId - 连接ID */ function sendHeartbeat(connectionId) { const connection = activeConnections.get(connectionId); if (!connection) { throw ERRORS.INVALID_SESSION; } connection.heartbeatAt = Date.now(); } /** * 清理过期握手和连接 */ function cleanupExpired() { const now = Date.now(); for (const [handshakeId, handshake] of pendingHandshakes.entries()) { if (now - handshake.createdAt > HANDSHAKE_EXPIRE_MS) { pendingHandshakes.delete(handshakeId); } } for (const [connectionId, connection] of activeConnections.entries()) { if (now - connection.heartbeatAt > HEARTBEAT_INTERVAL * 3) { activeConnections.delete(connectionId); } } } // 每1分钟清理一次 setInterval(cleanupExpired, 60 * 1000); module.exports = { initHandshake, completeHandshake, sendHeartbeat, ERRORS };