apppublic/aidocs/dataencoder.md
2025-10-05 11:23:33 +08:00

279 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `DataEncoder` 技术文档
```markdown
# DataEncoder - 安全数据编码与解码模块
## 概述
`DataEncoder` 是一个用于安全传输数据的 Python 类,提供数据加密、签名、压缩和结构化打包功能。它结合了 **RC4 对称加密****RSA 非对称加密**,确保数据在传输过程中的机密性、完整性和身份验证。
该类适用于点对点通信场景,支持动态获取对方公钥,并使用本地私钥进行签名,接收方通过公钥验证签名并解密数据。
---
## 依赖说明
### 第三方库
- `ujson`(可选):高性能 JSON 库,若不可用则回退至标准库 `json`
- `zlib`:内置,用于数据压缩
- `struct`:内置,用于二进制数据打包/解包
### 内部模块
- `appPublic.rsawrap.RSA`:封装 RSA 加密/解密、签名/验签功能
- `appPublic.rc4.RC4`:实现 RC4 流加密算法
- `appPublic.uniqueID.getID`:生成唯一 ID用作 RC4 密钥)
> 注:未启用 Brotli 压缩(相关导入被注释)
---
## 核心常量
| 常量 | 值 | 含义 |
|------|----|------|
| `DATA_TYPE_BYTES` | `1` | 数据类型为原始字节流 |
| `DATA_TYPE_STR` | `2` | 数据类型为字符串UTF-8 编码) |
| `DATA_TYPE_JSON` | `3` | 数据类型为 JSON 对象(序列化后为 UTF-8 字节) |
---
## `DataEncoder` 类定义
### 初始化方法:`__init__(self, myid, func_get_peer_pubkey, private_file=None)`
#### 参数:
| 参数名 | 类型 | 描述 |
|--------|------|------|
| `myid` | str 或 any | 当前节点的唯一标识符 |
| `func_get_peer_pubkey` | callable | 回调函数,用于根据 `peer_id` 获取对方公钥 |
| `private_file` | str, optional | 私钥文件路径;若未提供,则自动生成新密钥对 |
#### 功能:
- 创建或加载 RSA 密钥对(私钥 + 公钥)
- 初始化内部状态:
- `self.public_keys`: 缓存其他节点的公钥
- `self.rsa`, `self.rc4`: 实例化加解密工具
#### 示例回调函数签名:
```python
def get_pubkey(peer_id):
# 返回对应的 RSA 公钥对象
return rsa_public_key_obj
```
---
## 核心方法
### `identify_datatype(data)`
自动识别输入数据类型并转换为字节格式。
#### 返回值:
元组 `(data_type, encoded_bytes)`
其中 `data_type` 为上述三种之一。
| 输入类型 | 输出类型 | 处理方式 |
|---------|----------|--------|
| `bytes` | `DATA_TYPE_BYTES` | 直接返回 |
| `str` | `DATA_TYPE_STR` | `.encode('utf-8')` |
| 其他(如 dict/list | `DATA_TYPE_JSON` | `json.dumps().encode('utf-8')` |
---
### `pack(peer_id, data, uncrypt=False)`
将数据加密、签名并打包成安全格式发送给指定 `peer_id`
#### 参数:
| 参数 | 描述 |
|------|------|
| `peer_id` | 接收方的身份 ID |
| `data` | 待发送的数据(任意类型) |
| `uncrypt` | 是否跳过加密(调试用途),仅压缩不加密 |
#### 打包流程:
1. 调用 `identify_datatype(data)` 获取类型和字节数据。
2.`uncrypt=True`,直接构造明文包并返回压缩结果。
3. 否则:
- 生成随机 RC4 密钥(`getID()`
- 使用接收方公钥加密该密钥RSA 加密)
- 使用 RC4 加密原始数据
- 构造 struct 格式字符串(含长度信息)
- 将以下字段按顺序打包:
1. **格式头18字节**:描述后续结构
2. **数据类型1字节**
3. **加密后的数据**
4. **加密后的 RC4 密钥**
5. **数字签名(覆盖以上所有内容)**
- 使用 `zlib.compress()` 压缩整个包
- 返回压缩后的二进制数据
#### 结构体格式示例:
假设加密数据长 1024 字节,密钥长 256 字节,则格式为:
```
'18s b1024s256s256s'
```
最后一个 `256s` 是签名(固定长度由 RSA 决定)。
> 签名范围包括:头部 + 类型 + 加密数据 + 加密密钥
---
### `unpack(peer_id, data)`
解包并还原从 `peer_id` 收到的安全数据。
#### 参数:
| 参数 | 描述 |
|------|------|
| `peer_id` | 发送方的身份 ID |
| `data` | 接收到的压缩二进制数据 |
#### 解包流程:
1. 解压 `zlib.decompress(data)`
2. 判断是否为无加密模式(前 18 字节全为 `\x00`
- 是 → 按简单格式解析(仅类型+数据),返回明文
3. 否则进入安全解包流程:
- 提取前 18 字节作为格式字符串(去除尾部 `\x00`
- 解析剩余数据得到:类型、加密数据、加密密钥、签名
- 验证签名(使用发送方公钥)
- 若失败 → 抛出异常 `'data sign verify failed'`
- 成功后:
- 用本机私钥解密 RC4 密钥
- 用 RC4 解密数据
- 根据类型还原为原始数据bytes / str / json
- 返回解码后的原始数据
---
### 加解密辅助方法
| 方法 | 功能 |
|------|------|
| `encode_data(peer_pubkey, data)` | 使用 RC4 加密数据RSA 加密密钥 |
| `decode_data(data, encoded_key)` | RSA 解密密钥,再用 RC4 解密数据 |
| `sign_data(data)` | 使用本机私钥对数据签名 |
| `verify_sign(data, sign, pubkey)` | 使用对方公钥验证签名有效性 |
---
### 公钥管理方法
| 方法 | 功能 |
|------|------|
| `my_text_publickey()` | 获取本机公钥的文本表示Base64 编码等) |
| `get_peer_pubkey(peer_id)` | 获取某 peer 的公钥(缓存或调用回调函数) |
| `set_peer_pubkey(peer_id, pubkey)` | 设置某 peer 的公钥对象 |
| `set_peer_text_pubkey(peer_id, text_pubkey)` | 从文本形式设置公钥(自动解析) |
| `get_peer_text_pubkey(peer_id)` | 获取某 peer 的公钥文本形式(需已存在) |
| `exist_peer_publickeys(peer_id)` | 查询是否已有某 peer 的公钥 |
---
## 数据格式详解
### 安全包结构(压缩前)
| 偏移 | 字段 | 长度 | 说明 |
|------|------|-------|------|
| 0 | 格式描述字符串 | 18 字节 | 如 `'b1024s256s256s\x00...'` |
| 18 | 数据类型 | 1 字节 | `1=bytes`, `2=str`, `3=json` |
| 19 | 加密数据 | 可变 | RC4 加密后的 payload |
| 19+len(d) | 加密密钥 | 可变 | RSA 加密后的 RC4 密钥 |
| ... | 数字签名 | 固定(如 256 | RSA-PKCS1-V1_5 签名 |
> 总长度 = 18 + len(d) + len(k) + len(s)
### 明文包结构(`uncrypt=True`
| 偏移 | 字段 | 长度 | 说明 |
|------|------|-------|------|
| 0 | 填充零 | 18 字节 | 全 `\x00` 表示无加密 |
| 18 | 数据类型 | 1 字节 | 同上 |
| 19 | 原始数据 | 可变 | 未经加密 |
---
## 使用示例
### 初始化
```python
def fetch_pubkey(pid):
# 实现获取 peer 公钥逻辑
return rsa_pubkey_obj
encoder = DataEncoder(
myid="node_A",
func_get_peer_pubkey=fetch_pubkey,
private_file="private.key" # 或 None 自动生成
)
```
### 发送加密数据
```python
msg = {"cmd": "hello", "time": 123}
packed_data = encoder.pack("node_B", msg)
# 发送 packed_data 到 node_B
```
### 接收并解密
```python
received_data = ... # 来自网络的数据
decoded_msg = encoder.unpack("node_A", received_data)
print(decoded_msg) # {'cmd': 'hello', 'time': 123}
```
### 设置远程公钥(手动)
```python
pubkey_text = "-----BEGIN PUBLIC KEY-----\n..." # PEM 或 Base64 文本
encoder.set_peer_text_pubkey("node_C", pubkey_text)
```
---
## 注意事项
1. **安全性提醒**
- RC4 已被认为不安全,建议在非敏感环境使用或替换为 AES。
- RSA 密钥长度应 ≥ 2048 bits。
- 签名防止篡改,但不防重放攻击,需上层协议补充 nonce/timestamp。
2. **性能优化**
- 使用 `ujson` 提升 JSON 序列化速度。
- zlib 压缩有效减少传输体积。
3. **错误处理**
- 无法获取公钥时会抛出异常。
- 签名验证失败也会中断流程。
4. **扩展建议**
- 支持 Brotli 压缩以进一步减小体积(当前代码中被注释)。
- 添加 TTL、nonce、版本号等元数据字段。
---
## 辅助函数:`quotedstr(s)`
将字符串中的特殊字符转义,主要用于日志输出或字符串嵌入。
### 转义规则:
| 原字符 | 替换为 |
|--------|--------|
| `"` | `\"` |
| `\n` | `\\n` |
> 其他字符保持不变
### 示例:
```python
quotedstr('He said: "Hello\nWorld"')
# 输出: He said: \"Hello\\nWorld\"
```
---
## 版本信息
- 作者Auto-generated from source
- 最后更新:根据所提供代码生成
- 许可:请参考项目整体 LICENSE
```