25 KiB
Matrix 设备认证(SAS)— 知识沉淀
本文档是
ha-matrix-e2e(custom_components/matrix_e2ee,基于 matrix-nio 0.26.0 + vodozemac)在 Matrix 设备验证方面全部已知知识的汇总,范围:官方规范、官方文档/实现偏差、认证流程、代码定位、 注意事项。目的:避免重复排查、重复实现、重复踩坑。配套文档:
docs/DEVICE_VERIFICATION.md(人话操作指南 + 源码级流程)、SECURITY.md(信任模型与威胁边界)、docs/DEVELOPMENT.md(测试纪律)。维护约定:完成/发现新的验证相关 issue 后,把结论同步到本文档对应章节和「Issue 索引表」,不要只留在 Linear。
1. 官方规范(Matrix Spec)
规范来源:matrix-spec content/client-server-api/modules/end_to_end_encryption.md(注意:不是
sas_verification.md,后者 404)。涉及两个机制:验证框架(m.key.verification.request/ready/start/.../done/cancel)
与 SAS 方法(m.sas.v1)。
1.1 验证框架
- 消息通道:同一账号内验证用 to-device 消息;不同用户之间建议用 in-room 消息。本项目(bot 验证自己的 另一台设备)走 to-device。
- Session ID:to-device 用
transaction_id;in-room 用 event ID。 - 流程:
request(发起方声明支持的 methods)→ 对方提示接受 →ready(接受方回 methods 交集)→ 用户选方法 →start→ 方法内交换 →done。任意时点任一方都可发cancel(带 code)。 - 多设备广播:to-device 的
request广播到对方所有设备(同一 txn)。其中一台接受后,对其他设备发cancel(codem.accepted);用户在另一台拒绝则 codem.user。in-room 只发一次 request,无 cancel-to-others。 - 提示自动消失:to-device 按
timestamp(in-room 按origin_server_ts)起 10 分钟,或收到后 2 分钟, 先到为准。拒绝请求必须发cancel,code=m.user。
1.2 SAS 方法 m.sas.v1 — 安全原理
灵感来自 ZRTP hash commitment:responder 在 accept 里先发自己公钥的 hash(commitment),initiator 收到
commitment 后才发自己的公钥。攻击者只有一次猜的机会:验证 n bit 则成功率 1/2^n(SAS 全 40+ bit → ~1/10^12)。
分两个阶段:
- 密钥协商:双方各生成临时 Curve25519 密钥对,交换公钥(带 commitment 保护),ECDH 得共享秘密。
- 密钥验证:用共享秘密派生 HMAC,互相认证各自的设备 ed25519 key。
1.3 SAS 全流程(18 步,initiator = Alice / responder = Bob)
| # | 动作 | 消息 | 内容要点 |
|---|---|---|---|
| 1 | 线下安全会面 | — | 双方对照设备显示内容 |
| 2 | 开始验证 | — | 任一方发起 |
| 3 | Alice 发 start | m.key.verification.start |
必须先拿到 Bob 设备 key;含 key_agreement/hash/mac_method/short_authentication_string |
| 4 | Bob 选算法 | — | 从 Alice 支持列表里挑 key agreement/hash/MAC/SAS 方法 |
| 5 | Bob 需已持 Alice 设备 key | — | 否则先 key query |
| 6 | Bob 生成临时 Curve25519 对 | — | 对公钥做 SHA-256 |
| 7 | Bob 回 accept | m.key.verification.accept |
含 commitment(自己公钥的 hash) |
| 8 | Alice 存 commitment | — | 后续校验 |
| 9 | Alice 生成临时对、发 key | m.key.verification.key |
只发公钥 |
| 10 | Bob 发自己 key | m.key.verification.key |
已无 commitment 保护风险 |
| 11 | Alice 校验 commitment | — | commitment == hash(Bob 公钥 + Alice 的 start content) |
| 12 | 双方 ECDH | — | 临时密钥 → 共享秘密 |
| 13 | 双方显示 SAS | — | emoji/decimal(多方法时用户选) |
| 14 | 用户比对 | — | 两边一致才继续 |
| 15 | 算 MAC | — | 对每个要验证的 key + key ID 列表 |
| 16 | 双方发 mac | m.key.verification.mac |
|
| 17 | 校验对方 MAC | — | 每个 key 的 MAC + key 列表 MAC 全对 → 设备已验证 |
| 18 | 双方发 done | m.key.verification.done |
要验证哪些 key:本设备的 ed25519 key + master signing key(跨用户时应含 MSK;「验证他人单设备」的做法已废弃)。
1.4 错误处理与 cancel code
- 随时可 cancel;10 分钟超时(txn 闲置 10min 也过期)。
- 同一设备多次发起 → recipient 全部 cancel。
- 未知 txn → cancel(入站
start/cancel除外)。 - 无共同方法 → cancel;SAS 不符 → cancel;乱序 → cancel。
- SAS 专属 cancel code:
m.unknown_method、m.mismatched_commitment、m.mismatched_sas。 - 框架层 code:
m.user(用户拒绝)、m.accepted、m.timeout、m.unexpected_message、m.key_mismatch。
1.5 MAC 计算(wire 格式,容易错)
- HKDF 参数:HKDF-SHA256,IKM = 共享秘密,无 salt。
- MAC info 串(逐字节拼接,无分隔符):
MATRIX_KEY_VERIFICATION_MAC+ 发 MAC 方user_id+device_id+ 对方user_id+device_id+transaction_idkey_id(单个 key);对 key 列表用字符串KEY_IDS。
- HMAC 对象:
- 单个 key → unpadded base64 编码的公钥;
- key 列表 → 字典序排序、逗号连接、无空格的
alg:id列表,例如ed25519:Cross+Signing+Key,ed25519:DEVICEID。
- MAC 值 base64 编码后放进
mac消息的mac与keys字段。 - 版本(关键):规范要求「所有当前实现都应用
hkdf-hmac-sha256.v2」。legacyhkdf-hmac-sha256(v1)因 libolm 原始实现 bug 使用了错误 base64 编码,v2 修正;v1 已废弃,双方都支持 v2 时 MUST NOT 用 v1。
1.6 SAS 派生
- HKDF info(
curve25519-hkdf-sha256):MATRIX_KEY_VERIFICATION_SAS|+ start 发起方user_id|+device_id|+ start 方公钥(unpadded base64)|- accept 方
user_id|+device_id|+ accept 方公钥(unpadded base64)|+transaction_id。 废弃的curve25519方法:无|分隔、不含公钥。
- accept 方
- decimal:取 5 字节 → 3 个 13-bit 数(0–8191)各 +1000 → 三个 1000–9191 的数。
位运算:
(B0<<5|B1>>3)+1000;((B1&0x7)<<10|B2<<2|B3>>6)+1000;((B3&0x3F)<<7|B4>>1)+1000。 - emoji:取 6 字节 → 前 42 bit → 7 组 6-bit → 7 个 0–63 索引 → 查 64 格 emoji 表
(JSON 在
matrix-org/matrix-spec仓库data-definitions/sas-emoji.json)。
1.7 Cross-signing(本项目的「不做」依据)
三把 ed25519 对:MSK(master signing key,签 USK/SSK,代表用户身份)、USK(user-signing key,只自己可见, 签他人 MSK)、SSK(self-signing key,签自己的设备 key)。作用:只需验证一次 MSK 即可信任该用户所有设备。 本集成因 nio 0.26 不支持自签 cross-signing key,且自签会把 cross-signing 权威放到 HA 主机上(违背信任边界), 故不做 SSSS / Key Backup 导入(W1N-147 结论)。可对他人设备验证其 MSK,但 bot 自身无法 bootstrap 新设备。
2. 官方规范 vs nio 0.26 的偏差(文档/实现漂移 — 最容易重复踩)
| # | 规范/预期 | nio 0.26.0 实际 | 修复 | 出处 |
|---|---|---|---|---|
| 1 | request → ready 由框架处理 |
没有实现;request 被解析为 UnknownToDeviceEvent 丢弃 → Element 端显示取消 |
集成自建 _handle_verification_request + _send_verification_ready |
W1N-173, PR #25 |
| 2 | 会话有 10 分钟活动窗口 | Sas._last_event_time 只在 __init__ 赋值、从不刷新 → timed_out 60s 后必真;clear_verifications() 每 sync 取消 |
monkeypatch Sas.timed_out:只留 5min _max_age;集成超时 240s 先行触发 |
W1N-172, PR #23 |
| 3 | commitment 为 unpadded base64 | 迁移 vodozemac 后发 hexdigest,Element 拒收(m.key_mismatch) |
_apply_sas_commitment_patch:from_key_verification_start + _check_commitment 都改回 unpadded base64 |
PR #28, v0.2.8 |
| 4 | emoji 索引由底层算出 | vodozemac 已返回 7 个最终索引,nio 仍按 libolm 时代 bit-slicing 二次转换 → 两端 emoji 不一致、m.mismatched_sas |
monkeypatch _generate_emoji 直接用 indices |
W1N-175, PR #29 |
| 5 | 协商 hkdf-hmac-sha256(v1) 时 wire 用 libolm invalid base64 |
nio 用标准 calculate_mac → MAC 阶段失败 |
monkeypatch get_mac + receive_mac_event,按 chosen_mac_method 选 calculate_mac_invalid_base64(libolm-compat feature) |
W1N-177, PR #30 |
| 6 | 应优先/只用 .v2 MAC |
nio 0.26 只协商 v1,不提供 .v2 |
保持 v1 + invalid_base64 与 rust-sdk/Element 互操作;不要自己加 .v2(会扩大协商面但 nio 没实现) |
W1N-177 |
| 7 | keys_query 应覆盖会话各方 |
只取「共享加密房间用户」→ bot 与自身设备不共享房间 → 永不查询自己 → 入站 SAS 建不起来 | 登录/恢复后把自身 user_id 加进 users_for_key_query 并预热 |
W1N-166, PR #18 |
| 8 | 收到 start 前应有设备 key | 新设备首次 start 命中 device_store KeyError → 丢弃 + 不建 SAS | _repair_dropped_start:查 key 后把同一 start 重喂给 nio.olm.handle_key_verification |
W1N-170, PR #23 |
| 9 | 校验失败应发 m.key_mismatch cancel |
receive_mac_event「无已验证设备」分支置 canceled 后缺 return,下一行覆盖为 mac_received |
上游 bug,Backlog(W1N-179) | W1N-179 |
另:nio 的 Sas.verified = (state == mac_received) ∧ sas_accepted,get_mac() 在 sas_accepted=False 时抛
LocalProtocolError——集成代码曾因裸调 get_mac 被 except Exception 吞掉而卡死(W1N-142)。
3. 认证流程(本项目实际路径)
3.1 对外接口(已定型)
服务(admin-only 用 async_register_admin_service 强制,W1N-150):
| 服务 | 权限 | 说明 |
|---|---|---|
start_verification |
admin | bot 发起 SAS(入站由 Element 发起时无需调用) |
confirm_verification |
admin | 人工比对 emoji 后确认;内部 = accept_sas + get_mac(verified 时再 verify_device) |
cancel_verification |
admin | 取消 |
verify_device_by_fingerprint |
admin | 单向信任;user_id + device_id + ed25519 精确匹配,否则 fingerprint_mismatch |
reauthenticate |
admin | Config Flow reauth |
get_fingerprint |
普通注册 | 只读;返回 bot 的 ed25519/curve25519 |
send_message |
普通注册 | 发消息 |
事件:
matrix_e2ee_verification— 各stage:started/sas(含emojis)/done/canceled,带expires_at(W1N-145VerificationPrompt:transaction_id/user_id/device_id/emojis/expires_at)。matrix_e2ee_fingerprint— 启动后 emit bot 指纹。matrix_e2ee_command/matrix_e2ee_error— 命令事件(仅verified=True且 allowlist 命中才触发)。
错误码(const.py):verification_peer_denied(发起者非 bot 自身账号或 allowed_users)、verification_timeout
(集成超时 4min)、fingerprint_mismatch、invalid_transaction、device_missing。
3.2 双向流程(wire 时序)
方向 A:Element 发起(入站) — 由 handle_to_device_event + _handle_verification_request 驱动:
Element HA bot (matrix_e2ee)
│ m.key.verification.request {txn, methods:[m.sas.v1], timestamp}
│ ──────────────────────────────────────────────► _handle_verification_request
│ 校验: sender 允许 / from_device、txn 非空 /
│ methods 含 m.sas.v1 / timestamp 界内
│ ◄────────────────────────────────────────────── m.key.verification.ready {methods:[m.sas.v1]}
│ m.key.verification.start {txn, ...}
│ ──────────────────────────────────────────────► handle_to_device_event (start 分支)
│ _get_sas==None → _repair_dropped_start(查 key 重喂)
│ accept_key_verification(txn) → emit started
│ ◄────────────────────────────────────────────── m.key.verification.accept(nio 内部,含 commitment)
│ m.key.verification.key ... key 交换 → 双方显示 emoji
│ ──────────────────────────────────────────────► (key 分支) emit sas {emojis, expires_at}
│ HA 侧人工比对 emoji → confirm_verification(txn)
│ confirm_short_auth_string(txn)
│ = accept_sas + get_mac + (verified→verify_device)
│ ◄────────────────────────────────────────────── m.key.verification.mac(只发一次, W1N-169)
│ m.key.verification.mac → (mac 分支) sas.verified → emit done
方向 B:bot 发起(出站) — start_verification(user_id, device_id) → nio.start_key_verification(device) 发出
start;后续 accept/key/emoji 同方向 A;比对后同样 confirm_verification 完成。key 的发送完全由 nio 内部负责
(handle_key_verification 里 if not sas.we_started_it: share_key()),集成不再手动 share_key()(W1N-169)。
状态机要点:
- 集成只在 start 分支记录 monotonic 时间(
_mark_sas_started);超时判断 =sas.timed_out或 monotonic 差 ≥ 240s(_sas_is_timed_out),超时 →_timeout_verification:cancel + emitverification_timeout。 confirm_verification是唯一完成验证的路径:先查超时,再nio.confirm_short_auth_string(txn),sas.verified→ emitdone,否则 emitsas(等用户再次 confirm)。- 入站门控:所有分支(start/key/mac/cancel)统一
_bootstrap_allowed(sender)——sender == session.user_id或sender ∈ allowed_users,否则 emitverification_peer_denied(W1N-143)。
3.3 路径二:单向指纹验证(降级/备选)
get_fingerprint 取 bot 指纹 → Element「Manually Verify by Text」;信任对端用 verify_device_by_fingerprint
(精确匹配后 nio.olm.verify_device,本地单向信任,不是 SAS,不产生 SAS 事件)。
4. 代码地图(custom_components/matrix_e2ee/)
client.py(1525 行;无 models.py,模型在此文件)
nio 补丁区(160–398):
| 函数 | 行 | 作用 |
|---|---|---|
_apply_sas_timeout_patch |
164 | monkeypatch Sas.timed_out:verified/canceled→False;now-creation ≥ _max_age(5min)→canceled+_timeout_error+True。忽略 nio 60s 事件超时 bug |
_sas_commitment(pubkey, canonical) |
197 | SHA-256(pubkey+canonical) 的 unpadded base64 |
_apply_sas_commitment_patch |
208 | patch from_key_verification_start(accept commitment)+ _check_commitment(initiator 校验)→ unpadded base64 |
_apply_sas_emoji_patch |
256 | _generate_emoji 直接用 established_sas.bytes(info).emoji_indices 映射,不做 bit-slicing |
_apply_sas_mac_patch |
286 | 见下 |
_select_mac_func |
304 | chosen_mac_method=="hkdf-hmac-sha256" → calculate_mac_invalid_base64,否则 calculate_mac |
get_mac(patch) |
309 | sas_accepted=False/canceled 抛 LocalProtocolError;按规范拼 info、ed25519:{own_device} + KEY_IDS |
receive_mac_event(patch) |
339 | verified→return;state!=key_received→canceled;KEY_IDS 校验→_key_mismatch_error;逐 key device_id 匹配 + MAC 校验 → verified_devices;空→canceled(缺 return,复刻 W1N-179 bug) |
_patch_nio_sas_timeout/_commitment/_emoji/_mac |
188/246/277/390 | try: from nio … except Exception: return(测试无 nio 时 no-op) |
验证服务(936–1525):
| 函数 | 行 | 作用 |
|---|---|---|
enable_verification_callbacks |
936 | 注册 handle_to_device_event(KeyVerificationEvent)+ _handle_verification_request(ToDeviceEvent) |
_emit_verification(stage, **extra) |
947 | fire matrix_e2ee_verification + warning log |
_verification_expires_at |
953 | now UTC + _verification_timeout ISO 串 |
_lookup_device / _get_sas |
959 / 972 | device_store 查设备 / nio.key_verifications.get(txn) |
_sas_party / _sas_emojis |
978 / 990 | 提取对端 (user,device) / sas.get_emoji()(异常吞掉→None,list[list[str]]) |
_mark_sas_started / _sas_is_timed_out |
1006 / 1009 | monotonic 记录 / 超时判断 |
async_start_verification |
1018 | 拒软登出;查设备→nio.start_key_verification;emit started |
async_verify_device_by_fingerprint |
1060 | actual.strip()!=ed25519.strip() 精确等值(W1N-159)→fingerprint_mismatch;nio.olm.verify_device |
async_confirm_verification |
1090 | 超时检查→nio.confirm_short_auth_string(txn);verified→emit done,否则 emit sas。唯一验设备路径 |
async_cancel_verification |
1139 | nio.cancel_key_verification(txn, reject=False) → emit canceled |
_timeout_verification |
1169 | cancel + emit verification_timeout + timeout |
_bootstrap_allowed(sender) |
1187 | sender==session.user_id 或 sender in allowed_users(W1N-143 门控) |
_repair_dropped_start |
1196 | _query_device_keys(sender) 后重喂 nio.olm.handle_key_verification(event)(W1N-170) |
_handle_verification_request |
1222 | 解析 request;校验 sender/txn/methods/timestamp → _send_verification_ready;不建 SAS 状态(W1N-173) |
_send_verification_ready |
1283 | 回 ready {from_device: own, methods:[m.sas.v1], transaction_id} |
handle_to_device_event |
1314 | 主分发:门控→cancel→timeout→start(emoji 检查/repair/accept/emit)→key(emit sas)→mac(verified→emit done) |
_verification_kind |
1458 | 类名/type 串 → start/key/mac/cancel |
_request_timestamp_valid |
1447 | 未来≤5min 且 age≤10min |
_transaction_id_from_verifications |
1480 | 匹配 other user+device;唯一则兜底 |
_verification_error_code |
1496 | timeout→verification_timeout;LocalProtocolError/does not exist→invalid_transaction;unverified→unverified_device;否则 send_failed |
const.py(79 行)
SERVICE_START/CONFIRM/CANCEL_VERIFICATION、SERVICE_VERIFY_DEVICE_BY_FINGERPRINT;ATTR_ED25519/TRANSACTION_ID/ USER_ID/DEVICE_ID;EVENT_VERIFICATION/EVENT_FINGERPRINT;错误码见 §3.1;VERIFICATION_TIMEOUT_SECONDS=240;
VERIFICATION_REQUEST_MAX_FUTURE_MS/MAX_AGE_MS;SAS_METHOD_V1="m.sas.v1";VERIFICATION_REQUEST/READY/START/ ACCEPT/KEY/MAC/DONE/CANCEL 类型串。
__init__.py
_register_services(136) 服务→client 方法映射;_fire_event(123);_options(126) 读 allowed_users/allowed_rooms;
async_setup_entry(268)/async_unload_entry(321)。
5. 注意的问题(Checklist / 教训)
协议 / wire 层
- MAC 生成与校验必须一起改(
get_mac+receive_mac_event同步 monkeypatch),只改一边 = 互不兼容。 .v2不要自己加:nio 不提供_mac_v2,不要扩大协商面。- accept/key/mac 的发送权要分清:key 归 nio 内部(
we_started_it判断),mac 只由confirm_verification发; 集成只做事件映射与人工 confirm 触发(W1N-169 双发教训)。 - commitment 必须 unpadded base64(不是 hexdigest);emoji 索引不得二次转换;legacy MAC 用 invalid base64。
- SAS HKDF info / MAC info 都是无分隔符逐字节拼接,字段顺序(发起方在前)与方向不能错。
nio 状态机坑
6. Sas.timed_out 被 nio 60s bug 污染 → 必须 monkeypatch,用 240s 集成超时先行。
7. get_mac() 在 sas_accepted=False 抛异常 → 必须走 confirm_short_auth_string,别裸调。
8. 入站 start 需设备 key:_get_sas==None 时先 _repair_dropped_start(查 key 重喂),不要直接放弃。
9. keys_query 默认不含 bot 自己 → 登录/恢复后主动预热自身 keys。
信任 / 安全
10. 无自动信任:同账号 ≠ 可信,包括 @bot 自身设备(W1N-153)。
11. 指纹比较必须精确等值,禁 casefold()(unpadded base64 大小写敏感,W1N-159)。
12. 验证/指纹/reauth 服务必须 admin-only(W1N-150);入站所有分支统一门控 _bootstrap_allowed(W1N-143)。
13. request 的 timestamp 校验:未来≤5min、age≤10min(W1N-173)。
14. fail-closed:未验证设备不触发命令事件、不回退明文、ignore_unverified_devices 永不开启、日志不落 secret(W1N-136)。
运维 / 测试
15. 设备 ID 变了 = store 丢失 → 按 runbook 重新验证,不静默新建设备(W1N-134/138)。
16. 测试只用 FakeNio/FakeSas,协议级问题单测不可见 → 缺真实 homeserver e2e(W1N-171 Backlog)。
17. 上游 nio receive_mac_event 缺 return bug 已复刻到我们的 patch 里(W1N-179 Backlog),改上游时同步改。
6. Issue 索引表(Linear → 知识)
状态:✅ Done | ⏳ Backlog
| Issue | 主题 | 知识章节 | 状态 |
|---|---|---|---|
| W1N-134 | M2 E2EE 登录/原子 session/恢复同一设备 | §5-15 | ✅ |
| W1N-135 | M2 加密收发 + sync tokens(消费验证状态) | §3 | ✅ |
| W1N-136 | M2 fail-closed、allowlist、不记录 secret | §5-14 | ✅ |
| W1N-137 | M3 SAS 服务/事件 + verified-device 策略 | §3.1 | ✅ |
| W1N-138 | M4 软登出/store 丢失恢复/diagnostics | §5-15 | ✅ |
| W1N-141 | 集成设备安全验证指南(parent of A–F) | — | ✅ |
| W1N-142 | A SAS auto-completion(修 get_mac 卡死)→ 后被 W1N-153 撤销自动分支 | §2 / §5-7 | ✅ |
| W1N-143 | B 入站 SAS 发起者门控 | §3.2 / §5-12 | ✅ |
| W1N-144 | C 单向指纹验证(get_fingerprint / verify_device) | §3.3 | ✅ |
| W1N-145 | D VerificationPrompt 模型 + expires_at | §3.1 | ✅ |
| W1N-147 | F SECURITY.md + SAS/指纹指南(cross-signing 不做 SSSS) | §1.7 | ✅ |
| W1N-149 | 安全加固总纲(P0 自动确认 / P0 admin-only / P1 指纹门控) | §5 | ✅ |
| W1N-150 | B admin-only 强制 | §3.1 / §5-12 | ✅ |
| W1N-151 | C verify_device_by_fingerprint(ed25519 精确门控) | §3.3 | ✅ |
| W1N-153 | A 删除同账号 SAS 自动确认 | §5-10 | ✅ |
| W1N-154 | E 文档改为手动确认 + 新指纹服务名 | — | ✅ |
| W1N-156 | 加固跟进:allowlist 拆分 + P2 质量(未做) | §8 | ⏳ |
| W1N-157 | F 人话版验证指南(docs/DEVICE_VERIFICATION.md) | — | ✅ |
| W1N-158 | M5 Config Flow(reauth/设备验证 UI 路径动机) | — | ✅ |
| W1N-159 | casefold 指纹比较 bug → 精确匹配 | §5-11 | ✅ |
| W1N-162 | Config Flow reconfigure + reauth | — | ✅ |
| W1N-165 | 文档更新 + release 0.2.0(SAS 保持 service/event-based) | §8 | ✅ |
| W1N-166 | keys_query 不查同账号 → 预热 own device keys | §2-7 / §5-9 | ✅ |
| W1N-169 | key/mac 双发(协议正确性) | §5-3 | ✅ |
| W1N-170 | 启动后新增设备首次 start 被丢 → 自动查 key 重喂 | §2-8 / §5-8 | ✅ |
| W1N-171 | 缺真实 homeserver 端到端 SAS 测试 | §5-16 | ⏳ |
| W1N-172 | nio 60s 必死超时(_last_event_time 不刷新) | §2-2 / §5-6 | ✅ |
| W1N-173 | request → ready 桥接(nio 缺框架) | §2-1 / §5-13 | ✅ |
| W1N-174/176 | 部署 matrix_e2ee(e) 到 hass.windy.lan | — | ✅ |
| W1N-175 | emoji 双转换(vodozemac indices) | §2-4 | ✅ |
| W1N-177 | legacy MAC invalid-base64 | §2-5/6 / §5-1/2 | ✅ |
| W1N-178 | 部署 v0.2.10(含 legacy MAC 修复) | — | ✅ |
| W1N-179 | receive_mac_event 缺 return 覆盖 canceled(未做) | §2-9 / §5-17 | ⏳ |
7. 当前版本状态与待办
- 已发布至 v0.2.10(wire 修复线:commitment v0.2.8 → emoji v0.2.9 → legacy MAC v0.2.10,均已部署 hass.windy.lan)。
- SAS 保持 service/event-based(v0.2.x);HA 内 live SAS emoji UI 延后到 v0.3(W1N-165 / SECURITY.md)。
Backlog(避免重复工作,动手前先查这 3 条):
- W1N-156 — 拆分
allowed_users为「命令权限」与「SAS 验证权限」两组;SAS 不支持算法显式 cancel; setup/stop/reauth async lock;事务绑定(存预期 user/device/创建时间);CI manifest 依赖校验;ruff。 - W1N-179 — nio
receive_mac_event缺 return(上游 + 我们的 monkeypatch 同步修,加单测)。 - W1N-171 — 真实 homeserver 端到端 SAS 测试(docker Synapse 或人工 runbook)。