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

8.3 KiB
Raw Blame History

DataEncoder 技术文档

# 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 原始数据 可变 未经加密

使用示例

初始化

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 自动生成
)

发送加密数据

msg = {"cmd": "hello", "time": 123}
packed_data = encoder.pack("node_B", msg)
# 发送 packed_data 到 node_B

接收并解密

received_data = ...  # 来自网络的数据
decoded_msg = encoder.unpack("node_A", received_data)
print(decoded_msg)  # {'cmd': 'hello', 'time': 123}

设置远程公钥(手动)

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

其他字符保持不变

示例:

quotedstr('He said: "Hello\nWorld"')
# 输出: He said: \"Hello\\nWorld\"

版本信息

  • 作者Auto-generated from source
  • 最后更新:根据所提供代码生成
  • 许可:请参考项目整体 LICENSE