├── .env.example ├── .eslintrc.json ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab └── ci │ ├── api.yml │ ├── core.yml │ └── protocol.yml ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_CN.md ├── docker-compose.yml ├── docs ├── API.md ├── ARCHITECTURE.md ├── CI_CD.md ├── DEPLOYMENT.md ├── DEVELOPMENT.md └── assets │ └── logo.svg ├── jest.config.js ├── package-lock.json ├── package.json ├── packages ├── ai │ ├── .gitignore │ ├── README.md │ ├── data │ │ ├── training-data.json │ │ └── training-labels.json │ ├── docs │ │ └── api.md │ ├── examples │ │ └── advanced-training.ts │ ├── package.json │ ├── src │ │ ├── core │ │ │ ├── ClientManager.ts │ │ │ ├── FederatedCoordinator.ts │ │ │ ├── FederatedLearningProtocol.ts │ │ │ ├── ModelAggregator.ts │ │ │ ├── PrivacyManager.ts │ │ │ ├── algorithms │ │ │ │ └── FederatedAlgorithms.ts │ │ │ ├── defense │ │ │ │ └── ModelDefense.ts │ │ │ ├── federated.ts │ │ │ ├── optimization │ │ │ │ └── DistributedOptimizer.ts │ │ │ ├── privacy.ts │ │ │ ├── privacy │ │ │ │ ├── DifferentialPrivacy.ts │ │ │ │ ├── HomomorphicEncryption.ts │ │ │ │ └── ZeroKnowledgeProof.ts │ │ │ ├── secure │ │ │ │ └── SecureAggregation.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ ├── models │ │ │ └── base.ts │ │ ├── types.ts │ │ └── types │ │ │ ├── federated.ts │ │ │ └── index.ts │ ├── test │ │ └── federated.test.ts │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── crypto │ ├── docs │ │ └── api.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── core │ │ │ ├── __tests__ │ │ │ │ ├── asymmetric.test.ts │ │ │ │ ├── elgamal.test.ts │ │ │ │ ├── hash.test.ts │ │ │ │ ├── homomorphic.test.ts │ │ │ │ ├── kdf.test.ts │ │ │ │ ├── symmetric.test.ts │ │ │ │ └── zkp.test.ts │ │ │ ├── asymmetric.ts │ │ │ ├── asymmetric │ │ │ │ └── __tests__ │ │ │ │ │ ├── elgamal.test.ts │ │ │ │ │ └── rsa.test.ts │ │ │ ├── blind │ │ │ │ └── __tests__ │ │ │ │ │ └── blind-signature.test.ts │ │ │ ├── cipher │ │ │ │ └── __tests__ │ │ │ │ │ ├── aes-cfb.test.ts │ │ │ │ │ ├── aes-ctr.test.ts │ │ │ │ │ ├── aes-ofb.test.ts │ │ │ │ │ ├── aes.test.ts │ │ │ │ │ ├── chacha20.test.ts │ │ │ │ │ └── xchacha20.test.ts │ │ │ ├── curve25519 │ │ │ │ └── __tests__ │ │ │ │ │ └── curve25519.test.ts │ │ │ ├── dh │ │ │ │ └── __tests__ │ │ │ │ │ └── diffie-hellman.test.ts │ │ │ ├── ecdsa │ │ │ │ └── __tests__ │ │ │ │ │ └── ecdsa.test.ts │ │ │ ├── ed25519 │ │ │ │ └── __tests__ │ │ │ │ │ └── ed25519.test.ts │ │ │ ├── eddsa │ │ │ │ └── __tests__ │ │ │ │ │ └── ed25519.test.ts │ │ │ ├── elgamal.ts │ │ │ ├── encryption.ts │ │ │ ├── hash.ts │ │ │ ├── hash │ │ │ │ └── __tests__ │ │ │ │ │ ├── argon2.test.ts │ │ │ │ │ ├── blake2.test.ts │ │ │ │ │ ├── sha2.test.ts │ │ │ │ │ └── sha3.test.ts │ │ │ ├── homomorphic.ts │ │ │ ├── homomorphic │ │ │ │ ├── __tests__ │ │ │ │ │ └── fhe.test.ts │ │ │ │ ├── advanced.ts │ │ │ │ ├── bootstrap.ts │ │ │ │ ├── crt.ts │ │ │ │ ├── fft.ts │ │ │ │ ├── fhe.ts │ │ │ │ ├── fpga.ts │ │ │ │ ├── gpu.ts │ │ │ │ ├── keyswitch.ts │ │ │ │ ├── memory.ts │ │ │ │ ├── ml.ts │ │ │ │ ├── ntt.ts │ │ │ │ └── simd.ts │ │ │ ├── kdf.ts │ │ │ ├── kdf │ │ │ │ └── __tests__ │ │ │ │ │ ├── hkdf.test.ts │ │ │ │ │ └── pbkdf2.test.ts │ │ │ ├── mac │ │ │ │ ├── __tests__ │ │ │ │ │ ├── hmac.test.ts │ │ │ │ │ └── poly1305.test.ts │ │ │ │ ├── hmac.ts │ │ │ │ └── poly1305.ts │ │ │ ├── symmetric.ts │ │ │ ├── symmetric │ │ │ │ ├── __tests__ │ │ │ │ │ ├── aes.test.ts │ │ │ │ │ └── chacha20-poly1305.test.ts │ │ │ │ ├── aes.ts │ │ │ │ └── chacha20-poly1305.ts │ │ │ ├── threshold │ │ │ │ └── __tests__ │ │ │ │ │ └── threshold-signature.test.ts │ │ │ ├── tls │ │ │ │ ├── __tests__ │ │ │ │ │ ├── handshake.test.ts │ │ │ │ │ ├── key-schedule.test.ts │ │ │ │ │ └── record-layer.test.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── handshake.ts │ │ │ │ ├── key-schedule.ts │ │ │ │ └── record-layer.ts │ │ │ ├── xchacha20 │ │ │ │ ├── __tests__ │ │ │ │ │ └── xchacha20.test.ts │ │ │ │ └── xchacha20.ts │ │ │ ├── zkp.ts │ │ │ └── zkp │ │ │ │ ├── __tests__ │ │ │ │ ├── chaum-pedersen.test.ts │ │ │ │ ├── fiat-shamir.test.ts │ │ │ │ ├── homomorphic.test.ts │ │ │ │ ├── or-proof.test.ts │ │ │ │ ├── pedersen.test.ts │ │ │ │ ├── range.test.ts │ │ │ │ └── schnorr.test.ts │ │ │ │ ├── chaum-pedersen.ts │ │ │ │ ├── fiat-shamir.ts │ │ │ │ ├── homomorphic.ts │ │ │ │ ├── index.ts │ │ │ │ ├── or-proof.ts │ │ │ │ ├── pedersen.ts │ │ │ │ ├── range.ts │ │ │ │ └── schnorr.ts │ │ ├── index.ts │ │ ├── types.ts │ │ ├── types │ │ │ └── index.ts │ │ └── utils │ │ │ └── hash.ts │ ├── test │ │ └── encryption.test.ts │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo ├── protocol │ ├── README.md │ ├── docs │ │ ├── api.md │ │ ├── deployment.md │ │ └── examples.md │ ├── package.json │ ├── src │ │ ├── core │ │ │ ├── __tests__ │ │ │ │ ├── blind-signature.test.ts │ │ │ │ ├── commitment.test.ts │ │ │ │ ├── key-exchange.test.ts │ │ │ │ ├── mpc.benchmark.ts │ │ │ │ ├── mpc.test.ts │ │ │ │ ├── recovery.test.ts │ │ │ │ ├── ring-signature.test.ts │ │ │ │ ├── threshold-signature.test.ts │ │ │ │ └── zkp.test.ts │ │ │ ├── base.ts │ │ │ ├── blind-signature.ts │ │ │ ├── commitment.ts │ │ │ ├── key-exchange.ts │ │ │ ├── mpc.ts │ │ │ ├── privacy.ts │ │ │ ├── recovery.ts │ │ │ ├── ring-signature.ts │ │ │ ├── threshold-signature.ts │ │ │ ├── training.ts │ │ │ ├── types.ts │ │ │ └── zkp.ts │ │ ├── index.ts │ │ ├── marketplace │ │ │ ├── AccessManager.ts │ │ │ ├── AssetRegistry.ts │ │ │ ├── DataMarketplace.ts │ │ │ ├── DataMarketplaceService.ts │ │ │ ├── IncentiveManager.ts │ │ │ ├── MarketPaymentIntegrator.ts │ │ │ ├── OrderMatcher.ts │ │ │ ├── PriceOracle.ts │ │ │ └── QualityAssessor.ts │ │ ├── tee │ │ │ ├── TrustedExecutionManager.ts │ │ │ └── TrustedExecutionService.ts │ │ ├── token │ │ │ ├── TokenContract.ts │ │ │ └── TokenEconomyManager.ts │ │ ├── types.ts │ │ ├── types │ │ │ └── index.ts │ │ └── utils │ │ │ ├── logger.ts │ │ │ ├── validation.ts │ │ │ └── validator.ts │ ├── test │ │ └── privacy.test.ts │ ├── tsconfig.json │ └── tsconfig.tsbuildinfo └── ui │ ├── package-lock.json │ └── package.json ├── public └── index.html ├── scripts └── setup-dev.sh ├── src ├── App.tsx ├── api │ ├── client.ts │ └── services.ts ├── components │ ├── DataVisualization.tsx │ ├── DatasetList.tsx │ ├── ModelConfig.tsx │ ├── TrainingTaskList.tsx │ ├── analysis │ │ ├── AdvancedAnalysis.tsx │ │ └── DataAnalysis.tsx │ ├── auth │ │ └── AuthProvider.tsx │ └── training │ │ └── TrainingProgress.tsx ├── core │ ├── encryption.ts │ ├── federated-learning.ts │ ├── losses.ts │ ├── model.ts │ ├── neural-network.ts │ ├── optimizers.ts │ ├── preprocessing.ts │ └── privacy-protocol.ts ├── hooks │ ├── useDataAnalysis.ts │ ├── useDatasets.ts │ ├── useTrainingProgress.ts │ └── useTrainingTasks.ts ├── index.tsx ├── layouts │ └── MainLayout.tsx ├── pages │ ├── DatasetsPage.tsx │ ├── TrainingPage.tsx │ └── auth │ │ ├── LoginPage.tsx │ │ └── RegisterPage.tsx ├── services │ └── websocket.ts └── types │ └── index.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | # Environment 2 | NODE_ENV=development 3 | 4 | # API Configuration 5 | API_PORT=3000 6 | API_HOST=localhost 7 | 8 | # Database Configuration 9 | DB_HOST=localhost 10 | DB_PORT=5432 11 | DB_NAME=cipher_nexus 12 | DB_USER=postgres 13 | DB_PASSWORD=your_password 14 | 15 | # Redis Configuration 16 | REDIS_HOST=localhost 17 | REDIS_PORT=6379 18 | 19 | # JWT Configuration 20 | JWT_SECRET=your_jwt_secret 21 | JWT_EXPIRES_IN=7d 22 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/explicit-module-boundary-types": "error", 13 | "@typescript-eslint/no-explicit-any": "warn", 14 | "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | .pnp 4 | .pnp.js 5 | 6 | # Production 7 | dist/ 8 | build/ 9 | 10 | # Testing 11 | coverage/ 12 | 13 | # Environment 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | # IDE 21 | .idea/ 22 | .vscode/ 23 | *.swp 24 | *.swo 25 | 26 | # Logs 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # System 32 | .DS_Store 33 | Thumbs.db 34 | .npmrc 35 | 36 | # SSH keys 37 | .ssh/ 38 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - lint 3 | - test 4 | - build 5 | - deploy 6 | 7 | variables: 8 | DOCKER_DRIVER: overlay2 9 | DOCKER_TLS_CERTDIR: "" 10 | 11 | include: 12 | - local: .gitlab/ci/*.yml 13 | 14 | cache: 15 | paths: 16 | - node_modules/ 17 | - packages/*/node_modules/ 18 | 19 | lint: 20 | stage: lint 21 | script: 22 | - npm run lint 23 | - npm run type-check 24 | 25 | test: 26 | stage: test 27 | script: 28 | - npm run test:coverage 29 | coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' 30 | artifacts: 31 | reports: 32 | coverage_report: 33 | coverage_format: cobertura 34 | path: coverage/cobertura-coverage.xml 35 | 36 | build: 37 | stage: build 38 | script: 39 | - npm run build 40 | artifacts: 41 | paths: 42 | - dist/ 43 | 44 | deploy: 45 | stage: deploy 46 | script: 47 | - npm run deploy 48 | environment: 49 | name: production 50 | only: 51 | - master -------------------------------------------------------------------------------- /.gitlab/ci/api.yml: -------------------------------------------------------------------------------- 1 | build:ai: 2 | stage: build 3 | script: 4 | - cd packages/ai 5 | - npm run build 6 | 7 | artifacts: 8 | paths: 9 | - packages/ai/dist 10 | 11 | build:protocol: 12 | stage: build 13 | script: 14 | - cd packages/protocol 15 | - npm run build 16 | 17 | artifacts: 18 | paths: 19 | - packages/protocol/dist 20 | 21 | build:crypto: 22 | stage: build 23 | script: 24 | - cd packages/crypto 25 | - npm run build 26 | 27 | artifacts: 28 | paths: 29 | - packages/crypto/dist 30 | 31 | test:ai: 32 | stage: test 33 | script: 34 | - cd packages/ai 35 | - npm run test:coverage 36 | coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' 37 | artifacts: 38 | reports: 39 | coverage_report: 40 | coverage_format: cobertura 41 | path: packages/ai/coverage/cobertura-coverage.xml 42 | 43 | integration:ai: 44 | stage: test 45 | services: 46 | - docker:dind 47 | script: 48 | - docker-compose up -d 49 | - cd packages/ai 50 | - npm run test:integration 51 | artifacts: 52 | reports: 53 | junit: packages/ai/test-results/integration/*.xml 54 | 55 | test:crypto: 56 | stage: test 57 | script: 58 | - cd packages/crypto 59 | - npm run test:coverage 60 | coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' 61 | artifacts: 62 | reports: 63 | coverage_report: 64 | coverage_format: cobertura 65 | path: packages/crypto/coverage/cobertura-coverage.xml 66 | 67 | integration:crytpo: 68 | stage: test 69 | services: 70 | - docker:dind 71 | script: 72 | - docker-compose up -d 73 | - cd packages/crypto 74 | - npm run test:integration 75 | artifacts: 76 | reports: 77 | junit: packages/crypto/test-results/integration/*.xml 78 | 79 | test:protocol: 80 | stage: test 81 | script: 82 | - cd packages/protocol 83 | - npm run test:coverage 84 | coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' 85 | artifacts: 86 | reports: 87 | coverage_report: 88 | coverage_format: cobertura 89 | path: packages/protocol/coverage/cobertura-coverage.xml 90 | 91 | integration:protocol: 92 | stage: test 93 | services: 94 | - docker:dind 95 | script: 96 | - docker-compose up -d 97 | - cd packages/protocol 98 | - npm run test:integration 99 | artifacts: 100 | reports: 101 | junit: packages/protocol/test-results/integration/*.xml 102 | -------------------------------------------------------------------------------- /.gitlab/ci/core.yml: -------------------------------------------------------------------------------- 1 | build:core: 2 | stage: build 3 | script: 4 | - cd packages/core 5 | - npm run build 6 | artifacts: 7 | paths: 8 | - packages/core/dist/ 9 | 10 | test:core: 11 | stage: test 12 | script: 13 | - cd packages/core 14 | - npm run test:coverage 15 | coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' 16 | artifacts: 17 | reports: 18 | coverage_report: 19 | coverage_format: cobertura 20 | path: packages/core/coverage/cobertura-coverage.xml -------------------------------------------------------------------------------- /.gitlab/ci/protocol.yml: -------------------------------------------------------------------------------- 1 | build:protocol: 2 | stage: build 3 | script: 4 | - cd packages/protocol 5 | - npm run build 6 | artifacts: 7 | paths: 8 | - packages/protocol/dist/ 9 | 10 | test:protocol: 11 | stage: test 12 | script: 13 | - cd packages/protocol 14 | - npm run test:coverage 15 | coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' 16 | artifacts: 17 | reports: 18 | coverage_report: 19 | coverage_format: cobertura 20 | path: packages/protocol/coverage/cobertura-coverage.xml -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "printWidth": 100, 6 | "tabWidth": 2 7 | } 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Cipher Nexus 2 | 3 | We love your input! We want to make contributing to Cipher Nexus as easy and transparent as possible, whether it's: 4 | 5 | - Reporting a bug 6 | - Discussing the current state of the code 7 | - Submitting a fix 8 | - Proposing new features 9 | - Becoming a maintainer 10 | 11 | ## Development Process 12 | 13 | We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. 14 | 15 | 1. Fork the repo and create your branch from `main`. 16 | 2. If you've added code that should be tested, add tests. 17 | 3. If you've changed APIs, update the documentation. 18 | 4. Ensure the test suite passes. 19 | 5. Make sure your code lints. 20 | 6. Issue that pull request! 21 | 22 | ## Pull Request Process 23 | 24 | 1. Update the README.md with details of changes to the interface, if applicable. 25 | 2. Update the docs/ with any necessary documentation changes. 26 | 3. The PR will be merged once you have the sign-off of two other developers. 27 | 28 | ## Any contributions you make will be under the MIT Software License 29 | 30 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 31 | 32 | ## Report bugs using GitHub's [issue tracker](https://github.com/ciphernx/Cipher-Nexus/issues) 33 | 34 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/ciphernx/Cipher-Nexus/issues/new). 35 | 36 | ## Write bug reports with detail, background, and sample code 37 | 38 | **Great Bug Reports** tend to have: 39 | 40 | - A quick summary and/or background 41 | - Steps to reproduce 42 | - Be specific! 43 | - Give sample code if you can. 44 | - What you expected would happen 45 | - What actually happens 46 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 47 | 48 | ## Use a Consistent Coding Style 49 | 50 | * Use TypeScript for type safety 51 | * 2 spaces for indentation rather than tabs 52 | * You can try running `npm run lint` for style unification 53 | 54 | ## License 55 | 56 | By contributing, you agree that your contributions will be licensed under its MIT License. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Cipher Nexus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Cipher Nexus 2 | 3 |
4 | Cipher Nexus Logo 5 |
6 | 7 | 一个保护数据隐私的 AI 框架,支持安全的联邦学习和数据市场,在推进 AI 能力的同时保护数据隐私。 8 | 9 | [English](README.md) | [中文](README_CN.md) 10 | 11 | ## 概述 12 | 13 | Cipher Nexus 是一个综合性的 AI 框架,将联邦学习与隐私计算和安全数据市场功能相结合。它通过先进的密码学技术和可信执行环境,在确保数据隐私和安全的同时实现协作机器学习。 14 | 15 | 该框架解决了 AI 开发中的关键挑战: 16 | - **数据隐私**:在模型训练和推理过程中保护敏感数据 17 | - **协作学习**:使多方能够在不共享原始数据的情况下训练 AI 模型 18 | - **数据变现**:为高质量训练数据创建安全的市场 19 | - **模型安全**:确保模型完整性并防止未授权访问 20 | 21 | ## 核心功能 22 | 23 | - **先进的 AI 能力** 24 | - 基于联邦学习的分布式模型训练 25 | - 安全的模型聚合和更新机制 26 | - 隐私保护的推理和预测 27 | - 支持多种 AI 模型架构 28 | 29 | - **隐私计算基础设施** 30 | - 用于数据集保护的差分隐私 31 | - 用于安全计算的同态加密 32 | - 用于验证的零知识证明 33 | - 安全多方计算协议 34 | 35 | - **可信执行环境** 36 | - 用于敏感计算的安全飞地 37 | - 硬件级隔离和保护 38 | - 远程认证机制 39 | - 安全密钥管理 40 | 41 | - **数据市场** 42 | - 带访问控制的通证化数据资产 43 | - 质量评估和验证 44 | - 安全数据交换协议 45 | - 公平定价机制 46 | 47 | - **代币经济** 48 | - 数据共享激励机制 49 | - 模型训练贡献奖励 50 | - 用于协议决策的治理代币 51 | - 用于安全的质押机制 52 | 53 | ## 系统架构 54 | 55 | ```mermaid 56 | graph TD 57 | A[客户端节点] -->|模型更新| B[基础设施管理器] 58 | B --> C[资源管理器] 59 | B --> D[任务调度器] 60 | B --> E[隐私管理器] 61 | 62 | F[数据提供方] -->|数据资产| G[数据市场] 63 | G --> H[代币合约] 64 | G --> I[访问控制] 65 | 66 | J[模型训练] -->|加密更新| K[联邦协调器] 67 | K --> L[模型聚合器] 68 | K --> M[隐私计算] 69 | 70 | N[TEE管理器] -->|安全执行| O[计算节点] 71 | O --> P[资源监控] 72 | O --> Q[状态管理] 73 | ``` 74 | 75 | ## 模块说明 76 | 77 | - `@ciphernx/ai`: 联邦学习和模型管理 78 | - 实现联邦学习协议 79 | - 模型训练和聚合 80 | - 隐私保护机制 81 | - 支持主流 AI 框架 82 | - 安全模型服务和推理 83 | 84 | - `@cipher-nexus/core`: 基础设施和资源管理 85 | - 计算节点管理 86 | - 资源分配 87 | - 任务调度 88 | - 性能监控 89 | - 容错机制 90 | 91 | - `@ciphernx/crypto`: 密码学原语和协议 92 | - 同态加密 93 | - 零知识证明 94 | - 安全多方计算 95 | - 密钥管理 96 | - 隐私预算 97 | 98 | - `@ciphernx/protocol`: 网络协议和数据市场 99 | - 数据资产管理 100 | - 访问控制 101 | - 交易处理 102 | - 质量验证 103 | - 信誉系统 104 | 105 | - `@ciphernx/ui`: 用户界面组件 106 | - 数据集管理 107 | - 模型训练监控 108 | - 系统管理界面 109 | - 分析仪表盘 110 | - 隐私设置控制 111 | 112 | ## 快速开始 113 | 114 | 1. 安装依赖 115 | ```bash 116 | npm install 117 | ``` 118 | 119 | 2. 启动开发服务器 120 | ```bash 121 | npm run dev 122 | ``` 123 | 124 | 3. 构建项目 125 | ```bash 126 | npm run build 127 | ``` 128 | 129 | ## 文档 130 | 131 | - [API 文档](docs/API.md) 132 | - [架构设计](docs/ARCHITECTURE.md) 133 | - [部署指南](docs/DEPLOYMENT.md) 134 | - [开发指南](docs/DEVELOPMENT.md) 135 | 136 | ## 许可证 137 | 138 | MIT 许可证 139 | 140 | ## 常见问题 141 | 142 | ### AI 为什么需要隐私? 143 | - 数据安全:AI 训练需要大量数据,这些数据往往包含敏感信息(个人信息、商业机密等) 144 | - 法律合规:全球各地的数据保护法规(如 GDPR、CCPA)对数据隐私提出了严格要求 145 | - 用户信任:保护用户隐私是建立用户信任的关键,影响 AI 系统的采用率 146 | - 防止滥用:避免 AI 模型被用于未经授权的目的或被恶意攻击者利用 147 | - 跨组织协作:使不同组织能够在保护各自数据隐私的前提下进行 AI 协作 148 | 149 | ### 我们的 AI 隐私框架有什么特点? 150 | - 全面的隐私保护机制: 151 | * 联邦学习支持分布式训练 152 | * 差分隐私保护个体数据 153 | * 同态加密实现加密计算 154 | * 零知识证明确保计算正确性 155 | - 可信执行环境(TEE)保障 156 | - 灵活的数据市场机制 157 | - 代币经济激励系统 158 | - 模块化设计便于扩展 159 | - 完整的隐私度量和审计机制 160 | 161 | ### 为什么要使用我们的框架? 162 | - 一站式解决方案:集成了主流隐私计算技术 163 | - 性能优化:在保护隐私的同时保持高效率 164 | - 易于使用:提供友好的 API 和完善的文档 165 | - 安全可靠:多层次的安全保障机制 166 | - 社区支持:开源项目持续改进 167 | - 合规性:符合主流隐私保护法规 168 | 169 | ### 谁可以在什么需求下使用我们的隐私框架? 170 | - 适用人群: 171 | * 企业 AI 团队 172 | * 研究机构 173 | * 医疗机构 174 | * 金融机构 175 | * 政府部门 176 | * 数据服务提供商 177 | 178 | - 适用场景: 179 | * 跨组织数据协作 180 | * 医疗数据分析 181 | * 金融风控建模 182 | * 隐私保护的推荐系统 183 | * 多方安全计算 184 | * 需要严格数据保护的 AI 应用 185 | 186 | ### 我们是如何赋能加密货币的? 187 | - 代币经济系统设计: 188 | * 实用代币用于支付计算资源和数据访问 189 | * 治理代币用于社区治理和决策 190 | * 激励机制奖励数据提供者和验证者 191 | * 通证化数据资产 192 | - 隐私保护机制与加密货币的结合: 193 | * 零知识证明验证交易合法性 194 | * 同态加密支持加密状态下的计算 195 | * 安全多方计算实现隐私保护交易 196 | * 环签名提供交易匿名性 197 | - 智能合约功能: 198 | * 自动化市场制造者(AMM) 199 | * 去中心化交易 200 | * 质押合约 201 | * 治理合约 202 | - 经济模型创新: 203 | * 双通证模型 204 | * 动态定价机制 205 | * 通缩机制 206 | * 流动性挖矿 207 | - 跨链互操作: 208 | * 跨链资产转移 209 | * 跨链消息传递 210 | * 原子交换 211 | * 资产桥接 212 | - 安全保障: 213 | * 多重签名钱包 214 | * 时间锁定 215 | * 预言机整合 216 | * 紧急暂停 217 | - 生态系统建设: 218 | * 开发者激励 219 | * 社区建设 220 | * 合作伙伴计划 221 | * 去中心化自治组织(DAO) -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | # Database 5 | postgres: 6 | image: postgres:14 7 | environment: 8 | POSTGRES_DB: cipher_nexus 9 | POSTGRES_USER: postgres 10 | POSTGRES_PASSWORD: password 11 | ports: 12 | - "5432:5432" 13 | volumes: 14 | - postgres_data:/var/lib/postgresql/data 15 | 16 | # Redis Cache 17 | redis: 18 | image: redis:6 19 | ports: 20 | - "6379:6379" 21 | 22 | # AI Service 23 | ai_service: 24 | build: 25 | context: ./packages/ai 26 | dockerfile: Dockerfile 27 | ports: 28 | - "5000:5000" 29 | depends_on: 30 | - postgres 31 | 32 | volumes: 33 | postgres_data: 34 | -------------------------------------------------------------------------------- /docs/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 29 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | roots: ['/packages'], 5 | testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'], 6 | moduleNameMapper: { 7 | '^@cipher-nexus/(.*)$': '/packages//src' // Fix path mapping here 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cipher-nexus/ui", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.11.0", 7 | "@emotion/styled": "^11.11.0", 8 | "@mui/icons-material": "^5.11.16", 9 | "@mui/lab": "^5.0.0-alpha.129", 10 | "@mui/material": "^5.13.0", 11 | "@testing-library/jest-dom": "^5.16.5", 12 | "@testing-library/react": "^13.4.0", 13 | "@testing-library/user-event": "^13.5.0", 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "^16.18.27", 16 | "@types/react": "^18.2.6", 17 | "@types/react-dom": "^18.2.4", 18 | "@visx/scale": "^3.0.0", 19 | "@visx/xychart": "^3.1.2", 20 | "axios": "^1.4.0", 21 | "events": "^3.3.0", 22 | "react": "^18.2.0", 23 | "react-dom": "^18.2.0", 24 | "react-router-dom": "^6.11.1", 25 | "react-scripts": "5.0.1", 26 | "recharts": "^2.6.2", 27 | "typescript": "^4.9.5", 28 | "web-vitals": "^2.1.4" 29 | }, 30 | "scripts": { 31 | "start": "react-scripts start", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test", 34 | "eject": "react-scripts eject", 35 | "lint": "eslint src --ext .ts,.tsx", 36 | "format": "prettier --write \"src/**/*.{ts,tsx}\"" 37 | }, 38 | "eslintConfig": { 39 | "extends": [ 40 | "react-app", 41 | "react-app/jest" 42 | ] 43 | }, 44 | "browserslist": { 45 | "production": [ 46 | ">0.2%", 47 | "not dead", 48 | "not op_mini all" 49 | ], 50 | "development": [ 51 | "last 1 chrome version", 52 | "last 1 firefox version", 53 | "last 1 safari version" 54 | ] 55 | }, 56 | "devDependencies": { 57 | "@typescript-eslint/eslint-plugin": "^5.59.5", 58 | "@typescript-eslint/parser": "^5.59.5", 59 | "eslint": "^8.40.0", 60 | "eslint-config-prettier": "^8.8.0", 61 | "eslint-plugin-prettier": "^4.2.1", 62 | "eslint-plugin-react": "^7.32.2", 63 | "prettier": "^2.8.8" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/ai/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | .npm 4 | 5 | # Build output 6 | dist/ 7 | build/ 8 | 9 | # IDE 10 | .idea/ 11 | .vscode/ 12 | *.swp 13 | *.swo 14 | 15 | # Logs 16 | logs/ 17 | *.log 18 | npm-debug.log* 19 | 20 | # Runtime data 21 | pids/ 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Testing 27 | coverage/ 28 | 29 | # Environment variables 30 | .env 31 | .env.local 32 | .env.*.local 33 | 34 | # Misc 35 | .DS_Store 36 | *.pem 37 | .cache/ -------------------------------------------------------------------------------- /packages/ai/README.md: -------------------------------------------------------------------------------- 1 | # @ciphernx/ai 2 | 3 | Advanced AI module integrating cryptographic and protocol functionalities. 4 | 5 | ## Features 6 | 7 | - Multiple model type support (TensorFlow, PyTorch, Scikit-learn, etc.) 8 | - Data augmentation capabilities 9 | - Model compression and optimization 10 | - Distributed training support 11 | - Model interpretability tools 12 | 13 | ## Installation 14 | 15 | ```bash 16 | npm install @ciphernx/ai 17 | ``` 18 | 19 | Or install from the repository: 20 | 21 | ```bash 22 | npm run setup 23 | ``` 24 | 25 | ## Usage 26 | 27 | See the examples directory for detailed usage examples. Here's a quick start: 28 | 29 | ```typescript 30 | import { ModelFactory, ModelType, TaskType } from '@ciphernx/ai'; 31 | 32 | async function main() { 33 | const model = await ModelFactory.createModel({ 34 | type: ModelType.TENSORFLOW, 35 | path: 'models/classifier', 36 | inputShape: [28, 28, 1], 37 | outputShape: [10], 38 | taskType: TaskType.CLASSIFICATION 39 | }); 40 | 41 | // Train the model 42 | // ... 43 | } 44 | ``` 45 | 46 | ## Development 47 | 48 | 1. Install dependencies: 49 | ```bash 50 | npm run setup 51 | ``` 52 | 53 | 2. Build the package: 54 | ```bash 55 | npm run build 56 | ``` 57 | 58 | 3. Run tests: 59 | ```bash 60 | npm test 61 | ``` 62 | 63 | 4. Run example: 64 | ```bash 65 | npm run example 66 | ``` 67 | 68 | ## License 69 | 70 | MIT -------------------------------------------------------------------------------- /packages/ai/data/training-data.json: -------------------------------------------------------------------------------- 1 | [ 2 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 3 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 4 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 5 | ] -------------------------------------------------------------------------------- /packages/ai/data/training-labels.json: -------------------------------------------------------------------------------- 1 | [0, 1, 2] -------------------------------------------------------------------------------- /packages/ai/docs/api.md: -------------------------------------------------------------------------------- 1 | # AI Package API Documentation 2 | 3 | ## Overview 4 | 5 | The AI package provides core functionality for federated learning, model management, and distributed training. 6 | 7 | ## Core Components 8 | 9 | ### FederatedLearningProtocol 10 | 11 | ```typescript 12 | class FederatedLearningProtocol extends EventEmitter { 13 | constructor(modelConfig: ModelConfig, fedConfig: FederatedConfig); 14 | 15 | async initialize(): Promise; 16 | async registerClient(clientState: ClientState): Promise; 17 | async startTrainingRound(): Promise; 18 | async submitUpdate(clientId: string, update: ModelUpdate): Promise; 19 | getGlobalModel(): Float32Array[] | null; 20 | getRoundStatus(roundId: number): TrainingRound | undefined; 21 | } 22 | ``` 23 | 24 | ### DistributedTrainer 25 | 26 | ```typescript 27 | class DistributedTrainer { 28 | constructor(config: DistributedConfig); 29 | 30 | async train( 31 | model: tf.LayersModel, 32 | data: number[][], 33 | labels: number[][], 34 | epochs: number 35 | ): Promise; 36 | } 37 | ``` 38 | 39 | ### ModelCompressor 40 | 41 | ```typescript 42 | class ModelCompressor { 43 | constructor(config: CompressionConfig); 44 | 45 | async compress(model: tf.LayersModel): Promise; 46 | async quantize(model: tf.LayersModel): Promise; 47 | async prune(model: tf.LayersModel): Promise; 48 | } 49 | ``` 50 | 51 | ### DataProcessor 52 | 53 | ```typescript 54 | class DataProcessor { 55 | static normalizeData(data: number[][]): number[][]; 56 | static standardizeData(data: number[][]): number[][]; 57 | static oneHotEncode(labels: number[], numClasses: number): number[][]; 58 | static processOutput(output: number[], taskType: TaskType): any; 59 | } 60 | ``` 61 | 62 | ## Configuration Types 63 | 64 | ### ModelConfig 65 | 66 | ```typescript 67 | interface ModelConfig { 68 | architecture: string; 69 | hyperparameters: { 70 | learningRate: number; 71 | batchSize: number; 72 | epochs: number; 73 | optimizer: string; 74 | }; 75 | inputShape: number[]; 76 | outputShape: number[]; 77 | } 78 | ``` 79 | 80 | ### FederatedConfig 81 | 82 | ```typescript 83 | interface FederatedConfig { 84 | minClients: number; 85 | roundTimeout: number; 86 | aggregationStrategy: 'FedAvg' | 'FedProx' | 'FedMA'; 87 | clientSelectionStrategy: 'Random' | 'PowerOfChoice' | 'Reputation'; 88 | privacyConfig: { 89 | differentialPrivacy: { 90 | enabled: boolean; 91 | epsilon: number; 92 | delta: number; 93 | }; 94 | secureSummation: { 95 | enabled: boolean; 96 | threshold: number; 97 | }; 98 | }; 99 | } 100 | ``` 101 | 102 | ### DistributedConfig 103 | 104 | ```typescript 105 | interface DistributedConfig { 106 | strategy: DistributedStrategy; 107 | numWorkers: number; 108 | batchSizePerWorker: number; 109 | } 110 | ``` 111 | 112 | ### CompressionConfig 113 | 114 | ```typescript 115 | interface CompressionConfig { 116 | type: CompressionType; 117 | quantizationBits?: number; 118 | pruningThreshold?: number; 119 | accuracy?: number; 120 | } 121 | ``` 122 | 123 | ## Events 124 | 125 | The FederatedLearningProtocol emits the following events: 126 | 127 | - `initialized`: When protocol is initialized 128 | - `clientRegistered`: When a new client is registered 129 | - `roundStarted`: When a training round begins 130 | - `updateReceived`: When a client update is received 131 | - `roundCompleted`: When a training round is completed 132 | - `error`: When an error occurs 133 | 134 | ## Usage Examples 135 | 136 | ### Initialize Federated Learning 137 | 138 | ```typescript 139 | const protocol = new FederatedLearningProtocol(modelConfig, fedConfig); 140 | await protocol.initialize(); 141 | ``` 142 | 143 | ### Start Training Round 144 | 145 | ```typescript 146 | await protocol.startTrainingRound(); 147 | ``` 148 | 149 | ### Submit Client Update 150 | 151 | ```typescript 152 | await protocol.submitUpdate(clientId, update); 153 | ``` 154 | 155 | ### Compress Model 156 | 157 | ```typescript 158 | const compressor = new ModelCompressor({ 159 | type: CompressionType.QUANTIZATION, 160 | quantizationBits: 8 161 | }); 162 | 163 | const compressedModel = await compressor.compress(model); 164 | ``` 165 | 166 | ### Process Data 167 | 168 | ```typescript 169 | const normalizedData = DataProcessor.normalizeData(data); 170 | const encodedLabels = DataProcessor.oneHotEncode(labels, numClasses); 171 | ``` -------------------------------------------------------------------------------- /packages/ai/examples/advanced-training.ts: -------------------------------------------------------------------------------- 1 | import { ModelFactory } from '../src/core/models/model-factory'; 2 | import { 3 | ModelType, 4 | TaskType, 5 | AugmentationType, 6 | CompressionType, 7 | DistributedStrategy 8 | } from '../src/core/types'; 9 | import { DataAugmentor } from '../src/utils/data-augmentor'; 10 | import { ModelCompressor } from '../src/utils/model-compressor'; 11 | import { DistributedTrainer } from '../src/utils/distributed-trainer'; 12 | import { ModelInterpreter } from '../src/utils/model-interpreter'; 13 | import * as fs from 'fs/promises'; 14 | import * as path from 'path'; 15 | 16 | // Data loading functions 17 | async function loadData(): Promise { 18 | try { 19 | const dataPath = path.join(__dirname, '../data/training-data.json'); 20 | const rawData = await fs.readFile(dataPath, 'utf-8'); 21 | return JSON.parse(rawData); 22 | } catch (error) { 23 | console.error('Error loading training data:', error); 24 | return []; 25 | } 26 | } 27 | 28 | async function loadLabels(): Promise { 29 | try { 30 | const labelsPath = path.join(__dirname, '../data/training-labels.json'); 31 | const rawLabels = await fs.readFile(labelsPath, 'utf-8'); 32 | const labels = JSON.parse(rawLabels); 33 | // Convert single labels to one-hot encoded arrays 34 | return labels.map((label: number) => { 35 | const oneHot = new Array(10).fill(0); 36 | oneHot[label] = 1; 37 | return oneHot; 38 | }); 39 | } catch (error) { 40 | console.error('Error loading training labels:', error); 41 | return []; 42 | } 43 | } 44 | 45 | async function main() { 46 | // 1. Create and configure the model 47 | const model = await ModelFactory.createModel({ 48 | type: ModelType.TENSORFLOW, 49 | path: 'models/classifier', 50 | inputShape: [28, 28, 1], 51 | outputShape: [10], 52 | taskType: TaskType.CLASSIFICATION, 53 | encryptionEnabled: true, 54 | compressionConfig: { 55 | type: CompressionType.QUANTIZATION, 56 | quantizationBits: 8 57 | }, 58 | distributedConfig: { 59 | strategy: DistributedStrategy.DATA_PARALLEL, 60 | numWorkers: 4 61 | }, 62 | interpretabilityConfig: { 63 | methods: ['gradientBased', 'layerActivation', 'featureImportance'], 64 | numSamples: 1000 65 | } 66 | }); 67 | 68 | // 2. Load sample data 69 | const data = await loadData(); // Your data loading function 70 | const labels = await loadLabels(); // Your label loading function 71 | 72 | // 3. Configure data augmentation 73 | const augmentor = new DataAugmentor({ 74 | noiseScale: 0.1, 75 | rotationRange: 30, 76 | flipProbability: 0.5, 77 | scaleRange: [0.8, 1.2], 78 | cropSize: 0.8, 79 | mixupAlpha: 0.2, 80 | cutoutSize: 0.2 81 | }); 82 | 83 | // Apply data augmentation 84 | const augmentedData = augmentor.augment(data, [ 85 | AugmentationType.NOISE, 86 | AugmentationType.ROTATION, 87 | AugmentationType.FLIP 88 | ]); 89 | 90 | // 4. Configure distributed training 91 | const trainer = new DistributedTrainer({ 92 | strategy: DistributedStrategy.DATA_PARALLEL, 93 | numWorkers: 4, 94 | batchSizePerWorker: 32 95 | }); 96 | 97 | // Train the model 98 | const trainingMetrics = await trainer.train( 99 | model, 100 | augmentedData, 101 | labels, 102 | 10 // epochs 103 | ); 104 | 105 | console.log('Training metrics:', trainingMetrics); 106 | 107 | // 5. Compress the model 108 | const compressor = new ModelCompressor({ 109 | type: CompressionType.QUANTIZATION, 110 | quantizationBits: 8, 111 | accuracy: 0.95 112 | }); 113 | 114 | const compressedModel = await compressor.compress(model); 115 | console.log('Model compressed successfully'); 116 | 117 | // 6. Interpret model predictions 118 | const interpreter = new ModelInterpreter({ 119 | methods: ['gradientBased', 'layerActivation', 'featureImportance'], 120 | numSamples: 1000, 121 | targetLayers: ['conv1', 'conv2'] 122 | }); 123 | 124 | // Get interpretations for a sample input 125 | const sampleInput = data[0]; 126 | const interpretations = await interpreter.interpret( 127 | compressedModel, 128 | sampleInput, 129 | labels[0] 130 | ); 131 | 132 | console.log('Model interpretations:', interpretations); 133 | 134 | // Save the compressed model 135 | await compressedModel.save('models/compressed-classifier'); 136 | } 137 | 138 | main().catch(console.error); -------------------------------------------------------------------------------- /packages/ai/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cipher-nexus/ai", 3 | "version": "1.0.0", 4 | "main": "dist/index.js", 5 | "types": "dist/index.d.ts", 6 | "scripts": { 7 | "build": "tsc", 8 | "test": "mocha -r ts-node/register 'test/**/*.test.ts'" 9 | }, 10 | "dependencies": { 11 | "@cipher-nexus/protocol": "^1.0.0" 12 | }, 13 | "devDependencies": { 14 | "@types/chai": "^4.3.0", 15 | "@types/mocha": "^9.1.0", 16 | "@types/node": "^17.0.21", 17 | "chai": "^4.3.6", 18 | "mocha": "^9.2.2", 19 | "ts-node": "^10.7.0", 20 | "typescript": "^4.6.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/ai/src/core/privacy.ts: -------------------------------------------------------------------------------- 1 | export class DifferentialPrivacy { 2 | constructor( 3 | private epsilon: number, 4 | private delta: number, 5 | private sensitivity: number 6 | ) {} 7 | 8 | addLaplaceNoise(value: number): number { 9 | // TODO: Implement Laplace noise mechanism 10 | return value; 11 | } 12 | 13 | addGaussianNoise(value: number): number { 14 | // TODO: Implement Gaussian noise mechanism 15 | return value; 16 | } 17 | 18 | computePrivacyBudget(epochs: number): number { 19 | // TODO: Implement privacy budget tracking 20 | return this.epsilon; 21 | } 22 | } 23 | 24 | export interface PrivacyConfig { 25 | encryptionLevel: 'basic' | 'medium' | 'high'; 26 | useHomomorphicEncryption: boolean; 27 | useZeroKnowledgeProof: boolean; 28 | } 29 | 30 | export class PrivacyProtocol { 31 | private config: PrivacyConfig; 32 | 33 | constructor(config: PrivacyConfig) { 34 | this.config = config; 35 | } 36 | 37 | public getConfig(): PrivacyConfig { 38 | return { ...this.config }; 39 | } 40 | 41 | public async encryptData(data: any): Promise { 42 | // TODO: Implement actual encryption logic 43 | return data; 44 | } 45 | 46 | public async decryptData(data: any): Promise { 47 | // TODO: Implement actual decryption logic 48 | return data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/ai/src/core/types.ts: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer'; 2 | 3 | export enum ModelType { 4 | TENSORFLOW = 'tensorflow', 5 | ONNX = 'onnx', 6 | PYTORCH = 'pytorch', 7 | SCIKIT = 'scikit', 8 | XGBOOST = 'xgboost', 9 | LIGHTGBM = 'lightgbm' 10 | } 11 | 12 | export enum TaskType { 13 | CLASSIFICATION = 'classification', 14 | REGRESSION = 'regression', 15 | CLUSTERING = 'clustering', 16 | ANOMALY_DETECTION = 'anomaly_detection', 17 | OBJECT_DETECTION = 'object_detection', 18 | SEGMENTATION = 'segmentation', 19 | NLP = 'nlp' 20 | } 21 | 22 | export enum AugmentationType { 23 | NOISE = 'noise', 24 | ROTATION = 'rotation', 25 | FLIP = 'flip', 26 | SCALE = 'scale', 27 | CROP = 'crop', 28 | MIXUP = 'mixup', 29 | CUTOUT = 'cutout' 30 | } 31 | 32 | export enum CompressionType { 33 | QUANTIZATION = 'quantization', 34 | PRUNING = 'pruning', 35 | KNOWLEDGE_DISTILLATION = 'knowledge_distillation', 36 | WEIGHT_CLUSTERING = 'weight_clustering' 37 | } 38 | 39 | export enum DistributedStrategy { 40 | DATA_PARALLEL = 'data_parallel', 41 | MODEL_PARALLEL = 'model_parallel', 42 | PIPELINE_PARALLEL = 'pipeline_parallel', 43 | FEDERATED = 'federated' 44 | } 45 | 46 | export interface ModelConfig { 47 | layers: { 48 | units: number; 49 | inputDim: number; 50 | }[]; 51 | } 52 | 53 | export interface TrainingConfig { 54 | batchSize: number; 55 | epochs: number; 56 | learningRate: number; 57 | optimizer: string; 58 | loss: string; 59 | metrics: string[]; 60 | validationSplit?: number; 61 | augmentationTypes?: AugmentationType[]; 62 | augmentationConfig?: AugmentationConfig; 63 | distributedStrategy?: DistributedStrategy; 64 | } 65 | 66 | export interface PredictionResult { 67 | output: number[] | number[][]; 68 | confidence?: number; 69 | metadata?: Record; 70 | } 71 | 72 | export interface ModelMetrics { 73 | accuracy?: number; 74 | loss?: number; 75 | precision?: number; 76 | recall?: number; 77 | f1Score?: number; 78 | [key: string]: number | undefined; 79 | } 80 | 81 | export interface EncryptedData { 82 | data: Buffer; 83 | iv: Buffer; 84 | tag?: Buffer; 85 | } 86 | 87 | export interface SecureModelConfig extends ModelConfig { 88 | encryptionKey?: Buffer; 89 | threshold?: number; 90 | participants?: string[]; 91 | } 92 | 93 | export interface ModelState { 94 | weights: number[][][]; 95 | round: number; 96 | metrics: { 97 | accuracy: number; 98 | loss: number; 99 | timestamp: Date; 100 | }; 101 | } 102 | 103 | export interface ModelUpdate { 104 | clientId: string; 105 | weights: number[][][]; 106 | metrics?: { 107 | accuracy: number; 108 | loss: number; 109 | }; 110 | } 111 | 112 | export interface CompressionConfig { 113 | type: CompressionType; 114 | targetSize?: number; 115 | accuracy?: number; 116 | sparsity?: number; 117 | quantizationBits?: number; 118 | teacherModel?: string; 119 | } 120 | 121 | export interface DistributedConfig { 122 | strategy: DistributedStrategy; 123 | numWorkers: number; 124 | workerEndpoints?: string[]; 125 | batchSizePerWorker?: number; 126 | communicationProtocol?: string; 127 | } 128 | 129 | export interface InterpretabilityConfig { 130 | methods: string[]; 131 | numSamples?: number; 132 | targetLayers?: string[]; 133 | visualizationFormat?: string; 134 | } 135 | 136 | export interface AugmentationConfig { 137 | noiseScale?: number; 138 | rotationRange?: number; 139 | flipProbability?: number; 140 | scaleRange?: [number, number]; 141 | cropSize?: number; 142 | mixupAlpha?: number; 143 | cutoutSize?: number; 144 | } 145 | 146 | export interface FederatedConfig { 147 | enablePrivacy: boolean; 148 | maxWeightMagnitude: number; 149 | minClientUpdates: number; 150 | } -------------------------------------------------------------------------------- /packages/ai/src/index.ts: -------------------------------------------------------------------------------- 1 | // Core types 2 | export * from './core/types'; 3 | 4 | // Models 5 | export { BaseModel } from './core/models/base-model'; 6 | export { TensorFlowModel } from './core/models/tensorflow-model'; 7 | export { ONNXModel } from './core/models/onnx-model'; 8 | export { ModelFactory } from './core/models/model-factory'; 9 | 10 | // Utils 11 | export { DataProcessor } from './utils/data-processor'; 12 | export { DataAugmentor } from './utils/data-augmentor'; 13 | export { ModelCompressor } from './utils/model-compressor'; 14 | export { DistributedTrainer } from './utils/distributed-trainer'; 15 | export { ModelInterpreter } from './utils/model-interpreter'; 16 | 17 | export { FederatedLearning } from './core/federated'; 18 | export * from './types'; 19 | -------------------------------------------------------------------------------- /packages/ai/src/models/base.ts: -------------------------------------------------------------------------------- 1 | import { ModelConfig } from '../types'; 2 | 3 | export abstract class BaseModel { 4 | constructor(protected config: ModelConfig) {} 5 | 6 | abstract async train(data: any[], labels: any[]): Promise; 7 | abstract async predict(data: any[]): Promise; 8 | abstract async save(): Promise; 9 | abstract async load(path: string): Promise; 10 | 11 | protected validateConfig(): boolean { 12 | if (!this.config.layers || this.config.layers.length === 0) { 13 | throw new Error('Invalid model configuration: layers not specified'); 14 | } 15 | if (this.config.layers.length !== this.config.activations.length + 1) { 16 | throw new Error('Invalid model configuration: mismatched layers and activations'); 17 | } 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ai/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface ModelConfig { 2 | layers: { 3 | units: number; 4 | inputDim: number; 5 | }[]; 6 | } 7 | 8 | export interface FederatedConfig { 9 | enablePrivacy: boolean; 10 | maxWeightMagnitude: number; 11 | minClientUpdates: number; 12 | } 13 | 14 | export interface ModelState { 15 | weights: number[][][]; 16 | round: number; 17 | metrics: { 18 | accuracy: number; 19 | loss: number; 20 | timestamp: Date; 21 | }; 22 | } 23 | 24 | export interface ModelUpdate { 25 | clientId: string; 26 | weights: number[][][]; 27 | metrics?: { 28 | accuracy: number; 29 | loss: number; 30 | }; 31 | } -------------------------------------------------------------------------------- /packages/ai/src/types/federated.ts: -------------------------------------------------------------------------------- 1 | export interface ModelConfig { 2 | architecture: string; 3 | hyperparameters: { 4 | learningRate: number; 5 | batchSize: number; 6 | epochs: number; 7 | optimizer: string; 8 | }; 9 | inputShape: number[]; 10 | outputShape: number[]; 11 | } 12 | 13 | export interface FederatedConfig { 14 | minClients: number; 15 | roundTimeout: number; 16 | aggregationStrategy: 'FedAvg' | 'FedProx' | 'FedMA'; 17 | clientSelectionStrategy: 'Random' | 'PowerOfChoice' | 'Reputation'; 18 | privacyConfig: { 19 | differentialPrivacy: { 20 | enabled: boolean; 21 | epsilon: number; 22 | delta: number; 23 | }; 24 | secureSummation: { 25 | enabled: boolean; 26 | threshold: number; 27 | }; 28 | }; 29 | } 30 | 31 | export interface ClientState { 32 | clientId: string; 33 | datasetSize: number; 34 | lastUpdate: Date; 35 | computeCapability: { 36 | flops: number; 37 | memory: number; 38 | bandwidth: number; 39 | }; 40 | reputation: number; 41 | status: 'IDLE' | 'TRAINING' | 'AGGREGATING' | 'ERROR'; 42 | } 43 | 44 | export interface ModelUpdate { 45 | clientId: string; 46 | round: number; 47 | weights: Float32Array[]; 48 | metrics: { 49 | loss: number; 50 | accuracy: number; 51 | trainingDuration: number; 52 | }; 53 | timestamp: Date; 54 | } 55 | 56 | export interface TrainingRound { 57 | roundId: number; 58 | startTime: Date; 59 | endTime?: Date; 60 | selectedClients: string[]; 61 | status: 'INITIALIZING' | 'IN_PROGRESS' | 'AGGREGATING' | 'COMPLETED' | 'FAILED'; 62 | updates: ModelUpdate[]; 63 | aggregatedMetrics?: { 64 | globalLoss: number; 65 | globalAccuracy: number; 66 | participationRate: number; 67 | }; 68 | } 69 | 70 | export interface PrivacyMetrics { 71 | epsilon: number; 72 | delta: number; 73 | clipNorm: number; 74 | noiseScale: number; 75 | gradientNorm: number; 76 | } -------------------------------------------------------------------------------- /packages/ai/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface ModelConfig { 2 | architecture: string; 3 | layers: number[]; 4 | activations: string[]; 5 | optimizer: string; 6 | learningRate: number; 7 | } 8 | 9 | export interface FederatedConfig { 10 | roundsPerEpoch: number; 11 | minClients: number; 12 | clientsPerRound: number; 13 | localEpochs: number; 14 | localBatchSize: number; 15 | } 16 | 17 | export interface PrivateTrainingConfig { 18 | modelConfig: ModelConfig; 19 | federatedConfig: FederatedConfig; 20 | privacyBudget: number; 21 | noiseScale: number; 22 | } 23 | -------------------------------------------------------------------------------- /packages/ai/test/federated.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, use } from 'chai'; 2 | import chaiAsPromised from 'chai-as-promised'; 3 | import { FederatedLearning } from '../src/core/federated'; 4 | import { ModelConfig, FederatedConfig } from '../src/types'; 5 | import { PrivacyProtocol } from '../src/core/privacy'; 6 | 7 | use(chaiAsPromised); 8 | 9 | describe('FederatedLearning', () => { 10 | const modelConfig: ModelConfig = { 11 | layers: [ 12 | { units: 10, inputDim: 5 }, 13 | { units: 5, inputDim: 10 }, 14 | { units: 1, inputDim: 5 } 15 | ] 16 | }; 17 | 18 | const fedConfig: FederatedConfig = { 19 | enablePrivacy: true, 20 | maxWeightMagnitude: 10, 21 | minClientUpdates: 2 22 | }; 23 | 24 | let privacyProtocol: PrivacyProtocol; 25 | let federated: FederatedLearning; 26 | 27 | beforeEach(() => { 28 | privacyProtocol = new PrivacyProtocol({ 29 | encryptionLevel: 'basic', 30 | useHomomorphicEncryption: false, 31 | useZeroKnowledgeProof: false 32 | }); 33 | federated = new FederatedLearning(modelConfig, fedConfig, privacyProtocol); 34 | }); 35 | 36 | describe('Model Initialization', () => { 37 | it('should initialize model with random weights', async () => { 38 | await federated.initializeModel(); 39 | const model = await federated.distributeModel(); 40 | 41 | expect(model.weights).to.be.an('array'); 42 | expect(model.weights).to.have.lengthOf(modelConfig.layers.length); 43 | expect(model.round).to.equal(0); 44 | expect(model.metrics).to.have.all.keys(['accuracy', 'loss', 'timestamp']); 45 | }); 46 | }); 47 | 48 | describe('Update Aggregation', () => { 49 | beforeEach(async () => { 50 | await federated.initializeModel(); 51 | }); 52 | 53 | it('should aggregate valid client updates', async () => { 54 | const clientUpdates = [ 55 | { 56 | clientId: 'client1', 57 | weights: [ 58 | [[0.1, 0.2], [0.3, 0.4]], 59 | [[0.5, 0.6]] 60 | ], 61 | metrics: { accuracy: 0.8, loss: 0.2 } 62 | }, 63 | { 64 | clientId: 'client2', 65 | weights: [ 66 | [[0.2, 0.3], [0.4, 0.5]], 67 | [[0.6, 0.7]] 68 | ], 69 | metrics: { accuracy: 0.9, loss: 0.1 } 70 | } 71 | ]; 72 | 73 | const result = await federated.aggregateUpdates(clientUpdates); 74 | expect(result.round).to.equal(1); 75 | expect(result.metrics.accuracy).to.be.closeTo(0.85, 0.01); 76 | expect(result.metrics.loss).to.be.closeTo(0.15, 0.01); 77 | }); 78 | 79 | it('should reject invalid updates', async () => { 80 | const invalidUpdates = [ 81 | { 82 | clientId: 'client1', 83 | weights: [ 84 | [[NaN, 0.2], [0.3, 0.4]], 85 | [[0.5, 0.6]] 86 | ], 87 | metrics: { accuracy: 0.8, loss: 0.2 } 88 | } 89 | ]; 90 | 91 | await expect(federated.aggregateUpdates(invalidUpdates)) 92 | .to.be.rejectedWith(Error); 93 | }); 94 | }); 95 | 96 | describe('Model Distribution', () => { 97 | it('should throw error if model not initialized', async () => { 98 | await expect(federated.distributeModel()) 99 | .to.be.rejectedWith('Global model not initialized'); 100 | }); 101 | 102 | it('should distribute initialized model', async () => { 103 | await federated.initializeModel(); 104 | const model = await federated.distributeModel(); 105 | 106 | expect(model).to.have.all.keys(['weights', 'round', 'metrics']); 107 | expect(model.round).to.equal(0); 108 | }); 109 | }); 110 | }); -------------------------------------------------------------------------------- /packages/ai/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src", 6 | "composite": true 7 | }, 8 | "include": ["src"], 9 | "exclude": ["node_modules", "dist", "test"], 10 | "references": [ 11 | { "path": "../protocol" } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/crypto/docs/api.md: -------------------------------------------------------------------------------- 1 | # Crypto Package API Documentation 2 | 3 | ## Overview 4 | 5 | The Crypto package provides cryptographic primitives and protocols for secure computation and privacy protection. 6 | 7 | ## Core Components 8 | 9 | ### HomomorphicEncryption 10 | 11 | ```typescript 12 | class HomomorphicEncryption { 13 | constructor(config: HomomorphicConfig); 14 | 15 | async initialize(): Promise; 16 | async encrypt(data: number[]): Promise; 17 | async decrypt(data: EncryptedData): Promise; 18 | async add(a: EncryptedData, b: EncryptedData): Promise; 19 | async multiply(a: EncryptedData, b: EncryptedData): Promise; 20 | } 21 | ``` 22 | 23 | ### ZeroKnowledgeProof 24 | 25 | ```typescript 26 | class ZeroKnowledgeProof { 27 | constructor(config: ZKPConfig); 28 | 29 | async generateProof( 30 | statement: any, 31 | witness: any, 32 | context: ProofContext 33 | ): Promise; 34 | 35 | async verifyProof( 36 | proof: Proof, 37 | context: ProofContext 38 | ): Promise; 39 | } 40 | ``` 41 | 42 | ### DifferentialPrivacy 43 | 44 | ```typescript 45 | class DifferentialPrivacy { 46 | constructor(config: DPConfig); 47 | 48 | async addNoise(data: number[]): Promise; 49 | async computeSensitivity(query: Query): Promise; 50 | async verifyPrivacy(epsilon: number, delta: number): Promise; 51 | } 52 | ``` 53 | 54 | ### KeyManager 55 | 56 | ```typescript 57 | class KeyManager { 58 | constructor(config: KeyConfig); 59 | 60 | async generateKeyPair(): Promise; 61 | async storeKey(keyId: string, key: Key): Promise; 62 | async retrieveKey(keyId: string): Promise; 63 | async rotateKey(keyId: string): Promise; 64 | } 65 | ``` 66 | 67 | ## Configuration Types 68 | 69 | ### HomomorphicConfig 70 | 71 | ```typescript 72 | interface HomomorphicConfig { 73 | polyModulusDegree: number; 74 | coeffModulusBits: number[]; 75 | scaleBits: number; 76 | maxThreads?: number; 77 | useGPU?: boolean; 78 | securityLevel: number; 79 | } 80 | ``` 81 | 82 | ### ZKPConfig 83 | 84 | ```typescript 85 | interface ZKPConfig { 86 | securityParameter: number; 87 | numIterations: number; 88 | hashFunction: string; 89 | proofTimeout: number; 90 | maxProofSize: number; 91 | maxConstraints: number; 92 | fieldSize: bigint; 93 | } 94 | ``` 95 | 96 | ### DPConfig 97 | 98 | ```typescript 99 | interface DPConfig { 100 | epsilon: number; 101 | delta: number; 102 | maxGradientNorm: number; 103 | noiseMultiplier: number; 104 | minBatchSize: number; 105 | maxReservedBudget: number; 106 | } 107 | ``` 108 | 109 | ### KeyConfig 110 | 111 | ```typescript 112 | interface KeyConfig { 113 | algorithm: string; 114 | keySize: number; 115 | storageType: 'memory' | 'file' | 'database'; 116 | rotationPeriod: number; 117 | } 118 | ``` 119 | 120 | ## Usage Examples 121 | 122 | ### Homomorphic Encryption 123 | 124 | ```typescript 125 | const encryption = new HomomorphicEncryption({ 126 | polyModulusDegree: 8192, 127 | coeffModulusBits: [60, 40, 40, 60], 128 | scaleBits: 40, 129 | securityLevel: 128 130 | }); 131 | 132 | const encrypted = await encryption.encrypt(data); 133 | const result = await encryption.add(encrypted, encrypted); 134 | const decrypted = await encryption.decrypt(result); 135 | ``` 136 | 137 | ### Zero Knowledge Proof 138 | 139 | ```typescript 140 | const zkp = new ZeroKnowledgeProof({ 141 | securityParameter: 128, 142 | numIterations: 10, 143 | hashFunction: 'sha256' 144 | }); 145 | 146 | const proof = await zkp.generateProof(statement, witness, context); 147 | const isValid = await zkp.verifyProof(proof, context); 148 | ``` 149 | 150 | ### Differential Privacy 151 | 152 | ```typescript 153 | const dp = new DifferentialPrivacy({ 154 | epsilon: 0.1, 155 | delta: 1e-5, 156 | maxGradientNorm: 1.0 157 | }); 158 | 159 | const privatized = await dp.addNoise(data); 160 | ``` 161 | 162 | ### Key Management 163 | 164 | ```typescript 165 | const keyManager = new KeyManager({ 166 | algorithm: 'RSA', 167 | keySize: 2048, 168 | storageType: 'database', 169 | rotationPeriod: 30 * 24 * 60 * 60 // 30 days 170 | }); 171 | 172 | const keyPair = await keyManager.generateKeyPair(); 173 | await keyManager.storeKey('key1', keyPair.publicKey); 174 | ``` -------------------------------------------------------------------------------- /packages/crypto/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | roots: ['/src'], 5 | testMatch: ['**/__tests__/**/*.test.ts'], 6 | collectCoverageFrom: [ 7 | 'src/**/*.ts', 8 | '!src/**/*.d.ts', 9 | '!src/**/__tests__/**' 10 | ], 11 | coverageThreshold: { 12 | global: { 13 | branches: 80, 14 | functions: 80, 15 | lines: 80, 16 | statements: 80 17 | } 18 | } 19 | }; -------------------------------------------------------------------------------- /packages/crypto/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cipher-nexus/crypto", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "test": "mocha -r ts-node/register 'test/**/*.test.ts'", 10 | "test:watch": "jest --watch", 11 | "test:coverage": "jest --coverage", 12 | "lint": "eslint src --ext .ts" 13 | }, 14 | "dependencies": { 15 | "crypto": "^1.0.1" 16 | }, 17 | "devDependencies": { 18 | "@types/chai": "^4.3.0", 19 | "@types/mocha": "^9.1.0", 20 | "@types/node": "^17.0.21", 21 | "chai": "^4.3.6", 22 | "mocha": "^9.2.2", 23 | "ts-node": "^10.7.0", 24 | "typescript": "^4.6.2" 25 | }, 26 | "keywords": [], 27 | "author": "", 28 | "license": "ISC", 29 | "description": "" 30 | } 31 | -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/asymmetric.test.ts: -------------------------------------------------------------------------------- 1 | import { RSA, KeyPair } from '../asymmetric'; 2 | 3 | describe('RSA', () => { 4 | let keyPair: KeyPair; 5 | const testData = Buffer.from('Hello, World!'); 6 | 7 | beforeAll(async () => { 8 | keyPair = await RSA.generateKeyPair(); 9 | }); 10 | 11 | it('should generate key pair with correct format', async () => { 12 | expect(keyPair.publicKey).toBeDefined(); 13 | expect(keyPair.privateKey).toBeDefined(); 14 | expect(typeof keyPair.publicKey).toBe('string'); 15 | expect(typeof keyPair.privateKey).toBe('string'); 16 | expect(keyPair.publicKey).toContain('-----BEGIN PUBLIC KEY-----'); 17 | expect(keyPair.privateKey).toContain('-----BEGIN PRIVATE KEY-----'); 18 | }); 19 | 20 | it('should encrypt and decrypt data correctly', async () => { 21 | const encrypted = await RSA.encrypt(testData, keyPair.publicKey); 22 | const decrypted = await RSA.decrypt(encrypted, keyPair.privateKey); 23 | 24 | expect(decrypted.toString()).toBe(testData.toString()); 25 | }); 26 | 27 | it('should fail to decrypt with wrong private key', async () => { 28 | const encrypted = await RSA.encrypt(testData, keyPair.publicKey); 29 | const wrongKeyPair = await RSA.generateKeyPair(); 30 | 31 | await expect(RSA.decrypt(encrypted, wrongKeyPair.privateKey)).rejects.toThrow(); 32 | }); 33 | 34 | it('should handle small data', async () => { 35 | const smallData = Buffer.from('A'); 36 | const encrypted = await RSA.encrypt(smallData, keyPair.publicKey); 37 | const decrypted = await RSA.decrypt(encrypted, keyPair.privateKey); 38 | 39 | expect(decrypted.toString()).toBe(smallData.toString()); 40 | }); 41 | 42 | it('should fail to encrypt data larger than key size', async () => { 43 | const largeData = Buffer.alloc(RSA.KEY_SIZE / 8); // Data size equals key size 44 | largeData.fill('A'); 45 | 46 | await expect(RSA.encrypt(largeData, keyPair.publicKey)).rejects.toThrow(); 47 | }); 48 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/elgamal.test.ts: -------------------------------------------------------------------------------- 1 | import { ElGamal, ElGamalPublicKey, ElGamalPrivateKey } from '../elgamal'; 2 | import { BigInteger } from 'jsbn'; 3 | 4 | describe('ElGamal Encryption', () => { 5 | let publicKey: ElGamalPublicKey; 6 | let privateKey: ElGamalPrivateKey; 7 | 8 | beforeAll(async () => { 9 | // Generate a smaller key for testing (512 bits instead of 2048) 10 | const keyPair = await ElGamal.generateKeyPair(512); 11 | publicKey = keyPair.publicKey; 12 | privateKey = keyPair.privateKey; 13 | }); 14 | 15 | it('should generate valid key pair', () => { 16 | expect(publicKey.p).toBeDefined(); 17 | expect(publicKey.g).toBeDefined(); 18 | expect(publicKey.h).toBeDefined(); 19 | expect(privateKey.x).toBeDefined(); 20 | 21 | // Verify that h = g^x mod p 22 | expect(publicKey.g.modPow(privateKey.x, publicKey.p).equals(publicKey.h)).toBe(true); 23 | }); 24 | 25 | it('should encrypt and decrypt messages correctly', async () => { 26 | const message = new BigInteger('42'); 27 | const ciphertext = await ElGamal.encrypt(message, publicKey); 28 | const decrypted = await ElGamal.decrypt(ciphertext, privateKey, publicKey); 29 | 30 | expect(decrypted.equals(message)).toBe(true); 31 | }); 32 | 33 | it('should support homomorphic multiplication', () => { 34 | const m1 = new BigInteger('30'); 35 | const m2 = new BigInteger('12'); 36 | 37 | const c1 = ElGamal.encrypt(m1, publicKey); 38 | const c2 = ElGamal.encrypt(m2, publicKey); 39 | 40 | const cProduct = ElGamal.multiply(c1, c2, publicKey); 41 | const decryptedProduct = ElGamal.decrypt(cProduct, privateKey, publicKey); 42 | 43 | // m1 * m2 mod p 44 | const expectedProduct = m1.multiply(m2).mod(publicKey.p); 45 | expect(decryptedProduct.equals(expectedProduct)).toBe(true); 46 | }); 47 | 48 | it('should support exponentiation of ciphertext', () => { 49 | const message = new BigInteger('10'); 50 | const exponent = new BigInteger('3'); 51 | 52 | const ciphertext = ElGamal.encrypt(message, publicKey); 53 | const cPower = ElGamal.power(ciphertext, exponent, publicKey); 54 | const decryptedPower = ElGamal.decrypt(cPower, privateKey, publicKey); 55 | 56 | // message^exponent mod p 57 | const expectedPower = message.modPow(exponent, publicKey.p); 58 | expect(decryptedPower.equals(expectedPower)).toBe(true); 59 | }); 60 | 61 | it('should handle identity element (1) correctly', async () => { 62 | const message = new BigInteger('1'); 63 | const ciphertext = await ElGamal.encrypt(message, publicKey); 64 | const decrypted = await ElGamal.decrypt(ciphertext, privateKey, publicKey); 65 | 66 | expect(decrypted.equals(message)).toBe(true); 67 | }); 68 | 69 | it('should maintain homomorphic properties with multiple operations', () => { 70 | const m1 = new BigInteger('20'); 71 | const m2 = new BigInteger('30'); 72 | const exp = new BigInteger('2'); 73 | 74 | const c1 = ElGamal.encrypt(m1, publicKey); 75 | const c2 = ElGamal.encrypt(m2, publicKey); 76 | 77 | // (m1 * m2)^exp mod p 78 | const cProduct = ElGamal.multiply(c1, c2, publicKey); 79 | const cResult = ElGamal.power(cProduct, exp, publicKey); 80 | const decryptedResult = ElGamal.decrypt(cResult, privateKey, publicKey); 81 | 82 | const expectedResult = m1.multiply(m2).modPow(exp, publicKey.p); 83 | expect(decryptedResult.equals(expectedResult)).toBe(true); 84 | }); 85 | 86 | it('should throw error for invalid key sizes', async () => { 87 | await expect(ElGamal.generateKeyPair(256)).rejects.toThrow(); 88 | }); 89 | 90 | it('should throw error for messages larger than modulus', async () => { 91 | const largeMessage = publicKey.p.add(new BigInteger('1')); 92 | await expect(ElGamal.encrypt(largeMessage, publicKey)).rejects.toThrow(); 93 | }); 94 | 95 | it('should generate different ciphertexts for same message', async () => { 96 | const message = new BigInteger('42'); 97 | const c1 = await ElGamal.encrypt(message, publicKey); 98 | const c2 = await ElGamal.encrypt(message, publicKey); 99 | 100 | // Due to randomization, c1 and c2 should be different 101 | expect(c1.c1.equals(c2.c1)).toBe(false); 102 | expect(c1.c2.equals(c2.c2)).toBe(false); 103 | 104 | // But they should decrypt to the same message 105 | const d1 = await ElGamal.decrypt(c1, privateKey, publicKey); 106 | const d2 = await ElGamal.decrypt(c2, privateKey, publicKey); 107 | expect(d1.equals(d2)).toBe(true); 108 | }); 109 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/hash.test.ts: -------------------------------------------------------------------------------- 1 | import { Hash, Signature } from '../hash'; 2 | import { RSA } from '../asymmetric'; 3 | 4 | describe('Hash', () => { 5 | const testData = Buffer.from('Hello, World!'); 6 | const testKey = Buffer.from('secret-key'); 7 | 8 | it('should generate consistent SHA-256 hashes', async () => { 9 | const hash1 = await Hash.sha256(testData); 10 | const hash2 = await Hash.sha256(testData); 11 | 12 | expect(hash1.equals(hash2)).toBe(true); 13 | }); 14 | 15 | it('should generate different hashes for different data', async () => { 16 | const hash1 = await Hash.sha256(testData); 17 | const hash2 = await Hash.sha256(Buffer.from('Different data')); 18 | 19 | expect(hash1.equals(hash2)).toBe(false); 20 | }); 21 | 22 | it('should generate consistent HMAC values', async () => { 23 | const hmac1 = await Hash.hmac(testData, testKey); 24 | const hmac2 = await Hash.hmac(testData, testKey); 25 | 26 | expect(hmac1.equals(hmac2)).toBe(true); 27 | }); 28 | 29 | it('should generate different HMAC values for different keys', async () => { 30 | const hmac1 = await Hash.hmac(testData, testKey); 31 | const hmac2 = await Hash.hmac(testData, Buffer.from('different-key')); 32 | 33 | expect(hmac1.equals(hmac2)).toBe(false); 34 | }); 35 | }); 36 | 37 | describe('Signature', () => { 38 | const testData = Buffer.from('Hello, World!'); 39 | let keyPair: Awaited>; 40 | 41 | beforeAll(async () => { 42 | keyPair = await RSA.generateKeyPair(); 43 | }); 44 | 45 | it('should sign and verify data correctly', async () => { 46 | const signature = await Signature.sign(testData, keyPair.privateKey); 47 | const isValid = await Signature.verify(testData, signature, keyPair.publicKey); 48 | 49 | expect(isValid).toBe(true); 50 | }); 51 | 52 | it('should fail verification with wrong public key', async () => { 53 | const signature = await Signature.sign(testData, keyPair.privateKey); 54 | const wrongKeyPair = await RSA.generateKeyPair(); 55 | 56 | const isValid = await Signature.verify(testData, signature, wrongKeyPair.publicKey); 57 | expect(isValid).toBe(false); 58 | }); 59 | 60 | it('should fail verification with modified data', async () => { 61 | const signature = await Signature.sign(testData, keyPair.privateKey); 62 | const modifiedData = Buffer.from('Modified data'); 63 | 64 | const isValid = await Signature.verify(modifiedData, signature, keyPair.publicKey); 65 | expect(isValid).toBe(false); 66 | }); 67 | 68 | it('should fail verification with modified signature', async () => { 69 | const signature = await Signature.sign(testData, keyPair.privateKey); 70 | const modifiedSignature = Buffer.from(signature); // Create a copy 71 | modifiedSignature[0] ^= 1; // Modify one bit 72 | 73 | const isValid = await Signature.verify(testData, modifiedSignature, keyPair.publicKey); 74 | expect(isValid).toBe(false); 75 | }); 76 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/homomorphic.test.ts: -------------------------------------------------------------------------------- 1 | import { Paillier, PaillierKeyPair } from '../homomorphic'; 2 | 3 | describe('Paillier Homomorphic Encryption', () => { 4 | let keyPair: PaillierKeyPair; 5 | 6 | beforeAll(async () => { 7 | // Generate a smaller key for testing (512 bits instead of 2048) 8 | keyPair = await Paillier.generateKeyPair(512); 9 | }); 10 | 11 | it('should generate valid key pair', () => { 12 | expect(keyPair.publicKey.n).toBeDefined(); 13 | expect(keyPair.publicKey.g).toBeDefined(); 14 | expect(keyPair.privateKey.lambda).toBeDefined(); 15 | expect(keyPair.privateKey.mu).toBeDefined(); 16 | expect(keyPair.privateKey.p).toBeDefined(); 17 | expect(keyPair.privateKey.q).toBeDefined(); 18 | }); 19 | 20 | it('should encrypt and decrypt numbers correctly', async () => { 21 | const message = 42; 22 | const ciphertext = await Paillier.encrypt(message, keyPair.publicKey); 23 | const decrypted = await Paillier.decrypt(ciphertext, keyPair); 24 | 25 | expect(decrypted).toBe(message); 26 | }); 27 | 28 | it('should support homomorphic addition', async () => { 29 | const m1 = 30; 30 | const m2 = 12; 31 | 32 | const c1 = await Paillier.encrypt(m1, keyPair.publicKey); 33 | const c2 = await Paillier.encrypt(m2, keyPair.publicKey); 34 | 35 | const cSum = await Paillier.add(c1, c2, keyPair.publicKey); 36 | const decryptedSum = await Paillier.decrypt(cSum, keyPair); 37 | 38 | expect(decryptedSum).toBe(m1 + m2); 39 | }); 40 | 41 | it('should support multiplication by constant', async () => { 42 | const message = 10; 43 | const multiplier = 5; 44 | 45 | const ciphertext = await Paillier.encrypt(message, keyPair.publicKey); 46 | const cProduct = await Paillier.multiplyByConstant(ciphertext, multiplier, keyPair.publicKey); 47 | const decryptedProduct = await Paillier.decrypt(cProduct, keyPair); 48 | 49 | expect(decryptedProduct).toBe(message * multiplier); 50 | }); 51 | 52 | it('should handle zero correctly', async () => { 53 | const message = 0; 54 | const ciphertext = await Paillier.encrypt(message, keyPair.publicKey); 55 | const decrypted = await Paillier.decrypt(ciphertext, keyPair); 56 | 57 | expect(decrypted).toBe(0); 58 | }); 59 | 60 | it('should handle negative numbers through modular arithmetic', async () => { 61 | const message = -15; 62 | const ciphertext = await Paillier.encrypt(message, keyPair.publicKey); 63 | const decrypted = await Paillier.decrypt(ciphertext, keyPair); 64 | 65 | expect(decrypted).toBe(message); 66 | }); 67 | 68 | it('should maintain homomorphic properties with multiple operations', async () => { 69 | const m1 = 20; 70 | const m2 = 30; 71 | const m3 = 5; 72 | 73 | const c1 = await Paillier.encrypt(m1, keyPair.publicKey); 74 | const c2 = await Paillier.encrypt(m2, keyPair.publicKey); 75 | const c3 = await Paillier.encrypt(m3, keyPair.publicKey); 76 | 77 | // (m1 + m2) * m3 78 | const cSum = await Paillier.add(c1, c2, keyPair.publicKey); 79 | const cResult = await Paillier.multiplyByConstant(cSum, m3, keyPair.publicKey); 80 | const decryptedResult = await Paillier.decrypt(cResult, keyPair); 81 | 82 | expect(decryptedResult).toBe((m1 + m2) * m3); 83 | }); 84 | 85 | it('should throw error for invalid key sizes', async () => { 86 | await expect(Paillier.generateKeyPair(256)).rejects.toThrow(); 87 | }); 88 | 89 | it('should throw error for invalid message range', async () => { 90 | const message = Number.MAX_SAFE_INTEGER + 1; 91 | await expect(Paillier.encrypt(message, keyPair.publicKey)).rejects.toThrow(); 92 | }); 93 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/kdf.test.ts: -------------------------------------------------------------------------------- 1 | import { KDF } from '../kdf'; 2 | 3 | describe('KDF', () => { 4 | it('should derive different keys for different passwords', async () => { 5 | const password1 = 'password1'; 6 | const password2 = 'password2'; 7 | 8 | const result1 = await KDF.deriveKey(password1); 9 | const result2 = await KDF.deriveKey(password2); 10 | 11 | // Keys should be different even with same parameters 12 | expect(result1.key.equals(result2.key)).toBe(false); 13 | // Salts should be different 14 | expect(result1.salt.equals(result2.salt)).toBe(false); 15 | }); 16 | 17 | it('should derive same key for same password and salt', async () => { 18 | const password = 'testpassword'; 19 | const { key, salt } = await KDF.deriveKey(password); 20 | 21 | // Verify the password 22 | const isValid = await KDF.verifyKey(password, key, salt); 23 | expect(isValid).toBe(true); 24 | }); 25 | 26 | it('should reject incorrect passwords', async () => { 27 | const password = 'correctpassword'; 28 | const wrongPassword = 'wrongpassword'; 29 | const { key, salt } = await KDF.deriveKey(password); 30 | 31 | // Verify with wrong password 32 | const isValid = await KDF.verifyKey(wrongPassword, key, salt); 33 | expect(isValid).toBe(false); 34 | }); 35 | 36 | it('should support custom key lengths', async () => { 37 | const password = 'testpassword'; 38 | const keyLength = 64; // 512 bits 39 | 40 | const { key } = await KDF.deriveKey(password, keyLength); 41 | expect(key.length).toBe(keyLength); 42 | }); 43 | 44 | it('should support custom iteration counts', async () => { 45 | const password = 'testpassword'; 46 | const iterations = 1000; 47 | 48 | // This just verifies the function runs with custom iterations 49 | const { key, salt } = await KDF.deriveKey(password, undefined, iterations); 50 | const isValid = await KDF.verifyKey(password, key, salt, undefined, iterations); 51 | expect(isValid).toBe(true); 52 | }); 53 | 54 | it('should reject keys of different lengths', async () => { 55 | const password = 'testpassword'; 56 | const { key: key1 } = await KDF.deriveKey(password, 32); 57 | const { key: key2 } = await KDF.deriveKey(password, 64); 58 | 59 | // Verify with key of different length 60 | const isValid = await KDF.verifyKey(password, key1, key2); 61 | expect(isValid).toBe(false); 62 | }); 63 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/symmetric.test.ts: -------------------------------------------------------------------------------- 1 | import { AES, SymmetricKey } from '../symmetric'; 2 | 3 | describe('AES', () => { 4 | let key: SymmetricKey; 5 | const testData = Buffer.from('Hello, World!'); 6 | 7 | beforeAll(async () => { 8 | key = await AES.generateKey(); 9 | }); 10 | 11 | it('should generate key with correct sizes', async () => { 12 | expect(key.key.length).toBe(AES.KEY_SIZE); 13 | expect(key.iv.length).toBe(AES.IV_SIZE); 14 | }); 15 | 16 | it('should encrypt and decrypt data correctly', async () => { 17 | const encrypted = await AES.encrypt(testData, key); 18 | const decrypted = await AES.decrypt(encrypted, key.key); 19 | 20 | expect(decrypted.toString()).toBe(testData.toString()); 21 | }); 22 | 23 | it('should fail to decrypt with wrong key', async () => { 24 | const encrypted = await AES.encrypt(testData, key); 25 | const wrongKey = Buffer.alloc(AES.KEY_SIZE); 26 | 27 | await expect(AES.decrypt(encrypted, wrongKey)).rejects.toThrow(); 28 | }); 29 | 30 | it('should handle empty data', async () => { 31 | const emptyData = Buffer.from(''); 32 | const encrypted = await AES.encrypt(emptyData, key); 33 | const decrypted = await AES.decrypt(encrypted, key.key); 34 | 35 | expect(decrypted.length).toBe(0); 36 | }); 37 | 38 | it('should handle large data', async () => { 39 | const largeData = Buffer.alloc(1024 * 1024); // 1MB 40 | largeData.fill('A'); 41 | 42 | const encrypted = await AES.encrypt(largeData, key); 43 | const decrypted = await AES.decrypt(encrypted, key.key); 44 | 45 | expect(decrypted.toString()).toBe(largeData.toString()); 46 | }); 47 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/__tests__/zkp.test.ts: -------------------------------------------------------------------------------- 1 | import { Schnorr, SchnorrParams } from '../zkp'; 2 | import { BigInteger } from 'jsbn'; 3 | 4 | describe('Schnorr', () => { 5 | let params: SchnorrParams; 6 | const secretValue = new BigInteger('42'); // Secret discrete logarithm 7 | 8 | beforeAll(async () => { 9 | // Use smaller parameters for faster tests 10 | params = await Schnorr.generateParams(512); 11 | }); 12 | 13 | it('should generate valid group parameters', () => { 14 | const { p, q, g } = params; 15 | 16 | // Check that p and q are defined 17 | expect(p).toBeDefined(); 18 | expect(q).toBeDefined(); 19 | expect(g).toBeDefined(); 20 | 21 | // Check that p = 2q + 1 22 | expect(q.multiply(new BigInteger('2')).add(new BigInteger('1')).equals(p)).toBe(true); 23 | 24 | // Check that g^q mod p = 1 25 | expect(g.modPow(q, p).equals(new BigInteger('1'))).toBe(true); 26 | }); 27 | 28 | it('should generate and verify valid proofs', async () => { 29 | const { p, g } = params; 30 | 31 | // Compute public value h = g^x mod p 32 | const h = g.modPow(secretValue, p); 33 | 34 | // Generate proof 35 | const proof = await Schnorr.prove(secretValue, params); 36 | 37 | // Verify proof 38 | const isValid = await Schnorr.verify(h, proof, params); 39 | expect(isValid).toBe(true); 40 | }); 41 | 42 | it('should reject proofs with wrong secret', async () => { 43 | const { p, g } = params; 44 | const wrongSecret = new BigInteger('43'); 45 | 46 | // Compute public value h = g^x mod p 47 | const h = g.modPow(secretValue, p); 48 | 49 | // Generate proof with wrong secret 50 | const proof = await Schnorr.prove(wrongSecret, params); 51 | 52 | // Verify proof 53 | const isValid = await Schnorr.verify(h, proof, params); 54 | expect(isValid).toBe(false); 55 | }); 56 | 57 | it('should reject proofs with modified commitment', async () => { 58 | const { p, g } = params; 59 | 60 | // Compute public value h = g^x mod p 61 | const h = g.modPow(secretValue, p); 62 | 63 | // Generate proof 64 | const proof = await Schnorr.prove(secretValue, params); 65 | 66 | // Modify commitment 67 | const modifiedProof = { 68 | ...proof, 69 | commitment: proof.commitment.add(new BigInteger('1')) 70 | }; 71 | 72 | // Verify modified proof 73 | const isValid = await Schnorr.verify(h, modifiedProof, params); 74 | expect(isValid).toBe(false); 75 | }); 76 | 77 | it('should reject proofs with modified challenge', async () => { 78 | const { p, g } = params; 79 | 80 | // Compute public value h = g^x mod p 81 | const h = g.modPow(secretValue, p); 82 | 83 | // Generate proof 84 | const proof = await Schnorr.prove(secretValue, params); 85 | 86 | // Modify challenge 87 | const modifiedProof = { 88 | ...proof, 89 | challenge: proof.challenge.add(new BigInteger('1')) 90 | }; 91 | 92 | // Verify modified proof 93 | const isValid = await Schnorr.verify(h, modifiedProof, params); 94 | expect(isValid).toBe(false); 95 | }); 96 | 97 | it('should reject proofs with modified response', async () => { 98 | const { p, g } = params; 99 | 100 | // Compute public value h = g^x mod p 101 | const h = g.modPow(secretValue, p); 102 | 103 | // Generate proof 104 | const proof = await Schnorr.prove(secretValue, params); 105 | 106 | // Modify response 107 | const modifiedProof = { 108 | ...proof, 109 | response: proof.response.add(new BigInteger('1')) 110 | }; 111 | 112 | // Verify modified proof 113 | const isValid = await Schnorr.verify(h, modifiedProof, params); 114 | expect(isValid).toBe(false); 115 | }); 116 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/asymmetric.ts: -------------------------------------------------------------------------------- 1 | import { generateKeyPairSync, publicEncrypt, privateDecrypt, constants } from 'crypto'; 2 | 3 | export interface KeyPair { 4 | publicKey: string; 5 | privateKey: string; 6 | } 7 | 8 | export class RSA { 9 | static readonly KEY_SIZE = 2048; 10 | 11 | /** 12 | * Generate a new RSA key pair 13 | * @returns {Promise} Generated public and private keys in PEM format 14 | */ 15 | static async generateKeyPair(): Promise { 16 | const { publicKey, privateKey } = generateKeyPairSync('rsa', { 17 | modulusLength: RSA.KEY_SIZE, 18 | publicKeyEncoding: { 19 | type: 'spki', 20 | format: 'pem' 21 | }, 22 | privateKeyEncoding: { 23 | type: 'pkcs8', 24 | format: 'pem' 25 | } 26 | }); 27 | 28 | return { 29 | publicKey, 30 | privateKey 31 | }; 32 | } 33 | 34 | /** 35 | * Encrypt data using RSA-OAEP 36 | * @param {Buffer} data - Data to encrypt 37 | * @param {string} publicKey - Public key in PEM format 38 | * @returns {Promise} Encrypted data 39 | */ 40 | static async encrypt(data: Buffer, publicKey: string): Promise { 41 | return publicEncrypt( 42 | { 43 | key: publicKey, 44 | padding: constants.RSA_PKCS1_OAEP_PADDING, 45 | oaepHash: 'sha256' 46 | }, 47 | data 48 | ); 49 | } 50 | 51 | /** 52 | * Decrypt data using RSA-OAEP 53 | * @param {Buffer} encryptedData - Data to decrypt 54 | * @param {string} privateKey - Private key in PEM format 55 | * @returns {Promise} Decrypted data 56 | */ 57 | static async decrypt(encryptedData: Buffer, privateKey: string): Promise { 58 | return privateDecrypt( 59 | { 60 | key: privateKey, 61 | padding: constants.RSA_PKCS1_OAEP_PADDING, 62 | oaepHash: 'sha256' 63 | }, 64 | encryptedData 65 | ); 66 | } 67 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/encryption.ts: -------------------------------------------------------------------------------- 1 | import { EncryptionConfig, EncryptionKey, EncryptedData } from '../types'; 2 | import { randomBytes, createCipheriv, createDecipheriv, generateKeyPairSync } from 'crypto'; 3 | 4 | export class Encryption { 5 | constructor(private config: EncryptionConfig) {} 6 | 7 | async generateKeys(): Promise { 8 | switch (this.config.algorithm) { 9 | case 'RSA': 10 | return this.generateRSAKeys(); 11 | case 'AES': 12 | return this.generateAESKeys(); 13 | case 'FHE': 14 | return this.generateFHEKeys(); 15 | default: 16 | throw new Error(`Unsupported algorithm: ${this.config.algorithm}`); 17 | } 18 | } 19 | 20 | async encrypt(data: any, key: string): Promise { 21 | switch (this.config.algorithm) { 22 | case 'RSA': 23 | return this.encryptRSA(data, key); 24 | case 'AES': 25 | return this.encryptAES(data, key); 26 | case 'FHE': 27 | return this.encryptFHE(data, key); 28 | default: 29 | throw new Error(`Unsupported algorithm: ${this.config.algorithm}`); 30 | } 31 | } 32 | 33 | async decrypt(encryptedData: EncryptedData, key: string): Promise { 34 | switch (this.config.algorithm) { 35 | case 'RSA': 36 | return this.decryptRSA(encryptedData, key); 37 | case 'AES': 38 | return this.decryptAES(encryptedData, key); 39 | case 'FHE': 40 | return this.decryptFHE(encryptedData, key); 41 | default: 42 | throw new Error(`Unsupported algorithm: ${this.config.algorithm}`); 43 | } 44 | } 45 | 46 | private generateRSAKeys(): EncryptionKey { 47 | const { publicKey, privateKey } = generateKeyPairSync('rsa', { 48 | modulusLength: this.config.keySize, 49 | publicKeyEncoding: { 50 | type: 'spki', 51 | format: 'pem' 52 | }, 53 | privateKeyEncoding: { 54 | type: 'pkcs8', 55 | format: 'pem' 56 | } 57 | }); 58 | 59 | return { 60 | publicKey, 61 | privateKey 62 | }; 63 | } 64 | 65 | private generateAESKeys(): EncryptionKey { 66 | const key = randomBytes(this.config.keySize / 8).toString('hex'); 67 | return { 68 | publicKey: key, 69 | privateKey: key 70 | }; 71 | } 72 | 73 | private generateFHEKeys(): EncryptionKey { 74 | // TODO: Implement FHE key generation 75 | throw new Error('FHE key generation not implemented yet'); 76 | } 77 | 78 | private async encryptRSA(data: any, publicKey: string): Promise { 79 | const { publicEncrypt } = await import('crypto'); 80 | const buffer = Buffer.from(JSON.stringify(data)); 81 | const encrypted = publicEncrypt(publicKey, buffer); 82 | 83 | return { 84 | data: encrypted.toString('base64') 85 | }; 86 | } 87 | 88 | private async encryptAES(data: any, key: string): Promise { 89 | const iv = randomBytes(16); 90 | const cipher = createCipheriv( 91 | `aes-${this.config.keySize}-${this.config.mode || 'gcm'}`, 92 | Buffer.from(key, 'hex'), 93 | iv 94 | ); 95 | 96 | const buffer = Buffer.from(JSON.stringify(data)); 97 | const encrypted = Buffer.concat([cipher.update(buffer), cipher.final()]); 98 | const tag = cipher.getAuthTag(); 99 | 100 | return { 101 | data: encrypted.toString('base64'), 102 | iv: iv.toString('hex'), 103 | tag: tag.toString('hex') 104 | }; 105 | } 106 | 107 | private async encryptFHE(data: any, key: string): Promise { 108 | // TODO: Implement FHE encryption 109 | throw new Error('FHE encryption not implemented yet'); 110 | } 111 | 112 | private async decryptRSA(encryptedData: EncryptedData, privateKey: string): Promise { 113 | const { privateDecrypt } = await import('crypto'); 114 | const buffer = Buffer.from(encryptedData.data, 'base64'); 115 | const decrypted = privateDecrypt(privateKey, buffer); 116 | 117 | return JSON.parse(decrypted.toString()); 118 | } 119 | 120 | private async decryptAES(encryptedData: EncryptedData, key: string): Promise { 121 | if (!encryptedData.iv || !encryptedData.tag) { 122 | throw new Error('Missing IV or authentication tag for AES decryption'); 123 | } 124 | 125 | const decipher = createDecipheriv( 126 | `aes-${this.config.keySize}-${this.config.mode || 'gcm'}`, 127 | Buffer.from(key, 'hex'), 128 | Buffer.from(encryptedData.iv, 'hex') 129 | ); 130 | 131 | decipher.setAuthTag(Buffer.from(encryptedData.tag, 'hex')); 132 | 133 | const buffer = Buffer.from(encryptedData.data, 'base64'); 134 | const decrypted = Buffer.concat([decipher.update(buffer), decipher.final()]); 135 | 136 | return JSON.parse(decrypted.toString()); 137 | } 138 | 139 | private async decryptFHE(encryptedData: EncryptedData, key: string): Promise { 140 | // TODO: Implement FHE decryption 141 | throw new Error('FHE decryption not implemented yet'); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /packages/crypto/src/core/hash.ts: -------------------------------------------------------------------------------- 1 | import { createHash, createHmac, createSign, createVerify } from 'crypto'; 2 | 3 | export class Hash { 4 | /** 5 | * Compute SHA-256 hash of data 6 | * @param {Buffer} data - Data to hash 7 | * @returns {Promise} Hash value 8 | */ 9 | static async sha256(data: Buffer): Promise { 10 | return createHash('sha256').update(data).digest(); 11 | } 12 | 13 | /** 14 | * Compute HMAC using SHA-256 15 | * @param {Buffer} data - Data to authenticate 16 | * @param {Buffer} key - HMAC key 17 | * @returns {Promise} HMAC value 18 | */ 19 | static async hmac(data: Buffer, key: Buffer): Promise { 20 | return createHmac('sha256', key).update(data).digest(); 21 | } 22 | } 23 | 24 | export class Signature { 25 | /** 26 | * Sign data using RSA-SHA256 27 | * @param {Buffer} data - Data to sign 28 | * @param {string} privateKey - Private key in PEM format 29 | * @returns {Promise} Signature 30 | */ 31 | static async sign(data: Buffer, privateKey: string): Promise { 32 | const signer = createSign('SHA256'); 33 | signer.update(data); 34 | return signer.sign(privateKey); 35 | } 36 | 37 | /** 38 | * Verify RSA-SHA256 signature 39 | * @param {Buffer} data - Original data 40 | * @param {Buffer} signature - Signature to verify 41 | * @param {string} publicKey - Public key in PEM format 42 | * @returns {Promise} Whether signature is valid 43 | */ 44 | static async verify(data: Buffer, signature: Buffer, publicKey: string): Promise { 45 | const verifier = createVerify('SHA256'); 46 | verifier.update(data); 47 | return verifier.verify(publicKey, signature); 48 | } 49 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/homomorphic/crt.ts: -------------------------------------------------------------------------------- 1 | export class CRT { 2 | private primes: bigint[]; 3 | private N: bigint; 4 | private Ni: bigint[]; 5 | private yi: bigint[]; 6 | 7 | constructor(primes: bigint[]) { 8 | this.primes = primes; 9 | this.N = primes.reduce((a, b) => a * b, 1n); 10 | this.Ni = primes.map(p => this.N / p); 11 | this.yi = this.Ni.map((Ni, i) => this.modInverse(Ni, this.primes[i])); 12 | } 13 | 14 | /** 15 | * Convert a number to its CRT representation 16 | */ 17 | public toCRT(x: bigint): bigint[] { 18 | return this.primes.map(p => x % p); 19 | } 20 | 21 | /** 22 | * Convert from CRT representation back to normal form 23 | */ 24 | public fromCRT(remainders: bigint[]): bigint { 25 | if (remainders.length !== this.primes.length) { 26 | throw new Error('Invalid number of remainders'); 27 | } 28 | 29 | let result = 0n; 30 | for (let i = 0; i < this.primes.length; i++) { 31 | const term = (remainders[i] * this.Ni[i] * this.yi[i]) % this.N; 32 | result = (result + term) % this.N; 33 | } 34 | return result; 35 | } 36 | 37 | /** 38 | * Add two numbers in CRT representation 39 | */ 40 | public addCRT(a: bigint[], b: bigint[]): bigint[] { 41 | return a.map((ai, i) => (ai + b[i]) % this.primes[i]); 42 | } 43 | 44 | /** 45 | * Multiply two numbers in CRT representation 46 | */ 47 | public multiplyCRT(a: bigint[], b: bigint[]): bigint[] { 48 | return a.map((ai, i) => (ai * b[i]) % this.primes[i]); 49 | } 50 | 51 | /** 52 | * Convert a polynomial to CRT representation 53 | */ 54 | public polyToCRT(poly: bigint[]): bigint[][] { 55 | return poly.map(coeff => this.toCRT(coeff)); 56 | } 57 | 58 | /** 59 | * Convert a polynomial from CRT representation 60 | */ 61 | public polyFromCRT(crtPoly: bigint[][]): bigint[] { 62 | return crtPoly.map(coeffCRT => this.fromCRT(coeffCRT)); 63 | } 64 | 65 | /** 66 | * Multiply polynomials in CRT representation 67 | */ 68 | public polyMultiplyCRT(a: bigint[][], b: bigint[][]): bigint[][] { 69 | const degree = a.length + b.length - 1; 70 | const result: bigint[][] = Array(degree).fill(0).map(() => 71 | Array(this.primes.length).fill(0n) 72 | ); 73 | 74 | for (let i = 0; i < a.length; i++) { 75 | for (let j = 0; j < b.length; j++) { 76 | const product = this.multiplyCRT(a[i], b[j]); 77 | for (let k = 0; k < this.primes.length; k++) { 78 | result[i + j][k] = (result[i + j][k] + product[k]) % this.primes[k]; 79 | } 80 | } 81 | } 82 | 83 | return result; 84 | } 85 | 86 | private modInverse(a: bigint, m: bigint): bigint { 87 | let [old_r, r] = [a, m]; 88 | let [old_s, s] = [1n, 0n]; 89 | 90 | while (r !== 0n) { 91 | const quotient = old_r / r; 92 | [old_r, r] = [r, old_r - quotient * r]; 93 | [old_s, s] = [s, old_s - quotient * s]; 94 | } 95 | 96 | return (old_s % m + m) % m; 97 | } 98 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/homomorphic/ntt.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes } from 'crypto'; 2 | 3 | export class NTT { 4 | private n: number; 5 | private q: bigint; 6 | private w: bigint; 7 | private w_inv: bigint; 8 | private n_inv: bigint; 9 | 10 | constructor(n: number, q: bigint) { 11 | this.n = n; 12 | this.q = q; 13 | // Find primitive nth root of unity modulo q 14 | this.w = this.findPrimitiveRoot(); 15 | this.w_inv = this.modInverse(this.w); 16 | this.n_inv = this.modInverse(BigInt(n)); 17 | } 18 | 19 | // Forward NTT 20 | public transform(poly: bigint[]): bigint[] { 21 | const result = new Array(this.n).fill(0n); 22 | let w_pow = 1n; 23 | 24 | for (let k = 0; k < this.n; k++) { 25 | let sum = 0n; 26 | for (let i = 0; i < this.n; i++) { 27 | sum = (sum + poly[i] * this.modPow(this.w, BigInt(i * k))) % this.q; 28 | } 29 | result[k] = sum; 30 | } 31 | 32 | return result; 33 | } 34 | 35 | // Inverse NTT 36 | public inverseTransform(poly: bigint[]): bigint[] { 37 | const result = new Array(this.n).fill(0n); 38 | 39 | for (let k = 0; k < this.n; k++) { 40 | let sum = 0n; 41 | for (let i = 0; i < this.n; i++) { 42 | sum = (sum + poly[i] * this.modPow(this.w_inv, BigInt(i * k))) % this.q; 43 | } 44 | result[k] = (sum * this.n_inv) % this.q; 45 | } 46 | 47 | return result; 48 | } 49 | 50 | // Polynomial multiplication using NTT 51 | public multiply(a: bigint[], b: bigint[]): bigint[] { 52 | const a_ntt = this.transform(a); 53 | const b_ntt = this.transform(b); 54 | 55 | // Point-wise multiplication in NTT domain 56 | const prod_ntt = a_ntt.map((x, i) => (x * b_ntt[i]) % this.q); 57 | 58 | // Transform back 59 | return this.inverseTransform(prod_ntt); 60 | } 61 | 62 | // Helper methods 63 | private modPow(base: bigint, exp: bigint): bigint { 64 | let result = 1n; 65 | base = base % this.q; 66 | while (exp > 0n) { 67 | if (exp & 1n) { 68 | result = (result * base) % this.q; 69 | } 70 | base = (base * base) % this.q; 71 | exp >>= 1n; 72 | } 73 | return result; 74 | } 75 | 76 | private modInverse(a: bigint): bigint { 77 | return this.modPow(a, this.q - 2n); // Using Fermat's little theorem 78 | } 79 | 80 | private findPrimitiveRoot(): bigint { 81 | // Find a primitive nth root of unity 82 | // For simplicity, we assume q is a prime where q ≡ 1 (mod 2n) 83 | let w = 2n; 84 | while (this.modPow(w, BigInt(this.n)) !== 1n || 85 | this.modPow(w, BigInt(this.n / 2)) === 1n) { 86 | w += 1n; 87 | } 88 | return w; 89 | } 90 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/kdf.ts: -------------------------------------------------------------------------------- 1 | import { pbkdf2, randomBytes } from 'crypto'; 2 | import { promisify } from 'util'; 3 | 4 | const pbkdf2Async = promisify(pbkdf2); 5 | 6 | export class KDF { 7 | private static readonly DEFAULT_ITERATIONS = 100000; 8 | private static readonly DEFAULT_KEY_LENGTH = 32; // 256 bits 9 | private static readonly SALT_LENGTH = 16; // 128 bits 10 | 11 | /** 12 | * Generate a cryptographic key from a password using PBKDF2 13 | * @param {string} password - The password to derive key from 14 | * @param {number} keyLength - Length of the derived key in bytes (default: 32) 15 | * @param {number} iterations - Number of iterations (default: 100000) 16 | * @returns {Promise<{key: Buffer, salt: Buffer}>} Derived key and salt 17 | */ 18 | static async deriveKey( 19 | password: string, 20 | keyLength: number = this.DEFAULT_KEY_LENGTH, 21 | iterations: number = this.DEFAULT_ITERATIONS 22 | ): Promise<{ key: Buffer; salt: Buffer }> { 23 | // Generate random salt 24 | const salt = randomBytes(this.SALT_LENGTH); 25 | 26 | // Derive key using PBKDF2-SHA256 27 | const key = await pbkdf2Async( 28 | password, 29 | salt, 30 | iterations, 31 | keyLength, 32 | 'sha256' 33 | ); 34 | 35 | return { key, salt }; 36 | } 37 | 38 | /** 39 | * Verify a password against a known key and salt 40 | * @param {string} password - The password to verify 41 | * @param {Buffer} key - The known key to verify against 42 | * @param {Buffer} salt - The salt used to derive the known key 43 | * @param {number} keyLength - Length of the derived key in bytes (default: 32) 44 | * @param {number} iterations - Number of iterations (default: 100000) 45 | * @returns {Promise} Whether the password is correct 46 | */ 47 | static async verifyKey( 48 | password: string, 49 | key: Buffer, 50 | salt: Buffer, 51 | keyLength: number = this.DEFAULT_KEY_LENGTH, 52 | iterations: number = this.DEFAULT_ITERATIONS 53 | ): Promise { 54 | // Derive key using the same parameters 55 | const derivedKey = await pbkdf2Async( 56 | password, 57 | salt, 58 | iterations, 59 | keyLength, 60 | 'sha256' 61 | ); 62 | 63 | // Compare keys in constant time 64 | if (key.length !== derivedKey.length) { 65 | return false; 66 | } 67 | 68 | let diff = 0; 69 | for (let i = 0; i < key.length; i++) { 70 | diff |= key[i] ^ derivedKey[i]; 71 | } 72 | return diff === 0; 73 | } 74 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/mac/hmac.ts: -------------------------------------------------------------------------------- 1 | import { createHmac } from 'crypto'; 2 | 3 | export enum HashAlgorithm { 4 | SHA256 = 'sha256', 5 | SHA384 = 'sha384', 6 | SHA512 = 'sha512' 7 | } 8 | 9 | export class HMAC { 10 | private key: Buffer; 11 | private algorithm: HashAlgorithm; 12 | private hmac: any; 13 | 14 | constructor(key: Buffer, algorithm: HashAlgorithm = HashAlgorithm.SHA256) { 15 | this.key = key; 16 | this.algorithm = algorithm; 17 | this.hmac = createHmac(algorithm, key); 18 | } 19 | 20 | public static generate(data: Buffer, key: Buffer, algorithm: HashAlgorithm = HashAlgorithm.SHA256): Buffer { 21 | const hmac = createHmac(algorithm, key); 22 | hmac.update(data); 23 | return hmac.digest(); 24 | } 25 | 26 | public static verify(data: Buffer, mac: Buffer, key: Buffer, algorithm: HashAlgorithm = HashAlgorithm.SHA256): boolean { 27 | const expectedMac = HMAC.generate(data, key, algorithm); 28 | return mac.length === expectedMac.length && 29 | Buffer.compare(mac, expectedMac) === 0; 30 | } 31 | 32 | public update(data: Buffer): void { 33 | this.hmac.update(data); 34 | } 35 | 36 | public digest(): Buffer { 37 | return this.hmac.digest(); 38 | } 39 | 40 | public static deriveKey(key: Buffer, salt: Buffer, info: Buffer, length: number): Buffer { 41 | const hmac = createHmac('sha256', key); 42 | hmac.update(salt); 43 | hmac.update(info); 44 | return hmac.digest().slice(0, length); 45 | } 46 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/symmetric.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes, createCipheriv, createDecipheriv } from 'crypto'; 2 | 3 | export interface SymmetricKey { 4 | key: Buffer; 5 | iv: Buffer; 6 | } 7 | 8 | export class AES { 9 | static readonly KEY_SIZE = 32; // 256 bits 10 | static readonly IV_SIZE = 16; // 128 bits 11 | 12 | /** 13 | * Generate a new AES key 14 | * @returns {Promise} Generated key and IV 15 | */ 16 | static async generateKey(): Promise { 17 | return { 18 | key: randomBytes(AES.KEY_SIZE), 19 | iv: randomBytes(AES.IV_SIZE) 20 | }; 21 | } 22 | 23 | /** 24 | * Encrypt data using AES-256-GCM 25 | * @param {Buffer} data - Data to encrypt 26 | * @param {SymmetricKey} key - Encryption key 27 | * @returns {Promise} Encrypted data 28 | */ 29 | static async encrypt(data: Buffer, { key, iv }: SymmetricKey): Promise { 30 | const cipher = createCipheriv('aes-256-gcm', key, iv); 31 | const encrypted = Buffer.concat([cipher.update(data), cipher.final()]); 32 | const authTag = cipher.getAuthTag(); 33 | 34 | // Combine IV, encrypted data, and auth tag 35 | return Buffer.concat([iv, encrypted, authTag]); 36 | } 37 | 38 | /** 39 | * Decrypt data using AES-256-GCM 40 | * @param {Buffer} encryptedData - Data to decrypt 41 | * @param {Buffer} key - Decryption key 42 | * @returns {Promise} Decrypted data 43 | */ 44 | static async decrypt(encryptedData: Buffer, key: Buffer): Promise { 45 | const iv = encryptedData.subarray(0, AES.IV_SIZE); 46 | const authTag = encryptedData.subarray(encryptedData.length - 16); 47 | const data = encryptedData.subarray(AES.IV_SIZE, encryptedData.length - 16); 48 | 49 | const decipher = createDecipheriv('aes-256-gcm', key, iv); 50 | decipher.setAuthTag(authTag); 51 | 52 | return Buffer.concat([decipher.update(data), decipher.final()]); 53 | } 54 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/symmetric/aes.ts: -------------------------------------------------------------------------------- 1 | import { createCipheriv, createDecipheriv, CipherGCM, DecipherGCM } from 'crypto'; 2 | 3 | export enum AESMode { 4 | CBC = 'aes-256-cbc', 5 | GCM = 'aes-256-gcm' 6 | } 7 | 8 | export interface AESParams { 9 | key: Buffer; 10 | iv: Buffer; 11 | mode: AESMode; 12 | } 13 | 14 | export class AES { 15 | private params: AESParams; 16 | private cipher: CipherGCM | any; 17 | 18 | constructor(params: AESParams) { 19 | this.validateParams(params); 20 | this.params = params; 21 | this.cipher = createCipheriv(params.mode, params.key, params.iv); 22 | } 23 | 24 | public static async encrypt(data: Buffer, params: AESParams): Promise { 25 | const aes = new AES(params); 26 | return Buffer.concat([aes.update(data), await aes.final()]); 27 | } 28 | 29 | public static async decrypt(ciphertext: Buffer, params: AESParams): Promise { 30 | const decipher = createDecipheriv(params.mode, params.key, params.iv); 31 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 32 | } 33 | 34 | public static async encryptGCM(data: Buffer, params: AESParams): Promise<{ ciphertext: Buffer; tag: Buffer }> { 35 | if (params.mode !== AESMode.GCM) { 36 | throw new Error('GCM mode required'); 37 | } 38 | const cipher = createCipheriv(params.mode, params.key, params.iv) as CipherGCM; 39 | const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]); 40 | return { ciphertext, tag: cipher.getAuthTag() }; 41 | } 42 | 43 | public static async decryptGCM(ciphertext: Buffer, tag: Buffer, params: AESParams): Promise { 44 | if (params.mode !== AESMode.GCM) { 45 | throw new Error('GCM mode required'); 46 | } 47 | const decipher = createDecipheriv(params.mode, params.key, params.iv) as DecipherGCM; 48 | decipher.setAuthTag(tag); 49 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 50 | } 51 | 52 | public update(data: Buffer): Buffer { 53 | return this.cipher.update(data); 54 | } 55 | 56 | public async final(): Promise { 57 | return this.cipher.final(); 58 | } 59 | 60 | private validateParams(params: AESParams): void { 61 | if (![16, 24, 32].includes(params.key.length)) { 62 | throw new Error('Invalid key length'); 63 | } 64 | if (params.iv.length !== 16) { 65 | throw new Error('Invalid IV length'); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/symmetric/chacha20-poly1305.ts: -------------------------------------------------------------------------------- 1 | import { createCipheriv, createDecipheriv, CipherCCM, DecipherCCM } from 'crypto'; 2 | 3 | export interface ChaChaParams { 4 | key: Buffer; 5 | nonce: Buffer; 6 | } 7 | 8 | export class ChaCha20Poly1305 { 9 | private params: ChaChaParams; 10 | private cipher: CipherCCM; 11 | 12 | constructor(params: ChaChaParams) { 13 | this.validateParams(params); 14 | this.params = params; 15 | this.cipher = createCipheriv('chacha20-poly1305', params.key, params.nonce) as CipherCCM; 16 | } 17 | 18 | public static async encrypt(data: Buffer, params: ChaChaParams): Promise<{ ciphertext: Buffer; tag: Buffer }> { 19 | const cipher = createCipheriv('chacha20-poly1305', params.key, params.nonce) as CipherCCM; 20 | const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]); 21 | return { ciphertext, tag: cipher.getAuthTag() }; 22 | } 23 | 24 | public static async decrypt(ciphertext: Buffer, tag: Buffer, params: ChaChaParams): Promise { 25 | const decipher = createDecipheriv('chacha20-poly1305', params.key, params.nonce) as DecipherCCM; 26 | decipher.setAuthTag(tag); 27 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 28 | } 29 | 30 | public update(data: Buffer): Buffer { 31 | return this.cipher.update(data); 32 | } 33 | 34 | public async final(): Promise<{ finalCiphertext: Buffer; tag: Buffer }> { 35 | const finalCiphertext = this.cipher.final(); 36 | const tag = this.cipher.getAuthTag(); 37 | return { finalCiphertext, tag }; 38 | } 39 | 40 | private validateParams(params: ChaChaParams): void { 41 | if (params.key.length !== 32) { 42 | throw new Error('Invalid key length'); 43 | } 44 | if (params.nonce.length !== 12) { 45 | throw new Error('Invalid nonce length'); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/tls/constants.ts: -------------------------------------------------------------------------------- 1 | export enum HandshakeType { 2 | hello_request = 0, 3 | client_hello = 1, 4 | server_hello = 2, 5 | new_session_ticket = 4, 6 | end_of_early_data = 5, 7 | encrypted_extensions = 8, 8 | certificate = 11, 9 | server_key_exchange = 12, 10 | certificate_request = 13, 11 | server_hello_done = 14, 12 | certificate_verify = 15, 13 | client_key_exchange = 16, 14 | finished = 20, 15 | key_update = 24, 16 | message_hash = 254 17 | } 18 | 19 | export enum AlertLevel { 20 | warning = 1, 21 | fatal = 2 22 | } 23 | 24 | export enum AlertDescription { 25 | close_notify = 0, 26 | unexpected_message = 10, 27 | bad_record_mac = 20, 28 | record_overflow = 22, 29 | handshake_failure = 40, 30 | bad_certificate = 42, 31 | unsupported_certificate = 43, 32 | certificate_revoked = 44, 33 | certificate_expired = 45, 34 | certificate_unknown = 46, 35 | illegal_parameter = 47, 36 | unknown_ca = 48, 37 | access_denied = 49, 38 | decode_error = 50, 39 | decrypt_error = 51, 40 | protocol_version = 70, 41 | insufficient_security = 71, 42 | internal_error = 80, 43 | inappropriate_fallback = 86, 44 | user_canceled = 90, 45 | missing_extension = 109, 46 | unsupported_extension = 110, 47 | unrecognized_name = 112, 48 | bad_certificate_status_response = 113, 49 | unknown_psk_identity = 115, 50 | certificate_required = 116, 51 | no_application_protocol = 120 52 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/tls/handshake.ts: -------------------------------------------------------------------------------- 1 | import { HandshakeType } from './constants'; 2 | 3 | export interface HandshakeMessage { 4 | msgType: HandshakeType; 5 | serialize(): Buffer; 6 | } 7 | 8 | export class TLSHandshake { 9 | public static createClientHello(params: any): HandshakeMessage { 10 | return { 11 | msgType: HandshakeType.client_hello, 12 | serialize: () => Buffer.alloc(0) // TODO: Implement actual serialization 13 | }; 14 | } 15 | 16 | public static createServerHello(params: any): HandshakeMessage { 17 | return { 18 | msgType: HandshakeType.server_hello, 19 | serialize: () => Buffer.alloc(0) 20 | }; 21 | } 22 | 23 | public static createEncryptedExtensions(extensions: Map): HandshakeMessage { 24 | return { 25 | msgType: HandshakeType.encrypted_extensions, 26 | serialize: () => Buffer.alloc(0) 27 | }; 28 | } 29 | 30 | public static createCertificate(params: any): HandshakeMessage { 31 | return { 32 | msgType: HandshakeType.certificate, 33 | serialize: () => Buffer.alloc(0) 34 | }; 35 | } 36 | 37 | public static createCertificateVerify(params: any): HandshakeMessage { 38 | return { 39 | msgType: HandshakeType.certificate_verify, 40 | serialize: () => Buffer.alloc(0) 41 | }; 42 | } 43 | 44 | public static createFinished(verifyData: Buffer): HandshakeMessage { 45 | return { 46 | msgType: HandshakeType.finished, 47 | serialize: () => Buffer.alloc(0) 48 | }; 49 | } 50 | 51 | public static parseClientHello(data: Buffer): any { 52 | // TODO: Implement actual parsing 53 | if (data.length === 0) { 54 | throw new Error('Invalid handshake message'); 55 | } 56 | return {}; 57 | } 58 | 59 | public static parseServerHello(data: Buffer): any { 60 | if (data.length === 0) { 61 | throw new Error('Invalid handshake message'); 62 | } 63 | return {}; 64 | } 65 | 66 | public static parseEncryptedExtensions(data: Buffer): any { 67 | if (data.length === 0) { 68 | throw new Error('Invalid handshake message'); 69 | } 70 | return {}; 71 | } 72 | 73 | public static parseCertificate(data: Buffer): any { 74 | if (data.length === 0) { 75 | throw new Error('Invalid handshake message'); 76 | } 77 | return {}; 78 | } 79 | 80 | public static parseCertificateVerify(data: Buffer): any { 81 | if (data.length === 0) { 82 | throw new Error('Invalid handshake message'); 83 | } 84 | return {}; 85 | } 86 | 87 | public static parseFinished(data: Buffer): any { 88 | if (data.length === 0) { 89 | throw new Error('Invalid handshake message'); 90 | } 91 | return {}; 92 | } 93 | 94 | public static parsePreSharedKeyExtension(data: Buffer): any { 95 | return {}; 96 | } 97 | 98 | public static parseKeyShareExtension(data: Buffer): any { 99 | return {}; 100 | } 101 | 102 | public static createPreSharedKeyExtension(identities: any[], binders: Buffer[]): Buffer { 103 | return Buffer.alloc(0); 104 | } 105 | 106 | public static createKeyShareExtension(shares: any[]): Buffer { 107 | return Buffer.alloc(0); 108 | } 109 | 110 | public static parseExtension(type: string, data: Buffer): any { 111 | if (!type) { 112 | throw new Error('Unknown extension type'); 113 | } 114 | return {}; 115 | } 116 | 117 | public static fragment(data: Buffer): Buffer[] { 118 | const maxFragmentSize = 16384; 119 | const fragments: Buffer[] = []; 120 | 121 | for (let i = 0; i < data.length; i += maxFragmentSize) { 122 | fragments.push(data.slice(i, i + maxFragmentSize)); 123 | } 124 | 125 | return fragments; 126 | } 127 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/tls/key-schedule.ts: -------------------------------------------------------------------------------- 1 | import { createHmac } from 'crypto'; 2 | 3 | export class TLSKeySchedule { 4 | private static readonly HASH_LENGTH = 32; // SHA-256 5 | 6 | public static deriveEarlySecret(psk: Buffer): Buffer { 7 | return this.extract(Buffer.alloc(this.HASH_LENGTH), psk); 8 | } 9 | 10 | public static deriveEarlyTrafficSecret(secret: Buffer, label: 'client' | 'server'): Buffer { 11 | return this.expandLabel(secret, `${label} early traffic secret`, Buffer.alloc(0), this.HASH_LENGTH); 12 | } 13 | 14 | public static deriveEarlyExporterSecret(secret: Buffer): Buffer { 15 | return this.expandLabel(secret, 'early exporter secret', Buffer.alloc(0), this.HASH_LENGTH); 16 | } 17 | 18 | public static deriveHandshakeSecret(earlySecret: Buffer, sharedSecret: Buffer): Buffer { 19 | const derivedSecret = this.expandLabel(earlySecret, 'derived', Buffer.alloc(0), this.HASH_LENGTH); 20 | return this.extract(derivedSecret, sharedSecret); 21 | } 22 | 23 | public static deriveHandshakeTrafficSecret(secret: Buffer, label: 'client' | 'server'): Buffer { 24 | return this.expandLabel(secret, `${label} handshake traffic secret`, Buffer.alloc(0), this.HASH_LENGTH); 25 | } 26 | 27 | public static deriveMasterSecret(handshakeSecret: Buffer): Buffer { 28 | const derivedSecret = this.expandLabel(handshakeSecret, 'derived', Buffer.alloc(0), this.HASH_LENGTH); 29 | return this.extract(derivedSecret, Buffer.alloc(0)); 30 | } 31 | 32 | public static deriveApplicationTrafficSecret(secret: Buffer, label: 'client' | 'server'): Buffer { 33 | return this.expandLabel(secret, `${label} application traffic secret`, Buffer.alloc(0), this.HASH_LENGTH); 34 | } 35 | 36 | public static deriveExporterMasterSecret(secret: Buffer): Buffer { 37 | return this.expandLabel(secret, 'exporter master secret', Buffer.alloc(0), this.HASH_LENGTH); 38 | } 39 | 40 | public static deriveResumptionMasterSecret(secret: Buffer): Buffer { 41 | return this.expandLabel(secret, 'resumption master secret', Buffer.alloc(0), this.HASH_LENGTH); 42 | } 43 | 44 | public static updateTrafficSecret(secret: Buffer): Buffer { 45 | return this.expandLabel(secret, 'traffic upd', Buffer.alloc(0), this.HASH_LENGTH); 46 | } 47 | 48 | public static deriveKeyAndIV(secret: Buffer, cipherSuite: string): { key: Buffer; iv: Buffer } { 49 | const keyLength = cipherSuite.includes('256') ? 32 : 16; 50 | const ivLength = 12; 51 | 52 | const key = this.expandLabel(secret, 'key', Buffer.alloc(0), keyLength); 53 | const iv = this.expandLabel(secret, 'iv', Buffer.alloc(0), ivLength); 54 | 55 | return { key, iv }; 56 | } 57 | 58 | public static expandLabel(secret: Buffer, label: string, context: Buffer, length: number): Buffer { 59 | const hkdfLabel = Buffer.concat([ 60 | Buffer.from([0, length]), 61 | Buffer.from('tls13 ' + label), 62 | context 63 | ]); 64 | return this.expand(secret, hkdfLabel, length); 65 | } 66 | 67 | public static exportKeyingMaterial(secret: Buffer, label: string, context: Buffer, length: number): Buffer { 68 | return this.expandLabel(secret, label, context, length); 69 | } 70 | 71 | private static extract(salt: Buffer, ikm: Buffer): Buffer { 72 | const hmac = createHmac('sha256', salt); 73 | hmac.update(ikm); 74 | return hmac.digest(); 75 | } 76 | 77 | private static expand(prk: Buffer, info: Buffer, length: number): Buffer { 78 | const hmac = createHmac('sha256', prk); 79 | let output = Buffer.alloc(0); 80 | let T = Buffer.alloc(0); 81 | let i = 1; 82 | 83 | while (output.length < length) { 84 | const input = Buffer.concat([T, info, Buffer.from([i])]); 85 | hmac.update(input); 86 | T = hmac.digest(); 87 | output = Buffer.concat([output, T]); 88 | i++; 89 | } 90 | 91 | return output.slice(0, length); 92 | } 93 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/tls/record-layer.ts: -------------------------------------------------------------------------------- 1 | import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; 2 | 3 | export interface RecordLayerOptions { 4 | maxRecordSize?: number; 5 | keySize?: number; 6 | ivSize?: number; 7 | } 8 | 9 | export class RecordLayer { 10 | private static readonly DEFAULT_MAX_RECORD_SIZE = 16384; 11 | private static readonly DEFAULT_KEY_SIZE = 32; 12 | private static readonly DEFAULT_IV_SIZE = 12; 13 | private static readonly HEADER_SIZE = 5; 14 | private static readonly SEQUENCE_SIZE = 8; 15 | 16 | private readonly maxRecordSize: number; 17 | private readonly keySize: number; 18 | private readonly ivSize: number; 19 | private sequenceNumber: bigint; 20 | 21 | constructor(options: RecordLayerOptions = {}) { 22 | this.maxRecordSize = options.maxRecordSize || RecordLayer.DEFAULT_MAX_RECORD_SIZE; 23 | this.keySize = options.keySize || RecordLayer.DEFAULT_KEY_SIZE; 24 | this.ivSize = options.ivSize || RecordLayer.DEFAULT_IV_SIZE; 25 | this.sequenceNumber = BigInt(0); 26 | } 27 | 28 | /** 29 | * Encrypt a record 30 | * @param data Data to encrypt 31 | * @param key Encryption key 32 | * @param iv Initial vector 33 | * @param additionalData Additional authenticated data 34 | * @returns Encrypted record 35 | */ 36 | public encrypt( 37 | data: Buffer, 38 | key: Buffer, 39 | iv: Buffer, 40 | additionalData?: Buffer 41 | ): Buffer { 42 | this.validateInputs(key, iv); 43 | 44 | if (data.length > this.maxRecordSize) { 45 | throw new Error(`Record size exceeds maximum (${this.maxRecordSize} bytes)`); 46 | } 47 | 48 | // Construct nonce by XORing IV with sequence number 49 | const nonce = Buffer.alloc(this.ivSize); 50 | iv.copy(nonce); 51 | const sequenceBuffer = Buffer.alloc(8); 52 | sequenceBuffer.writeBigUInt64BE(this.sequenceNumber); 53 | for (let i = 0; i < 8; i++) { 54 | nonce[i + 4] ^= sequenceBuffer[i]; 55 | } 56 | 57 | // Encrypt data 58 | const cipher = createCipheriv('aes-256-gcm', key, nonce); 59 | if (additionalData) { 60 | cipher.setAAD(additionalData); 61 | } 62 | const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]); 63 | const tag = cipher.getAuthTag(); 64 | 65 | // Increment sequence number 66 | this.sequenceNumber = (this.sequenceNumber + BigInt(1)) % (BigInt(1) << BigInt(64)); 67 | 68 | // Construct record 69 | const record = Buffer.alloc(RecordLayer.HEADER_SIZE + ciphertext.length + 16); 70 | record.writeUInt16BE(ciphertext.length + 16, 3); // Record length 71 | ciphertext.copy(record, RecordLayer.HEADER_SIZE); 72 | tag.copy(record, RecordLayer.HEADER_SIZE + ciphertext.length); 73 | 74 | return record; 75 | } 76 | 77 | /** 78 | * Decrypt a record 79 | * @param record Record to decrypt 80 | * @param key Decryption key 81 | * @param iv Initial vector 82 | * @param additionalData Additional authenticated data 83 | * @returns Decrypted data 84 | */ 85 | public decrypt( 86 | record: Buffer, 87 | key: Buffer, 88 | iv: Buffer, 89 | additionalData?: Buffer 90 | ): Buffer { 91 | this.validateInputs(key, iv); 92 | 93 | // Parse record 94 | const recordLength = record.readUInt16BE(3); 95 | if (recordLength > this.maxRecordSize + 16) { 96 | throw new Error(`Record size exceeds maximum (${this.maxRecordSize} bytes)`); 97 | } 98 | 99 | const ciphertext = record.slice(RecordLayer.HEADER_SIZE, RecordLayer.HEADER_SIZE + recordLength - 16); 100 | const tag = record.slice(RecordLayer.HEADER_SIZE + recordLength - 16, RecordLayer.HEADER_SIZE + recordLength); 101 | 102 | // Construct nonce by XORing IV with sequence number 103 | const nonce = Buffer.alloc(this.ivSize); 104 | iv.copy(nonce); 105 | const sequenceBuffer = Buffer.alloc(8); 106 | sequenceBuffer.writeBigUInt64BE(this.sequenceNumber); 107 | for (let i = 0; i < 8; i++) { 108 | nonce[i + 4] ^= sequenceBuffer[i]; 109 | } 110 | 111 | // Decrypt data 112 | const decipher = createDecipheriv('aes-256-gcm', key, nonce); 113 | if (additionalData) { 114 | decipher.setAAD(additionalData); 115 | } 116 | decipher.setAuthTag(tag); 117 | const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]); 118 | 119 | // Increment sequence number 120 | this.sequenceNumber = (this.sequenceNumber + BigInt(1)) % (BigInt(1) << BigInt(64)); 121 | 122 | return plaintext; 123 | } 124 | 125 | /** 126 | * Get current sequence number 127 | * @returns Current sequence number 128 | */ 129 | public getSequenceNumber(): bigint { 130 | return this.sequenceNumber; 131 | } 132 | 133 | /** 134 | * Reset sequence number to 0 135 | */ 136 | public resetSequenceNumber(): void { 137 | this.sequenceNumber = BigInt(0); 138 | } 139 | 140 | private validateInputs(key: Buffer, iv: Buffer): void { 141 | if (key.length !== this.keySize) { 142 | throw new Error(`Key must be ${this.keySize} bytes`); 143 | } 144 | if (iv.length !== this.ivSize) { 145 | throw new Error(`IV must be ${this.ivSize} bytes`); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/xchacha20/xchacha20.ts: -------------------------------------------------------------------------------- 1 | import { createCipheriv, createDecipheriv, randomBytes, CipherGCM, DecipherGCM } from 'crypto'; 2 | 3 | export class XChaCha20 { 4 | private static readonly KEY_SIZE = 32; 5 | private static readonly NONCE_SIZE = 24; 6 | private static readonly BLOCK_SIZE = 64; 7 | 8 | /** 9 | * Encrypt data using XChaCha20 10 | * @param plaintext Data to encrypt 11 | * @param key 32-byte key 12 | * @param nonce 24-byte nonce 13 | * @param counter Optional counter for seeking 14 | * @returns Encrypted data 15 | */ 16 | public static encrypt( 17 | plaintext: Buffer, 18 | key: Buffer, 19 | nonce: Buffer, 20 | counter: bigint = BigInt(0) 21 | ): Buffer { 22 | this.validateInputs(key, nonce); 23 | const cipher = createCipheriv('chacha20', key, nonce); 24 | return Buffer.concat([cipher.update(plaintext), cipher.final()]); 25 | } 26 | 27 | /** 28 | * Decrypt data using XChaCha20 29 | * @param ciphertext Data to decrypt 30 | * @param key 32-byte key 31 | * @param nonce 24-byte nonce 32 | * @param counter Optional counter for seeking 33 | * @returns Decrypted data 34 | */ 35 | public static decrypt( 36 | ciphertext: Buffer, 37 | key: Buffer, 38 | nonce: Buffer, 39 | counter: bigint = BigInt(0) 40 | ): Buffer { 41 | this.validateInputs(key, nonce); 42 | const decipher = createDecipheriv('chacha20', key, nonce); 43 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 44 | } 45 | 46 | /** 47 | * Encrypt data using XChaCha20-Poly1305 AEAD 48 | * @param plaintext Data to encrypt 49 | * @param key 32-byte key 50 | * @param nonce 24-byte nonce 51 | * @param aad Additional authenticated data 52 | * @returns Object containing ciphertext and authentication tag 53 | */ 54 | public static encryptWithAEAD( 55 | plaintext: Buffer, 56 | key: Buffer, 57 | nonce: Buffer, 58 | aad?: Buffer 59 | ): { ciphertext: Buffer; tag: Buffer } { 60 | this.validateInputs(key, nonce); 61 | const cipher = createCipheriv('chacha20-poly1305', key, nonce) as CipherGCM; 62 | if (aad) { 63 | cipher.setAAD(aad); 64 | } 65 | const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); 66 | const tag = cipher.getAuthTag(); 67 | return { ciphertext, tag }; 68 | } 69 | 70 | /** 71 | * Decrypt data using XChaCha20-Poly1305 AEAD 72 | * @param ciphertext Data to decrypt 73 | * @param key 32-byte key 74 | * @param nonce 24-byte nonce 75 | * @param tag Authentication tag 76 | * @param aad Additional authenticated data 77 | * @returns Decrypted data 78 | */ 79 | public static decryptWithAEAD( 80 | ciphertext: Buffer, 81 | key: Buffer, 82 | nonce: Buffer, 83 | tag: Buffer, 84 | aad?: Buffer 85 | ): Buffer { 86 | this.validateInputs(key, nonce); 87 | const decipher = createDecipheriv('chacha20-poly1305', key, nonce) as DecipherGCM; 88 | if (aad) { 89 | decipher.setAAD(aad); 90 | } 91 | decipher.setAuthTag(tag); 92 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 93 | } 94 | 95 | /** 96 | * Derive a subkey using HChaCha20 97 | * @param key 32-byte key 98 | * @param nonce 24-byte nonce 99 | * @returns 32-byte subkey 100 | */ 101 | public static deriveSubkey(key: Buffer, nonce: Buffer): Buffer { 102 | this.validateInputs(key, nonce); 103 | const cipher = createCipheriv('chacha20', key, nonce); 104 | const subkey = cipher.update(Buffer.alloc(32)); 105 | cipher.final(); 106 | return subkey; 107 | } 108 | 109 | private static validateInputs(key: Buffer, nonce: Buffer): void { 110 | if (key.length !== this.KEY_SIZE) { 111 | throw new Error(`Key must be ${this.KEY_SIZE} bytes`); 112 | } 113 | if (nonce.length !== this.NONCE_SIZE) { 114 | throw new Error(`Nonce must be ${this.NONCE_SIZE} bytes`); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /packages/crypto/src/core/zkp/__tests__/chaum-pedersen.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { ChaumPedersen, ChaumPedersenParams, ChaumPedersenProof } from '../chaum-pedersen'; 3 | import { BigInteger } from 'jsbn'; 4 | import { randomBytes } from 'crypto'; 5 | 6 | describe('ChaumPedersen', () => { 7 | let params: ChaumPedersenParams; 8 | let secret: BigInteger; 9 | let y1: BigInteger; 10 | let y2: BigInteger; 11 | 12 | before(async () => { 13 | params = await ChaumPedersen.generateParams(512); 14 | const randomBuffer = Buffer.from(randomBytes(32)); 15 | secret = new BigInteger(randomBuffer.toString('hex'), 16); 16 | y1 = params.g.modPow(secret, params.p); 17 | y2 = params.h.modPow(secret, params.p); 18 | }); 19 | 20 | it('should generate valid parameters', () => { 21 | expect(params.p).to.exist; 22 | expect(params.q).to.exist; 23 | expect(params.g).to.exist; 24 | expect(params.h).to.exist; 25 | expect(params.g.modPow(params.q, params.p).equals(new BigInteger('1'))).to.be.true; 26 | expect(params.h.modPow(params.q, params.p).equals(new BigInteger('1'))).to.be.true; 27 | }); 28 | 29 | it('should generate and verify valid proofs', async () => { 30 | const proof = await ChaumPedersen.prove(secret, y1, y2, params); 31 | const isValid = await ChaumPedersen.verify(y1, y2, proof, params); 32 | expect(isValid).to.be.true; 33 | }); 34 | 35 | it('should reject invalid proofs with wrong secret', async () => { 36 | const wrongSecret = new BigInteger(randomBytes(32).toString('hex'), 16); 37 | const proof = await ChaumPedersen.prove(wrongSecret, y1, y2, params); 38 | const isValid = await ChaumPedersen.verify(y1, y2, proof, params); 39 | expect(isValid).to.be.false; 40 | }); 41 | 42 | it('should reject invalid proofs with wrong public values', async () => { 43 | const wrongY1 = params.g.modPow(new BigInteger('2'), params.p); 44 | const wrongY2 = params.h.modPow(new BigInteger('2'), params.p); 45 | const proof = await ChaumPedersen.prove(secret, y1, y2, params); 46 | const isValid = await ChaumPedersen.verify(wrongY1, wrongY2, proof, params); 47 | expect(isValid).to.be.false; 48 | }); 49 | 50 | it('should generate different proofs for the same values', async () => { 51 | const proof1 = await ChaumPedersen.prove(secret, y1, y2, params); 52 | const proof2 = await ChaumPedersen.prove(secret, y1, y2, params); 53 | 54 | expect(proof1.t1.equals(proof2.t1)).to.be.false; 55 | expect(proof1.t2.equals(proof2.t2)).to.be.false; 56 | 57 | const isValid1 = await ChaumPedersen.verify(y1, y2, proof1, params); 58 | const isValid2 = await ChaumPedersen.verify(y1, y2, proof2, params); 59 | expect(isValid1).to.be.true; 60 | expect(isValid2).to.be.true; 61 | }); 62 | 63 | it('should handle small values correctly', async () => { 64 | const smallSecret = new BigInteger('1'); 65 | const y1Small = params.g.modPow(smallSecret, params.p); 66 | const y2Small = params.h.modPow(smallSecret, params.p); 67 | const proof = await ChaumPedersen.prove(smallSecret, y1Small, y2Small, params); 68 | const isValid1 = await ChaumPedersen.verify(y1Small, y2Small, proof, params); 69 | expect(isValid1).to.be.true; 70 | }); 71 | 72 | it('should handle maximum values correctly', async () => { 73 | const maxSecret = params.q.subtract(BigInteger.ONE); 74 | const y1Max = params.g.modPow(maxSecret, params.p); 75 | const y2Max = params.h.modPow(maxSecret, params.p); 76 | const proof = await ChaumPedersen.prove(maxSecret, y1Max, y2Max, params); 77 | const isValidMax = await ChaumPedersen.verify(y1Max, y2Max, proof, params); 78 | expect(isValidMax).to.be.true; 79 | }); 80 | 81 | it('should reject invalid parameter sizes', async () => { 82 | await expect(ChaumPedersen.generateParams(256)).to.be.rejectedWith(Error); 83 | }); 84 | 85 | it('should reject invalid secrets', async () => { 86 | const invalidSecret = params.q; 87 | await expect(ChaumPedersen.prove(invalidSecret, y1, y2, params)).to.be.rejectedWith(Error); 88 | }); 89 | 90 | it('should generate unique proofs', async () => { 91 | const proof1 = await ChaumPedersen.prove(secret, y1, y2, params); 92 | const proof2 = await ChaumPedersen.prove(secret, y1, y2, params); 93 | 94 | expect(proof1).to.not.deep.equal(proof2); 95 | 96 | expect(proof1.t1.equals(y1)).to.be.false; 97 | expect(proof1.t2.equals(y2)).to.be.false; 98 | expect(proof2.t1.equals(y1)).to.be.false; 99 | expect(proof2.t2.equals(y2)).to.be.false; 100 | }); 101 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/zkp/__tests__/fiat-shamir.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { FiatShamir, FiatShamirParams, FiatShamirProof } from '../fiat-shamir'; 3 | import { BigInteger } from 'jsbn'; 4 | import { randomBytes } from 'crypto'; 5 | 6 | describe('FiatShamir', () => { 7 | let params: FiatShamirParams; 8 | let secret: BigInteger; 9 | let publicValue: BigInteger; 10 | 11 | before(async () => { 12 | params = await FiatShamir.generateParams(512); 13 | const randomBuffer = Buffer.from(randomBytes(32)); 14 | secret = new BigInteger(randomBuffer.toString('hex'), 16); 15 | publicValue = secret.modPow(new BigInteger('2'), params.n); 16 | }); 17 | 18 | it('should generate valid parameters', () => { 19 | expect(params.n).to.exist; 20 | expect(params.n.bitLength()).to.be.at.least(512); 21 | }); 22 | 23 | it('should generate and verify valid proofs', async () => { 24 | const proof = await FiatShamir.prove(secret, params); 25 | const isValid = await FiatShamir.verify(proof, publicValue, params); 26 | expect(isValid).to.be.true; 27 | }); 28 | 29 | it('should reject invalid proofs with wrong secret', async () => { 30 | const wrongSecret = new BigInteger(randomBytes(32).toString('hex'), 16); 31 | const proof = await FiatShamir.prove(wrongSecret, params); 32 | const isValid = await FiatShamir.verify(proof, publicValue, params); 33 | expect(isValid).to.be.false; 34 | }); 35 | 36 | it('should reject invalid proofs with wrong public value', async () => { 37 | const wrongPublicValue = params.g.modPow(new BigInteger('2'), params.n); 38 | const proof = await FiatShamir.prove(secret, params); 39 | const isValid = await FiatShamir.verify(proof, wrongPublicValue, params); 40 | expect(isValid).to.be.false; 41 | }); 42 | 43 | it('should generate different proofs for the same values', async () => { 44 | const proof1 = await FiatShamir.prove(secret, params); 45 | const proof2 = await FiatShamir.prove(secret, params); 46 | 47 | expect(proof1.commitment.equals(proof2.commitment)).to.be.false; 48 | expect(proof1.challenge.equals(proof2.challenge)).to.be.false; 49 | expect(proof1.response.equals(proof2.response)).to.be.false; 50 | 51 | const isValid1 = await FiatShamir.verify(proof1, publicValue, params); 52 | const isValid2 = await FiatShamir.verify(proof2, publicValue, params); 53 | expect(isValid1).to.be.true; 54 | expect(isValid2).to.be.true; 55 | }); 56 | 57 | it('should handle small values correctly', async () => { 58 | const smallSecret = new BigInteger('1'); 59 | const smallPublicValue = params.g.modPow(smallSecret, params.n); 60 | const proof = await FiatShamir.prove(smallSecret, params); 61 | const isValid1 = await FiatShamir.verify(proof, smallPublicValue, params); 62 | expect(isValid1).to.be.true; 63 | }); 64 | 65 | it('should handle maximum values correctly', async () => { 66 | const maxSecret = params.n.subtract(BigInteger.ONE); 67 | const maxPublicValue = params.g.modPow(maxSecret, params.n); 68 | const proof = await FiatShamir.prove(maxSecret, params); 69 | const isValidMax = await FiatShamir.verify(proof, maxPublicValue, params); 70 | expect(isValidMax).to.be.true; 71 | }); 72 | 73 | it('should reject invalid parameter sizes', async () => { 74 | await expect(FiatShamir.generateParams(256)).to.be.rejectedWith(Error); 75 | }); 76 | 77 | it('should generate unique proofs', async () => { 78 | const proof1 = await FiatShamir.prove(secret, params); 79 | const proof2 = await FiatShamir.prove(secret, params); 80 | 81 | expect(proof1).to.not.deep.equal(proof2); 82 | 83 | expect(proof1.commitment.equals(publicValue)).to.be.false; 84 | expect(proof2.commitment.equals(publicValue)).to.be.false; 85 | 86 | expect(proof1.response.equals(secret)).to.be.false; 87 | expect(proof2.response.equals(secret)).to.be.false; 88 | }); 89 | 90 | it('should maintain soundness property', async () => { 91 | const proof = await FiatShamir.prove(secret, params); 92 | const isValidOriginal = await FiatShamir.verify(proof, publicValue, params); 93 | expect(isValidOriginal).to.be.true; 94 | 95 | // Try to forge a proof without knowing the secret 96 | const fakeProof = { 97 | commitment: params.g.modPow(new BigInteger('123'), params.n), 98 | challenge: proof.challenge, 99 | response: proof.response 100 | }; 101 | 102 | const isValidFake = await FiatShamir.verify(fakeProof, publicValue, params); 103 | expect(isValidFake).to.be.false; 104 | }); 105 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/zkp/__tests__/range.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { RangeProver, RangeProof } from '../range'; 3 | import { Schnorr, SchnorrParams } from '../schnorr'; 4 | import { BigInteger } from 'jsbn'; 5 | import { randomBytes } from 'crypto'; 6 | 7 | describe('RangeProver', () => { 8 | let params: SchnorrParams; 9 | let value: BigInteger; 10 | const bits = 8; 11 | 12 | before(async () => { 13 | params = await Schnorr.generateParams(512); 14 | const valueBuffer = Buffer.from(randomBytes(Math.ceil(bits / 8))); 15 | value = new BigInteger(valueBuffer.toString('hex'), 16).mod(new BigInteger('2').pow(bits)); 16 | }); 17 | 18 | it('should generate valid parameters', () => { 19 | expect(params.p).to.exist; 20 | expect(params.q).to.exist; 21 | expect(params.g).to.exist; 22 | }); 23 | 24 | it('should generate and verify valid proofs', async () => { 25 | const proof = await RangeProver.prove(value, bits, params); 26 | const isValid = await RangeProver.verify(proof, bits, params); 27 | expect(isValid).to.be.true; 28 | }); 29 | 30 | it('should reject proofs with invalid value size', async () => { 31 | const invalidValue = new BigInteger('2').pow(bits + 1); 32 | await expect( 33 | RangeProver.prove(invalidValue, bits, params) 34 | ).to.be.rejectedWith(Error); 35 | }); 36 | 37 | it('should reject invalid proofs', async () => { 38 | const invalidProof = { 39 | commitments: [params.g], 40 | challenges: [new BigInteger('1')], 41 | responses: [new BigInteger('1')], 42 | finalCommitment: params.g 43 | }; 44 | 45 | const isValid = await RangeProver.verify(invalidProof, bits, params); 46 | expect(isValid).to.be.false; 47 | }); 48 | 49 | it('should handle zero value correctly', async () => { 50 | const zeroValue = new BigInteger('0'); 51 | const proof = await RangeProver.prove(zeroValue, bits, params); 52 | const isValidZero = await RangeProver.verify(proof, bits, params); 53 | expect(isValidZero).to.be.true; 54 | }); 55 | 56 | it('should handle maximum value correctly', async () => { 57 | const maxValue = new BigInteger('2').pow(bits).subtract(BigInteger.ONE); 58 | const proof = await RangeProver.prove(maxValue, bits, params); 59 | const isValidMax = await RangeProver.verify(proof, bits, params); 60 | expect(isValidMax).to.be.true; 61 | }); 62 | 63 | it('should generate different proofs for the same value', async () => { 64 | const proof1 = await RangeProver.prove(value, bits, params); 65 | const proof2 = await RangeProver.prove(value, bits, params); 66 | 67 | expect(proof1.commitments).to.not.deep.equal(proof2.commitments); 68 | expect(proof1.challenges).to.not.deep.equal(proof2.challenges); 69 | expect(proof1.responses).to.not.deep.equal(proof2.responses); 70 | 71 | const isValid1 = await RangeProver.verify(proof1, bits, params); 72 | const isValid2 = await RangeProver.verify(proof2, bits, params); 73 | expect(isValid1).to.be.true; 74 | expect(isValid2).to.be.true; 75 | }); 76 | 77 | it('should maintain zero-knowledge property', async () => { 78 | const proof = await RangeProver.prove(value, bits, params); 79 | 80 | // Check that individual bit commitments don't reveal the bits 81 | for (let i = 0; i < bits; i++) { 82 | const bit = value.testBit(i); 83 | const commitment = proof.commitments[i]; 84 | expect(commitment.equals(new BigInteger(bit ? '1' : '0'))).to.be.false; 85 | } 86 | 87 | // Check that final commitment doesn't reveal the value 88 | expect(proof.finalCommitment.equals(value)).to.be.false; 89 | }); 90 | 91 | it('should handle different bit lengths', async () => { 92 | for (let testBits = 1; testBits <= 16; testBits++) { 93 | const testBuffer = Buffer.from(randomBytes(Math.ceil(testBits / 8))); 94 | const testValue = new BigInteger(testBuffer.toString('hex'), 16).mod(new BigInteger('2').pow(testBits)); 95 | const proof = await RangeProver.prove(testValue, testBits, params); 96 | const isValid = await RangeProver.verify(proof, testBits, params); 97 | expect(isValid).to.be.true; 98 | expect(proof.commitments.length).to.equal(testBits); 99 | expect(proof.challenges.length).to.equal(testBits); 100 | expect(proof.responses.length).to.equal(testBits); 101 | } 102 | }); 103 | 104 | it('should reject values outside the range', async () => { 105 | const outsideValue = new BigInteger('2').pow(bits); 106 | const proof = await RangeProver.prove(value, bits, params); 107 | const isValid = await RangeProver.verify(proof, bits, params); 108 | expect(isValid).to.be.false; 109 | }); 110 | 111 | it('should reject invalid parameter sizes', async () => { 112 | await expect( 113 | RangeProver.generateParameters(256) 114 | ).to.be.rejectedWith(Error); 115 | }); 116 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/zkp/__tests__/schnorr.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { BigInteger } from 'jsbn'; 3 | import { SchnorrProtocol, SchnorrParams } from '../schnorr'; 4 | 5 | describe('Schnorr Protocol', () => { 6 | let protocol: SchnorrProtocol; 7 | let params: SchnorrParams; 8 | let secret: BigInteger; 9 | 10 | before(async () => { 11 | protocol = new SchnorrProtocol(); 12 | params = await SchnorrProtocol.generateParams(512); // Use smaller parameters for testing 13 | secret = new BigInteger(32, 1, crypto.getRandomValues(new Uint8Array(32))); 14 | }); 15 | 16 | it('should generate valid parameters', () => { 17 | expect(params.p).to.exist; 18 | expect(params.q).to.exist; 19 | expect(params.g).to.exist; 20 | expect(params.p.bitLength()).to.be.at.least(512); 21 | expect(params.g.compareTo(BigInteger.ONE)).to.be.above(0); 22 | expect(params.g.compareTo(params.p)).to.be.below(0); 23 | }); 24 | 25 | it('should generate and verify valid proofs', async () => { 26 | // Generate proof 27 | const proof = await protocol.prove(secret, params); 28 | expect(proof.commitment).to.exist; 29 | expect(proof.challenge).to.exist; 30 | expect(proof.response).to.exist; 31 | 32 | // Compute public value 33 | const publicValue = params.g.modPow(secret, params.p); 34 | 35 | // Verify proof 36 | const isValid = await protocol.verify(proof, publicValue, params); 37 | expect(isValid).to.be.true; 38 | }); 39 | 40 | it('should reject invalid proofs', async () => { 41 | // Generate valid proof 42 | const proof = await protocol.prove(secret, params); 43 | const publicValue = params.g.modPow(secret, params.p); 44 | 45 | // Modify proof to make it invalid 46 | const invalidProof = { 47 | ...proof, 48 | response: proof.response.add(BigInteger.ONE) 49 | }; 50 | 51 | // Verify invalid proof 52 | const isValid = await protocol.verify(invalidProof, publicValue, params); 53 | expect(isValid).to.be.false; 54 | }); 55 | 56 | it('should generate different proofs for same secret', async () => { 57 | // Generate two proofs for same secret 58 | const proof1 = await protocol.prove(secret, params); 59 | const proof2 = await protocol.prove(secret, params); 60 | 61 | // Verify proofs are different but both valid 62 | expect(proof1.commitment.equals(proof2.commitment)).to.be.false; 63 | expect(proof1.challenge.equals(proof2.challenge)).to.be.false; 64 | expect(proof1.response.equals(proof2.response)).to.be.false; 65 | 66 | const publicValue = params.g.modPow(secret, params.p); 67 | const isValid1 = await protocol.verify(proof1, publicValue, params); 68 | const isValid2 = await protocol.verify(proof2, publicValue, params); 69 | expect(isValid1).to.be.true; 70 | expect(isValid2).to.be.true; 71 | }); 72 | 73 | it('should reject proofs with invalid parameters', async () => { 74 | // Try to prove with invalid parameters 75 | const invalidParams = { 76 | ...params, 77 | p: params.p.add(BigInteger.ONE) // Make p not prime 78 | }; 79 | 80 | await expect(protocol.prove(secret, invalidParams)).to.be.rejectedWith( 81 | 'Parameter p must be prime' 82 | ); 83 | }); 84 | 85 | it('should maintain zero-knowledge property', async () => { 86 | // Generate proof 87 | const proof = await protocol.prove(secret, params); 88 | const publicValue = params.g.modPow(secret, params.p); 89 | 90 | // Verify proof components don't reveal secret 91 | expect(proof.commitment.modPow(secret, params.p).equals(publicValue)).to.be.false; 92 | expect(proof.challenge.multiply(secret).equals(secret)).to.be.false; 93 | expect(proof.response.equals(secret)).to.be.false; 94 | 95 | // Verify proof is still valid 96 | const isValid = await protocol.verify(proof, publicValue, params); 97 | expect(isValid).to.be.true; 98 | }); 99 | }); -------------------------------------------------------------------------------- /packages/crypto/src/core/zkp/index.ts: -------------------------------------------------------------------------------- 1 | export * from './schnorr'; 2 | export * from './range'; -------------------------------------------------------------------------------- /packages/crypto/src/core/zkp/range.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes } from 'crypto'; 2 | import { BigInteger } from 'jsbn'; 3 | import { Hash } from '../hash'; 4 | import { Schnorr, SchnorrParams } from './schnorr'; 5 | 6 | export interface RangeProof { 7 | commitments: BigInteger[]; // Pedersen commitments for each bit 8 | challenges: BigInteger[]; // Challenges for each bit 9 | responses: BigInteger[]; // Responses for each bit 10 | finalCommitment: BigInteger; // Commitment to the actual value 11 | } 12 | 13 | export class RangeProver { 14 | private static ONE = new BigInteger('1'); 15 | private static TWO = new BigInteger('2'); 16 | private static ZERO = new BigInteger('0'); 17 | 18 | /** 19 | * Generate a proof that a value lies in the range [0, 2^n - 1] 20 | * @param {BigInteger} value - The value to prove 21 | * @param {number} bitLength - The number of bits in the range 22 | * @param {SchnorrParams} params - Group parameters 23 | * @returns {Promise} Generated range proof 24 | */ 25 | static async prove(value: BigInteger, bitLength: number, params: SchnorrParams): Promise { 26 | // Ensure value is non-negative and within range 27 | if (value.compareTo(this.ZERO) < 0 || value.compareTo(this.TWO.pow(bitLength)) >= 0) { 28 | throw new Error('Value out of range'); 29 | } 30 | 31 | const bits = value.toString(2).padStart(bitLength, '0').split('').map(Number); 32 | const commitments: BigInteger[] = []; 33 | const challenges: BigInteger[] = []; 34 | const responses: BigInteger[] = []; 35 | const randoms: BigInteger[] = []; 36 | 37 | // Generate second base point h 38 | const h = await this.generateH(params); 39 | 40 | // Generate commitments for each bit 41 | for (let i = 0; i < bitLength; i++) { 42 | const bit = bits[i]; 43 | const r = this.generateRandom(params.q); 44 | randoms.push(r); 45 | 46 | // C = g^b * h^r mod p 47 | const commitment = params.g.modPow(new BigInteger(bit.toString()), params.p) 48 | .multiply(h.modPow(r, params.p)) 49 | .mod(params.p); 50 | commitments.push(commitment); 51 | 52 | // Generate challenge 53 | const challenge = await Hash.sha256( 54 | Buffer.from(commitment.toString() + i.toString()) 55 | ); 56 | challenges.push(new BigInteger(challenge.toString('hex'), 16).mod(params.q)); 57 | } 58 | 59 | // Generate responses 60 | for (let i = 0; i < bitLength; i++) { 61 | const bit = bits[i]; 62 | // s = r + c * b mod q 63 | const response = randoms[i].add( 64 | challenges[i].multiply(new BigInteger(bit.toString())) 65 | ).mod(params.q); 66 | responses.push(response); 67 | } 68 | 69 | // Calculate final commitment as product of bit commitments 70 | let finalCommitment = this.ONE; 71 | for (let i = 0; i < bitLength; i++) { 72 | const power = this.TWO.pow(i); 73 | finalCommitment = finalCommitment.multiply( 74 | commitments[i].modPow(power, params.p) 75 | ).mod(params.p); 76 | } 77 | 78 | return { 79 | commitments, 80 | challenges, 81 | responses, 82 | finalCommitment 83 | }; 84 | } 85 | 86 | /** 87 | * Verify a range proof 88 | * @param {RangeProof} proof - The proof to verify 89 | * @param {number} bitLength - The number of bits in the range 90 | * @param {SchnorrParams} params - Group parameters 91 | * @returns {Promise} Whether the proof is valid 92 | */ 93 | static async verify(proof: RangeProof, bitLength: number, params: SchnorrParams): Promise { 94 | const h = await this.generateH(params); 95 | 96 | // Verify each bit commitment 97 | for (let i = 0; i < bitLength; i++) { 98 | // Calculate g^s * h^c 99 | const lhs = params.g.modPow(proof.responses[i], params.p) 100 | .multiply(h.modPow(proof.challenges[i], params.p)) 101 | .mod(params.p); 102 | 103 | // Compare with C 104 | const rhs = proof.commitments[i]; 105 | 106 | if (!lhs.equals(rhs)) { 107 | return false; 108 | } 109 | } 110 | 111 | // Verify final commitment 112 | let product = this.ONE; 113 | for (let i = 0; i < bitLength; i++) { 114 | const power = this.TWO.pow(i); 115 | product = product.multiply( 116 | proof.commitments[i].modPow(power, params.p) 117 | ).mod(params.p); 118 | } 119 | 120 | return proof.finalCommitment.equals(product); 121 | } 122 | 123 | private static generateRandom(max: BigInteger): BigInteger { 124 | const bytes = Math.ceil(max.bitLength() / 8); 125 | let r: BigInteger; 126 | do { 127 | const hex = randomBytes(bytes).toString('hex'); 128 | r = new BigInteger(hex, 16).mod(max); 129 | } while (r.compareTo(this.ZERO) <= 0); 130 | return r; 131 | } 132 | 133 | private static async generateH(params: SchnorrParams): Promise { 134 | const hash = await Hash.sha256( 135 | Buffer.from(params.g.toString() + params.p.toString()) 136 | ); 137 | return new BigInteger(hash.toString('hex'), 16).mod(params.p); 138 | } 139 | } -------------------------------------------------------------------------------- /packages/crypto/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Encryption } from './core/encryption'; 2 | export * from './types'; 3 | export * from './core/encryption'; 4 | export * from './core/homomorphic'; 5 | export * from './core/zkp'; 6 | export * from './utils/hash'; 7 | export * from './core/symmetric'; 8 | export * from './core/asymmetric'; 9 | export * from './core/hash'; 10 | 11 | // Version and package info 12 | export const VERSION = '0.1.0'; 13 | export const PACKAGE_NAME = '@cipher-nexus/crypto'; 14 | -------------------------------------------------------------------------------- /packages/crypto/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface EncryptionConfig { 2 | algorithm: 'RSA' | 'AES' | 'FHE'; 3 | keySize: number; 4 | mode?: string; 5 | } 6 | 7 | export interface EncryptionKey { 8 | publicKey: string; 9 | privateKey: string; 10 | } 11 | 12 | export interface EncryptedData { 13 | data: string; 14 | iv?: string; 15 | tag?: string; 16 | } -------------------------------------------------------------------------------- /packages/crypto/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface EncryptionKey { 2 | publicKey: string; 3 | privateKey: string; 4 | } 5 | 6 | export interface EncryptionConfig { 7 | algorithm: 'AES' | 'RSA' | 'FHE'; 8 | keySize: number; 9 | mode?: string; 10 | } 11 | 12 | export interface EncryptedData { 13 | data: string; 14 | iv?: string; 15 | tag?: string; 16 | } 17 | -------------------------------------------------------------------------------- /packages/crypto/src/utils/hash.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | export const hashData = (data: string): string => { 4 | return crypto.createHash('sha256').update(data).digest('hex'); 5 | }; 6 | 7 | export const generateSalt = (length: number = 16): string => { 8 | return crypto.randomBytes(length).toString('hex'); 9 | }; 10 | 11 | export const hashWithSalt = (data: string, salt: string): string => { 12 | return crypto.createHmac('sha256', salt).update(data).digest('hex'); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/crypto/test/encryption.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, use } from 'chai'; 2 | import chaiAsPromised from 'chai-as-promised'; 3 | import { Encryption } from '../src/core/encryption'; 4 | import { EncryptionConfig } from '../src/types'; 5 | 6 | use(chaiAsPromised); 7 | 8 | describe('Encryption', () => { 9 | describe('RSA Encryption', () => { 10 | const config: EncryptionConfig = { 11 | algorithm: 'RSA', 12 | keySize: 2048 13 | }; 14 | let encryption: Encryption; 15 | 16 | beforeEach(() => { 17 | encryption = new Encryption(config); 18 | }); 19 | 20 | it('should generate RSA key pair', async () => { 21 | const keys = await encryption.generateKeys(); 22 | expect(keys.publicKey).to.be.a('string'); 23 | expect(keys.privateKey).to.be.a('string'); 24 | expect(keys.publicKey).to.include('BEGIN PUBLIC KEY'); 25 | expect(keys.privateKey).to.include('BEGIN PRIVATE KEY'); 26 | }); 27 | 28 | it('should encrypt and decrypt data correctly', async () => { 29 | const testData = { message: 'test message' }; 30 | const keys = await encryption.generateKeys(); 31 | 32 | const encrypted = await encryption.encrypt(testData, keys.publicKey); 33 | expect(encrypted.data).to.be.a('string'); 34 | 35 | const decrypted = await encryption.decrypt(encrypted, keys.privateKey); 36 | expect(decrypted).to.deep.equal(testData); 37 | }); 38 | }); 39 | 40 | describe('AES Encryption', () => { 41 | const config: EncryptionConfig = { 42 | algorithm: 'AES', 43 | keySize: 256, 44 | mode: 'gcm' 45 | }; 46 | let encryption: Encryption; 47 | 48 | beforeEach(() => { 49 | encryption = new Encryption(config); 50 | }); 51 | 52 | it('should generate AES key', async () => { 53 | const keys = await encryption.generateKeys(); 54 | expect(keys.publicKey).to.be.a('string'); 55 | expect(keys.privateKey).to.be.a('string'); 56 | expect(keys.publicKey).to.have.lengthOf(64); // 256 bits = 64 hex chars 57 | }); 58 | 59 | it('should encrypt and decrypt data correctly', async () => { 60 | const testData = { message: 'test message' }; 61 | const keys = await encryption.generateKeys(); 62 | 63 | const encrypted = await encryption.encrypt(testData, keys.publicKey); 64 | expect(encrypted.data).to.be.a('string'); 65 | expect(encrypted.iv).to.be.a('string'); 66 | expect(encrypted.tag).to.be.a('string'); 67 | 68 | const decrypted = await encryption.decrypt(encrypted, keys.privateKey); 69 | expect(decrypted).to.deep.equal(testData); 70 | }); 71 | 72 | it('should throw error for invalid data', async () => { 73 | const keys = await encryption.generateKeys(); 74 | const invalidData = { data: 'invalid', iv: 'invalid', tag: 'invalid' }; 75 | 76 | await expect(encryption.decrypt(invalidData, keys.privateKey)) 77 | .to.be.rejectedWith(Error); 78 | }); 79 | }); 80 | }); -------------------------------------------------------------------------------- /packages/crypto/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src", 6 | "composite": true, 7 | // "noEmit": true, 8 | 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", "dist", "test"] 12 | } -------------------------------------------------------------------------------- /packages/protocol/docs/api.md: -------------------------------------------------------------------------------- 1 | # API 文档 2 | 3 | ## 类 4 | 5 | ### MPCProtocol 6 | 7 | 安全多方计算协议的主要实现类。 8 | 9 | #### 构造函数 10 | 11 | ```typescript 12 | constructor() 13 | ``` 14 | 15 | 创建新的MPCProtocol实例。 16 | 17 | #### 方法 18 | 19 | ##### 初始化和会话管理 20 | 21 | ```typescript 22 | async initialize(): Promise 23 | ``` 24 | 初始化协议,必须在使用其他方法前调用。 25 | 26 | ```typescript 27 | async start(): Promise 28 | ``` 29 | 启动协议,开始处理消息。 30 | 31 | ```typescript 32 | async stop(): Promise 33 | ``` 34 | 停止协议,清理资源。 35 | 36 | ```typescript 37 | async createSession(participants: Participant[]): Promise 38 | ``` 39 | 创建新的计算会话。 40 | - `participants`: 参与计算的成员列表 41 | - 返回: 创建的会话对象 42 | 43 | ```typescript 44 | async joinSession(sessionId: string): Promise 45 | ``` 46 | 加入已存在的会话。 47 | - `sessionId`: 要加入的会话ID 48 | 49 | ```typescript 50 | async leaveSession(sessionId: string): Promise 51 | ``` 52 | 离开当前会话。 53 | - `sessionId`: 要离开的会话ID 54 | 55 | ##### 计算操作 56 | 57 | ```typescript 58 | setLocalValue(value: Buffer): void 59 | ``` 60 | 设置本地计算值。 61 | - `value`: 要参与计算的本地值 62 | 63 | ```typescript 64 | async startComputation(type: MPCComputationType): Promise 65 | ``` 66 | 开始指定类型的计算。 67 | - `type`: 计算类型 68 | 69 | ```typescript 70 | isComplete(): boolean 71 | ``` 72 | 检查计算是否完成。 73 | - 返回: 如果计算完成则为true 74 | 75 | ```typescript 76 | getResult(): Buffer | undefined 77 | ``` 78 | 获取计算结果。 79 | - 返回: 计算结果,如果未完成则为undefined 80 | 81 | ##### 消息处理 82 | 83 | ```typescript 84 | onMessage(handler: (message: Message) => Promise): void 85 | ``` 86 | 注册消息处理器。 87 | - `handler`: 处理接收到消息的回调函数 88 | 89 | ```typescript 90 | setBatchParameters(batchSize: number, batchTimeout: number): void 91 | ``` 92 | 设置消息批处理参数。 93 | - `batchSize`: 每批处理的消息数量 94 | - `batchTimeout`: 批处理超时时间(毫秒) 95 | 96 | ### Message 97 | 98 | 表示协议中传输的消息。 99 | 100 | ```typescript 101 | interface Message { 102 | type: string; // 消息类型 103 | sender: string; // 发送者ID 104 | receiver: string; // 接收者ID 105 | content: Buffer; // 消息内容 106 | timestamp: Date; // 时间戳 107 | } 108 | ``` 109 | 110 | ### Session 111 | 112 | 表示一个计算会话。 113 | 114 | ```typescript 115 | interface Session { 116 | id: string; // 会话ID 117 | participants: Participant[]; // 参与者列表 118 | state: any; // 会话状态 119 | startTime: Date; // 开始时间 120 | endTime?: Date; // 结束时间 121 | } 122 | ``` 123 | 124 | ### Participant 125 | 126 | 表示参与计算的成员。 127 | 128 | ```typescript 129 | interface Participant { 130 | id: string; // 参与者ID 131 | publicKey: Buffer; // 公钥 132 | } 133 | ``` 134 | 135 | ## 枚举 136 | 137 | ### MPCComputationType 138 | 139 | 支持的计算类型。 140 | 141 | ```typescript 142 | enum MPCComputationType { 143 | SUM = 'SUM', // 计算和 144 | AVERAGE = 'AVERAGE', // 计算平均值 145 | MAX = 'MAX', // 计算最大值 146 | MIN = 'MIN', // 计算最小值 147 | MEDIAN = 'MEDIAN', // 计算中位数 148 | VARIANCE = 'VARIANCE', // 计算方差 149 | MODE = 'MODE', // 计算众数 150 | STD_DEV = 'STD_DEV', // 计算标准差 151 | QUARTILE = 'QUARTILE',// 计算四分位数 152 | RANGE = 'RANGE' // 计算范围 153 | } 154 | ``` 155 | 156 | ### MPCMessageType 157 | 158 | 协议中使用的消息类型。 159 | 160 | ```typescript 161 | enum MPCMessageType { 162 | SET_VALUE = 'SET_VALUE', // 设置值 163 | SHARE = 'SHARE', // 共享值 164 | RESULT = 'RESULT', // 结果 165 | RECOVERY = 'RECOVERY', // 恢复 166 | VERIFY = 'VERIFY', // 验证 167 | COMMITMENT = 'COMMITMENT' // 承诺 168 | } 169 | ``` 170 | 171 | ## 错误处理 172 | 173 | ### ProtocolError 174 | 175 | 协议错误类型。 176 | 177 | ```typescript 178 | enum ProtocolErrorType { 179 | INVALID_STATE = 'INVALID_STATE', // 无效状态 180 | INVALID_MESSAGE = 'INVALID_MESSAGE', // 无效消息 181 | TIMEOUT = 'TIMEOUT', // 超时 182 | VERIFICATION_FAILED = 'VERIFICATION_FAILED' // 验证失败 183 | } 184 | ``` 185 | 186 | ## 安全特性 187 | 188 | ### 值盲化 189 | 190 | ```typescript 191 | private blindValue(value: Buffer): Buffer 192 | ``` 193 | 对值进行盲化处理。 194 | - `value`: 要盲化的值 195 | - 返回: 盲化后的值 196 | 197 | ### 审计日志 198 | 199 | ```typescript 200 | interface AuditLog { 201 | timestamp: Date; // 时间戳 202 | event: string; // 事件类型 203 | participantId: string;// 参与者ID 204 | sessionId: string; // 会话ID 205 | details: any; // 详细信息 206 | } 207 | ``` 208 | 209 | ## 批处理 210 | 211 | ### MessageBatch 212 | 213 | 表示一批要处理的消息。 214 | 215 | ```typescript 216 | interface MessageBatch { 217 | messages: Message[]; // 消息列表 218 | timestamp: Date; // 时间戳 219 | } 220 | ``` 221 | 222 | ## 使用建议 223 | 224 | 1. 初始化顺序: 225 | ```typescript 226 | const protocol = new MPCProtocol(); 227 | await protocol.initialize(); 228 | await protocol.start(); 229 | ``` 230 | 231 | 2. 错误处理: 232 | ```typescript 233 | try { 234 | await protocol.startComputation(MPCComputationType.SUM); 235 | } catch (error) { 236 | if (error.type === ProtocolErrorType.TIMEOUT) { 237 | // 处理超时 238 | } 239 | } 240 | ``` 241 | 242 | 3. 性能优化: 243 | ```typescript 244 | // 根据网络条件调整批处理参数 245 | protocol.setBatchParameters(100, 50); 246 | ``` 247 | 248 | 4. 安全性: 249 | - 使用足够长的密钥 250 | - 定期更换会话 251 | - 启用审计日志 252 | - 验证所有输入 -------------------------------------------------------------------------------- /packages/protocol/docs/deployment.md: -------------------------------------------------------------------------------- 1 | # 部署指南 2 | 3 | ## 系统要求 4 | 5 | ### 硬件要求 6 | - CPU: 2核或以上 7 | - 内存: 4GB或以上 8 | - 磁盘空间: 1GB或以上 9 | 10 | ### 软件要求 11 | - Node.js 16.x或更高版本 12 | - npm 7.x或更高版本 13 | - TypeScript 4.x或更高版本 14 | 15 | ## 安装 16 | 17 | ### 使用npm安装 18 | ```bash 19 | npm install @ciphernx/protocol 20 | ``` 21 | 22 | ### 使用yarn安装 23 | ```bash 24 | yarn add @ciphernx/protocol 25 | ``` 26 | 27 | ## 环境配置 28 | 29 | ### 开发环境 30 | ```bash 31 | # 安装依赖 32 | npm install 33 | 34 | # 编译TypeScript 35 | npm run build 36 | 37 | # 运行测试 38 | npm test 39 | ``` 40 | 41 | ### 生产环境 42 | ```bash 43 | # 安装生产依赖 44 | npm install --production 45 | 46 | # 编译并压缩 47 | npm run build:prod 48 | ``` 49 | 50 | ## 配置选项 51 | 52 | ### 环境变量 53 | ```bash 54 | # 日志级别 55 | LOG_LEVEL=info 56 | 57 | # 批处理大小 58 | BATCH_SIZE=100 59 | 60 | # 批处理超时(毫秒) 61 | BATCH_TIMEOUT=50 62 | 63 | # 会话超时(秒) 64 | SESSION_TIMEOUT=3600 65 | ``` 66 | 67 | ### 配置文件示例 68 | ```json 69 | { 70 | "protocol": { 71 | "logLevel": "info", 72 | "batchSize": 100, 73 | "batchTimeout": 50, 74 | "sessionTimeout": 3600, 75 | "security": { 76 | "keyLength": 2048, 77 | "hashAlgorithm": "sha256", 78 | "enableAuditLog": true 79 | }, 80 | "recovery": { 81 | "enabled": true, 82 | "backupCount": 3 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | ## 安全配置 89 | 90 | ### 密钥管理 91 | - 使用安全的密钥生成方法 92 | - 定期轮换密钥 93 | - 安全存储密钥 94 | - 使用密钥管理服务(推荐) 95 | 96 | ### 网络安全 97 | - 启用TLS/SSL 98 | - 配置防火墙规则 99 | - 限制访问IP 100 | - 使用安全的通信协议 101 | 102 | ### 审计日志 103 | - 配置日志存储位置 104 | - 设置日志轮转策略 105 | - 实现日志监控 106 | - 定期备份日志 107 | 108 | ## 性能优化 109 | 110 | ### 批处理配置 111 | ```typescript 112 | // 根据系统负载调整批处理参数 113 | protocol.setBatchParameters({ 114 | batchSize: 100, // 每批消息数 115 | batchTimeout: 50, // 批处理超时时间 116 | maxBatchSize: 1000, // 最大批大小 117 | minBatchSize: 10 // 最小批大小 118 | }); 119 | ``` 120 | 121 | ### 内存管理 122 | - 定期清理过期会话 123 | - 监控内存使用 124 | - 实现资源限制 125 | - 优化大型计算 126 | 127 | ### 并发处理 128 | - 合理设置并发数 129 | - 使用连接池 130 | - 实现负载均衡 131 | - 错误重试机制 132 | 133 | ## 监控和维护 134 | 135 | ### 健康检查 136 | ```typescript 137 | // 实现健康检查接口 138 | app.get('/health', (req, res) => { 139 | const status = protocol.getStatus(); 140 | res.json({ 141 | status: status.isRunning ? 'UP' : 'DOWN', 142 | details: { 143 | activeSessions: status.sessionCount, 144 | pendingComputations: status.computationCount, 145 | memoryUsage: process.memoryUsage() 146 | } 147 | }); 148 | }); 149 | ``` 150 | 151 | ### 指标收集 152 | - CPU使用率 153 | - 内存使用 154 | - 活动会话数 155 | - 消息处理延迟 156 | - 计算完成时间 157 | 158 | ### 告警配置 159 | - 设置资源使用阈值 160 | - 配置错误率告警 161 | - 监控响应时间 162 | - 异常行为检测 163 | 164 | ## 灾难恢复 165 | 166 | ### 备份策略 167 | - 定期备份配置 168 | - 备份会话状态 169 | - 存储审计日志 170 | - 实现快速恢复 171 | 172 | ### 故障转移 173 | - 配置备用节点 174 | - 实现自动切换 175 | - 数据同步机制 176 | - 恢复验证 177 | 178 | ## 扩展性 179 | 180 | ### 水平扩展 181 | - 添加新节点 182 | - 配置负载均衡 183 | - 会话同步 184 | - 状态一致性 185 | 186 | ### 垂直扩展 187 | - 增加系统资源 188 | - 优化性能配置 189 | - 调整内存分配 190 | - 提升处理能力 191 | 192 | ## 生产环境检查清单 193 | 194 | ### 部署前 195 | - [ ] 完成所有测试 196 | - [ ] 检查配置文件 197 | - [ ] 验证环境变量 198 | - [ ] 确认依赖版本 199 | - [ ] 检查安全设置 200 | - [ ] 准备回滚方案 201 | 202 | ### 部署后 203 | - [ ] 验证服务状态 204 | - [ ] 检查日志输出 205 | - [ ] 测试核心功能 206 | - [ ] 监控系统指标 207 | - [ ] 确认告警配置 208 | - [ ] 验证备份恢复 209 | 210 | ## 常见问题 211 | 212 | ### 性能问题 213 | 1. 消息处理延迟 214 | - 检查网络连接 215 | - 优化批处理参数 216 | - 增加系统资源 217 | 218 | 2. 内存泄漏 219 | - 及时清理会话 220 | - 优化资源使用 221 | - 监控内存占用 222 | 223 | ### 安全问题 224 | 1. 密钥泄露 225 | - 立即轮换密钥 226 | - 审计访问日志 227 | - 更新安全策略 228 | 229 | 2. 未授权访问 230 | - 检查访问控制 231 | - 更新防火墙规则 232 | - 加强认证机制 233 | 234 | ## 维护指南 235 | 236 | ### 日常维护 237 | - 监控系统状态 238 | - 检查错误日志 239 | - 清理过期数据 240 | - 更新安全补丁 241 | 242 | ### 版本升级 243 | - 备份当前版本 244 | - 测试新版本 245 | - 规划升级时间 246 | - 准备回滚方案 247 | 248 | ### 故障处理 249 | - 记录问题现象 250 | - 分析错误日志 251 | - 实施修复方案 252 | - 验证系统恢复 253 | 254 | ## 支持资源 255 | 256 | ### 文档 257 | - [API文档](./api.md) 258 | - [使用示例](./examples.md) 259 | - [更新日志](./CHANGELOG.md) 260 | 261 | ### 社区 262 | - GitHub Issues 263 | - Stack Overflow 264 | - 技术支持邮箱 265 | 266 | ### 工具 267 | - 监控面板 268 | - 日志分析 269 | - 性能测试 270 | - 安全扫描 -------------------------------------------------------------------------------- /packages/protocol/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cipher-nexus/protocol", 3 | "version": "1.0.0", 4 | "description": "Privacy-preserving protocols for CipherNexus", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "test": "mocha -r ts-node/register 'test/**/*.test.ts'", 10 | "clean": "rimraf dist", 11 | "lint": "eslint src --ext .ts" 12 | }, 13 | "dependencies": { 14 | "@cipher-nexus/crypto": "^1.0.0" 15 | }, 16 | "devDependencies": { 17 | "@types/chai": "^4.3.0", 18 | "@types/mocha": "^9.1.0", 19 | "@types/node": "^17.0.21", 20 | "chai": "^4.3.6", 21 | "mocha": "^9.2.2", 22 | "ts-node": "^10.7.0", 23 | "typescript": "^4.6.2", 24 | "rimraf": "^5.0.1", 25 | "eslint": "^8.38.0", 26 | "@typescript-eslint/parser": "^5.59.0", 27 | "@typescript-eslint/eslint-plugin": "^5.59.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/protocol/src/core/privacy.ts: -------------------------------------------------------------------------------- 1 | import { PrivacyConfig } from '../types'; 2 | import { Encryption } from '@cipher-nexus/crypto'; 3 | 4 | export class PrivacyProtocol { 5 | private encryption: Encryption; 6 | 7 | constructor(private config: PrivacyConfig) { 8 | this.encryption = new Encryption(this.getEncryptionConfig()); 9 | } 10 | 11 | async encrypt(data: any): Promise { 12 | try { 13 | // Generate encryption keys 14 | const keys = await this.encryption.generateKeys(); 15 | 16 | // Encrypt data 17 | const encryptedData = await this.encryption.encrypt(data, keys.publicKey); 18 | 19 | return encryptedData; 20 | } catch (error: any) { 21 | throw new Error(`Encryption failed: ${error.message}`); 22 | } 23 | } 24 | 25 | async decrypt(encryptedData: any): Promise { 26 | try { 27 | // Generate or retrieve decryption key 28 | const keys = await this.encryption.generateKeys(); 29 | 30 | // Decrypt data 31 | return await this.encryption.decrypt(encryptedData, keys.privateKey); 32 | } catch (error: any) { 33 | throw new Error(`Decryption failed: ${error.message}`); 34 | } 35 | } 36 | 37 | private getEncryptionConfig() { 38 | switch (this.config.encryptionLevel) { 39 | case 'high': 40 | return { 41 | algorithm: 'FHE', 42 | keySize: 3072 43 | }; 44 | case 'medium': 45 | return { 46 | algorithm: 'RSA', 47 | keySize: 2048 48 | }; 49 | case 'basic': 50 | default: 51 | return { 52 | algorithm: 'AES', 53 | keySize: 256, 54 | mode: 'gcm' 55 | }; 56 | } 57 | } 58 | 59 | private async applyHomomorphicEncryption(data: any): Promise { 60 | // TODO: Implement homomorphic encryption transformation 61 | if (this.config.useHomomorphicEncryption) { 62 | throw new Error('Homomorphic encryption not implemented yet'); 63 | } 64 | return data; 65 | } 66 | 67 | private async reverseHomomorphicEncryption(data: any): Promise { 68 | // TODO: Implement homomorphic encryption reverse transformation 69 | if (this.config.useHomomorphicEncryption) { 70 | throw new Error('Homomorphic encryption reverse transformation not implemented yet'); 71 | } 72 | return data; 73 | } 74 | 75 | private async generateZKProof(data: any): Promise { 76 | // TODO: Implement zero-knowledge proof generation 77 | if (this.config.useZeroKnowledgeProof) { 78 | throw new Error('Zero-knowledge proof generation not implemented yet'); 79 | } 80 | return data; 81 | } 82 | 83 | private async verifyZKProof(data: any): Promise { 84 | // TODO: Implement zero-knowledge proof verification 85 | if (this.config.useZeroKnowledgeProof) { 86 | throw new Error('Zero-knowledge proof verification not implemented yet'); 87 | } 88 | return true; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /packages/protocol/src/core/training.ts: -------------------------------------------------------------------------------- 1 | import { TrainingConfig } from '../types'; 2 | import { PrivacyProtocol } from './privacy'; 3 | 4 | export class TrainingProtocol { 5 | private privacyProtocol: PrivacyProtocol; 6 | 7 | constructor(private config: TrainingConfig) { 8 | this.privacyProtocol = new PrivacyProtocol(config.privacyConfig); 9 | } 10 | 11 | async initializeTraining(): Promise { 12 | // TODO: Initialize training environment 13 | } 14 | 15 | async processDataBatch(data: any[]): Promise { 16 | const encryptedData = await this.privacyProtocol.encrypt(data); 17 | // TODO: Process encrypted data 18 | return encryptedData; 19 | } 20 | 21 | async validateResults(results: any): Promise { 22 | // TODO: Implement validation logic 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/protocol/src/core/types.ts: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer'; 2 | 3 | /** 4 | * Base interface for all protocol participants 5 | */ 6 | export interface Participant { 7 | id: string; 8 | publicKey: Buffer; 9 | } 10 | 11 | /** 12 | * Protocol session interface 13 | */ 14 | export interface Session { 15 | id: string; 16 | participants: string[]; 17 | localParticipantId: string; 18 | startTime: Date; 19 | endTime?: Date; 20 | } 21 | 22 | /** 23 | * Protocol message interface 24 | */ 25 | export interface Message { 26 | session: Session; 27 | type: string; 28 | data?: any; 29 | senderId: string; 30 | receiverId: string; 31 | timestamp: Date; 32 | } 33 | 34 | /** 35 | * Protocol error types 36 | */ 37 | export enum ProtocolErrorType { 38 | INVALID_STATE = 'INVALID_STATE', 39 | INVALID_MESSAGE = 'INVALID_MESSAGE', 40 | INVALID_PARTICIPANT = 'INVALID_PARTICIPANT', 41 | INTERNAL_ERROR = 'INTERNAL_ERROR', 42 | NETWORK_ERROR = 'NETWORK_ERROR', 43 | TIMEOUT = 'TIMEOUT' 44 | } 45 | 46 | /** 47 | * Protocol error interface 48 | */ 49 | export interface ProtocolError extends Error { 50 | type: ProtocolErrorType; 51 | details?: any; 52 | } 53 | 54 | /** 55 | * Protocol options interface 56 | */ 57 | export interface ProtocolOptions { 58 | timeout?: number; 59 | retryCount?: number; 60 | verifySignatures?: boolean; 61 | logLevel?: 'debug' | 'info' | 'warn' | 'error'; 62 | } 63 | 64 | /** 65 | * Base protocol interface 66 | */ 67 | export interface Protocol { 68 | // Protocol lifecycle 69 | initialize(options?: ProtocolOptions): Promise; 70 | start(): Promise; 71 | stop(): Promise; 72 | 73 | // Session management 74 | createSession(participants: Participant[]): Promise; 75 | joinSession(sessionId: string): Promise; 76 | leaveSession(sessionId: string): Promise; 77 | 78 | // Message handling 79 | sendMessage(message: Message): Promise; 80 | onMessage(handler: (message: Message) => Promise): void; 81 | 82 | // Error handling 83 | onError(handler: (error: ProtocolError) => void): void; 84 | } 85 | 86 | /** 87 | * Protocol state interface 88 | */ 89 | export interface ProtocolState { 90 | isInitialized: boolean; 91 | isRunning: boolean; 92 | currentSession?: Session; 93 | activeSessions: Map; 94 | messageHandlers: Set<(message: Message) => Promise>; 95 | errorHandlers: Set<(error: ProtocolError) => void>; 96 | } -------------------------------------------------------------------------------- /packages/protocol/src/index.ts: -------------------------------------------------------------------------------- 1 | // Core protocols 2 | export * from './core/types'; 3 | export * from './core/base'; 4 | export * from './core/key-exchange'; 5 | 6 | // Protocol utilities 7 | export * from './utils/logger'; 8 | export * from './utils/validator'; 9 | 10 | export { PrivacyProtocol } from './core/privacy'; 11 | export * from './types'; 12 | -------------------------------------------------------------------------------- /packages/protocol/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface PrivacyConfig { 2 | encryptionLevel: 'basic' | 'medium' | 'high'; 3 | useHomomorphicEncryption: boolean; 4 | useZeroKnowledgeProof: boolean; 5 | } -------------------------------------------------------------------------------- /packages/protocol/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface PrivacyConfig { 2 | encryptionLevel: 'basic' | 'medium' | 'high'; 3 | useHomomorphicEncryption: boolean; 4 | useZeroKnowledgeProof: boolean; 5 | } 6 | 7 | export interface TrainingConfig { 8 | batchSize: number; 9 | epochs: number; 10 | learningRate: number; 11 | privacyConfig: PrivacyConfig; 12 | } 13 | -------------------------------------------------------------------------------- /packages/protocol/src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Log levels 3 | */ 4 | export enum LogLevel { 5 | DEBUG = 'debug', 6 | INFO = 'info', 7 | WARN = 'warn', 8 | ERROR = 'error' 9 | } 10 | 11 | /** 12 | * Logger interface 13 | */ 14 | export interface Logger { 15 | debug(message: string, ...args: any[]): void; 16 | info(message: string, ...args: any[]): void; 17 | warn(message: string, ...args: any[]): void; 18 | error(message: string, ...args: any[]): void; 19 | } 20 | 21 | /** 22 | * Protocol logger implementation 23 | */ 24 | export class ProtocolLogger implements Logger { 25 | private level: LogLevel; 26 | 27 | constructor(level: LogLevel = LogLevel.INFO) { 28 | this.level = level; 29 | } 30 | 31 | public setLevel(level: LogLevel): void { 32 | this.level = level; 33 | } 34 | 35 | public debug(message: string, ...args: any[]): void { 36 | if (this.shouldLog(LogLevel.DEBUG)) { 37 | console.debug(`[DEBUG] ${message}`, ...args); 38 | } 39 | } 40 | 41 | public info(message: string, ...args: any[]): void { 42 | if (this.shouldLog(LogLevel.INFO)) { 43 | console.info(`[INFO] ${message}`, ...args); 44 | } 45 | } 46 | 47 | public warn(message: string, ...args: any[]): void { 48 | if (this.shouldLog(LogLevel.WARN)) { 49 | console.warn(`[WARN] ${message}`, ...args); 50 | } 51 | } 52 | 53 | public error(message: string, ...args: any[]): void { 54 | if (this.shouldLog(LogLevel.ERROR)) { 55 | console.error(`[ERROR] ${message}`, ...args); 56 | } 57 | } 58 | 59 | private shouldLog(level: LogLevel): boolean { 60 | const levels = Object.values(LogLevel); 61 | const currentIndex = levels.indexOf(this.level); 62 | const messageIndex = levels.indexOf(level); 63 | return messageIndex >= currentIndex; 64 | } 65 | } -------------------------------------------------------------------------------- /packages/protocol/src/utils/validation.ts: -------------------------------------------------------------------------------- 1 | import { PrivacyConfig, TrainingConfig } from '../types'; 2 | 3 | export const validatePrivacyConfig = (config: PrivacyConfig): boolean => { 4 | const validLevels = ['basic', 'medium', 'high']; 5 | if (!validLevels.includes(config.encryptionLevel)) { 6 | throw new Error('Invalid encryption level'); 7 | } 8 | return true; 9 | }; 10 | 11 | export const validateTrainingConfig = (config: TrainingConfig): boolean => { 12 | if (config.batchSize <= 0 || config.epochs <= 0 || config.learningRate <= 0) { 13 | throw new Error('Invalid training parameters'); 14 | } 15 | validatePrivacyConfig(config.privacyConfig); 16 | return true; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/protocol/src/utils/validator.ts: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer'; 2 | import { Message, Participant, Session } from '../core/types'; 3 | 4 | /** 5 | * Protocol validator implementation 6 | */ 7 | export class ProtocolValidator { 8 | /** 9 | * Validate participant 10 | */ 11 | public static validateParticipant(participant: Participant): void { 12 | if (!participant) { 13 | throw new Error('Participant is required'); 14 | } 15 | 16 | if (!participant.id) { 17 | throw new Error('Participant ID is required'); 18 | } 19 | 20 | if (!participant.publicKey || !(participant.publicKey instanceof Buffer)) { 21 | throw new Error('Participant public key must be a Buffer'); 22 | } 23 | } 24 | 25 | /** 26 | * Validate session 27 | */ 28 | public static validateSession(session: Session): void { 29 | if (!session) { 30 | throw new Error('Session is required'); 31 | } 32 | 33 | if (!session.id) { 34 | throw new Error('Session ID is required'); 35 | } 36 | 37 | if (!session.participants || !Array.isArray(session.participants)) { 38 | throw new Error('Session participants must be an array'); 39 | } 40 | 41 | if (session.participants.length < 2) { 42 | throw new Error('Session must have at least 2 participants'); 43 | } 44 | 45 | session.participants.forEach(participant => { 46 | this.validateParticipant(participant); 47 | }); 48 | 49 | if (!session.startTime || !(session.startTime instanceof Date)) { 50 | throw new Error('Session start time must be a Date'); 51 | } 52 | 53 | if (session.endTime && !(session.endTime instanceof Date)) { 54 | throw new Error('Session end time must be a Date'); 55 | } 56 | } 57 | 58 | /** 59 | * Validate message 60 | */ 61 | public static validateMessage(message: Message): void { 62 | if (!message) { 63 | throw new Error('Message is required'); 64 | } 65 | 66 | if (!message.type) { 67 | throw new Error('Message type is required'); 68 | } 69 | 70 | if (!message.sender) { 71 | throw new Error('Message sender is required'); 72 | } 73 | 74 | if (!message.receiver) { 75 | throw new Error('Message receiver is required'); 76 | } 77 | 78 | if (!message.content || !(message.content instanceof Buffer)) { 79 | throw new Error('Message content must be a Buffer'); 80 | } 81 | 82 | if (message.signature && !(message.signature instanceof Buffer)) { 83 | throw new Error('Message signature must be a Buffer'); 84 | } 85 | 86 | if (!message.timestamp || !(message.timestamp instanceof Date)) { 87 | throw new Error('Message timestamp must be a Date'); 88 | } 89 | } 90 | 91 | /** 92 | * Validate buffer size 93 | */ 94 | public static validateBufferSize(buffer: Buffer, expectedSize: number, name: string): void { 95 | if (!buffer || !(buffer instanceof Buffer)) { 96 | throw new Error(`${name} must be a Buffer`); 97 | } 98 | 99 | if (buffer.length !== expectedSize) { 100 | throw new Error(`${name} must be ${expectedSize} bytes`); 101 | } 102 | } 103 | 104 | /** 105 | * Validate number range 106 | */ 107 | public static validateNumberRange(value: number, min: number, max: number, name: string): void { 108 | if (typeof value !== 'number') { 109 | throw new Error(`${name} must be a number`); 110 | } 111 | 112 | if (value < min || value > max) { 113 | throw new Error(`${name} must be between ${min} and ${max}`); 114 | } 115 | } 116 | 117 | /** 118 | * Validate array length 119 | */ 120 | public static validateArrayLength(array: any[], min: number, max: number, name: string): void { 121 | if (!array || !Array.isArray(array)) { 122 | throw new Error(`${name} must be an array`); 123 | } 124 | 125 | if (array.length < min || array.length > max) { 126 | throw new Error(`${name} must have between ${min} and ${max} elements`); 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /packages/protocol/test/privacy.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, use } from 'chai'; 2 | import chaiAsPromised from 'chai-as-promised'; 3 | import { PrivacyProtocol } from '../src/core/privacy'; 4 | import { PrivacyConfig } from '../src/types'; 5 | 6 | use(chaiAsPromised); 7 | 8 | describe('PrivacyProtocol', () => { 9 | describe('Basic Encryption', () => { 10 | const config: PrivacyConfig = { 11 | encryptionLevel: 'basic', 12 | useHomomorphicEncryption: false, 13 | useZeroKnowledgeProof: false 14 | }; 15 | let protocol: PrivacyProtocol; 16 | 17 | beforeEach(() => { 18 | protocol = new PrivacyProtocol(config); 19 | }); 20 | 21 | it('should encrypt and decrypt data correctly', async () => { 22 | const testData = { message: 'test message' }; 23 | 24 | const encrypted = await protocol.encrypt(testData); 25 | expect(encrypted).to.be.an('object'); 26 | 27 | const decrypted = await protocol.decrypt(encrypted); 28 | expect(decrypted).to.deep.equal(testData); 29 | }); 30 | }); 31 | 32 | describe('Medium Security', () => { 33 | const config: PrivacyConfig = { 34 | encryptionLevel: 'medium', 35 | useHomomorphicEncryption: false, 36 | useZeroKnowledgeProof: false 37 | }; 38 | let protocol: PrivacyProtocol; 39 | 40 | beforeEach(() => { 41 | protocol = new PrivacyProtocol(config); 42 | }); 43 | 44 | it('should use RSA encryption', async () => { 45 | const testData = { message: 'test message' }; 46 | 47 | const encrypted = await protocol.encrypt(testData); 48 | expect(encrypted).to.be.an('object'); 49 | 50 | const decrypted = await protocol.decrypt(encrypted); 51 | expect(decrypted).to.deep.equal(testData); 52 | }); 53 | }); 54 | 55 | describe('High Security', () => { 56 | const config: PrivacyConfig = { 57 | encryptionLevel: 'high', 58 | useHomomorphicEncryption: true, 59 | useZeroKnowledgeProof: true 60 | }; 61 | let protocol: PrivacyProtocol; 62 | 63 | beforeEach(() => { 64 | protocol = new PrivacyProtocol(config); 65 | }); 66 | 67 | it('should throw error for unimplemented FHE', async () => { 68 | const testData = { message: 'test message' }; 69 | 70 | await expect(protocol.encrypt(testData)) 71 | .to.be.rejectedWith('Homomorphic encryption not implemented yet'); 72 | }); 73 | }); 74 | 75 | describe('Error Handling', () => { 76 | const config: PrivacyConfig = { 77 | encryptionLevel: 'basic', 78 | useHomomorphicEncryption: false, 79 | useZeroKnowledgeProof: false 80 | }; 81 | let protocol: PrivacyProtocol; 82 | 83 | beforeEach(() => { 84 | protocol = new PrivacyProtocol(config); 85 | }); 86 | 87 | it('should handle invalid data gracefully', async () => { 88 | const invalidData = undefined; 89 | 90 | await expect(protocol.encrypt(invalidData)) 91 | .to.be.rejectedWith(Error); 92 | }); 93 | 94 | it('should handle decryption of invalid data', async () => { 95 | const invalidEncrypted = { data: 'invalid' }; 96 | 97 | await expect(protocol.decrypt(invalidEncrypted)) 98 | .to.be.rejectedWith(Error); 99 | }); 100 | }); 101 | }); -------------------------------------------------------------------------------- /packages/protocol/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src", 6 | "composite": true, 7 | }, 8 | "include": ["src"], 9 | "exclude": ["node_modules", "dist", "test"], 10 | "references": [ 11 | { "path": "../crypto" } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "dependencies": { 13 | "@auth0/auth0-react": "^2.3.0", 14 | "@mui/x-data-grid": "^7.24.0", 15 | "recharts": "^2.15.0", 16 | "socket.io-client": "^4.8.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | Cipher Nexus 15 | 16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /scripts/setup-dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install dependencies 4 | npm install 5 | 6 | # Create necessary directories 7 | mkdir -p packages/core/dist 8 | mkdir -p packages/protocol/dist 9 | mkdir -p packages/crypto/dist 10 | mkdir -p packages/ai/dist 11 | mkdir -p packages/ui/dist 12 | 13 | # Copy environment configuration 14 | cp .env.example .env 15 | 16 | # Start Docker services 17 | docker-compose up -d 18 | 19 | echo "Development environment setup completed!" 20 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; 3 | import { ThemeProvider, createTheme, CssBaseline } from '@mui/material'; 4 | import { MainLayout } from './layouts/MainLayout'; 5 | import { DatasetsPage } from './pages/DatasetsPage'; 6 | import { TrainingPage } from './pages/TrainingPage'; 7 | 8 | const theme = createTheme({ 9 | palette: { 10 | mode: 'light', 11 | primary: { 12 | main: '#1976d2', 13 | }, 14 | secondary: { 15 | main: '#dc004e', 16 | }, 17 | }, 18 | typography: { 19 | fontFamily: [ 20 | '-apple-system', 21 | 'BlinkMacSystemFont', 22 | '"Segoe UI"', 23 | 'Roboto', 24 | '"Helvetica Neue"', 25 | 'Arial', 26 | 'sans-serif', 27 | ].join(','), 28 | }, 29 | components: { 30 | MuiButton: { 31 | styleOverrides: { 32 | root: { 33 | textTransform: 'none', 34 | }, 35 | }, 36 | }, 37 | }, 38 | }); 39 | 40 | export function App() { 41 | return ( 42 | 43 | 44 | 45 | 46 | 47 | } /> 48 | } /> 49 | } /> 50 | 51 | 52 | 53 | 54 | ); 55 | } -------------------------------------------------------------------------------- /src/api/client.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; 2 | import { ApiResponse } from '../types'; 3 | 4 | class ApiClient { 5 | private client: AxiosInstance; 6 | 7 | constructor() { 8 | this.client = axios.create({ 9 | baseURL: process.env.REACT_APP_API_URL || 'http://localhost:3000/api', 10 | headers: { 11 | 'Content-Type': 'application/json', 12 | }, 13 | }); 14 | 15 | // Add request interceptor for authentication 16 | this.client.interceptors.request.use( 17 | (config) => { 18 | const token = localStorage.getItem('token'); 19 | if (token && config.headers) { 20 | config.headers.Authorization = `Bearer ${token}`; 21 | } 22 | return config; 23 | }, 24 | (error) => { 25 | return Promise.reject(error); 26 | } 27 | ); 28 | 29 | // Add response interceptor for error handling 30 | this.client.interceptors.response.use( 31 | (response) => response, 32 | (error) => { 33 | if (error.response?.status === 401) { 34 | // Handle unauthorized access 35 | localStorage.removeItem('token'); 36 | window.location.href = '/login'; 37 | } 38 | return Promise.reject(error); 39 | } 40 | ); 41 | } 42 | 43 | async get(url: string, config?: AxiosRequestConfig): Promise> { 44 | try { 45 | const response = await this.client.get>(url, config); 46 | return response.data; 47 | } catch (error: any) { 48 | return { 49 | success: false, 50 | error: error.message, 51 | }; 52 | } 53 | } 54 | 55 | async post(url: string, data?: any, config?: AxiosRequestConfig): Promise> { 56 | try { 57 | const response = await this.client.post>(url, data, config); 58 | return response.data; 59 | } catch (error: any) { 60 | return { 61 | success: false, 62 | error: error.message, 63 | }; 64 | } 65 | } 66 | 67 | async put(url: string, data?: any, config?: AxiosRequestConfig): Promise> { 68 | try { 69 | const response = await this.client.put>(url, data, config); 70 | return response.data; 71 | } catch (error: any) { 72 | return { 73 | success: false, 74 | error: error.message, 75 | }; 76 | } 77 | } 78 | 79 | async delete(url: string, config?: AxiosRequestConfig): Promise> { 80 | try { 81 | const response = await this.client.delete>(url, config); 82 | return response.data; 83 | } catch (error: any) { 84 | return { 85 | success: false, 86 | error: error.message, 87 | }; 88 | } 89 | } 90 | } 91 | 92 | export const apiClient = new ApiClient(); -------------------------------------------------------------------------------- /src/api/services.ts: -------------------------------------------------------------------------------- 1 | import { apiClient } from './client'; 2 | import { Dataset, TrainingTask, ApiResponse } from '../types'; 3 | 4 | // Dataset services 5 | export const datasetService = { 6 | getAll: () => apiClient.get('/datasets'), 7 | 8 | getById: (id: string) => apiClient.get(`/datasets/${id}`), 9 | 10 | create: (data: Omit) => 11 | apiClient.post('/datasets', data), 12 | 13 | update: (id: string, data: Partial) => 14 | apiClient.put(`/datasets/${id}`, data), 15 | 16 | delete: (id: string) => apiClient.delete(`/datasets/${id}`), 17 | 18 | upload: (id: string, file: File) => { 19 | const formData = new FormData(); 20 | formData.append('file', file); 21 | return apiClient.post(`/datasets/${id}/upload`, formData, { 22 | headers: { 23 | 'Content-Type': 'multipart/form-data', 24 | }, 25 | }); 26 | }, 27 | }; 28 | 29 | // Training task services 30 | export const trainingService = { 31 | getAll: () => apiClient.get('/training'), 32 | 33 | getById: (id: string) => apiClient.get(`/training/${id}`), 34 | 35 | create: (data: Omit) => 36 | apiClient.post('/training', data), 37 | 38 | update: (id: string, data: Partial) => 39 | apiClient.put(`/training/${id}`, data), 40 | 41 | delete: (id: string) => apiClient.delete(`/training/${id}`), 42 | 43 | start: (id: string) => apiClient.post(`/training/${id}/start`), 44 | 45 | stop: (id: string) => apiClient.post(`/training/${id}/stop`), 46 | 47 | getProgress: (id: string) => apiClient.get<{progress: number}>(`/training/${id}/progress`), 48 | 49 | getResults: (id: string) => apiClient.get>(`/training/${id}/results`), 50 | }; -------------------------------------------------------------------------------- /src/components/DataVisualization.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Box, 4 | Card, 5 | CardContent, 6 | Typography, 7 | Grid, 8 | FormControl, 9 | InputLabel, 10 | Select, 11 | MenuItem, 12 | } from '@mui/material'; 13 | import { 14 | LineChart, 15 | Line, 16 | XAxis, 17 | YAxis, 18 | CartesianGrid, 19 | Tooltip, 20 | Legend, 21 | ResponsiveContainer, 22 | BarChart, 23 | Bar, 24 | ScatterChart, 25 | Scatter, 26 | } from 'recharts'; 27 | 28 | interface DataPoint { 29 | [key: string]: number | string; 30 | } 31 | 32 | interface DataVisualizationProps { 33 | data: DataPoint[]; 34 | title: string; 35 | xAxis: string; 36 | yAxis: string; 37 | chartType?: 'line' | 'bar' | 'scatter'; 38 | } 39 | 40 | export function DataVisualization({ 41 | data, 42 | title, 43 | xAxis, 44 | yAxis, 45 | chartType = 'line', 46 | }: DataVisualizationProps) { 47 | const [selectedChart, setSelectedChart] = React.useState(chartType); 48 | 49 | const renderChart = () => { 50 | switch (selectedChart) { 51 | case 'line': 52 | return ( 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ); 62 | case 'bar': 63 | return ( 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ); 73 | case 'scatter': 74 | return ( 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | ); 84 | default: 85 | return null; 86 | } 87 | }; 88 | 89 | return ( 90 | 91 | 92 | 93 | 94 | {title} 95 | 96 | 97 | Chart Type 98 | 107 | 108 | 109 | 110 | 111 | {renderChart()} 112 | 113 | 114 | 115 | 116 | ); 117 | } -------------------------------------------------------------------------------- /src/components/DatasetList.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Card, 4 | CardContent, 5 | Typography, 6 | Grid, 7 | IconButton, 8 | Chip, 9 | Box, 10 | LinearProgress, 11 | Menu, 12 | MenuItem, 13 | } from '@mui/material'; 14 | import { 15 | MoreVert as MoreVertIcon, 16 | CloudUpload as UploadIcon, 17 | Edit as EditIcon, 18 | Delete as DeleteIcon, 19 | } from '@mui/icons-material'; 20 | import { Dataset } from '../types'; 21 | 22 | interface DatasetListProps { 23 | datasets: Dataset[]; 24 | onEdit: (dataset: Dataset) => void; 25 | onDelete: (dataset: Dataset) => void; 26 | onUpload: (dataset: Dataset) => void; 27 | } 28 | 29 | export function DatasetList({ datasets, onEdit, onDelete, onUpload }: DatasetListProps) { 30 | const [anchorEl, setAnchorEl] = React.useState(null); 31 | const [selectedDataset, setSelectedDataset] = React.useState(null); 32 | 33 | const handleMenuOpen = (event: React.MouseEvent, dataset: Dataset) => { 34 | setAnchorEl(event.currentTarget); 35 | setSelectedDataset(dataset); 36 | }; 37 | 38 | const handleMenuClose = () => { 39 | setAnchorEl(null); 40 | setSelectedDataset(null); 41 | }; 42 | 43 | const handleAction = (action: 'edit' | 'delete' | 'upload') => { 44 | if (selectedDataset) { 45 | switch (action) { 46 | case 'edit': 47 | onEdit(selectedDataset); 48 | break; 49 | case 'delete': 50 | onDelete(selectedDataset); 51 | break; 52 | case 'upload': 53 | onUpload(selectedDataset); 54 | break; 55 | } 56 | } 57 | handleMenuClose(); 58 | }; 59 | 60 | const getStatusColor = (status: Dataset['status']) => { 61 | switch (status) { 62 | case 'active': 63 | return 'success'; 64 | case 'processing': 65 | return 'warning'; 66 | case 'error': 67 | return 'error'; 68 | default: 69 | return 'default'; 70 | } 71 | }; 72 | 73 | return ( 74 | 75 | {datasets.map((dataset) => ( 76 | 77 | 78 | 79 | 80 | 81 | {dataset.name} 82 | 83 | handleMenuOpen(e, dataset)} 86 | > 87 | 88 | 89 | 90 | 91 | 92 | {dataset.description} 93 | 94 | 95 | 96 | 101 | 106 | 107 | 108 | 109 | Size: {(dataset.size / 1024 / 1024).toFixed(2)} MB 110 | 111 | 112 | {dataset.status === 'processing' && ( 113 | 114 | 115 | 116 | )} 117 | 118 | 119 | 120 | ))} 121 | 122 | 127 | handleAction('edit')}> 128 | 129 | Edit 130 | 131 | handleAction('upload')}> 132 | 133 | Upload Data 134 | 135 | handleAction('delete')} sx={{ color: 'error.main' }}> 136 | 137 | Delete 138 | 139 | 140 | 141 | ); 142 | } -------------------------------------------------------------------------------- /src/core/encryption.ts: -------------------------------------------------------------------------------- 1 | import { BigInteger } from 'jsbn'; 2 | import { randomBytes } from 'crypto'; 3 | 4 | interface KeyPair { 5 | publicKey: { 6 | n: BigInteger; 7 | e: BigInteger; 8 | }; 9 | privateKey: { 10 | n: BigInteger; 11 | d: BigInteger; 12 | }; 13 | } 14 | 15 | export class Encryption { 16 | private keyPair: KeyPair | null = null; 17 | 18 | // 生成RSA密钥对 19 | async generateKeys(bits: number = 2048): Promise { 20 | // 生成两个大素数 21 | const p = await this.generatePrime(bits / 2); 22 | const q = await this.generatePrime(bits / 2); 23 | 24 | // 计算n = p * q 25 | const n = p.multiply(q); 26 | 27 | // 计算欧拉函数φ(n) = (p-1)(q-1) 28 | const p1 = p.subtract(BigInteger.ONE); 29 | const q1 = q.subtract(BigInteger.ONE); 30 | const phi = p1.multiply(q1); 31 | 32 | // 选择公钥e,通常使用65537 33 | const e = new BigInteger('65537'); 34 | 35 | // 计算私钥d,使得ed ≡ 1 (mod φ(n)) 36 | const d = e.modInverse(phi); 37 | 38 | this.keyPair = { 39 | publicKey: { n, e }, 40 | privateKey: { n, d } 41 | }; 42 | 43 | return this.keyPair; 44 | } 45 | 46 | // 加密数据 47 | encrypt(data: string, publicKey: { n: BigInteger; e: BigInteger }): string { 48 | const m = new BigInteger(Buffer.from(data).toString('hex'), 16); 49 | if (m.compareTo(publicKey.n) >= 0) { 50 | throw new Error('Message too large'); 51 | } 52 | 53 | // 使用公钥加密: c = m^e mod n 54 | const c = m.modPow(publicKey.e, publicKey.n); 55 | return c.toString(16); 56 | } 57 | 58 | // 解密数据 59 | decrypt(encryptedData: string, privateKey: { n: BigInteger; d: BigInteger }): string { 60 | const c = new BigInteger(encryptedData, 16); 61 | 62 | // 使用私钥解密: m = c^d mod n 63 | const m = c.modPow(privateKey.d, privateKey.n); 64 | const decryptedBuffer = Buffer.from(m.toString(16), 'hex'); 65 | return decryptedBuffer.toString(); 66 | } 67 | 68 | // 生成大素数 69 | private async generatePrime(bits: number): Promise { 70 | while (true) { 71 | const candidate = this.generateRandomBigInt(bits); 72 | if (await this.isProbablePrime(candidate, 20)) { 73 | return candidate; 74 | } 75 | } 76 | } 77 | 78 | // 生成指定位数的随机大整数 79 | private generateRandomBigInt(bits: number): BigInteger { 80 | const bytes = Math.ceil(bits / 8); 81 | const randomBuffer = randomBytes(bytes); 82 | 83 | // 确保最高位为1 84 | randomBuffer[0] |= 0x80; 85 | 86 | return new BigInteger(randomBuffer.toString('hex'), 16); 87 | } 88 | 89 | // Miller-Rabin素性测试 90 | private async isProbablePrime(n: BigInteger, k: number): Promise { 91 | if (n.equals(BigInteger.ONE)) return false; 92 | if (n.equals(new BigInteger('2'))) return true; 93 | if (n.mod(new BigInteger('2')).equals(BigInteger.ZERO)) return false; 94 | 95 | let s = 0; 96 | let d = n.subtract(BigInteger.ONE); 97 | while (d.mod(new BigInteger('2')).equals(BigInteger.ZERO)) { 98 | s++; 99 | d = d.divide(new BigInteger('2')); 100 | } 101 | 102 | witnessLoop: for (let i = 0; i < k; i++) { 103 | const a = this.generateRandomBigInt(n.bitLength() - 1); 104 | let x = a.modPow(d, n); 105 | 106 | if (x.equals(BigInteger.ONE) || x.equals(n.subtract(BigInteger.ONE))) { 107 | continue; 108 | } 109 | 110 | for (let r = 1; r < s; r++) { 111 | x = x.modPow(new BigInteger('2'), n); 112 | if (x.equals(BigInteger.ONE)) return false; 113 | if (x.equals(n.subtract(BigInteger.ONE))) continue witnessLoop; 114 | } 115 | 116 | return false; 117 | } 118 | 119 | return true; 120 | } 121 | 122 | // 同态加密支持 123 | encryptForHomomorphic(data: number, publicKey: { n: BigInteger; e: BigInteger }): string { 124 | const m = new BigInteger(data.toString()); 125 | return this.encrypt(m.toString(), publicKey); 126 | } 127 | 128 | // 同态解密支持 129 | decryptForHomomorphic(encryptedData: string, privateKey: { n: BigInteger; d: BigInteger }): number { 130 | const decrypted = this.decrypt(encryptedData, privateKey); 131 | return parseInt(decrypted); 132 | } 133 | 134 | // 同态加法 135 | homomorphicAdd( 136 | enc1: string, 137 | enc2: string, 138 | publicKey: { n: BigInteger; e: BigInteger } 139 | ): string { 140 | const c1 = new BigInteger(enc1, 16); 141 | const c2 = new BigInteger(enc2, 16); 142 | const result = c1.multiply(c2).mod(publicKey.n); 143 | return result.toString(16); 144 | } 145 | 146 | // 同态乘法(标量) 147 | homomorphicMultiply( 148 | enc: string, 149 | scalar: number, 150 | publicKey: { n: BigInteger; e: BigInteger } 151 | ): string { 152 | const c = new BigInteger(enc, 16); 153 | const result = c.modPow(new BigInteger(scalar.toString()), publicKey.n); 154 | return result.toString(16); 155 | } 156 | } -------------------------------------------------------------------------------- /src/core/model.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for model architecture 3 | */ 4 | export interface ModelArchitecture { 5 | /** Array of layer configurations */ 6 | layers: Array<{ 7 | /** Type of the layer */ 8 | type: 'dense' | 'conv2d' | 'maxpool2d'; 9 | /** Number of units/filters in the layer */ 10 | units?: number; 11 | /** Activation function for the layer */ 12 | activation?: string; 13 | /** Kernel size for convolutional layers */ 14 | kernelSize?: [number, number]; 15 | /** Stride for convolutional layers */ 16 | stride?: [number, number]; 17 | /** Padding for convolutional layers */ 18 | padding?: 'valid' | 'same'; 19 | }>; 20 | } 21 | 22 | /** 23 | * Configuration for model training and architecture 24 | */ 25 | export interface ModelConfig { 26 | /** Model architecture configuration */ 27 | architecture: ModelArchitecture; 28 | /** Learning rate for training */ 29 | learningRate: number; 30 | /** Batch size for training */ 31 | batchSize: number; 32 | /** Number of training epochs */ 33 | epochs: number; 34 | /** Optimizer type */ 35 | optimizer: 'sgd' | 'adam'; 36 | } 37 | 38 | /** 39 | * Training and evaluation metrics 40 | */ 41 | export interface ModelMetrics { 42 | /** Loss value */ 43 | loss: number; 44 | /** Accuracy value */ 45 | accuracy: number; 46 | /** Current epoch */ 47 | epoch: number; 48 | /** Current step within epoch */ 49 | step: number; 50 | /** Total steps in epoch */ 51 | totalSteps: number; 52 | /** Validation loss (optional) */ 53 | validationLoss?: number; 54 | /** Validation accuracy (optional) */ 55 | validationAccuracy?: number; 56 | } 57 | 58 | /** 59 | * Abstract base class for machine learning models 60 | * Provides common functionality and interface for different model implementations 61 | */ 62 | export abstract class Model { 63 | /** Model configuration */ 64 | protected config: ModelConfig; 65 | /** Model parameters */ 66 | protected parameters: number[] = []; 67 | /** Current training metrics */ 68 | protected metrics: ModelMetrics = { 69 | loss: 0, 70 | accuracy: 0, 71 | epoch: 0, 72 | step: 0, 73 | totalSteps: 0 74 | }; 75 | 76 | /** 77 | * Creates a new model instance 78 | * @param config Model configuration 79 | */ 80 | constructor(config: ModelConfig) { 81 | this.config = config; 82 | } 83 | 84 | /** 85 | * Initializes model parameters and state 86 | * Must be implemented by derived classes 87 | */ 88 | abstract initialize(): Promise; 89 | 90 | /** 91 | * Trains the model on provided dataset 92 | * Must be implemented by derived classes 93 | * @param data Training data 94 | * @param labels Training labels 95 | * @returns Training metrics 96 | */ 97 | abstract train(data: number[][], labels: number[][]): Promise; 98 | 99 | /** 100 | * Makes predictions using the trained model 101 | * Must be implemented by derived classes 102 | * @param data Input data 103 | * @returns Predicted outputs 104 | */ 105 | abstract predict(data: number[][]): Promise; 106 | 107 | /** 108 | * Evaluates model performance on test dataset 109 | * Must be implemented by derived classes 110 | * @param data Test data 111 | * @param labels Test labels 112 | * @returns Evaluation metrics 113 | */ 114 | abstract evaluate(data: number[][], labels: number[][]): Promise; 115 | 116 | /** 117 | * Gets current model parameters 118 | * @returns Array of model parameters 119 | */ 120 | getParameters(): number[] { 121 | return this.parameters; 122 | } 123 | 124 | /** 125 | * Updates model parameters 126 | * @param parameters New parameter values 127 | */ 128 | updateParameters(parameters: number[]): void { 129 | if (parameters.length !== this.parameters.length) { 130 | throw new Error('Parameter length mismatch'); 131 | } 132 | this.parameters = [...parameters]; 133 | } 134 | 135 | /** 136 | * Gets current training metrics 137 | * @returns Current metrics 138 | */ 139 | getMetrics(): ModelMetrics { 140 | return { ...this.metrics }; 141 | } 142 | 143 | /** 144 | * Gets model configuration 145 | * @returns Current configuration 146 | */ 147 | getConfig(): ModelConfig { 148 | return { ...this.config }; 149 | } 150 | 151 | /** 152 | * Computes gradients for parameter updates 153 | * Must be implemented by derived classes 154 | * @param data Input data 155 | * @param labels Target labels 156 | * @returns Computed gradients 157 | */ 158 | protected abstract computeGradients(data: number[][], labels: number[][]): Promise; 159 | 160 | /** 161 | * Applies computed gradients to update parameters 162 | * Must be implemented by derived classes 163 | * @param gradients Computed gradients 164 | */ 165 | protected abstract applyGradients(gradients: number[]): Promise; 166 | } -------------------------------------------------------------------------------- /src/hooks/useDatasets.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useCallback } from 'react'; 2 | import { Dataset, ApiResponse } from '../types'; 3 | import { datasetService } from '../api/services'; 4 | 5 | export function useDatasets() { 6 | const [datasets, setDatasets] = useState([]); 7 | const [loading, setLoading] = useState(false); 8 | const [error, setError] = useState(null); 9 | 10 | const fetchDatasets = useCallback(async () => { 11 | setLoading(true); 12 | setError(null); 13 | try { 14 | const response = await datasetService.getAll(); 15 | if (response.success && response.data) { 16 | setDatasets(response.data); 17 | } else { 18 | setError(response.error || 'Failed to fetch datasets'); 19 | } 20 | } catch (err: any) { 21 | setError(err.message); 22 | } finally { 23 | setLoading(false); 24 | } 25 | }, []); 26 | 27 | const createDataset = useCallback(async (data: Omit) => { 28 | setLoading(true); 29 | setError(null); 30 | try { 31 | const response = await datasetService.create(data); 32 | if (response.success && response.data) { 33 | setDatasets(prev => [...prev, response.data]); 34 | return response.data; 35 | } else { 36 | setError(response.error || 'Failed to create dataset'); 37 | return null; 38 | } 39 | } catch (err: any) { 40 | setError(err.message); 41 | return null; 42 | } finally { 43 | setLoading(false); 44 | } 45 | }, []); 46 | 47 | const updateDataset = useCallback(async (id: string, data: Partial) => { 48 | setLoading(true); 49 | setError(null); 50 | try { 51 | const response = await datasetService.update(id, data); 52 | if (response.success && response.data) { 53 | setDatasets(prev => prev.map(dataset => 54 | dataset.id === id ? response.data : dataset 55 | )); 56 | return response.data; 57 | } else { 58 | setError(response.error || 'Failed to update dataset'); 59 | return null; 60 | } 61 | } catch (err: any) { 62 | setError(err.message); 63 | return null; 64 | } finally { 65 | setLoading(false); 66 | } 67 | }, []); 68 | 69 | const deleteDataset = useCallback(async (id: string) => { 70 | setLoading(true); 71 | setError(null); 72 | try { 73 | const response = await datasetService.delete(id); 74 | if (response.success) { 75 | setDatasets(prev => prev.filter(dataset => dataset.id !== id)); 76 | return true; 77 | } else { 78 | setError(response.error || 'Failed to delete dataset'); 79 | return false; 80 | } 81 | } catch (err: any) { 82 | setError(err.message); 83 | return false; 84 | } finally { 85 | setLoading(false); 86 | } 87 | }, []); 88 | 89 | const uploadDataset = useCallback(async (id: string, file: File) => { 90 | setLoading(true); 91 | setError(null); 92 | try { 93 | const response = await datasetService.upload(id, file); 94 | if (response.success) { 95 | await fetchDatasets(); // Refresh the list 96 | return true; 97 | } else { 98 | setError(response.error || 'Failed to upload dataset'); 99 | return false; 100 | } 101 | } catch (err: any) { 102 | setError(err.message); 103 | return false; 104 | } finally { 105 | setLoading(false); 106 | } 107 | }, [fetchDatasets]); 108 | 109 | useEffect(() => { 110 | fetchDatasets(); 111 | }, [fetchDatasets]); 112 | 113 | return { 114 | datasets, 115 | loading, 116 | error, 117 | fetchDatasets, 118 | createDataset, 119 | updateDataset, 120 | deleteDataset, 121 | uploadDataset, 122 | }; 123 | } -------------------------------------------------------------------------------- /src/hooks/useTrainingProgress.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useCallback } from 'react'; 2 | import wsService from '../services/websocket'; 3 | 4 | interface Metrics { 5 | loss: number; 6 | accuracy: number; 7 | epoch: number; 8 | step: number; 9 | totalSteps: number; 10 | learningRate: number; 11 | } 12 | 13 | interface TrainingEvent { 14 | type: 'info' | 'warning' | 'error' | 'success'; 15 | message: string; 16 | timestamp: string; 17 | } 18 | 19 | interface TrainingUpdate { 20 | taskId: string; 21 | status: string; 22 | metrics: Metrics; 23 | event?: TrainingEvent; 24 | } 25 | 26 | interface TrainingProgress { 27 | status: 'running' | 'paused' | 'completed' | 'failed'; 28 | metrics: Metrics; 29 | events: TrainingEvent[]; 30 | } 31 | 32 | const initialMetrics: Metrics = { 33 | loss: 0, 34 | accuracy: 0, 35 | epoch: 0, 36 | step: 0, 37 | totalSteps: 0, 38 | learningRate: 0, 39 | }; 40 | 41 | export function useTrainingProgress(taskId: string) { 42 | const [progress, setProgress] = useState({ 43 | status: 'paused', 44 | metrics: initialMetrics, 45 | events: [], 46 | }); 47 | const [error, setError] = useState(null); 48 | const [isConnected, setIsConnected] = useState(false); 49 | 50 | useEffect(() => { 51 | // Connect to WebSocket when the hook is first used 52 | wsService.connect(); 53 | 54 | // Handle WebSocket connection errors 55 | const handleError = (error: Error) => { 56 | setError('WebSocket connection error'); 57 | setIsConnected(false); 58 | }; 59 | 60 | // Handle max reconnection attempts reached 61 | const handleMaxReconnect = () => { 62 | setError('Unable to connect to server. Please refresh the page.'); 63 | setIsConnected(false); 64 | }; 65 | 66 | wsService.on('error', handleError); 67 | wsService.on('max_reconnect_attempts', handleMaxReconnect); 68 | 69 | return () => { 70 | wsService.off('error', handleError); 71 | wsService.off('max_reconnect_attempts', handleMaxReconnect); 72 | }; 73 | }, []); 74 | 75 | useEffect(() => { 76 | // Subscribe to training updates for the specific task 77 | const handleUpdate = (update: TrainingUpdate) => { 78 | setProgress((prev) => { 79 | const newEvents = update.event 80 | ? [...prev.events, update.event] 81 | : prev.events; 82 | 83 | return { 84 | status: update.status as TrainingProgress['status'], 85 | metrics: update.metrics, 86 | events: newEvents, 87 | }; 88 | }); 89 | setIsConnected(true); 90 | setError(null); 91 | }; 92 | 93 | const updateEventName = `training_update:${taskId}`; 94 | wsService.on(updateEventName, handleUpdate); 95 | wsService.subscribeToTrainingUpdates(taskId); 96 | 97 | return () => { 98 | wsService.off(updateEventName, handleUpdate); 99 | wsService.unsubscribeFromTrainingUpdates(taskId); 100 | }; 101 | }, [taskId]); 102 | 103 | const startTraining = useCallback(async () => { 104 | try { 105 | await fetch(`/api/training/${taskId}/start`, { 106 | method: 'POST', 107 | }); 108 | } catch (err) { 109 | setError('Failed to start training'); 110 | } 111 | }, [taskId]); 112 | 113 | const stopTraining = useCallback(async () => { 114 | try { 115 | await fetch(`/api/training/${taskId}/stop`, { 116 | method: 'POST', 117 | }); 118 | } catch (err) { 119 | setError('Failed to stop training'); 120 | } 121 | }, [taskId]); 122 | 123 | const refreshProgress = useCallback(async () => { 124 | try { 125 | const response = await fetch(`/api/training/${taskId}/progress`); 126 | const data: TrainingUpdate = await response.json(); 127 | 128 | setProgress((prev) => ({ 129 | status: data.status as TrainingProgress['status'], 130 | metrics: data.metrics, 131 | events: data.event ? [...prev.events, data.event] : prev.events, 132 | })); 133 | } catch (err) { 134 | setError('Failed to refresh training progress'); 135 | } 136 | }, [taskId]); 137 | 138 | return { 139 | ...progress, 140 | error, 141 | isConnected, 142 | startTraining, 143 | stopTraining, 144 | refreshProgress, 145 | }; 146 | } -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import { App } from './App'; 4 | 5 | const container = document.getElementById('root'); 6 | if (!container) { 7 | throw new Error('Failed to find the root element'); 8 | } 9 | 10 | const root = createRoot(container); 11 | root.render( 12 | 13 | 14 | 15 | ); -------------------------------------------------------------------------------- /src/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, AppBar, Toolbar, Typography, Drawer, List, ListItem, ListItemIcon, ListItemText, IconButton } from '@mui/material'; 3 | import { styled } from '@mui/material/styles'; 4 | import { useNavigate, useLocation } from 'react-router-dom'; 5 | import { 6 | Menu as MenuIcon, 7 | Dataset as DatasetIcon, 8 | Psychology as TrainingIcon, 9 | Dashboard as DashboardIcon, 10 | } from '@mui/icons-material'; 11 | 12 | const drawerWidth = 240; 13 | 14 | const Root = styled('div')({ 15 | display: 'flex', 16 | }); 17 | 18 | const StyledAppBar = styled(AppBar)(({ theme }) => ({ 19 | zIndex: theme.zIndex.drawer + 1, 20 | })); 21 | 22 | const StyledDrawer = styled(Drawer)({ 23 | width: drawerWidth, 24 | flexShrink: 0, 25 | '& .MuiDrawer-paper': { 26 | width: drawerWidth, 27 | boxSizing: 'border-box', 28 | }, 29 | }); 30 | 31 | const MainContent = styled('main')(({ theme }) => ({ 32 | flexGrow: 1, 33 | padding: theme.spacing(3), 34 | marginTop: 64, 35 | })); 36 | 37 | interface MainLayoutProps { 38 | children: React.ReactNode; 39 | } 40 | 41 | export function MainLayout({ children }: MainLayoutProps) { 42 | const navigate = useNavigate(); 43 | const location = useLocation(); 44 | const [mobileOpen, setMobileOpen] = React.useState(false); 45 | 46 | const menuItems = [ 47 | { text: 'Dashboard', icon: , path: '/' }, 48 | { text: 'Datasets', icon: , path: '/datasets' }, 49 | { text: 'Training Tasks', icon: , path: '/training' }, 50 | ]; 51 | 52 | const handleDrawerToggle = () => { 53 | setMobileOpen(!mobileOpen); 54 | }; 55 | 56 | const drawer = ( 57 |
58 | 59 | 60 | {menuItems.map((item) => ( 61 | { 66 | navigate(item.path); 67 | setMobileOpen(false); 68 | }} 69 | > 70 | {item.icon} 71 | 72 | 73 | ))} 74 | 75 |
76 | ); 77 | 78 | return ( 79 | 80 | 81 | 82 | 89 | 90 | 91 | 92 | Cipher Nexus 93 | 94 | 95 | 96 | 97 | 98 | 109 | {drawer} 110 | 111 | 112 | 119 | {drawer} 120 | 121 | 122 | 123 | 124 | {children} 125 | 126 | 127 | ); 128 | } -------------------------------------------------------------------------------- /src/pages/auth/LoginPage.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link as RouterLink } from 'react-router-dom'; 3 | import { 4 | Container, 5 | Box, 6 | Typography, 7 | TextField, 8 | Button, 9 | Link, 10 | Alert, 11 | Paper, 12 | } from '@mui/material'; 13 | import { useAuth } from '../../components/auth/AuthProvider'; 14 | 15 | export function LoginPage() { 16 | const { login, error } = useAuth(); 17 | const [email, setEmail] = useState(''); 18 | const [password, setPassword] = useState(''); 19 | const [isSubmitting, setIsSubmitting] = useState(false); 20 | 21 | const handleSubmit = async (e: React.FormEvent) => { 22 | e.preventDefault(); 23 | setIsSubmitting(true); 24 | try { 25 | await login(email, password); 26 | } catch (err) { 27 | // Error is handled by AuthProvider 28 | } finally { 29 | setIsSubmitting(false); 30 | } 31 | }; 32 | 33 | return ( 34 | 35 | 43 | 53 | 54 | Sign in 55 | 56 | {error && ( 57 | 58 | {error} 59 | 60 | )} 61 | 62 | setEmail(e.target.value)} 73 | /> 74 | setPassword(e.target.value)} 85 | /> 86 | 95 | 96 | 97 | {"Don't have an account? Sign Up"} 98 | 99 | 100 | 101 | 102 | 103 | 104 | ); 105 | } -------------------------------------------------------------------------------- /src/pages/auth/RegisterPage.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link as RouterLink } from 'react-router-dom'; 3 | import { 4 | Container, 5 | Box, 6 | Typography, 7 | TextField, 8 | Button, 9 | Link, 10 | Alert, 11 | Paper, 12 | } from '@mui/material'; 13 | import { useAuth } from '../../components/auth/AuthProvider'; 14 | 15 | export function RegisterPage() { 16 | const { register, error } = useAuth(); 17 | const [username, setUsername] = useState(''); 18 | const [email, setEmail] = useState(''); 19 | const [password, setPassword] = useState(''); 20 | const [confirmPassword, setConfirmPassword] = useState(''); 21 | const [isSubmitting, setIsSubmitting] = useState(false); 22 | const [validationError, setValidationError] = useState(null); 23 | 24 | const validateForm = () => { 25 | if (password !== confirmPassword) { 26 | setValidationError('Passwords do not match'); 27 | return false; 28 | } 29 | if (password.length < 8) { 30 | setValidationError('Password must be at least 8 characters long'); 31 | return false; 32 | } 33 | setValidationError(null); 34 | return true; 35 | }; 36 | 37 | const handleSubmit = async (e: React.FormEvent) => { 38 | e.preventDefault(); 39 | if (!validateForm()) return; 40 | 41 | setIsSubmitting(true); 42 | try { 43 | await register(username, email, password); 44 | } catch (err) { 45 | // Error is handled by AuthProvider 46 | } finally { 47 | setIsSubmitting(false); 48 | } 49 | }; 50 | 51 | return ( 52 | 53 | 61 | 71 | 72 | Sign up 73 | 74 | {(error || validationError) && ( 75 | 76 | {error || validationError} 77 | 78 | )} 79 | 80 | setUsername(e.target.value)} 91 | /> 92 | setEmail(e.target.value)} 102 | /> 103 | setPassword(e.target.value)} 114 | /> 115 | setConfirmPassword(e.target.value)} 126 | /> 127 | 136 | 137 | 138 | {'Already have an account? Sign in'} 139 | 140 | 141 | 142 | 143 | 144 | 145 | ); 146 | } -------------------------------------------------------------------------------- /src/services/websocket.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | 3 | interface WebSocketMessage { 4 | type: string; 5 | payload: any; 6 | } 7 | 8 | interface TrainingUpdate { 9 | taskId: string; 10 | status: string; 11 | metrics: { 12 | loss: number; 13 | accuracy: number; 14 | epoch: number; 15 | step: number; 16 | totalSteps: number; 17 | learningRate: number; 18 | }; 19 | event?: { 20 | type: 'info' | 'warning' | 'error' | 'success'; 21 | message: string; 22 | timestamp: string; 23 | }; 24 | } 25 | 26 | class WebSocketService extends EventEmitter { 27 | private ws: WebSocket | null = null; 28 | private reconnectAttempts = 0; 29 | private maxReconnectAttempts = 5; 30 | private reconnectTimeout = 1000; 31 | private pingInterval: NodeJS.Timeout | null = null; 32 | private subscriptions: Set = new Set(); 33 | 34 | constructor(private baseUrl: string) { 35 | super(); 36 | } 37 | 38 | connect() { 39 | if (this.ws?.readyState === WebSocket.OPEN) return; 40 | 41 | this.ws = new WebSocket(this.baseUrl); 42 | 43 | this.ws.onopen = () => { 44 | console.log('WebSocket connected'); 45 | this.reconnectAttempts = 0; 46 | this.startPingInterval(); 47 | this.resubscribe(); 48 | }; 49 | 50 | this.ws.onclose = () => { 51 | console.log('WebSocket disconnected'); 52 | this.cleanup(); 53 | this.attemptReconnect(); 54 | }; 55 | 56 | this.ws.onerror = (error) => { 57 | console.error('WebSocket error:', error); 58 | this.emit('error', error); 59 | }; 60 | 61 | this.ws.onmessage = (event) => { 62 | try { 63 | const message: WebSocketMessage = JSON.parse(event.data); 64 | this.handleMessage(message); 65 | } catch (error) { 66 | console.error('Error parsing WebSocket message:', error); 67 | } 68 | }; 69 | } 70 | 71 | private handleMessage(message: WebSocketMessage) { 72 | switch (message.type) { 73 | case 'training_update': 74 | const update: TrainingUpdate = message.payload; 75 | this.emit(`training_update:${update.taskId}`, update); 76 | break; 77 | case 'pong': 78 | // Handle pong response 79 | break; 80 | default: 81 | console.warn('Unknown message type:', message.type); 82 | } 83 | } 84 | 85 | private startPingInterval() { 86 | this.pingInterval = setInterval(() => { 87 | this.send({ type: 'ping' }); 88 | }, 30000); 89 | } 90 | 91 | private cleanup() { 92 | if (this.pingInterval) { 93 | clearInterval(this.pingInterval); 94 | this.pingInterval = null; 95 | } 96 | } 97 | 98 | private attemptReconnect() { 99 | if (this.reconnectAttempts >= this.maxReconnectAttempts) { 100 | console.error('Max reconnection attempts reached'); 101 | this.emit('max_reconnect_attempts'); 102 | return; 103 | } 104 | 105 | this.reconnectAttempts++; 106 | const timeout = this.reconnectTimeout * Math.pow(2, this.reconnectAttempts - 1); 107 | 108 | console.log(`Attempting to reconnect in ${timeout}ms (attempt ${this.reconnectAttempts})`); 109 | setTimeout(() => this.connect(), timeout); 110 | } 111 | 112 | private resubscribe() { 113 | this.subscriptions.forEach((taskId) => { 114 | this.send({ 115 | type: 'subscribe', 116 | payload: { taskId }, 117 | }); 118 | }); 119 | } 120 | 121 | subscribeToTrainingUpdates(taskId: string) { 122 | this.subscriptions.add(taskId); 123 | if (this.ws?.readyState === WebSocket.OPEN) { 124 | this.send({ 125 | type: 'subscribe', 126 | payload: { taskId }, 127 | }); 128 | } 129 | } 130 | 131 | unsubscribeFromTrainingUpdates(taskId: string) { 132 | this.subscriptions.delete(taskId); 133 | if (this.ws?.readyState === WebSocket.OPEN) { 134 | this.send({ 135 | type: 'unsubscribe', 136 | payload: { taskId }, 137 | }); 138 | } 139 | } 140 | 141 | private send(message: WebSocketMessage) { 142 | if (this.ws?.readyState === WebSocket.OPEN) { 143 | this.ws.send(JSON.stringify(message)); 144 | } else { 145 | console.warn('WebSocket is not connected'); 146 | } 147 | } 148 | 149 | disconnect() { 150 | this.cleanup(); 151 | if (this.ws) { 152 | this.ws.close(); 153 | this.ws = null; 154 | } 155 | } 156 | } 157 | 158 | // Create a singleton instance 159 | const wsService = new WebSocketService(process.env.REACT_APP_WS_URL || 'ws://localhost:8080/ws'); 160 | 161 | export default wsService; -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | // Dataset types 2 | export interface Dataset { 3 | id: string; 4 | name: string; 5 | description: string; 6 | size: number; 7 | created: Date; 8 | updated: Date; 9 | status: 'active' | 'processing' | 'error'; 10 | type: 'public' | 'private'; 11 | owner: string; 12 | } 13 | 14 | // Training task types 15 | export interface TrainingTask { 16 | id: string; 17 | name: string; 18 | description: string; 19 | dataset: string; 20 | model: string; 21 | parameters: Record; 22 | status: 'pending' | 'running' | 'completed' | 'failed'; 23 | progress: number; 24 | created: Date; 25 | updated: Date; 26 | owner: string; 27 | results?: Record; 28 | } 29 | 30 | // User types 31 | export interface User { 32 | id: string; 33 | username: string; 34 | email: string; 35 | role: 'admin' | 'user'; 36 | created: Date; 37 | lastLogin: Date; 38 | } 39 | 40 | // API Response types 41 | export interface ApiResponse { 42 | success: boolean; 43 | data?: T; 44 | error?: string; 45 | message?: string; 46 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "src" 19 | }, 20 | "include": ["src"] 21 | } 22 | --------------------------------------------------------------------------------