# QN-TOTP **Repository Path**: YuYao-Gith/QN-TOTP ## Basic Information - **Project Name**: QN-TOTP - **Description**: 一个完全离线,免费的TOTP验证码生成器,用于2FA等场景 - **Primary Language**: C++ - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-04 - **Last Updated**: 2026-08-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # QN-TOTP 离线 TOTP(RFC 6238)验证码生成器 —— 基于 Qt Widgets 的跨平台桌面应用(Linux 优先,兼容 Windows)。 > **⚠️ 官方渠道声明**:本项目的唯一官方发布渠道是以下两个仓库,**任何其他来源均为仿冒**: > > - Gitee: > - GitHub: > > 请只从上述仓库获取源码与构建产物。第三方打包站、网盘分享、改名 fork、镜像站等渠道出现的任何「QN-TOTP」版本,均无法保证与官方代码一致,**可能被植入后门、木马或恶意修改**;因使用非官方渠道版本而遭受的任何损失,作者不承担任何责任。若在官方仓库之外发现自称 QN-TOTP 的可疑分发,欢迎通过仓库 Issue 举报。 ## 功能 - **添加账户**:输入账户名称与 Base32 TOTP 密钥,输入时实时校验格式 - **实时列表**:账户名、6 位验证码(等宽字体高亮)、30 秒倒计时进度条 + 剩余秒数,每秒自动刷新 - **复制验证码**:双击验证码单元格复制到系统剪贴板,状态栏提示 - **删除账户**:工具栏按钮或右键菜单 - **本地加密存储**: - 首次启动设置主密码,经 PBKDF2-HMAC-SHA256 派生 32 字节密钥并保存于本地密钥文件 `master.key`(Unix 权限 0600) - 账户数据经 AES-256-CBC + HMAC-SHA256 加密后写入 `accounts.json`,密钥、账户名均不以明文落盘 - **核心算法**:严格遵循 RFC 6238(HMAC-SHA1 + 动态截断,30 秒周期,6 位验证码) ## 构建 ### Linux(Ubuntu 22.04 / Debian 系) ```bash sudo apt update sudo apt install cmake g++ qt6-base-dev libssl-dev # 若系统只有 Qt5:sudo apt install qtbase5-dev ./build.sh # 等价于: # cmake -S . -B build -DCMAKE_BUILD_TYPE=Release # cmake --build build -j"$(nproc)" ./build/qn-totp ``` ### Windows(MSVC) 1. 安装 Qt 6(MSVC 套件,勾选 Qt Widgets)、CMake ≥ 3.16、OpenSSL(或通过 vcpkg 安装 `openssl`) 2. 打开 "x64 Native Tools Command Prompt",执行: ```bat cmake -S . -B build cmake --build build --config Release build\Release\qn-totp.exe ``` 3. 部署:用 windeployqt 收集 Qt 运行库,并将 OpenSSL 的 `libcrypto-3-x64.dll` / `libssl-3-x64.dll`(若为动态链接)复制到 exe 同目录: ```bat windeployqt build\Release\qn-totp.exe ``` > CMake 已为 MSVC 添加 `/utf-8`,源码中的中文字符串可正常编译。 ## 使用 - 首次启动弹出「设置主密码」对话框;派生出的密钥保存至 `~/.config/qn-totp/master.key`(Windows 下为 `%APPDATA%/qn-totp/master.key`),后续启动直接读取,无需重复输入。 - 账户数据加密存放于 `~/.config/qn-totp/accounts.json`:文件是 JSON 容器,`data` 字段为 Base64 编码的密文(`IV || 密文 || HMAC-SHA256`),不含任何明文密钥。 - 双击验证码即可复制;右键选中行可删除账户。 > **安全说明**:按需求设计,主密钥保存在本机。能读取该文件的用户即可解密账户数据, > 因此请确保操作系统账户权限(密钥文件已设为 0600)。本应用不联网、不发送任何数据。 ## 配置导出工具详解(qn_totp_export.py) 仓库自带的零依赖导出/迁移脚本:不依赖 Qt、不依赖 OpenSSL 动态库,加密解密用 Python `cryptography`(缺失时自动回退 `openssl` CLI)。面向两种典型场景:**记得密码 → 解密拿原始码**;**不记得 / 不想给密码 → 打包走人**。 ### 交互式(推荐) ```bash python3 qn_totp_export.py [--config 配置目录] ``` 直接运行即进入菜单,全程回车流即可完成,密码隐藏输入、错误自动重试(最多 3 次): ``` ============================================ QN-TOTP 配置导出工具(交互式) - [1][2] 解密导出原始 Base32 密钥 - [3] 整体拷贝配置文件夹 ============================================ 配置目录 [/home/user/.config/qn-totp]: ← 回车确认,或输入其他路径 检测到: master.key ✓ accounts.json ✓ 请选择操作: [1] 解密导出(记得主密码) [2] 解密导出(不记得密码,直接用 master.key 里的密钥) [3] 拷贝配置文件夹(master.key + accounts.json) [q] 退出 > ``` 解密后可选「只打印到屏幕」或「写入文件」(默认 `./qn-totp-export-<时间戳>/`,可改),并可选择屏幕打码显示。 ### 命令行方式 ```bash # 方式一:记得初始主密码 —— 解密 accounts.json,直接导出原始 Base32 密钥 python3 qn_totp_export.py decrypt -p '你的主密码' # 打印到屏幕 python3 qn_totp_export.py decrypt -p '你的主密码' --out ./backup # 另写三份导出文件 python3 qn_totp_export.py decrypt --password-file pwd.txt --out ./backup # 密码走文件,不进 shell 历史 python3 qn_totp_export.py decrypt # 不带 -p 则交互输入(隐藏) # 方式二:不知道 / 不想提供密码 —— 整体拷贝配置文件夹(master.key + accounts.json) python3 qn_totp_export.py copy [--dest ./backup] # 附赠:master.key 里本来就存着派生好的 32 字节密钥,忘了密码也能直接解密 python3 qn_totp_export.py decrypt --use-stored-key --out ./backup ``` ### 导出文件 | 文件 | 内容 | 用途 | | --- | --- | --- | | `accounts.json` | 解密后的明文 JSON(`{"accounts":[{name, secret}...]}`) | 与程序内存格式一致,可手工核对 | | `accounts.csv` | `name,secret` 两列(带 BOM,Excel 直接开) | 表格查看 / 二次加工 | | `otpauth.txt` | 每行一个 `otpauth://totp/...` URI | 直接导入 Aegis / 2FAS / Google Authenticator 等 | > `otpauth.txt` 按 SHA1 / 6 位 / 30 秒 生成(本项目 TOTP 参数),导入其他应用时以实际显示为准。 ### 它如何解密(与 C++ 侧逐字节对应) 1. **读密钥**:`master.key` = `"QTOTPKEY"`(8) + 版本 u32 LE(4) + salt(16) + key(32)。 方式一用密码重算:`key = PBKDF2-HMAC-SHA256(密码 UTF-8, salt, 600000 轮, 32 字节)`(与 `Encryption::deriveKey` 一致); 方式 `--use-stored-key` 直接取文件里存的 32 字节 key。 2. **拆密文**:`accounts.json` 的 `data` 字段 Base64 解码得 `iv(16) || 密文(PKCS7) || mac(32)`。 3. **验 MAC**:`HMAC-SHA256(key, iv || 密文)` 与尾部 mac 比对(常数时间比较)——密码错、文件被改都会在此报「完整性校验失败」。 4. **解密**:AES-256-CBC + 去 PKCS7 填充,明文即 `{"accounts":[...]}`。 ### 安全说明(认真读) - `master.key` 存的是**派生后的密钥**而非口令哈希:能读到该文件的用户即可解密全部账户。因此「方式二拷贝文件夹」与「方式一输密码」拥有**完全相同的解密能力**——这是单机单用户模型下的刻意取舍(README 开头已声明),代价就是拷贝即全部。请保护好 `master.key`(0600)与任何备份。 - PBKDF2 600000 轮的价值在于 **master.key 泄露之后**:攻击者无法从 salt 反推口令,只能离线爆破(现代 GPU 约每秒几百至一千多次尝试),8 位以上混合口令基本无解。 - 密码传递:`-p` 会出现在 shell 历史与进程列表;敏感场合用 `--password-file`、交互输入,或干脆 `--use-stored-key`(本机信任模型下它等价)。 - 本工具全程不联网、不写配置目录,导出内容只落在你指定的输出路径。 ### 依赖与兼容 - Python ≥ 3.6;`cryptography` 缺失时自动用系统 `openssl enc` 解密(项目本就依赖 OpenSSL,一般都在)。 - 配置目录自动检测:Linux `~/.config/qn-totp`(尊重 `XDG_CONFIG_HOME`)、Windows `%APPDATA%\qn-totp`、macOS `~/Library/Application Support/qn-totp`;可用 `--config` 覆盖。 - 非交互终端下不带参数运行会打印帮助并退出,避免管道里傻等输入。 ## 目录结构 ``` main.cpp 程序入口 totp_generator.h/cpp RFC 6238 TOTP 核心(Base32 解码 + HMAC-SHA1 + 动态截断) encryption.h/cpp AES-256-CBC + HMAC-SHA256 加密模块(独立于 GUI,可复用于 Boolean Encryptor) account_store.h/cpp 账户列表与加密文件读写(master.key / accounts.json) mainwindow.h/cpp 主窗口(列表、倒计时委托、右键菜单、剪贴板) add_account_dialog.h/cpp 添加账户对话框 qn_totp_export.py 配置导出工具(解密导出 / 拷贝备份,见上文) CMakeLists.txt CMake 构建(Qt6 优先,自动回退 Qt5;链接 OpenSSL::Crypto) ``` ## 算法与测试向量 - 密钥:Base32(RFC 4648),自动忽略空格、连字符,大小写不敏感 - 时间步:30 秒;验证码:6 位;HMAC-SHA1(RFC 6238) - 参考向量(RFC 6238 附录 B,密钥为 ASCII 字符串 `12345678901234567890`): | 时间步 T | 8 位验证码 | 6 位验证码 | | ----------- | ---------- | ---------- | | 59 | 94287082 | 287082 | | 1111111109 | 07081804 | 081804 | | 1111111111 | 14050471 | 050471 | | 1234567890 | 89005924 | 005924 | | 2000000000 | 69279037 | 279037 | | 20000000000 | 65353130 | 353130 | ## 许可证 AGPL-3.0(详见 [LICENSE](LICENSE))。