# chongming-license 机器码授权共享包:硬件指纹采集、license 载荷格式、Ed25519 验签与插件白名单判定。 网关与厂商侧签发工具共用同一份实现 —— 尤其是**规范化 JSON 只有一处实现**, 避免"自己签的自己验不过"。 ## 设计要点 - **纯离线**:设备用内置公钥验签,运行期零联网。与现有 air-gapped 部署形态 (Zenoh localhost、Caddy `tls internal`、无 phone-home)一致。 - **非对称**:Ed25519。注意根仓 `resources/*.key` 那套 `.mbank` 验签是 **HMAC 对称**且密钥与银行同目录发布,只能算防篡改/混淆,**不能**作为厂商 信任根 —— 授权必须用非对称,公钥可安全内置。 - **N-of-M 容错**:license 里存各来源的原始指纹,校验按"命中数达阈值"判定, 而非把所有来源哈希成一个值 —— 否则换一块网卡就直接失效。 展示给客户的仍是单个 `machine_code`。 - **weak 来源**:`/etc/machine-id` 在同镜像批量刷机场景下**所有机器相同**, 故标记 `weak` 且**不参与判定阈值**(只作诊断参考),避免它给攻击者白送一票。 ## 采集来源 | 来源 | 位置 | 说明 | | --- | --- | --- | | `board_serial` | `/proc/device-tree/serial-number`、DMI `board_serial` | 逐机唯一,最稳定 | | `emmc_cid` | `/sys/block/mmcblk*/device/cid` | 芯片出厂唯一,不随系统重装变化 | | `cpu_serial` | `/proc/cpuinfo` 的 `Serial` | ARM 常见;部分型号全 0(会被剔除) | | `mac` | `/sys/class/net/*/address` | 取字典序最小者:插入/移除其它网卡不改变它 | | `product_uuid` | `/sys/class/dmi/id/product_uuid` | x86 有效,ARM 通常缺失 | | `machine_id` | `/etc/machine-id` | ⚠ weak,见上 | ## 文件格式 ```json { "payload": { "v": 1, "lic_id": "L2026-0001", "customer": "某某科技", "machine_code": "A3F9-1B77-9E04-2C8D-6F31-0A55", "sources": {"board_serial": "...", "emmc_cid": "...", "mac": "..."}, "plugins": ["aibox_sophon_adapter", "ip_sound_column"], "not_before": "2026-09-01", "expires_at": "2027-09-01", "issued_at": "2026-09-01T10:00:00Z", "note": "" }, "alg": "ed25519", "kid": "vendor-2026", "sig": "" } ``` 签名覆盖 payload **原始字节的规范化形式**(`canonical_json`:键排序、无空白), 因此载荷内不允许浮点数(float 文本表示存在多种合法写法)。校验顺序是 先验签、后解析模型 —— 反过来会让默认值填充/字段顺序改变字节导致验签失败。 ## 用法 网关侧: ```python from chongming_license import LicenseManager mgr = LicenseManager( license_path="license.lic", public_key=config["license"]["public_key"], clock_store=cache, # chongming_cache.Cache,存时间高水位 enforce=is_packaged(), # 打包产物强制校验;开发/测试放行 ) decision = mgr.check_plugin("aibox_sophon_adapter") if not decision.allowed: ... # 不加载该插件 ``` 厂商侧(`chongming-cli`,私钥只在这边): ```bash uv run chongming-license keygen -o license_private.pem # 一次性生成密钥对 uv run chongming-license issue --machine-code <码> --sources-json <采集导出> \ --plugins aibox_sophon_adapter,ip_sound_column \ --customer 某某科技 --expires 2027-09-01 -k license_private.pem -o license.lic ``` ## 诚实的边界 离线 + root 的天花板是**阻止随手拷贝**,不是阻止逆向:有 root 者可改二进制 跳过校验,或 `LD_PRELOAD` 伪造指纹读取。`clock` 模块的时间回拨检测同理 —— 删掉缓存文件即失效,它只是抬高随手改时间的门槛。 真正难以绕过的是**签名**:伪造 license 需要厂商私钥,而公钥可明文放置 (换公钥只会验签失败,无法据以伪造)。