Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f231468
feat(crypto-box): implement secure token-based crypto authorization s…
Gaubee Jan 19, 2026
0f64eee
test(wallet-handlers): fix mock path for getChainProvider
Gaubee Jan 19, 2026
f624435
feat(miniapps): add remote miniapps plugin with ETag caching
Gaubee Jan 19, 2026
3161693
feat(remote-miniapps): use metadata.json for dynamic version discovery
Gaubee Jan 19, 2026
a4f488a
fix(remote-miniapps): download before configureServer and use URL() f…
Gaubee Jan 19, 2026
351ef0b
refactor(miniapps): use URL() for all path resolution
Gaubee Jan 19, 2026
50923f3
fix(remote-miniapps): use zip ETag for incremental update detection
Gaubee Jan 19, 2026
1cd9a38
fix(crypto-box): security fix and miniapp header wallet info display
Gaubee Jan 19, 2026
470bc0b
fix: resolve typecheck errors
Gaubee Jan 19, 2026
dded27d
fix(crypto-box): return address in RequestCryptoTokenResponse
Gaubee Jan 19, 2026
cec9938
feat(crypto-box): add bio_getCryptoTokenInfo API for Token validation
Gaubee Jan 19, 2026
2cdcfd8
feat(dweb-compat): add getCryptoTokenInfo API and fix rwaLogin address
Gaubee Jan 19, 2026
541deb2
chore(e2e): update ecosystem miniapp screenshots
Gaubee Jan 19, 2026
cc90b9b
chore(tsconfig): exclude i18n folder to workaround TS 5.9.3 bug
Gaubee Jan 19, 2026
1241d4c
fix(dweb-compat): sync TokenDuration types and default to 1day
Gaubee Jan 19, 2026
73b527d
feat(dweb-compat): add Token caching to rwaLogin for address consistency
Gaubee Jan 20, 2026
6b8178a
fix(dweb-compat): add missing vite.config.ts for vitest
Gaubee Jan 20, 2026
ebbf86d
fix(i18n): remove duplicate namespace prefix in discover-page transla…
Gaubee Jan 20, 2026
8359fc2
fix(i18n): add ecosystem namespace registration and enhance i18n-check
Gaubee Jan 20, 2026
5802072
fix(dweb-compat): fix TS2454 variable used before assigned error
Gaubee Jan 20, 2026
cc635ea
fix(dweb-compat): add --passWithNoTests to test:run script
Gaubee Jan 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ migrations.json
.DS_Store
Thumbs.db

# Remote miniapps (downloaded at dev/build time)
miniapps/rwa-hub/

# Playwright
e2e/test-results/
e2e/report/
Expand Down
242 changes: 242 additions & 0 deletions docs/white-book/08-Security-Ref/05-CryptoBox-Authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# Crypto Box 授权系统

> 源码: [`src/services/crypto-box/`](https://github.com/BioforestChain/KeyApp/blob/main/src/services/crypto-box/)

---

## 概述

Crypto Box 是一个"加密黑盒"系统,允许 Miniapp 执行加密操作(非对称加密、签名)而**不暴露私钥**。

```
┌─────────────────┐ ┌─────────────────┐
│ Miniapp │ bio_requestToken │ KeyApp │
│ (RWA Hub) │ ◄────────────────► │ (Crypto Box) │
│ │ bio_cryptoExecute │ │
│ 无法访问私钥 │ │ 私钥安全存储 │
└─────────────────┘ └─────────────────┘
```

---

## 安全设计

### Token 授权流程

```
1. Miniapp 请求 Token
2. 用户输入手势密码授权
3. 派生 sessionSecret = SHA256(walletId + patternKey + miniappId + tokenId)
4. 加密 Payload(含 patternKey)存入 IndexedDB
5. 返回 tokenId + sessionSecret 给 Miniapp
6. Miniapp 使用 tokenId + sessionSecret 执行加密操作
7. KeyApp 解密 Payload,获取 patternKey,执行操作
```

### 密钥安全

| 安全约束 | 实现 |
|----------|------|
| **MUST** | patternKey 不在内存中长期保留 |
| **MUST** | patternKey 使用 sessionSecret 加密存储 |
| **MUST** | sessionSecret 派生需要 patternKey 参与 |
| **MUST** | Token 验证从加密 Payload 读取(不信任明文副本)|

### sessionSecret 派生

```typescript
sessionSecret = SHA256(walletId + ":" + patternKey + ":" + miniappId + ":" + tokenId)
```

- 包含 `patternKey`:确保只有授权时的用户才能派生相同的 secret
- 包含 `tokenId`:每个 Token 的 sessionSecret 唯一
- 包含 `miniappId`:防止跨 app 使用

---

## API 参考

### bio_requestCryptoToken

请求加密操作授权,用户需输入手势密码。

**请求参数:**

```typescript
interface RequestCryptoTokenParams {
actions: ('asymmetricEncrypt' | 'sign')[]
duration: '5min' | '15min' | '1hour' | '1day'
address: string
chainId?: string
}
```

**响应:**

```typescript
interface RequestCryptoTokenResponse {
tokenId: string
sessionSecret: string // 用于后续执行
expiresAt: number
grantedActions: CryptoAction[]
}
```

### bio_cryptoExecute

使用 Token 执行加密操作。

**请求参数:**

```typescript
interface CryptoExecuteParams {
tokenId: string
sessionSecret: string
action: 'asymmetricEncrypt' | 'sign'
params: AsymmetricEncryptParams | SignParams
}
```

**响应:**

```typescript
interface CryptoExecuteResponse {
result: string // hex 编码结果
publicKey: string // hex 编码公钥
}
```

---

## Token 存储

### IndexedDB Schema

```typescript
interface StoredToken {
tokenId: string
miniappId: string // 明文副本(便于查询)
walletId: string // 明文副本
address: string // 明文副本
actions: CryptoAction[] // 明文副本
expiresAt: number // 明文副本
createdAt: number
encryptedPayload: string // 加密的真实数据
}
```

### 加密 Payload 内容

```typescript
interface TokenPayload {
patternKey: string // 手势密码(加密存储)
miniappId: string
walletId: string
address: string
actions: CryptoAction[]
expiresAt: number
}
```

验证时**始终从解密的 Payload 读取**,明文副本仅用于查询展示。

---

## 非对称加密实现

### Ed25519 → X25519 转换

BFMetaSignUtil 使用 X25519 ECDH,而 KeyApp 使用 Ed25519 密钥对:

```typescript
import ed2curve from 'ed2curve'

// 转换公钥
const curvePublicKey = ed2curve.convertPublicKey(ed25519PublicKey)

// 转换私钥
const curveSecretKey = ed2curve.convertSecretKey(ed25519SecretKey)

// X25519 box 加密
const encrypted = nacl.box(message, nonce, curveRecipientPK, curveSecretKey)
```

### 固定 Nonce

与 BFMetaSignUtil 兼容,使用全零 nonce:

```typescript
const nonce = new Uint8Array(24) // 24 字节全零
```

> ⚠️ 固定 nonce 在密码学上不推荐,但为兼容现有系统必须使用。

---

## 错误代码

| 代码 | 含义 |
|------|------|
| 4100 | Token 未找到 |
| 4101 | Miniapp ID 不匹配 |
| 4102 | Token 已过期 |
| 4103 | 操作未授权 |
| 4104 | Session Secret 无效 |
| 4001 | 用户拒绝授权 |

---

## 使用示例

### RWA Hub 登录

```typescript
import { rwaLogin } from '@biochain/dweb-compat'

// 一键登录(封装了完整流程)
const { address, publicKey, signcode } = await rwaLogin(systemPublicKey)

// 发送到 RWA 后端验证
await rwaBackend.login({ address, publicKey, signcode })
```

### 手动流程

```typescript
import { requestCryptoToken, asymmetricEncrypt } from '@biochain/dweb-compat'

// 1. 请求授权
const { tokenId, sessionSecret } = await requestCryptoToken(
['asymmetricEncrypt'],
'5min',
address
)

// 2. 执行加密
const { result, publicKey } = await asymmetricEncrypt(
tokenId,
sessionSecret,
'data to encrypt',
recipientPublicKey
)
```

---

## 相关文档

- [密钥管理](./01-Key-Management.md)
- [身份认证](./02-Authentication.md)
- [DWEB 授权](./03-DWEB-Authorization.md)
1 change: 1 addition & 0 deletions docs/white-book/08-Security-Ref/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
| [02-Authentication](./02-Authentication.md) | 图案锁、生物识别、自动锁定 |
| [03-DWEB-Authorization](./03-DWEB-Authorization.md) | Plaoc 协议、地址授权、签名授权 |
| [04-Security-Audit](./04-Security-Audit.md) | 审计清单、攻击防护、合规要求 |
| [05-CryptoBox-Authorization](./05-CryptoBox-Authorization.md) | Crypto Box 黑盒授权、Token 机制 |

---

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"buffer": "^6.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"ed2curve": "^0.3.0",
"i18next": "^25.7.1",
"idb": "^8.0.3",
"jsqr": "^1.4.0",
Expand All @@ -123,6 +124,7 @@
"swiper": "^12.0.3",
"tailwind-merge": "^3.4.0",
"tw-animate-css": "^1.4.0",
"tweetnacl": "^1.0.3",
"vaul": "^1.1.2",
"viem": "^2.43.3",
"yargs": "^18.0.0",
Expand All @@ -145,6 +147,7 @@
"@testing-library/user-event": "^14.6.1",
"@types/big.js": "^6.2.2",
"@types/bun": "^1.3.5",
"@types/ed2curve": "^0.2.4",
"@types/lodash": "^4.17.21",
"@types/node": "^24.10.1",
"@types/qrcode": "^1.5.6",
Expand All @@ -165,6 +168,7 @@
"eslint-plugin-unused-imports": "^4.3.0",
"fake-indexeddb": "^6.2.5",
"jsdom": "^27.2.0",
"jszip": "^3.10.1",
"oxlint": "^1.39.0",
"playwright": "^1.57.0",
"prettier": "^3.7.4",
Expand All @@ -174,6 +178,7 @@
"semver": "^7.7.3",
"shadcn": "^3.6.1",
"sharp": "^0.34.5",
"sirv": "^3.0.2",
"ssh2-sftp-client": "^12.0.1",
"storybook": "^10.1.4",
"tailwindcss": "^4.0.0",
Expand Down
9 changes: 0 additions & 9 deletions packages/bio-sdk/src/chain-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@ export const API_CHAIN_TO_KEYAPP: Record<string, string> = {
bfchain: 'bfchain',
} as const

/** KeyApp chain ID to display name */
export const CHAIN_DISPLAY_NAMES: Record<string, string> = {
ethereum: 'Ethereum',
binance: 'BNB Smart Chain',
tron: 'Tron',
bfmeta: 'BFMeta',
bfchain: 'BFChain',
} as const

/**
* Convert decimal chain ID to hex string (EIP-155 format)
* @example toHexChainId(56) => '0x38'
Expand Down
34 changes: 34 additions & 0 deletions packages/dweb-compat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@biochain/dweb-compat",
"version": "0.1.0",
"description": "dweb-plaoc 到 KeyApp bio provider 的兼容适配器",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./src/index.ts"
},
"./is-dweb": {
"types": "./src/is-dweb.ts",
"import": "./src/is-dweb.ts"
},
"./plugins-stub": {
"types": "./src/plugins-stub.ts",
"import": "./src/plugins-stub.ts"
}
},
"scripts": {
"typecheck": "tsc --noEmit",
"typecheck:run": "tsc --noEmit",
"test": "vitest",
"test:run": "vitest run --passWithNoTests",
"test:storybook": "echo 'SDK has no storybook'"
},
"peerDependencies": {},
"devDependencies": {
"typescript": "^5.9.3",
"vitest": "^4.0.0"
}
}
Loading