Appearance
gen 工作线开发文档(passno-gen / passno-totp / passno-match)
本文记录 feat/gen 分支在 v0.1 阶段交付的三个核心库 crate、内置外部数据及更新脚本。
1. 交付内容
| 组件 | 路径 | 职责 |
|---|---|---|
passno-gen | crates/passno-gen | 随机密码、EFF 长词表口令短语、PIN 码、Apple PMR 网站密码规则生成与密码强度/熵估算 |
passno-totp | crates/passno-totp | otpauth://totp/... URI 与容错裸 Base32 密钥解析,基于 RFC 6238 生成动态验证码 |
passno-match | crates/passno-match | URL 规范化(IDN/punycode、默认端口、尾点、大小写、裸主机)、PSL(含私有段)匹配、PMR 共享凭据组与自动填充排序 |
| 词表与 PMR 数据 | crates/passno-gen/data/ | EFF 长词表(eff_large_wordlist.txt,CC-BY-3.0 署名见 NOTICE)与 Apple PMR 4 个 JSON 文件(MIT 见 pmr/LICENSE.md) |
| 数据更新脚本 | tools/update-pmr.sh | 从 apple/password-manager-resources 拉取最新 quirks/*.json 与 LICENSE.md |
2. 构建与测试
在工作区根目录运行:
bash
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test --workspace刷新内置 Apple PMR 数据(需要网络):
bash
./tools/update-pmr.sh验证文档同步检查:
bash
./tools/ci/check-docs-sync.sh origin/main HEAD测试覆盖情况
passno-gen(crates/passno-gen/tests/gen_tests.rs):- 遍历
uppercase × digits × symbols × avoid_ambiguous × length全组合样本校验约束与防歧义字符过滤。 - 每类最少字符数、
extra_chars自定义字符集及非法参数拒绝。 - EFF 长词表(7,776 词)口令短语(分隔符、首字母大写、附加数字)与 PIN 码生成。
- 遍历内置
password-rules.json全部 458 条真实网站规则(含activision.com、aeon.co.jp、amundi-ee.com、google.com、ototoy.jp等)与原始passwordrules字符串,逐条用password-rules-parser校验生成结果完全合规。 - 统计一致性:4,000 次样本验证首尾位置无字符类偏向;5,000 次 1 位 PIN 验证 0–9 分布均匀。
- 精确熵公式与
zxcvbn手输口令强度映射验证。
- 遍历
passno-totp(crates/passno-totp/src/lib.rs):- 覆盖 RFC 6238 Appendix B 全部 SHA1 / SHA256 / SHA512 测试向量(8 位,时间戳 59 至 20,000,000,000)。
- 验证
remaining跨步长边界倒计时、tests/fixtures/import/apple/passwords.csv中的otpauth://用例、URL 编码标签与参数优先级、空格/连字符/小写/无填充 Base32 容错、Debug脱敏与非法输入拒绝。
passno-match(crates/passno-match/tests/match_tests.rs):- 表驱动覆盖
Domain/Host/StartsWith/Exact/Never五种模式、IDN/punycode、默认端口(80/443)、非默认端口、尾点、大小写、裸主机、HTTPS 禁降级 HTTP、私有 PSL 隔离(a.github.io不匹配b.github.io)、单向子域(example.com匹配login.example.com,反向不匹配)。 - 验证
shared-credentials.json双向共享组(safeway.com↔acmemarkets.com)与单向迁移(twitter.com→x.com,反向不匹配)。 - 验证
tests/fixtures/import/apple/passwords.csv全部测试 URL 及Exact > Host > Domain自动填充排序。
- 表驱动覆盖
3. 设计决策与依据
- 避免
passno-gen与passno-match循环依赖:passno-match通过include_str!("../../passno-gen/data/pmr/shared-credentials.json")直接读取统一存放的 PMR 共享凭据文件,不依赖passno-gen;passno-gen则依赖passno-match来规范化for_domain主机名并查找父域至eTLD+1对应的password-rules.json规则,做到数据零重复、单向依赖。 - 严格对齐
docs/dev/api.md并提供扩展配置类型:GeneratorOptions、Generated、Strength、GeneratorKind(以及passno-totp::TotpCode)字段和名称与docs/dev/api.md保持 100% 一致,确保其他工作线直接按契约构造结构体字面量即可编译;同时额外导出RandomOptions、PassphraseOptions、PinOptions、generate_from_rules与estimate_user_password,完整覆盖每类最小数量、extra_chars、口令附加数字及原始passwordrules字符串。 - 密码学随机源与无偏采样: 使用
rand0.10 的SysRng(经UnwrapErr(SysRng)直接调用 OS CSPRNG),以random_range做无偏索引采样,先放入必需字符再经 Fisher-Yates(SliceRandom::shuffle)整体打乱,避免用户态 PRNG 残留或位置偏向。 - 精确熵与强度阈值:
Random密码:利用对数空间指数生成函数(EGF)动态规划计算长度 $L$ 且满足各类最小字符数约束后的精确密码空间大小 $\log_2(\text{valid_count})$。Passphrase:$k \cdot \log_2(7776) + (\log_2 10 \text{ 若附加数字})$(首字母大写为确定性变换,不计入额外熵)。Pin:$L \cdot \log_2(|\text{pool}|)$。- 生成密码强度阈值:
< 40.0bits →Weak;40.0..60.0bits →Fair;60.0..80.0bits →Strong;>= 80.0bits →VeryStrong(使 6 词口令短语≈77.5 bits 为Strong,6 词+数字≈80.9 bits 为VeryStrong)。 - 手输密码强度:使用
zxcvbn3.1(score0–1 →Weak、2 →Fair、3 →Strong、4 →VeryStrong),支持传入user_inputs惩罚用户名/邮箱等上下文词汇。
- PMR 规则处理细节:
- 通用
Special/AsciiPrintable/Unicode字符类默认过滤空格' ',避免无意在常规网站密码中插入空格(Custom类仅在全是空格时保留)。 target_len自动夹紧在[max(minlength, required.len()), maxlength]区间内;遇到max-consecutive限制时通过保持字符多重集的交换修复与重试确保既满足连续重复上限又不破坏required约束。
- 通用
- 密钥内存保护:
TotpConfig内部密钥使用zeroize在Drop时清零,并自定义Debug输出为[REDACTED]。
4. 已知缺口与 v0.2 待办
- 契约可选扩展:若 v0.2 需要让桌面端 / CLI 通过单一
GeneratorOptions直接传入min_*、extra_chars或页面抓取到的原始passwordrules属性,届时可协商扩展docs/dev/api.md(当前v0.1通过passno_gen::RandomOptions与generate_from_rules已可调用)。 - 复杂 PMR 规则熵估值:当
passwordrules同时包含重叠的required组或max-consecutive约束时,estimate_rules_entropy采用允许字符集上界或一阶马尔可夫公式估算,未对任意重叠条件构造精确自动机计数。 fromDomainsAreObsoleted提示:shared-credentials.json中的fromDomainsAreObsoleted标志已解析并保存在SharedGroupEntry::Migrated,v0.2 浏览器扩展与安全体检可据此向用户提示旧站点条目可更新为新域名。- 最小化根
Cargo.toml:当前分支根Cargo.toml仅包含本工作线三个 crate,合并进主分支时需由协调者合并进完整 workspace。