NikPak

技术指南

完整的技术指南:架构与 .nik 格式、压缩引擎与媒体编解码器、加密设计与量子抗性、加固、全部基准测试,以及构建、测试和路线图说明。

1 引言

nikpak 是一个用于 .nik 格式的命令行压缩工具。它的设计围绕四个理念:比现有工具压缩得更好、更快,用现代密码学保护数据,并以单个小型可执行文件在 Windows、macOS 和 Linux 上原样运行。

1.1 设计目标

  • 小。在人们大量实际归档的数据上击败 RAR 7。音频、图像和跟踪器音乐通过内容检测,并用专门的无损编解码器压缩;其余所有数据则使用调优过的多线程 Zstandard 流。
  • 快。压缩和解压应受磁盘限制,而非 CPU。在其快速级别 -m2 下,nikpak 在单核上不到一秒即可压缩约 200 MB 的源文件。
  • 安全。带内存困难密钥派生函数的认证加密,文件名始终隐藏,篡改始终可检测,解压器不会被诱骗把文件写到目标文件夹之外。
  • 抗量子。仅使用 256 位对称密码学。nikpak 中没有任何 RSA、椭圆曲线或 Diffie-Hellman 代码,因此 Shor 算法无物可破。
  • 可移植、可脚本化。单个静态链接的可执行文件、与 RAR 兼容的命令行、可预测的退出码,以及记录每个选项的 -help。

1.2 结果速览

指标结果对比对象
BMP 照片小 38.8 %RAR 7.00 -m5
立体声音乐,44.1 kHz 16 位小 14.6 %(与 FLAC -8 相当)RAR 7.00 -m5
ProTracker MOD 文件小 13.3 %RAR 7.00 -m5
单声道音频,8 位和 16 位分别小 12.2 % 和 15.3 %RAR 7.00 -m5
197 MB 源文件,最高级别小 5.5 %(24.4 MB 对 25.9 MB)RAR 7.00 -m5 -s -md256m
同样数据,快速级别 -m2 0.9 sRAR 7.00 -m3 固实:16.7 秒
加密Argon2id + XChaCha20-Poly1305RAR5: AES-256-CBC + PBKDF2
全部 10 个测试集,最高级别,对比 7-Zip小 2.4 % 至 31.7 %7-Zip 23.01 -mx9(第 11.7 节)
Office 文件(doc、docx、xls、xlsx、csv、pdf)小 31.7 %7-Zip 23.01 -mx9(第 11.7 节)
全部 10 个测试集,最高级别,对比 ZIP小 19.9 % 至 60.4 %ZIP -9,Info-ZIP 3.0(第 11.7 节)
可执行文件单文件、无依赖;带窗口约 9 MB,仅命令行 2.3 MBWindows, macOS, Linux



数字是如何获得的
所有测量都来自同一台机器(Linux x86-64 虚拟机,单 CPU 核),使用 RAR 7.00 和 nikpak 1.1.0。每个压缩包都再次解压并与原始文件逐字节比较。完整的方法、测试集以及 RAR 更快的那些情形,在第 11 章中有描述。



1.3 它是为谁准备的?

nikpak 面向需要大量归档媒体或源代码并关注大小和速度的人,面向需要可脚本化压缩工具(有合理退出码和文档化格式)的开发者,也面向任何需要强加密而又不想信任闭源实现的人。由于命令行遵循 RAR 约定,Total Commander 等前端程序几乎无需重新配置即可驱动它。

1.4 如何阅读本指南

第 2 章和第 3 章介绍 nikpak 的功能及使用方法。第 4 至 7 章解释架构、文件格式、压缩引擎和媒体编解码器。第 8 至 10 章涵盖加密、抗量子和加固。第 11 章给出基准测试,第 12 章如实比较 nikpak 与 RAR,第 13 至 15 章讨论构建、测试和路线图。

1.5 社区与联系方式

新闻、问题、想法和错误报告:可通过以下方式联系 nikpak 的作者

  • Telegramhttps://t.me/cryptonikko
  • Xhttps://x.com/cryptonikkoid
  • Discordhttps://discord.com/users/1537826350759415811

同样的三个链接在 nikpak 窗口底部、目标文件夹(第 3.7 节)下方显示为圆形彩色按钮,也在 nikpak -help 的末尾显示。

2 功能概览

2.1 功能

领域nikpak 提供的功能
压缩在 -m1 至 -m4 使用 Zstandard(多线程、固实),-m4 带有 128 MiB 窗口和长距离匹配。在 -m5 使用多引擎主体:二进制数据用 LZMA2,文本用 PPMd,通过试算选择;文件按类型排序,与 7-Zip 相同。
媒体智能基于内容检测 WAV、AIFF、ProTracker MOD 和 BMP;对每个文件在四个过滤器(NAC、Shuffle、Shuffle+Delta、MED)间试算;自动回退到普通压缩。
Office、PDF、gzipDeflate 再压缩(preflate):docx、xlsx、pptx、odt、ods、epub、jar、zip、pdf 和 gz 文件内部的 Deflate 流被解码,压缩得更好,并在解压时逐位重建(-m4、-m5)。
加密Argon2id(64 MiB)密钥派生,XChaCha20-Poly1305 以 64 KiB 认证分块;名称、大小、日期和权限均被加密。
密码带确认的交互式提示、环境变量、stdin、命令行;未提供密码时自动生成强随机密码。
完整性每个文件原始字节的 CRC32;加密压缩包的 AEAD 认证;带条目计数的结束标记;自验证音频编解码器。
命令行RAR 风格命令(a x e l v lb t p d),提取单个文件、文件夹或通配符,以及开关、zip 风格别名、通配符、@listfiles、排除、覆盖策略、安静模式、明确的退出码。
安全性Zip-slip 防护、拒绝符号链接、去除 setuid、有界解码器、原子化压缩包写入、Rust 内存安全。
可移植性Windows(PowerShell、cmd)、macOS、Linux;单个静态可执行文件;在每个平台上结果逐字节相同。
图形界面不带参数启动时,nikpak 打开一个极简拖放窗口:拖入(或选择)文件或文件夹即可用最佳压缩压缩它们;进度环、记住的目标文件夹(第 3.7 节)。
压缩包浏览器unNik.exe,对所有人免费:浏览文件夹、搜索、查看文本和二进制文件、提取所选内容并在 .nik 压缩包中删除文件(第 3.9 节)。
皮肤55 种窗口皮肤,包括 14 种手绘场景皮肤;一个位于窗口旁的皮肤面板,带有 Apply Skin 和 Cancel 按钮;可导入你自己的 Nikkomander .skin.json 文件(第 3.10 节)。
文件管理器菜单在 Explorer、Dolphin、Nemo、Nautilus 和 Finder 中的“NikPak”子菜单:添加到压缩包、解压到这里、解压到其专属文件夹、用 unNik 打开;多个选中项合并为一个压缩包(第 3.11 节)。



2.2 nikpak 与 RAR 对比

下表比较了本项目中测量的 nikpak 1.1 与 RAR 7,并如实说明 RAR 领先之处。第 12 章讨论各要点。


nikpak 1.1RAR 7
媒体压缩按文件通过试算选择专门的无损编解码器带内置过滤器的通用算法
源代码,最高级别24.4 MB(更小),45.6 秒25.9 MB,26.7 - 36.2 秒
快速级别的速度0.9 秒 处理 197 MB(-m2) 9.7 - 16.7 s (-m3)
加密Argon2id + XChaCha20-Poly1305(已认证)AES-256-CBC + PBKDF2-HMAC-SHA256
文件名始终加密是仅使用 -hp 时
内存困难密码散列是(64 MiB)否
随机密码生成器内置非内置
源代码、语言完整源代码,Rust(内存安全)专有、C++
恢复记录、分卷、自解压、原地更新尚未支持是(WinRAR)
使用记录较新,尚未审计数十年



3 命令行参考

nikpak 遵循 RAR 用户熟悉的语法:nikpak [options] [files...]。选项可以出现在压缩包名称之前或之后的任意位置;-- 结束选项解析。

3.1 命令

命令含义
a创建新压缩包。名称没有扩展名时追加 .nik。文件夹始终递归处理。
x按完整路径解压:全部内容,或仅解压压缩包名称之后给出的名称、文件夹和通配符(nikpak x backup.nik Docs/report.pdf 解压单个文件)。尾部的路径分隔符(或 -d dir)选择目标位置。
e不带路径解压:每个文件直接进入目标文件夹。
l列出内容(全部,或仅匹配的名称):属性、大小、日期(UTC)、名称、总计。
v详细列表:增加每个文件的 CRC32。
lb裸列表:每行一个名称,专为脚本和 GUI 前端设计。
t测试:解压全部内容,检查每个 CRC32,并对加密压缩包验证认证。
p将文件打印到标准输出:全部,或指定的那些(nikpak p notes.nik todo.txt | more)。
d从压缩包中删除文件或文件夹(文件夹连带其内容)。压缩包会重新写入而不包含它们;加密压缩包保留其密码。必须给出名称。
mt媒体测试:检测 WAV/AIFF/MOD/BMP 文件,并显示每种媒体过滤器能达到的大小。不写入任何内容。
(none)不带任何参数启动时(例如双击 nikpak.exe),nikpak 改为打开其窗口(第 3.7 节)。



3.2 选项

RAR 风格zip 风格含义
-p -e加密 / 解密,询问密码。空输入会生成随机密码并显示。
-p -P 命令行上的密码(对机器上的其他用户可见)。
-hp[password]
与 -p 相同。nikpak 中文件名始终加密。
-m0 .. -m5 -0 .. -9存储、最快、快速、良好、高(加入 Deflate 再压缩)、最高(自 1.2.1 起为默认;LZMA2/PPMd,第 6.7 节)。当速度比大小更重要时使用 -m2。
-mt
线程数,1 到 256(默认:全部内核)。
-mc- / -mc+
所有内容过滤器(媒体编解码器和 Deflate 再压缩)关闭 / 媒体过滤器始终开启。默认:按文件自动试算。
-r
递归进入文件夹(始终开启,为兼容而接受)。
-ep -j仅存储每个参数的最后一个路径组成部分。
-ep1
不存储文件夹参数的基础文件夹名称。
-o+ -o不加询问覆盖已有文件。
-o- -n不加询问跳过已有文件。
-or
重命名已存在的被解压文件:name (1).ext。
-y
对所有问题都假定为是。
-x -x 排除文件;可重复使用。
-d -d x 和 e 的目标文件夹。
-inul -idq -q安静:仅打印错误。
@listfile
从文本文件读取文件名,每行一个。
-h -help -?
完整帮助。-V 打印版本。



RAR 前端经常发送但在此处无意义的选项(-s、-ma5、-md64m、-ibck、-idp、-rr、-ts*、-w 及类似选项)会被接受并忽略,因此现有工具配置可继续工作。其他任何选项都会产生清晰的错误消息和退出码 7。

文件管理器菜单添加了几个长选项(第 3.11 节):nikpak --add 和 --add-here 将给定项目压缩为新压缩包,nikpak --install-menu 和 --uninstall-menu 添加或移除右键菜单(--all-users 用于为所有用户安装),unNik --extract、--extract-here 和 --extract-to 从菜单驱动解压。

3.3 密码详解

当使用 -p 创建压缩包时,nikpak 按以下顺序解析密码,并且绝不会仅仅因为没有提供密码而失败:

  • 命令行上的 -p 或 -P :按原样使用(最少 8 个字符)。
  • 环境变量 NIKPAK_PASSWORD,可让密码不出现在进程列表中。
  • 交互式提示,询问两次以确认。空输入会生成随机密码。
  • 没有终端时(例如在 GUI 内部),密码通过 stdin 管道传入;如果没有,则生成随机密码。

生成的密码有 24 个字符,来自 56 个无歧义符号的字母表(不含 0/O、1/l/I),约 139 位熵。它在控制台上以方框形式打印一次。没有恢复途径:丢失密码的压缩包任何人都无法打开。

打开加密压缩包时,nikpak 使用同样的来源,然后用退出码 11 和说明性消息失败;它从不生成任何东西,因为生成的密码不可能匹配。

3.4 退出码

代码含义典型原因
0成功
1警告部分文件被跳过(不可读、不安全路径、符号链接)
2致命错误无法创建或读取文件,内部错误
3压缩包损坏或 CRC 错误压缩包损坏或被截断,校验和失败
7命令行错误未知选项,缺少压缩包名称
10没有匹配项没有文件与名称或通配符匹配
11密码错误或缺失密码错误,或加密压缩包被篡改



3.5 示例

nikpak a backup.nik C:\Docs\*.txt Photos\ # create an archive
nikpak a -m5 -mt8 big.nik Projects\ # maximum, 8 threads
nikpak a -p secret.nik Taxes2026\ # encrypted, prompts
nikpak a -pMyLongPassw0rd s.nik f.pdf # password given
nikpak x backup.nik C:\Restore\ # extract with paths
nikpak e backup.nik *.jpg C:\Pics\ # only *.jpg, flat
nikpak x backup.nik Docs/report.pdf # extract ONE file
nikpak e backup.nik Docs/report.pdf -d C:\Out # one file, no path
nikpak x backup.nik Photos -d C:\Restore # one folder
nikpak l backup.nik *.pdf # list only PDFs
nikpak p backup.nik notes.txt # show a file
nikpak d backup.nik Temp *.bak # delete entries
nikpak l backup.nik # list
nikpak t secret.nik # test (asks password)
nikpak mt song.wav tracker.mod photo.bmp # which filter wins?



3.6 与 Total Commander 及其他前端的集成

由于 nikpak 接受 RAR 的命令语法(a、x、e、l、t、@listfile、-p、-ep1、-o+、-y、-m),能够驱动外部 rar.exe 的程序也能驱动 nikpak.exe。在 Total Commander 中,在配置、压缩器、附加压缩程序下注册扩展名 nik,并将压缩和解压命令指向 nikpak.exe。无人值守解压时使用 -o+ 或 -y,因为没有控制台时 nikpak 无法询问是否覆盖,而是会跳过已有文件。菜单名称因 Total Commander 版本而异;请查阅其内置帮助了解具体字段。

Windows 上带空格的文件夹
以反斜杠结尾的加引号路径(如 "C:\My Folder\")会被 Windows 错误解析(末尾的引号被视为转义)。对于带空格的目标位置,请改用 -d "C:\My Folder"。



3.7 图形界面

nikpak 不带参数启动时(例如双击 nikpak.exe),会打开一个小窗口而不是打印用法。它只做一件事:压缩你拖到它上面的内容。

The nikpak window: waiting for files (left) and compressing (right).
nikpak 窗口:等待文件(左)和正在压缩(右)。
  • 拖入文件或文件夹,或一次拖入多个,放在窗口上;或点击圆圈(或 choose files / choose a folder 链接)在对话框中选择;点击圆圈会在环内显示 Choose files 和 Choose a folder 两个按钮。它们以最佳压缩(-m5)压缩到一个压缩包中,以文件(去掉扩展名)、文件夹命名;若有多个项目,则以它们所在的文件夹命名。已有压缩包绝不会被覆盖:report.nik 变为 report (2).nik。
  • 进度以环形仪表中的大百分比显示,并带阶段(分析中、压缩中、收尾中)、已处理数量和耗时。其下方的 Cancel 按钮(或 Esc 键)会立即停止长时间压缩并删除未完成的压缩包;窗口保持打开以备下一次。压缩过程中关闭窗口效果相同。
  • 完成:环变为绿色,显示压缩包名称、其与原始大小的对比和耗时。Show in folder 在 Explorer、Finder 或文件管理器中打开它;Compress more(或点击圆圈)开始下一个。
  • 保存到:底部的目标文件夹可以输入或用 Browse 选择。下次会记住(Windows:%APPDATA%\nikpak\settings.txt;macOS:~/Library/Application Support/nikpak;Linux:~/.config/nikpak)。首次为桌面。
  • 社区:右下角的三个圆形按钮打开 nikpak 的 Telegram、X 和 Discord 页面(第 1.5 节)。
  • 切换到 unNik:i 按钮左侧带向上箭头的圆形按钮会关闭 NikPak 并启动 unNik(第 3.9 节)。压缩完成后,它以及完成屏幕上的 Open in unNik 链接会立即打开新压缩包。压缩进行中该按钮被禁用。
  • 打开目标文件夹:Save to 字段和 Browse 之间的文件夹按钮会在 Explorer 或 Finder 中打开该文件夹;压缩完成后,打开时新压缩包处于选中状态。
  • 声音:按钮、链接和选项有轻柔的点击声,每个动作(开始工作、成功、错误、取消、切换程序、文件悬停在窗口上)有短促的提示音。它们由程序本身生成,没有声音文件。标题栏的扬声器按钮可关闭和开启声音;默认开启,且此选择与 unNik 共享。
  • 语言:左下角的按钮带旗帜和该语言的自称,打开世界上使用人数最多的十种语言的菜单(Ethnologue 2025):English、中文(普通话)、हिन्दी(印地语)、Español(西班牙语)、العربية(阿拉伯语)、Français(法语)、বাংলা(孟加拉语)、Português(葡萄牙语)、Русский(俄语)和 Bahasa Indonesia(印尼语)。选择一种会保存并以此语言重启 nikpak。首次运行时,nikpak 跟随 Windows(或系统)的语言,否则用英语。命令行始终为英语。
  • 以管理员身份运行:Windows 不允许把文件从 Explorer 拖到以管理员身份运行的程序上。当 nikpak.exe 以管理员身份启动时(例如从管理员 PowerShell 启动),它会通过 Explorer 以普通用户身份重启自身一次,以便拖放正常工作。如果无法做到,窗口会显示提示;请改用按钮选择文件。
  • 状态与关于:左上角显示 EVALUATION COPY · REGISTER(点击打开注册框)或 REGISTERED TO 及许可证持有人。右上角的小 i 按钮(在最小化和关闭旁边)打开 About 框:版本、© 2026 Nicolas Choukroun,由 Nikkoid LLC 发行、许可证状态、网站和许可证条款(LICENSE.txt)。

窗口尺寸为 500 × 560 点(约 9:10),因此整个窗口的截图无需裁剪即可适合 X(Twitter)图片帖。它无边框:通过顶部区域拖动;右上角的按钮打开关于框、最小化和关闭。当 nikpak.exe 从 Explorer 启动时,Windows 为它打开的控制台窗口会自动关闭;从命令提示符启动时,提示符保持不变。没有图形显示时(例如通过 SSH),nikpak 像以前一样打印用法。界面使用 egui/eframe,并内嵌 Inter、Noto Sans SC、Noto Sans Devanagari、Noto Sans Bengali 和 Noto Sans Arabic 字体的子集(SIL Open Font License,见 assets/fonts/OFL.txt)。仅命令行的可执行文件用 cargo build --release --no-default-features 构建。

The window in Chinese, Hindi and Arabic, and the language menu.
中文和阿拉伯语的窗口、印地语的提醒以及语言菜单。

印地语、孟加拉语和阿拉伯语需要整形:字母会变形和连写,元音符号围绕其辅音移动,阿拉伯语从右向左书写。egui 每个字符绘制一个字形,这样会把这些语言印错,因此 nikpak 用 Unicode 双向算法(unicode-bidi)为这些文本排序,用 rustybuzz(HarfBuzz 的 Rust 移植版)整形,用 ab_glyph 光栅化,并把结果绘制为图像(textshape.rs)。其中的拉丁词和数字保留 Inter 字体。较长的翻译会缩小以适应按钮,框会随文本增长。翻译(i18n.rs)在机器辅助下完成;欢迎母语者审阅。

3.8 许可证与注册(共享软件)

NikPak 是共享软件,© 2026 Nicolas Choukroun,由Nikkoid LLC 发行,采用与 WinRAR 相同的许可方式:同一个软件包既是免费评估版,输入许可证密钥后即为注册版。评估版中没有任何功能被禁用。

对象条款
个人个人非商业用途免费,无时间限制。个人许可证(19 美元)可移除提醒;涵盖持有人使用的所有计算机。
公司及组织免费 40 天评估后,每个席位一个公司许可证;席位为计算机或用户,取两者中较高者。每个席位的批量价格参照 WinRAR:1–9 席 19 美元,10–24 席 15 美元,25–49 席 12 美元,50–99 席 9 美元,100–199 席 7 美元,200–499 席 6 美元,500 席及以上 5 美元。
分发未修改的软件包(含 LICENSE.txt 和 THIRD-PARTY-NOTICES.txt)可放在任何共享软件或下载站点;禁止添加密钥、密钥文件或破解。.nik 压缩包免版税。



The reminder of the evaluation copy, the registration box and the file chooser.
评估版的提醒(左)、注册框(中)和文件选择器(右)。
The main window with the i button, and the About box.
右上角带小 i 按钮的主窗口(左)和关于框(右)。
  • 提醒在未注册副本打开其窗口时出现。它有两个按钮 Register 和 Test for free,其左右顺序在每次启动时随机变化。Test for free(或 Esc)关闭它;之后 nikpak 无任何限制地工作。
  • 注册框要求填写姓名、电子邮件地址和许可证密钥。三者必须匹配:密钥是姓名和电子邮件的签名。大小写、多余空格以及形似字符 O/0 和 I/L/1 无关紧要。Buy a licence 打开 https://nikpak.nikkoid.com/buy.php。
  • 注册后,nikpak 在该计算机上不再为该用户询问。许可证存储在 %APPDATA%\nikpak\license.txt(macOS:~/Library/Application Support/nikpak;Linux:~/.config/nikpak)。公司可在 nikpak.exe 旁放置文件 nikpak.key,为该 PC 的每个用户注册。
  • 命令行:nikpak register "Jane Doe" jane@example.com NPK1-… 进行注册;nikpak license 显示状态;nikpak unregister 移除许可证。nikpak -V 和 nikpak -help 也显示状态。未注册副本在错误输出(stderr)上打印一行评估通知,从不在标准输出上;-q 可抑制它。

密钥是如何工作的

许可证密钥以 NPK1- 开头,后跟 108 个字符,每五个一组(Crockford base32)。它包含许可证类型(个人或公司)、席位数量,以及 NIKPAK-LICENSE-1、类型、席位、姓名和电子邮件地址的 Ed25519 签名,每项都经过规范化(空格折叠、转为小写)。nikpak 仅包含 32 字节公钥,位于 src/license_pubkey.rs,因此研究程序无法让任何人制作密钥。

密钥用私钥制作,私钥从不离开 Nikkoid LLC:既可用私有工具 nikpak-keygen(cargo run --release --features keygen --bin nikpak-keygen -- make --name "Jane Doe" --email jane@example.com;公司用 --company --seats 25;newkeys 创建新密钥对),也可在 Web 服务器上用 server/ 中的 PHP 套件(PHP 7.2+、MySQL 5.7)制作,后者每次付款签发一个密钥,且生成完全相同的密钥。server/README.md 描述了设置和付款流程。

3.9 unNik,免费的压缩包浏览器

unNik(unNik.exe)是本软件包的第二个程序。它打开 .nik 压缩包,显示其中内容并解压你选择的部分。它对所有人免费,包括公司,无需注册也没有提醒(LICENSE.txt,第 2a 节)。安装程序将它安装在 nikpak.exe 旁,添加到开始菜单,并使其成为 .nik 文件的关联程序,因此双击压缩包即可在 unNik 中打开;unNik archive.nik 从命令提示符也能达到同样效果。

unNik waiting for an archive and its built-in viewer
unNik 等待压缩包(左)及其内置查看器,正在搜索源文件(右)。

空窗口看起来与 NikPak 完全一样,环中有一个向上的箭头。把 .nik 压缩包拖到上面,点击环,或选择一个最近的压缩包:窗口会扩展为浏览器(1120 × 720,可从边缘调整大小,也可以最大化)。加密压缩包会先要求输入密码。

The unNik archive browser
浏览器:文件夹树和压缩摘要(左)、文件(中)和所选文件的详细信息(右)。
  • 用文件夹树、面包屑、Back、Forward 和 Up(Alt+Left、Alt+Right、Backspace)以及双击来导航。Search(Ctrl+F)在整个压缩包中按名称查找文件;每个结果显示其文件夹。
  • 用单击、Ctrl+单击、Shift+单击、复选框、Ctrl+A 或方向键来选择。单击列标题按名称、大小、类型或日期排序。右键单击打开包含所有操作的菜单。
  • 解压全部,或解压所选内容(Ctrl+E):选择目标(默认是与压缩包同名、位于其旁边的文件夹),是否保留文件夹,以及文件已存在时如何处理:替换、两者都保留(name (1).ext)或跳过。从文件夹内取出的文件不带该文件夹的路径创建,与 WinRAR 相同。读取会在最后一个所选文件之后立即停止。
  • 查看(F3,或双击文本文件):带行号的文本、高亮搜索、自动换行和复制;二进制文件以十六进制显示。显示前 16 MB;在记事本中打开打开整个文件。Open 用文件自己的程序打开它(先解压到临时文件夹)。
  • 删除(Del)在确认后从压缩包中删除文件和文件夹。压缩包是固实的,因此会在没有它们的情况下重新写入一个临时文件,只有在全部成功后才替换原文件。加密压缩包保留其密码;其他条目保留其媒体或 Deflate 过滤器及其 -m5 引擎。
  • 测试解压整个压缩包并检查每个文件。详细信息窗格显示大小、日期、文件夹、CRC32、文件的存储方式及其属性;对于压缩包本身则显示其大小、方法和加密。树下方的摘要显示节省的空间。
  • 与 NikPak 相同的十种语言、语言按钮、关于框(i)和社区按钮。任何文字系统的文件名都用系统字体作为后备绘制。
  • 切换到 NikPak:i 按钮左侧带向下箭头的圆形按钮会关闭 unNik 并启动 NikPak。两个程序位于同一文件夹。它们共享一个设置文件,因此在一个程序中选择的语言会被另一个使用,已打开的窗口会在两秒内跟随更改。
  • 打开目标文件夹:弹出按钮左侧的按钮在 Explorer 或 Finder 中打开最后一次解压的文件夹(任何解压之前为压缩包所在文件夹)。unNik 播放与 NikPak 相同的声音,并有打开压缩包、解压、删除和测试的声音;其扬声器按钮与 NikPak 的共享一个设置。
Extracting a selection and deleting a file in unNik
解压所选内容(左)和删除文件(右)。

3.10 皮肤

NikPak 和 unNik 共享同样的 55 种窗口皮肤:Nikkomander 的 41 种皮肤——从 Nikkoid Blue(默认)和 Nikkoid Bright,到 Dracula、Nord、Solarized、Classic Blue、Matrix、Pizza、Christmas 和 8-Bit Game——外加 14 种手绘皮肤,其背景由程序自己绘制:Moon、America、Pattaya Neons、Burgers Tokyo、Sushi、Amiga、MSX、Colecovision、ZX Spectrum、Cat Life、Dog Life、Mario Bros、Counter Strike 和 Chess。

  • 皮肤按钮(一个调色板,位于两个程序的标题栏):打开皮肤菜单,其中每种皮肤以自身颜色绘制。指向一种皮肤会在整个窗口上预览;单击保留;Esc 或点击外部返回原来的皮肤。
  • 旁边的 太阳/月亮按钮 一键在 Nikkoid Blue(深色)和 Nikkoid Bright 之间切换。
  • 皮肤面板位于程序之外。面板打开时,窗口向右扩展面板的宽度:程序保持其 500 × 560 面板,面板位于其旁边的额外透明空间中,因此整个界面在试用的皮肤后面保持可见。面板可用标题栏拖回程序上方或窗口中的任何位置,并带有 Apply Skin 和 Cancel 按钮。点击皮肤会在整个窗口上试用;Apply 保留并为两个程序保存;Cancel(或 Esc 或 ×)返回原来的皮肤。点击程序不再关闭面板。unNik 的窗口不会扩展。
  • 所选皮肤会被保存(设置文件中的 skin=)并共享:在一个程序中选择的皮肤会在两秒内被另一个程序采用。
  • 你自己的皮肤:菜单底部的“Import a skin…”读取 Nikkomander .skin.json 文件(一个名称和一列颜色)并复制到设置文件旁的 skins 文件夹;导入皮肤磁贴上的 × 将其移除。
  • 手绘皮肤退到界面之后。手绘背景由 src/skin_scenes.rs 绘制,而非 CSS 渐变,因此可缩放到任意窗口大小。自 1.6.2 起,每种皮肤都经过一次 discreet() 处理(在 src/skin.rs 中):画面被拉向其自身局部均值,轻度模糊并向皮肤的背景色淡化——大部分在放置环和文本列之后,在标志处略多——因此标题、环和状态文本在所有十四种皮肤上都保持清晰。

3.11 文件管理器菜单

文件管理器右键菜单中的“NikPak”子菜单(类似 WinRAR)由安装程序安装(Windows:默认勾选的选项;Linux:nikpak --install-menu;macOS:Finder 快速操作)。

  • 针对文件和文件夹:添加到压缩包…(询问名称和文件夹,然后在 NikPak 窗口中压缩)和在这里添加到压缩包(压缩包放在项目旁,命名方式与拖放到窗口上相同)。
  • 针对 .nik 压缩包:用 unNik 打开、解压文件…、解压到这里和解压到其专属文件夹。
  • 多个项目合并为一个压缩包;多个选中的压缩包在一个窗口中依次解压。
  • 命令通过 unNik.exe 运行,因此不会有控制台窗口闪烁;标签跟随 NikPak 的语言。Windows 10 直接显示子菜单,Windows 11 在 Show more options(Shift+F10)下显示;Linux 添加 KDE Dolphin 服务菜单、Nemo 动作和 Nautilus 脚本,macOS 添加 Finder 快速操作。
  • 菜单使用的新命令行选项:nikpak --add 和 --add-here(压缩给定项目),nikpak --install-menu、--uninstall-menu 和 --all-users;unNik --extract、--extract-here 和 --extract-to。解压到这里 / 到其专属文件夹显示 unNik 的小窗口和进度环(加密压缩包为密码框),全部解压完成后关闭;警告和错误保留在屏幕上。
  • 第二个安装程序 unnik--setup.exe 仅安装 unNik(免费浏览器),面向只接收 .nik 压缩包的人;当 NikPak 已安装时它只安装该程序,因此菜单不会重复。

4 架构

nikpak 是单个 Rust 程序,约 9,600 行(含单元测试),分布在十六个源文件中,外加私有许可证工具 nikpak-keygen。其核心设计规则是流式:文件以 256 KiB 块读取,推过一串写入器,从不整体保存在内存中(唯一的例外是 BMP 像素数据,限制为 256 MiB,以及编解码器的 64 KiB 工作缓冲区)。

4.1 创建压缩包时的数据流

Figure 1: the creation pipeline. The two lower boxes are inputs to the stage above them.
图 1:创建流水线。下方两个框是它们上方阶段的输入。

每个阶段都是标准 Write trait 的一个实现,包裹在下一个周围,因此同一代码路径服务于普通压缩包、加密压缩包和媒体试算。

4.2 源代码图

文件职责
main.rs入口点。解析参数、分发命令、把错误转换为退出码。安静地处理关闭的管道(nikpak lb x | head)。
cli.rs手写的 RAR/zip 风格参数解析器、选项表和完整的 -help 文本。Clap 风格库无法干净地表达 -pSECRET 或 -m5 这类开关。
archive.rs创建、列出、测试、解压(按过滤器或按精确选择)、删除、打印和媒体测试;还有 unNik 使用的索引和单文件读取。包含条目流写入器和读取器、覆盖策略和原子临时文件逻辑。
media.rsWAV、AIFF、MOD 和 BMP 的内容检测;Shuffle、Delta 和 MED 过滤器;试算选择;CRC 和分块框架辅助。
nac.rsNAC 无损音频编解码器:立体声去相关、预测器、残差编码。
bits.rsNAC 使用的 MSB 优先位写入器和读取器。
crypto.rsArgon2id 密钥派生和 XChaCha20-Poly1305 分块写入器与读取器。
format.rs磁盘格式的常量:魔数、版本、记录类型、KDF 限制。
util.rs密码提示和生成、路径清理、通配符匹配、日期和大小格式化。
error.rs错误类型、其退出码,以及 I/O 错误的分类(损坏与系统故障)。
engine.rs-m5 的多引擎主体:块写入器和读取器、LZMA2 和 PPMd 引擎、引擎试算(第 5.6、6.7 节)。
recomp.rs用 preflate 对 ZIP 容器、PDF 和 gzip 进行 Deflate 再压缩(第 7.9 节)。
gui.rs图形界面:拖放窗口、进度环、目标设置、社区链接(第 3.7 节)。
unnik.rsunNik,压缩包浏览器:树、列表、搜索、查看器、解压和删除框(第 3.9 节)。
lib.rs, bin/unnik.rsnikpak 和 unNik 共享的库,以及 unNik.exe 的入口点(窗口程序,无控制台)。
license.rs共享软件许可证:密钥格式、Ed25519 验证、许可证存储、注册(第 3.8 节)。
i18n.rs将窗口翻译为使用人数最多的十种语言;语言设置(第 3.7 节)。
textshape.rs印地语、孟加拉语和阿拉伯语的整形文本:双向排序、整形、光栅化(第 3.7 节)。



4.3 逐步创建压缩包

  • 验证输出位置。已有压缩包仅在使用 -o+(或交互式确认)时被替换;否则 nikpak 会说明不支持追加。
  • 收集输入:展开通配符(PowerShell 不会这样做)、读取 @listfiles、应用排除、跳过符号链接和特殊文件并给出警告,且压缩包绝不会添加到自身。
  • 若请求了加密,则解析密码(第 3.3 节)并用 Argon2id 派生密钥。
  • 写入文件头,然后构建写入器链:文件、缓冲写入器、可选的分块加密器、Zstandard 编码器。
  • 写入每个条目。媒体文件经过检测和试算(第 7 章);其他一切以 256 KiB 块复制,同时计算 CRC32。
  • 写入结束标记(含条目计数),完成 Zstandard 帧,密封最后一个加密分块并刷写到磁盘。
  • 重命名 name.nik.tmp 为 name.nik。若此前任何步骤失败,守护对象会删除临时文件,因此半写入的压缩包绝不会替换完好的压缩包。

4.4 逐步解压压缩包

  • 读取并验证文件头:魔数、版本、标志、方法,以及(加密压缩包)Argon2id 参数,后者在任何内存分配之前先做范围检查。
  • 获取密码并派生密钥。第一个认证分块随后证明或否定该密码。
  • 遍历条目流。每个条目名称在任何文件系统访问之前先经过清理(第 10.1 节)。
  • 用 create_new 写入文件,设置时间戳和权限,并验证重建字节的 CRC32。CRC 失败的文件会被删除而不是保留。
  • 验证压缩包结尾:结束标记之后流必须真正结束,且(若加密)最后一个认证分块必须已被处理。这可捕获截断和追加的数据。

4.5 内存与线程

Zstandard 用工作线程压缩(默认全部内核,用 -mt 限制)。解压、音频编解码器和加密在调用线程上运行。Argon2id 密钥派生需要 64 MiB,最高两个级别使用 128 MiB 匹配窗口,因此解压此类压缩包最多需要这么多内存。

5 .nik 容器格式

.nik 文件是一个小的固定文件头,后跟主体。主体是一条固实流:所有条目串接在一起,一起压缩,并可选择加密。格式第 2 版增加媒体记录;第 1 版压缩包仍可读取。

5.1 文件头

偏移大小字段备注
0 8魔数89 4E 49 4B 0D 0A 1A 0A(PNG 的思路:检测文本模式传输损坏)
8 1版本3(1 和 2 仍可读取)
9 1标志第 0 位 = 已加密;其他位必须为 0
10 1方法0 = 存储,1 = Zstandard,2 = 多引擎块(5.6)
11 1保留 0
12 4Argon2id 内存(KiB)仅加密压缩包,小端
16 4Argon2id 遍数仅加密
20 4Argon2id 通道数仅加密
24 16盐每个压缩包随机,仅加密
40 19流随机数前缀每个压缩包随机,仅加密



完整的文件头(12 字节,加密时 59 字节)随后作为每个加密分块的关联数据进行认证,因此它无法被改动而不被发现。

5.2 条目记录

在(解密、解压后的)流内部,每个条目是一条记录。所有整数均为小端。

类型布局
0 ENDcount:u64。标记结束并说明其前面有多少个条目。
1 FILEname_len:u16 name mtime:i64 mode:u32 size:u64 data[size] crc32:u32
2 DIRname_len:u16 name mtime:i64 mode:u32
3 MEDIAname_len:u16 name mtime:i64 mode:u32 size:u64 filter:u8 method:u8 params_len:u8 params framed_stream crc32:u32



  • 名称是 UTF-8,使用 / 分隔符,最多 4096 字节。
  • mtime 是 Unix 秒;mode 是 Unix 权限字(Windows 文件得到 0644 或 0755,只读时为 0444)。
  • CRC32 始终覆盖原始字节,这正是媒体过滤器可验证的原因。
  • 在 MEDIA 记录中,size 是原始大小。framed_stream 是一系列以零长度结尾的 [u32 length][bytes] 分块,因此读取器总能跳过媒体条目而不必理解其过滤器。

媒体参数

过滤器参数内容
1 音频19 字节offset:u64 frames:u64 channels:u8 bits:u8 flags:u8(标志位 0 = 大端,位 1 = 有符号)
2 图像21 字节offset:u64 width:u32 rows:u32 bpp:u8 stride:u32
3 Deflate0 字节none: the framed stream holds raw segments and recompressed Deflate streams (section 7.9)



offset 之前的一切(文件头、标签、调色板、MOD 模式)以及采样或像素区域之后的一切(尾部块)都在过滤数据周围原样存储。

5.3 加密主体分帧

设置加密标志时,zstd 输出被切成 64 KiB 明文的块。每个块存储为 32 位大端长度后跟密文,因 Poly1305 标签而长 16 字节。块只有在后面至少还有一个字节时才密封,因此最后一个块永远不会为空,并可标记为最终块(第 8.3 节)。

5.4 为什么是固实流?

把所有文件压缩为一条流能让压缩器发现文件之间的重复,这对源代码树和文档集合极为重要(第 11 章的基准测试显示,在 10,949 个文件上 RAR 的非固实输出几乎是其固实输出的两倍大)。代价是顺序访问:解压单个文件需要解压它前面的内容。nikpak 通过跳过媒体条目而不解码来降低这一成本。

5.5 版本化

版本字节会对照程序理解的最新版本检查。较新的压缩包会产生消息 Unsupported archive version N; please update nikpak,而不是误导性的损坏错误。未知的标志、方法或媒体过滤器也以同样方式拒绝。

5.6 多引擎主体(版本 3)

用 -m5 制作的压缩包使用方法 2。5.2 中描述的内部记录流不变,但它只在条目之间被切成块,每个块用自己的引擎压缩。串接起来后,解压出的块恰好是单引擎压缩包的记录流,因此列出、测试和解压以相同方式工作。

元素布局
主体block* 后跟单个 0 字节
块engine:u8 参数,然后是帧 [u32 长度 1 .. 1 MiB][压缩字节],以零长度结尾
引擎 1Zstandard(无参数)
引擎 2LZMA2 原始流:字典大小 u32、lc u8、lp u8、pb u8
引擎 3PPMd 变体 I(PPMd8):阶数 u8、模型大小 u32、恢复方法 u8(0 = 重启)



该分帧让读取器无需信任解码器即可找到每个块的结尾,且每个参数都在分配内存前做范围检查(LZMA2 字典最多 1.5 GiB,PPMd 模型最多 1 GiB,阶数 2 到 16)。加密时,主体由与其他任何压缩包相同的认证分块保护(第 8 章)。

6 压缩引擎

6.1 为什么选择 Zstandard

Zstandard 将 LZ77 匹配与由 Huffman 编码和有限状态熵(FSE)构建的熵编码阶段相结合。与 ZIP 的 Deflate 算法相比,它能在远更大的窗口上找到匹配,用更紧凑的熵码对字面量和匹配长度建模,并以每秒数 GB 的速度解码。它还是一个开放、被广泛审计的格式,使 nikpak 的数据能被独立工具恢复。

6.2 级别映射

nikpak 接受 RAR 风格级别(-m0 .. -m5)和 zip 风格级别(-0 .. -9),并将它们映射到 Zstandard 级别:

选项Zstandard 级别特点
-m0无(存储)无压缩、无媒体过滤器
-m1 1最快
-m2 3快速;单核上 197 MB 约 0.9 秒(1.2.0 之前的默认值)
-m3 9良好平衡
-m4 15高;128 MiB 窗口和长距离匹配;Deflate 再压缩(7.9)
-m5 (default)LZMA2 / PPMd最高,且自 1.2.1 起为默认:多引擎主体(6.7)和 Deflate 再压缩



zip 风格级别映射如下:-0 存储,-1 到 -6 使用 Zstandard 1 到 6,-7 使用 9,-8 使用 12,-9 与 -m5 相同。

6.3 大窗口与长距离匹配

在 Zstandard 级别 15 及以上,nikpak 启用 2^27 字节(128 MiB)匹配窗口和长距离匹配。源代码树、文档集和备份包含许多在流中相距很远的近似重复文件;普通 8 MiB 窗口无法看到它们之间。在 197 MB 测试集上,这使 -m5 的输出从 26.3 MB 降至 24.4 MB,-m4 从 29.5 MB 降至 26.8 MB,-m4 的速度无损失,-m5 略有损失。2^27 的窗口在 Zstandard 默认解码器限制之内,因此任何标准解码器都能读取这些数据。

6.4 多线程

编码器把输入分成在(默认全部内核的)工作线程上运行的作业。因此压缩速度随内核数量扩展,而格式仍为单条流。本指南中的所有数字都在单核上测量;在典型多核机器上,nikpak 比所示快得多。

6.5 流内的完整性

Zstandard 帧携带自身的内容校验和,nikpak 为每个文件增加 CRC32 和带条目计数的结束标记。因此,损坏的未加密压缩包在三个层面被检测:帧校验和、每文件 CRC 和结束标记。

6.6 存储模式

-m0 不加压缩地写入条目。它对已经压缩过的数据有用,也是唯一有意绕过媒体过滤器的模式:要求存储的用户并不希望数据被变换。

6.7 最高级别:LZMA2 和 PPMd

Zstandard 是速度的正确引擎,但在其最强设置下,源代码和文档仍比 LZMA2 落后几个百分点,在散文上远落后于 PPMd。因此自 1.2 版起,-m5 用两个块构建压缩包(格式见 5.6):

  • 二进制块。文件夹、二进制文件、媒体条目和经 Deflate 再压缩的条目用 LZMA2 压缩:预设 9、nice 长度 273、匹配查找器深度 32,字典大小随数据调整(1 到 128 MiB)。
  • 文本块。前 64 KiB 不含零字节且几乎不含控制字符的文件(源代码、文本、CSV、HTML、XML……)进入第二个块。这些文件连续四个 1 MiB 段分别用 PPMd 变体 I(阶数 8,192 MiB 模型)和为文本调优的 LZMA2(lc 4、pb 0)试压缩,取较小者。散文偏向 PPMd;含有大量近似相同文件的大源代码树偏向 LZMA2,因为它能跨文件复制长重复。
  • 顺序。在每个块内,文件按扩展名排序,再按文件名排序,如同 7-Zip,使相同或相似的文件相邻。连同连续试算样本,这使通用数据集缩小 7.5 %。

"Extreme" LZMA2 设置经过测量后被否决:在电子表格 XML 上它们慢达十倍且并不更小。-m5 在单线程上运行,无论 -mt 如何。

7 媒体智能

通用压缩器看到的是字节。它不知道 WAV 文件是波形,其下一个采样可由前几个预测,也不知道照片的相邻像素几乎相等。nikpak 的媒体层提供这一知识:它识别此类文件,应用揭示冗余的无损变换,并让压缩器(或专用编解码器)利用它。这正是 nikpak 相对 RAR 获得最大优势之处。

7.1 按内容检测

检测查看每个至少 8 KiB 文件的开头字节,从不信任扩展名,因此改名为 .dat 的 WAV 仍会被发现,而叫 .wav 的文本文件不会被发现。

类型如何识别过滤什么
WAVRIFF..WAVE、带整数 PCM 的 fmt 块(格式 1,或带 PCM 子格式的 WAVE_FORMAT_EXTENSIBLE)、8/16/24/32 位、1 到 8 声道、一致的块对齐;然后是 data 块采样区域。零、过大或虚假的数据长度会被钳制到文件范围;剩余字节和尾部块保持原样。
AIFF / AIFCFORM..AIFF 或 AIFC(压缩 NONE 或 twos)、COMM 和 SSND 块、大端有符号采样声音数据区域。
ProTracker MOD偏移 1080 处的签名:M.K.、M!K!、FLT4、4CHN、6CHN、8CHN、FLT8、OCTA 或 CD81模式数据之后的 8 位采样区域(第 7.6 节)。
BMPBM、40 字节或更多的 DIB 头、无压缩、每像素 8、24 或 32 位、像素行适合文件、区域在 4 KiB 和 256 MiB 之间像素行(第 7.7 节)。



不处理、因而以普通压缩存储的:浮点 WAV、ADPCM 和其他压缩音频、RLE 或位字段 BMP,以及 ProTracker MOD 之外的跟踪器格式。已压缩的媒体如 MP3、JPEG、FLAC、PNG 和 MP4 完全不被识别,保持原样,因为变换它们无济于事。

7.2 试算:为每个文件选择最佳过滤器

没有单一变换对每个文件都最佳。预测编码在录制的音乐上胜出;字节平面分离在来自 8 位压扩源的音频上胜出(其中只出现约一千个不同的 16 位值);普通压缩在静音和高度重复的跟踪器采样上胜出。因此 nikpak 是测量而非猜测。

  • 取样:采样区域最多 3 MiB 时取整个区域,否则在音频的 5 %、45 % 和 85 % 处取三个 768 KiB 窗口。图像取前约 4 MiB 的行。
  • 用普通 Zstandard 级别 3(基线)和每个适用的过滤器压缩样本:16/24/32 位音频用 NAC、Shuffle 和 Shuffle+Delta;8 位音频用 NAC 和 Shuffle+Delta;图像用 MED。
  • 保留估计值最小的过滤器,但仅当其至少比基线小 2 %。否则文件以普通压缩存储。
  • 用胜出者写入文件。所有这些都按文件进行,因此一个压缩包可以包含以不同方法编码的 WAV 与普通文件相邻。

nikpak mt 在整个文件上运行同样的测量并打印表格,第 11 章基准测试正是这样探索的。在选项中,-mc- 禁用过滤器,-mc+ 强制默认过滤器而不试算。

无损、已验证、可绕过
每个过滤器都精确可逆。每个条目存储的 CRC32 对原始字节计算并在解压时检查,NAC 编码器在写入时验证每个块(第 7.4 节),-mc- 关闭整个层面。



7.3 NAC 音频编解码器

NAC(nikpak 音频编解码器)是用于整数 PCM 的基于块的无损编解码器。它与 FLAC 同族:线性预测后跟 Rice 编码。它有自己专为本压缩包设计的位流,用于 WAV、AIFF 和 MOD 文件的采样数据。

Figure 2: encoding one block of 4096 sample frames.
图 2:编码一个 4096 采样帧的块。

块与立体声去相关

音频被切成 4096 采样帧的块,每块独立编码,这使编解码器稳健(错误不会扩散),并让编码器适应变化的素材。对于立体声,编码器在每个块从四种表示中选择一种:

模式编码的声道重建
0左、右直接
1左、边 R = L - S
2边、右 L = R + S
3中、边sum = (M << 1) | (S & 1), L = (sum + S) >> 1, R = (sum - S) >> 1



其中 Side = L - R、Mid = (L + R) >> 1。边声道比普通声道需要多一位,以 bits + 1 编码。模式选择很廉价:编码器对 L、R、M 和 S 的 2 阶固定预测器残差绝对值求和,取总和最小的一对。对于声道强相关的录音,中/边通常能节省几个百分点。

子帧类型

块中每个编码声道存储为四种子帧之一,取最小者:

类型用途成本
常量所有采样相等(数字静音)整个块 2 + 采样位宽位
原样类噪声素材,或安全后备n × 位宽 位
固定0 到 4 阶多项式预测器预热采样 + 残差
LPC1 到 32 阶线性预测器,带传输的系数系数 + 预热 + 残差



浪费的位。若块中每个采样的低位始终为零(16 位容器中存储的 8 位录音,或量化信号),则移除它们并用 5 位存储其计数。

固定预测器

order 0: e[i] = x[i]
order 1: e[i] = x[i] - x[i-1]
order 2: e[i] = x[i] - 2*x[i-1] + x[i-2]
order 3: e[i] = x[i] - 3*x[i-1] + 3*x[i-2] - x[i-3]
order 4: e[i] = x[i] - 4*x[i-1] + 6*x[i-2] - 4*x[i-3] + x[i-4]



块的前 order 个采样按原样存储为预热值。

线性预测编码(LPC)

对于有调性和和声的音乐,系数拟合到块的预测器远优于固定多项式。编码器按如下步骤计算它们:

  • 对块应用 Tukey 窗(在首尾四分之一处渐隐),使边缘不扭曲统计量。
  • 计算加窗信号的自相关 r[0..32]。
  • 运行 Levinson-Durbin 递归,它一次遍历即给出 1 到 32 每个阶数的最优系数集:k = (r[i] - sum a[j] r[i-j]) / err、a[i] = k、a[j] = a[j] - k a[i-j]、err = err (1 - k^2)。
  • 把实系数量化为整数,精度 12、14 或 15 位(取决于采样位宽),选择 2 的幂标度(shift,0 到 20),使最大系数刚好放得下。舍入使用误差反馈,因此量化误差不会跨系数累积。
  • 评估若干阶数(集合取决于努力级别,见下),计算每个的残差,保留估计总大小最小者(包括发送其系数的成本)。

预测器本身是纯整数运算,这正是解码器运行的:

pred[i] = (q[0]*x[i-1] + q[1]*x[i-2] + ... + q[n-1]*x[i-n]) >> shift
e[i] = x[i] - pred[i] (n = order; the sum uses 64 bits)



浮点仅由编码器用于决定发送哪些整数系数。解码器从不接触浮点,因此解码在每个处理器和每个编译器上都逐位精确。

残差编码

预测误差用 zig-zag 变换(u = (e << 1) XOR (e >> 63),使小的正负值变成小的数)映射为无符号数,并用分区 Rice 编码编码。块的残差分成 2^p 个分区(p 从 0 到 8,由编码器选择);每个分区有自己的 Rice 参数 k(5 位)。一个值写为 u >> k 的一元表示(那么多零后跟一个一)加低 k 位。分区的最佳 k 由其值之和推导,因此每个候选预测器的成本可从运行和廉价估计,而无需实际编码。

努力级别

压缩包的级别(-m 选项)选择每个块尝试多少 LPC 阶数。阶数更多意味着结果略小、编码器更慢;解码器速度相同。

压缩包级别NAC 努力尝试的 LPC 阶数
-m1 0 8, 16
-m2, -m3 1 4, 8, 12, 16, 24, 32
-m4, -m5 2 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32



0 到 4 阶固定预测器始终尝试。在基准测试中,努力级别改变立体声音乐结果的幅度小于 0.05 %:巨大收益来自使用预测本身。

块的位布局

block := [stereo mode: 2 bits] sub-frame [sub-frame] (byte-padded)
sub-frame := type:2 ...
CONSTANT : value:width
VERBATIM : wasted:5 samples:(n x (width - wasted))
FIXED : wasted:5 order:3 warm-up residual
LPC : wasted:5 order-1:5 precision-1:4 shift:5
coefficients warm-up residual
residual := partition-order:4 per partition: k:5 Rice-coded values



每个块在压缩包中存储时前面有 32 位长度,因此解码器始终知道下一个块从哪里开始。

7.4 自验证

写入压缩包是检测编码器错误的唯一机会,因此 NAC 编码器不信任自己。编码每个块后,它会再次解码该块并与原始采样比较。任何差异时,它把该块重新编码为原样(不会失败)并再次检查;如果连这也失败,压缩停止,给出明确错误并建议使用 -mc-。后备路径由单元测试覆盖,它们对位深和声道数的每种组合强制原样编码;该机制把可能的静默数据丢失错误变成可检测、可恢复的状况。

此外,解码器经过模糊测试:单元测试向其输入数千个随机字节块和翻转位的块,它必须返回错误而不 panic 或分配不合理的内存。开发期间这发现了一个真实错误(对 32 位素材,Rice 参数搜索可能结果为空,并回退到一个会产生数 GB 码的参数);已修复并有测试覆盖。

7.5 Shuffle 和 Shuffle+Delta

有些音频对预测没有反应。基准测试使用的画廊 WAV 文件是 16 位,却只包含约一千个不同的采样值,因为它们由 8 位压扩源产生。预测器无法利用这一点,FLAC 把它们压缩到原始的 84 %,而普通 Zstandard 需要 63 %。有帮助的是排列字节,使压缩器看到同质数据:

  • Shuffle 把采样的字节分成平面:先是声道 0 的所有最低有效字节,再是声道 0 的所有最高有效字节,然后声道 1,依此类推,以 32768 帧为块。高字节平面平滑,低字节平面字母表非常受限;Zstandard 的熵编码器对两者的建模远好于交织混合。
  • Shuffle+Delta 先把每个采样替换为其与同声道前一个采样的差(按采样位宽取模,因此精确可逆,状态跨块携带),然后洗牌。当信号平滑但不足以让 NAC 胜出时,这有帮助。

在 35 个画廊文件上,按文件选择最佳过滤器(由试算选出)的压缩包在 -m2 为原始大小的 51 %,-m5 为 47 %,而普通 Zstandard 为 63 %。

7.6 ProTracker MOD 文件

MOD 模块是 1084 字节的头(标题、31 个采样描述符、128 项的模式顺序表和偏移 1080 处的 4 字节签名),然后是模式数据,再是所有乐器的原始 8 位有符号采样。nikpak 计算采样从哪里开始以及多长:

patterns = max(order table) + 1
sample_start = 1084 + patterns * 256 * channels (4 ch: 1024 B per pattern)
sample_bytes = sum of (length in words * 2) over the 31 descriptors,
clamped to the end of the file



头和模式原样存储(压缩器能很好处理那些重复音符事件),采样区域作为单声道、有符号 8 位流用于试算。按文件,NAC、Shuffle+Delta 或普通压缩胜出:采样乐器大多偏向 NAC,而某些模块(例如采样非常短或高度重复的)普通压缩胜出。截断的模块通过把区域钳制到实际存在的数据来处理。

7.7 BMP 图像

未压缩的 BMP 存储填充到四字节倍数的像素行。过滤器分四步工作:

  • 拆分像素区域为每个声道一个平面(B、G、R,32 位还有 A),单独记住每行的填充字节。
  • 去相关颜色。从蓝和红平面减去绿平面(模 256)。照片中三个声道强相关,因此差值很小。
  • 用 MED(中值边缘检测)预测器——LOCO-I 图像编码器所用的那个——预测每个像素:对于左 a、上 b 和左上 c,预测为 min(a,b)(若 c >= max(a,b))、max(a,b)(若 c <= min(a,b)),否则为 a + b - c。预测误差替换像素(模 256)。在边缘上预测器跟随边缘;在平滑梯度上它外推梯度。
  • 串接残差平面和填充字节,让 Zstandard 压缩它们。平滑区域变成近零字节的游程。

解码器精确反向执行这些步骤。8 位 BMP 跳过颜色步骤;试算随后决定预测对该调色板是否有帮助,因为预测调色板索引只对灰度或平滑有序的调色板有意义。256 MiB 以上的 BMP 文件不过滤,以限制内存使用。

7.8 为什么这能胜过 RAR

RAR 用其通用匹配加 Huffman 引擎外加少量字节过滤器压缩媒体。nikpak 则给音频真正的预测编解码器,带逐块立体声去相关和自适应 Rice 编码;给图像真正的二维预测器,带颜色去相关;并按文件测量选择。后果在基准测试中可见:各种音频小 12 到 15 %,跟踪器音乐小 13 %,照片小 39 %,立体声轨道的 NAC 结果与专用 FLAC 编码器相差 0.2 % 以内。

7.9 Office 文档、PDF 和 gzip:Deflate 再压缩

现代 Office 文件是 XML 的 ZIP 压缩包(docx、xlsx、pptx、odt、ods),PDF 文件把页面存储在 Deflate(FlateDecode)流中。因此其内容已经压缩,没有压缩工具能大幅缩小:在 1.1 基准测试中,6.6 MB 的 .xlsx 文件用 7-Zip 只降到 5.6 MB。nikpak 1.2 移除了这一障碍。

  • 按内容检测。文件开头的 ZIP 本地头(PK\x03\x04)、%PDF- 或 gzip 头;从不使用扩展名。
  • 解码。找到每个 Deflate 流(方法 8 的 ZIP 条目、带 zlib 头的 PDF 流、每个 gzip 成员)并用 preflate(微软的 preflate-rs)分析。preflate 返回普通数据外加一个小的修正记录,精确描述原编码器(无论哪个)是如何做出选择的。
  • 存储。文件存储为原始段和(普通数据、修正)对的序列。普通 XML 或页面描述随后进入固实流,LZMA2 把它压缩到其 Deflate 大小的一小部分。
  • 验证。在使用结果之前,nikpak 重建每个 Deflate 流,并把整个文件的长度和 CRC32 与原始比较。若有任何差异,或 preflate 在畸形文件上失败或 panic,文件就简单地按普通方式存储。
  • 解压。Deflate 流逐位重建,因此解压出的 .docx 或 .pdf 逐字节相同,其 CRC32 与每个条目一样被检查。

在第 11.7 节的 Office 集上,这正是 nikpak 比所有其他压缩工具都小的原因。6.6 MB 的 .xlsx 文件解码后含 71 MB XML,而该 XML 的压缩远好于 Deflate。代价是时间:preflate 每秒约分析 15 MB 普通数据。因此再压缩仅在 -m4 和 -m5 使用,那里物有所值(实测:-m3 花六倍时间只多得 0.5 %)。限制:文件最大 512 MiB,每条流最多 512 MiB 普通数据和 64 MiB 修正。-mc- 将其关闭。

8 加密

nikpak 的加密完全由标准的、经过充分研究的组件构建(Argon2id 和 XChaCha20-Poly1305 的 RustCrypto 实现)。没有为本项目发明任何原语;工作在于正确组合它们。

8.1 威胁模型

该设计假设攻击者取得压缩包文件,可从容研究和修改它,并能离线无限次尝试密码猜测。nikpak 旨在保证:

  • 文件内容以及它们的一切相关信息的机密性:名称、大小、时间戳、权限和文件数量。
  • 完整性与真实性:对压缩包的任何更改,无论是对数据、文件头、数据顺序还是其长度,都在错误数据被接受之前被检测。
  • 抗猜测:每次密码猜测都尽可能昂贵。

它不隐藏压缩包存在的事实,也不隐藏其大致大小;它无法防御创建或打开压缩包的机器上的恶意软件,也无法防御弱密码。

8.2 从密码到密钥:Argon2id

密码是糟糕的密钥:短、可预测、由人输入。它用 Argon2id 变成 256 位密钥——密码哈希竞赛的获胜者,也是 RFC 9106 标准化的算法。Argon2id 故意内存困难:计算一个密钥需要一大块内存,先填充,再以数据相关模式反复读取。nikpak 使用该 RFC 的低内存配置:

参数值含义
内存64 MiB每次猜测需要 64 MiB 快速内存
遍数 3内存处理三次
通道数 4一次派生内部的并行度
盐16 个随机字节每个压缩包都新生成:相同密码产生不同密钥,预计算表无用
输出32 字节XChaCha20-Poly1305 密钥



内存困难正是 Argon2id 与较旧的 PBKDF2(RAR5 和 ZIP 的 AES 模式使用)的区别:PBKDF2 几乎不需要内存,因此显卡或定制芯片可以并行测试海量猜测。用 Argon2id,攻击者的硬件必须为每次并行猜测提供 64 MiB,这消除了大部分优势。合法用户的成本是几分之一秒,每个压缩包付一次。

参数存储在文件头中,因此未来版本可以提高它们而不破坏旧压缩包。读取时,先做范围检查(内存 8 × 通道数 最多 1 GiB,遍数 1 到 64,通道数 1 到 64),这样恶意压缩包无法在密码被检查之前让 nikpak 分配荒谬数量的内存。

8.3 STREAM 构造中的 XChaCha20-Poly1305

主体数据用 XChaCha20-Poly1305 保护,这是一种带关联数据的认证加密(AEAD)密码:ChaCha20 提供 256 位密钥流密码,X 变体把其随机数扩展到 192 位(大到可以随机选择而永不重复),Poly1305 为每条密封消息添加 128 位认证标签。ChaCha20 无需特殊指令在任何 CPU 上都很快,且与基于表的 AES 不同,天然没有缓存时序泄露。

压缩包远大于一条消息,因此 nikpak 使用 STREAM 构造(现代文件加密工具所用的同一种):数据切成 64 KiB 块,第 i 个块在随机数下密封

nonce (24 bytes) = random prefix (19 bytes)
| chunk counter (4 bytes, big endian)
| last-chunk flag (1 byte)



这同时给出三项保证。每个块都有唯一随机数,这对流密码至关重要。因为计数器是随机数的一部分,重排或复制块会导致认证失败。因为最终块携带不同标志,把压缩包截短,即使恰好截在块边界,也会导致认证失败:新的最后一个块是作为非最终块密封的。19 字节前缀每个压缩包随机。

此外,完整的压缩包头(版本、标志、方法、Argon2id 参数、盐和随机数前缀)作为关联数据传给每个块。它被认证但不加密;翻转其中任何一位都会使每个块失败。这也阻止了试图削弱存储参数的降级攻击。

项目值
密码XChaCha20-Poly1305:256 位密钥、192 位随机数、128 位标签
块最多 64 KiB 明文;存储为 u32 length + 密文 + 16 字节标签
大小开销约 0.03 %(每 64 KiB 20 字节),在 222 MB 数据集上实测 0.03 %
速度开销实测:222 MB 数据集上总计 +0.3 秒(0.92 秒而非 0.64 秒),含密钥派生
密码错误在第一个块即被检测;不产生任何明文



8.4 观察者能看到和不能看到什么

无密码时可见受保护
魔数、版本、标志和压缩方法文件名和文件夹结构
Argon2id 参数、盐和随机数前缀文件大小、时间戳和权限
压缩包总大小和块数量文件数量
它是 nikpak 压缩包这一事实所有内容,包括是否含媒体



先压缩后加密的一个后果是压缩包大小反映数据的可压缩性。这对每个压缩并加密的压缩工具都是固有的;只有当攻击者能控制部分数据并观察大小时才重要,而这不适用于压缩文件的情形。

8.5 密码处理

  • 最小长度。创建压缩包时 8 个字符;更短的密码被拒绝并给出说明。
  • 确认。交互式提示询问两次,不回显键入的字符,不匹配则拒绝。
  • 生成。没有密码时,nikpak 从省略形似字符(0/O、1/l/I)的 56 符号字母表中抽取 24 个字符,使用操作系统密码学安全随机生成器上的拒绝采样,使没有符号比另一个更可能。那是 24 × log2(56),约 139 位熵,且只显示一次,在清晰标记的框中。
  • 内存卫生。派生的密钥和密码字符串在不再需要时从内存擦除(zeroize crate)。
  • 通道。命令行(-p)、环境变量 NIKPAK_PASSWORD、交互式提示,或没有终端时的 stdin。命令行形式方便但在进程列表中可见;其他形式更可取。

8.6 攻击测试

以下对加密压缩包的操纵是针对完成后的程序执行的。全部在任何错误数据被接受之前被检测:

操纵结果
翻转密文中部的一个字节拒绝:认证失败(退出码 11)
翻转文件头中盐的一个字节拒绝(文件头是关联数据且密钥改变)
更改文件头中的方法字节拒绝
把文件截短 100 字节作为损坏拒绝(退出码 3)
恰好在一个块边界删除最终块拒绝:新的最后一个块缺少最终标志
交换两个块拒绝:计数器不匹配
提供错误的密码在第一个块即被拒绝(退出码 11)
把一个随机文件当作压缩包输入拒绝:签名错误(退出码 3)



剩余风险,如实说明
弱密码总能被猜出;Argon2id 让它昂贵,但救不了 password123。nikpak 尚未经过独立审计。它无法防御键盘记录器、恶意软件或读取运行进程内存的人。对于泄露会造成严重危害的数据,请先让密码学家审查实现。



9 抗量子

量子计算机严重威胁某些密码学,而几乎不影响其他密码学。nikpak 的设计站在几乎不受影响的一边。

9.1 量子计算机改变什么

算法影响受影响者
Shor(1994)在多项式时间内分解整数并计算离散对数RSA、Diffie-Hellman、DSA、椭圆曲线密码学:被大型量子计算机破解
Grover(1996)在约 N 的平方根步内搜索 N 项的无结构空间对称密钥和散列函数:有效密钥长度被减半



Shor 算法是戏剧性的那个:它会破解一切依赖公钥数学的系统,这正是政府和标准机构迁移到 ML-KEM(FIPS 203)等后量子公钥算法的原因。Grover 算法是温和的那个:256 位密钥对它提供约 128 位安全性,且 Grover 的迭代本质上是有顺序的,因此把工作分散到多台量子机器上只能获得其数量的平方根作为加速。

9.2 nikpak 的立场

组件原语量子影响
加密XChaCha20,256 位密钥已知最佳攻击:Grover,约 2^128 次运算。遥不可及。
认证Poly1305,128 位标签,由 256 位秘密派生密钥不涉及公钥数学
密码到密钥Argon2id,64 MiB,3 遍Grover 至多把密码熵减半,且每次迭代都需要评估一个内存困难函数
随机性操作系统 CSPRNG不受影响
公钥密码学无Shor 算法无物可攻



256 位对称密钥是 NIST 后量子标准化中最高安全类别(第 5 类)的基准:NIST 标准化的公钥算法按它们与穷举搜索 256 位 AES 密钥难度的接近程度评级。换句话说,nikpak 的数据加密已经处于后量子标准所瞄准的强度。

9.3 密码怎么办?

密码的密钥空间是 256 位,但密钥由密码派生,因此真实强度是密码熵与 256 位中较小者。面对量子对手,密码熵至多(对攻击者最有利)减半:

密码熵经典搜索Grover 搜索(至多)
nikpak 生成的(24 符号)约 139 位约 2^139 次猜测约 2^70 次迭代,每次需要一个相干的 64 MiB Argon2id 评估
六个随机 Diceware 词约 77 位约 2^77 次猜测同一昂贵电路的约 2^39 次迭代
人为选择的 10 字符密码通常 30 - 45 位可行可行



结论是熟悉的那个,只是说得更尖锐:弱点是密码,而不是加密算法。使用生成的密码或长随机口令,压缩包即可在可预见的未来抵御经典和量子攻击者。Argon2id 在两个世界都有帮助,因为每次猜测的成本由内存主导,而 Grover 算法并不让内存变便宜。

9.4“先收割,后解密”

经常被引用的量子威胁是,对手今天记录加密数据,等量子计算机出现后再解密。对于用 RSA 或椭圆曲线交换密钥的系统,如 TLS 连接或电子邮件加密,这是真实的。nikpak 压缩包没有密钥交换:其密钥来自一个从不离开用户头脑或密码管理器的秘密。存储的副本以后只能靠猜测该秘密来打开,而量子计算机并没有从根本上让这变得更容易。

9.5 如实的局限与前路

  • “抗量子”意味着没有任何已知量子算法能对这些原语胜过通用搜索。这不是证明;对 ChaCha20 和 Argon2 的密码分析仍在继续,且格式已版本化,因此参数或算法可以更改。
  • 如果将来加入公钥共享(把压缩包加密给接收方的密钥),将使用混合方案,X25519 与 ML-KEM 结合,这样任一组件成立即安全。
  • 抗量子不能防止实现错误。见第 8.6 和 10 节。

10 加固与健壮性

压缩工具读取陌生人写的文件,并在用户磁盘上写文件,这使它成为典型目标。nikpak 把每个压缩包都当作不可信输入。

10.1 安全解压

  • 路径穿越(“zip slip”)。每个条目名称被规范化:反斜杠变斜杠,空的和 . 组成部分被丢弃,任何 .. 组成部分导致该条目被跳过并警告。绝对名称失去其根,因此 /etc/passwd 被解压到目标文件夹内部。
  • Windows 名称。在 Windows 上运行时,包含 < > : " | ? * 或控制字符、以空格或点结尾、或等于保留设备名(CON、PRN、AUX、NUL、COM1-9、LPT1-9)的组成部分被拒绝。
  • 符号链接。写入前,检查目标路径在目标文件夹之下的每个已有组成部分;链接从不被跟随。已有文件先删除,新文件用 create_new 创建,它拒绝打开已有路径,因此链接无法用于重定向写入。
  • 特权位。setuid、setgid 和 sticky 位从不恢复;权限被掩码到普通 rwx 位。
  • 失败的文件。CRC32 不匹配的文件被删除,不留残余。

在测试中解压了一个手工构建的压缩包,内含 ../../evil.txt、/tmp/evil.txt、..\..\evil.txt 和 ok/../../evil.txt:没有任何东西逃出目标文件夹,不安全条目被报告并跳过,目标文件夹中植入的符号链接被拒绝。

10.2 有界解码器

  • Argon2id 参数在任何分配之前做范围检查(第 8.2 节)。
  • 加密块的长度必须在 16 字节到 64 KiB + 16 之间;名称最多 4096 字节;音频块必须声明合理的长度;BMP 区域限制为 256 MiB;一元 Rice 编码有上限。
  • 音频和媒体解码器在单元测试中被输入数千个随机和位翻转的输入,必须返回错误,从不 panic 或中止。
  • nikpak 自己的代码不含 unsafe 块,Rust 的边界检查把在 C 中会是内存损坏错误的任何逻辑错误变成干净失败。

nikpak 不限制压缩包的扩展比,因此与每个压缩工具一样,精心构造的“压缩炸弹”解压时可以填满磁盘。如果这很重要,先用 t 测试未知压缩包。

10.3 安全创建与如实的错误

  • 压缩包写入 name.nik.tmp,完成后才重命名;任何失败时守护对象删除临时文件,因此中断的运行绝不会留下半写入或被替换的压缩包。
  • 如果文件在被压缩期间变短,nikpak 用明确消息中止,而不是写入不一致的条目。不可读文件、符号链接和特殊文件被跳过并警告(退出码 1),压缩包绝不会添加到自身。
  • 错误被分类,以便脚本响应:压缩包内的校验和或帧错误是损坏(退出码 3),认证失败是密码错误或篡改(退出码 11),被拒绝的写入是致命错误(退出码 2)。

11 基准测试

本章报告测量,而非声称。它准确描述测量了什么,包含不利于 nikpak 的结果,并给出复现一切所需的命令。

第 11.1 至 11.6 节用 nikpak 1.1.0 测量,其 -m5 仍使用 Zstandard。第 11.7 节在一台机器上比较 nikpak 1.2.0 与 RAR、7-Zip 和 ZIP。

11.1 方法

项目细节
机器Linux x86-64 虚拟机,一个 CPU 核。所有时间都是该单核上的墙钟秒;多核机器对两个工具都更快。
软件RAR 7.00(2024 年 2 月 26 日,Linux 试用版构建,-ma5 格式);nikpak 1.1.0 以 LTO 发布模式构建;FLAC 1.x -8 作为音频参考。
RAR 设置-m3(RAR 默认)和 -m5(最佳)。通用数据测试另加 -s(固实)和 -md256m(256 MB 字典),因为 nikpak 压缩包始终固实,公平比较需要允许 RAR 同样如此。
验证每个 nikpak 压缩包都被解压并与原始比较(媒体文件用 cmp,通用集用全部 10,949 个文件的 SHA-256)。



测试素材

集合内容
画廊 WAV35 个来自 LibreOffice 画廊的声音文件:单声道 16 位,23 个 11,025 Hz,12 个 22,050 Hz。它们只含约一千个不同的采样值(压扩源),是故意给预测编码出的难题。
音乐一段 100 秒多乐器作品(钢琴、弦乐、贝斯、长笛、鼓),用 TiMidity++ 和 Freepats 声音集渲染为 44.1 kHz 立体声 16 位(17.4 MB)。SoX 派生出一个 22.05 kHz 单声道 16 位、一个 22.05 kHz 单声道 8 位和一个 48 kHz 立体声 24 位版本。
MOD16 个来自 libxmp 测试集的 ProTracker 模块。
BMP6 张图像:五张来自 scikit-image 样本集的照片(24 位,外加其中一张的 8 位灰度转换)和一张来自 TeX Live 的纹理。
通用数据197.6 MB、10,949 个文件:Python 标准库和包、文档以及测试机的 C 头文件,符号链接已移除。



此测试素材的局限
音乐是合成的:它由 MIDI 渲染,包含重复段落,这对每个基于 LZ 的工具都有帮助,包括 NAC 之后的 Zstandard 阶段。真实录音包含更少的精确重复,因此绝对大小会不同。比较对一切工具一视同仁,FLAC 在同一文件上的结果证实音频编解码器本身有竞争力,但有特定素材的读者应自行测量:nikpak mt 正是做这件事。



11.2 媒体文件

集合原始RAR 7 -m5nikpak -m2nikpak -m5m5 vs RAR
6 张 BMP 照片 9,183,956 2,862,744 2,035,223 1,750,706小 38.8 %
16 个 ProTracker MOD 文件 2,507,557 1,070,290 966,622 927,973小 13.3 %
35 个画廊 WAV(单声道 16 位) 2,991,820 1,605,879 1,529,219 1,418,532小 11.7 %
音乐 44.1 kHz 立体声 16 位 17,371,848 11,024,411 9,415,519 9,411,898小 14.6 %
音乐 22.05 kHz 单声道 16 位 4,342,996 3,112,135 2,635,819 2,634,669小 15.3 %
音乐 22.05 kHz 单声道 8 位 2,171,520 844,160 742,462 740,751小 12.2 %
音乐 48 kHz 立体声 24 位 28,362,206 21,467,283 (-m3) 18,689,529 18,688,030小 12.9 %



所有大小均为字节。24 位文件 RAR 只在 -m3 测量过,因此该行是比其他行更弱的比较。

Figure 3: archive size as a percentage of the original data (lower is better).
图 3:压缩包大小占原始数据的百分比(越低越好)。
Figure 4: size reduction of nikpak compared with RAR 7.00 -m5.
图 4:nikpak 相对 RAR 7.00 -m5 的大小缩减。

媒体过滤器带来什么

同样的 nikpak 不带媒体过滤器(1.0 版,默认级别普通 Zstandard)是衡量媒体层价值的正确标尺:

集合nikpak 1.0(无过滤器)nikpak 1.1 -m2缩减
6 张 BMP 照片 3,580,971 2,035,223 43.2 %
16 个 ProTracker MOD 文件 1,260,607 966,622 23.3 %
35 个画廊 WAV(单声道 16 位) 1,876,722 1,529,219 18.5 %
音乐 44.1 kHz 立体声 16 位 15,585,422 9,415,519 39.6 %
音乐 22.05 kHz 单声道 16 位 3,887,183 2,635,819 32.2 %
音乐 22.05 kHz 单声道 8 位 1,078,493 742,462 31.2 %



与 FLAC 的比较

对于 44.1 kHz 立体声音乐,专用参考编码器 FLAC -8 产生 9,407,019 字节。同一音频的 NAC 流为 9,425,305 字节,相差 0.2 % 以内,完成的 nikpak 压缩包为 9,415,519 字节。对于 22 kHz 单声道和 8 位版本,NAC 比 FLAC 小(2,635,446 对 2,638,954,仅音频区域 785,890 对 798,810),对于 24 位,压缩包(18.69 MB)小于 FLAC 的 19.12 MB。FLAC 在画廊文件上弱得多(2.50 MB 对 RAR 的 1.61 MB),原因见第 7.5 节;这正是其他过滤器证明价值的情形。

11.3 通用数据

源代码和文档不是媒体,因此此测试检验 nikpak 在其他方面的收益是否在普通文件上付出代价。

工具与级别大小(字节)时间解压
nikpak -m5 24,443,483 45.6 s 1.8 s
RAR 7 -m5 -s -md256m 25,862,475 36.2 s
RAR 7 -m5 -s 26,002,812 26.7 s
RAR 7 -m3 -s 26,536,863 16.7 s 1.5 s
nikpak -m4 26,814,023 13.5 s
nikpak -m3 30,329,965 2.0 s
nikpak -m2(1.1 默认) 36,266,743 0.9 s 0.8 s
RAR 7 -m5(默认,非固实) 50,851,230 14.8 s
RAR 7 -m3(默认,非固实) 50,974,773 9.7 s 2.3 s



Figure 5: general data, size and creation time on one core.
图 5:通用数据,单核上的大小和创建时间。

如实地读表:

  • 最高级别:nikpak 产生的压缩包比 RAR 最佳小 5.5 %(24.4 对 25.9 MB)。RAR 在此级别更快(26.7 到 36.2 秒对 45.6 秒)。
  • 相同大小:nikpak -m4 在 RAR 默认固实级别 81 % 的时间内达到其约 1 % 之内(13.5 秒对 16.7 秒)。
  • 快速级别:-m2 用压缩比换速度:比 RAR 固实 -m3 快 18 倍,大 37 %。这是为备份和流水线准备的级别,时间比最后几 MB 更重要。
  • 非固实:在这个多小文件的数据上,RAR 默认(非固实)压缩包几乎是其固实的两倍大。任何比较 nikpak 与 RAR 的人都应给 RAR 加 -s。

11.4 单个大媒体文件上的速度

Figure 6: creating an archive from the 17 MB stereo WAV.
图 6:从 17 MB 立体声 WAV 创建压缩包。
操作nikpakRAR 7大小
创建,快速 / RAR 默认 1.45 s (-m2) 2.54 s (-m3)9,415,519 vs 11,128,595
创建,最高级别 3.14 s (-m5) 5.87 s (-m5)9,411,898 vs 11,024,411
解压 0.28 s 0.23 s相同输出



在媒体上 nikpak 既更小,创建也更快。RAR 解压略快;一个可能的原因是 NAC 解码对每个采样做整数预测,其成本略高于主导通用解码的匹配复制。

11.5 加密开销

在 222 MB 数据集上,加密压缩包耗时 0.92 秒,未加密 0.64 秒,且大 11,087 字节(0.03 %)。差异包含 Argon2id 派生,因此成本由固定的几分之一秒主导。

11.6 复现结果

# media: which filter wins, per file
nikpak mt song.wav tracker.mod photo.bmp


# archive with nikpak and RAR, then compare
nikpak a -q -ep -m2 test.nik *.wav
rar a -ep -m5 -ma5 test.rar *.wav


# fair comparison on many files: give RAR solid mode
rar a -r -s -m5 -ma5 -md256m data.rar data
nikpak a -m5 data.nik data


# verify
nikpak x -o+ test.nik restore\



11.7 第二轮:nikpak、RAR 7、7-Zip 和 ZIP

针对 nikpak 1.2.0 更新。在本轮的第一个版本中,用 nikpak 1.1.0,7-Zip -mx9 在文本(领先 1.4 %)、Office 文件(3.3 %)和源代码(8.5 %)上领先。nikpak 1.2.0 加入 Deflate 再压缩(7.9)和基于 LZMA2 与 PPMd 的新最高级别(6.7),并在同一台机器上用相同文件重新测量。RAR、7-Zip 和 ZIP 的数字来自第一轮。

第 11.2 至 11.5 节仅比较 nikpak 与 RAR。第二轮加入大多数人已有的两个压缩工具,7-Zip 和经典 ZIP 格式,以及每天被归档的两种文件:纯文本和 Office 文档(.doc、.docx、.xls、.xlsx、.csv、.pdf)。四个工具在同一台机器和相同文件上依次运行,RAR 重新测量。语料从相同公共源重建,因此绝对大小和时间与 11.2 至 11.4 不同。在本节内比较数字,不要跨节比较。

方法

项目细节
机器Linux x86-64 虚拟机(Intel Xeon,2.8 GHz)。每个命令用 taskset 固定到一个 CPU 核并以单线程运行(nikpak -mt1、RAR -mt1、7-Zip -mmt=1;ZIP 天生单线程)。
软件nikpak 1.2.0(发布构建);RAR 7.00(-ma5,固实);7-Zip 23.01(.7z 格式,LZMA2,默认固实);Info-ZIP Zip 3.0 和 UnZip 6.0(Deflate)。
级别nikpak:快速级别 -m2 和最高 -m5(自 1.2.1 起为默认,另有 -m4)。其他:默认和最高,RAR -m3 -s / -m5 -s,7-Zip -mx5 / -mx9,ZIP -6 / -9。给 RAR 固实模式(-s),因为其他三个固实,或如 ZIP 那样无法固实。
计时墙钟秒。50 MB 以下的集合:三次运行取最佳。通用集:一次运行。
验证全部 92 个压缩包都被解压并用 diff -r 与原始比较。全部相同。



测试素材

集合文件大小(字节)内容
BMP 图像 8 6,987,840六张来自 scikit-image 样本集的 24 位 BMP 照片,外加一张 8 位灰度照片和一张 8 位碎石纹理。
ProTracker MOD 16 2,252,604libxmp 测试集中最大的 16 个 ProTracker 模块。
画廊 WAV 35 2,991,820与 11.1 相同的 35 个 LibreOffice 画廊声音。
音乐,四种格式 4 × 1 53,417,226一段新的 100 秒作品(钢琴、弦乐、贝斯、长笛、鼓),用 TiMidity++ 和 Freepats 渲染为 44.1 kHz 立体声 16 位,再由 SoX 转换为 22.05 kHz 单声道 16 位和 8 位以及 48 kHz 立体声 24 位。
纯文本 18 11,793,318十八本古腾堡计划(Project Gutenberg)书籍的 .txt(奥斯汀、梅尔维尔、莎士比亚、钦定版圣经等)。
Office 文件 60 39,100,91512 份 .docx、.doc 和 .pdf 报告(文本、标题、表格和一些照片)以及 8 个工作簿,500 到 30,000 行,.xlsx、.xls 和 .csv(含公式),由 LibreOffice 保存。
通用数据 11,473 190,454,632Python 3.12 和 3.13 标准库、Debian Python 包、C 头文件(/usr/include)和 /usr/share/doc。符号链接已移除。



最高级别:压缩包大小

集合原始nikpak -m5RAR 7 -m57-Zip -mx9ZIP -9
8 张 BMP 图像 6,987,840 3,557,350 4,394,310 4,195,043 4,974,680
16 个 ProTracker MOD 文件 2,252,604 1,127,607 1,264,245 1,166,855 1,408,085
35 个画廊 WAV(单声道 16 位) 2,991,820 1,346,484 1,596,971 1,484,879 1,852,279
音乐 44.1 kHz 立体声 16 位 17,760,408 8,731,989 10,460,042 10,666,922 15,225,764
音乐 22.05 kHz 单声道 16 位 4,440,136 2,458,867 2,797,026 2,784,080 3,736,722
音乐 22.05 kHz 单声道 8 位 2,220,090 606,840 725,416 642,341 836,989
音乐 48 kHz 立体声 24 位 28,996,592 18,198,056 21,229,465 25,542,036 26,980,770
18 本纯文本书籍(.txt) 11,793,318 2,741,898 3,519,622 3,259,868 4,223,579
60 个 Office 文件 39,100,915 6,887,729 11,186,217 10,085,779 17,399,998
通用数据(11,473 个文件) 190,454,632 28,175,618 33,606,893 28,879,398 62,198,510



所有大小均为字节。每行最小的压缩包以粗体显示。

Figure 7: archive size at the maximum level of each tool, as a percentage of the original (lower is better).
图 7:每个工具最高级别的压缩包大小,占原始的百分比(越低越好)。

快速级别:大小与创建时间

集合nikpak -m2RAR 7 -m37-Zip -mx5ZIP -6
8 张 BMP 图像 54.0 % · 0.39 s 63.0 % · 2.09 s 60.1 % · 3.51 s 71.2 % · 0.35 s
16 个 ProTracker MOD 文件 52.4 % · 0.61 s 56.2 % · 0.43 s 51.9 % · 0.69 s 62.7 % · 0.10 s
35 个画廊 WAV(单声道 16 位) 51.1 % · 0.41 s 53.6 % · 0.71 s 49.6 % · 1.96 s 61.9 % · 0.24 s
音乐 44.1 kHz 立体声 16 位 49.2 % · 1.72 s 59.9 % · 3.60 s 60.1 % · 14.03 s 85.7 % · 0.82 s
音乐 22.05 kHz 单声道 16 位 55.4 % · 0.58 s 64.1 % · 0.95 s 62.7 % · 2.14 s 84.2 % · 0.22 s
音乐 22.05 kHz 单声道 8 位 27.4 % · 0.77 s 33.3 % · 0.52 s 28.9 % · 2.13 s 38.2 % · 0.32 s
音乐 48 kHz 立体声 24 位 62.8 % · 2.06 s 73.6 % · 7.54 s 88.1 % · 13.54 s 93.0 % · 1.19 s
18 本纯文本书籍(.txt) 35.2 % · 0.11 s 30.4 % · 5.53 s 27.7 % · 11.76 s 36.0 % · 0.89 s
60 个 Office 文件 38.0 % · 0.22 s 29.5 % · 8.25 s 26.2 % · 21.85 s 44.7 % · 1.83 s
通用数据(11,473 个文件) 25.3 % · 1.67 s 18.0 % · 48.19 s 17.0 % · 93.71 s 32.9 % · 7.32 s



nikpak -m2 对其他工具的默认级别。每个单元格显示压缩包大小占原始的百分比和单核创建时间。每行最小的压缩包为粗体。

通用数据详览

工具与级别大小(字节)占原始的 %创建解压
nikpak -m5 28,175,618 14.8 % 154.3 s 5.6 s
7-Zip -mx9 28,879,398 15.2 % 120.9 s 6.0 s
RAR 7 -m5 -s -md256m 32,350,575 17.0 % 73.6 s 7.0 s
7-Zip -mx5 32,375,802 17.0 % 93.7 s 4.5 s
nikpak -m4 33,295,133 17.5 % 37.8 s 3.3 s
RAR 7 -m5 -s 33,606,893 17.6 % 72.4 s 6.7 s
RAR 7 -m3 -s 34,236,198 18.0 % 48.2 s 5.9 s
nikpak -m2 48,229,284 25.3 % 1.7 s 0.9 s
RAR 7 -m3(非固实) 56,038,351 29.4 % 20.2 s 6.9 s
ZIP -9 62,198,510 32.7 % 28.2 s 6.9 s
ZIP -6 62,672,415 32.9 % 7.3 s 5.4 s



Figure 8: general data, size and creation time of all four tools on one core.
图 8:通用数据,四个工具单核上的大小和创建时间。

从 1.1.0 到 1.2.0

集合nikpak 1.1.0 -m5nikpak 1.2.0 -m57-Zip -mx91.2.0 vs 7-Zip
18 本纯文本书籍 3,306,819 2,741,898 3,259,868小 15.9 %
60 个 Office 文件 10,417,750 6,887,729 10,085,779小 31.7 %
通用数据 31,322,041 28,175,618 28,879,398小 2.4 %



如实地解读结果

  • nikpak -m5 在每个集合上都是最小的。相对 7-Zip -mx9 小 2.4 到 31.7 %,相对 RAR 7 -m5 小 10.8 到 38.4 %,相对 ZIP -9 小 19.9 到 60.4 %。在录制音频上 NAC 预测了基于 LZ 的压缩器只能匹配的东西:24 位音乐上 7-Zip 节省 12 %,nikpak 节省 37 %。
  • Office 文件:比 7-Zip 小 31.7 %。Deflate 再压缩把 6.6 MB .xlsx 和 1.9 MB .docx 变成普通 XML,LZMA2 的压缩远好于 Deflate(6.89 MB 对 10.09 MB)。即使 -m4 也比 7-Zip -mx9 小 1.3 % 且更快(16 秒对 26 秒)。-m5 的代价是时间:81 秒对 26 秒。
  • 文本:小 15.9 %,用时减半。PPMd 在散文上胜出(创建 6.4 秒对 12.9 秒)。因为 PPMd 解码和编码一样慢,解压需 2.7 秒而非 0.2 秒。
  • 源代码:小 2.4 %,微弱领先。28.2 MB 对 28.9 MB,154 秒对 121 秒。按文件类型排序并在连续样本上选择引擎(6.7)造成了差异。
  • 快速级别 -m2:最快,不是最小。nikpak -m2 处理通用集需 1.7 秒,7-Zip -mx5 需 93.7 秒(长 56 倍),RAR 48.2 秒,ZIP 7.3 秒。它在图像和大多数音频上比 7-Zip -mx5 小(MOD 文件和画廊 WAV 大 1 到 3 %),但在文本、Office 文件和源代码上更大(通用集 49.0 %)。因此自 1.2.1 版起 nikpak 默认使用 -m5;-m2 需显式要求。
  • ZIP 快但处处最大。即使 nikpak -m2 也在每个集合上胜过 ZIP -9。ZIP 创建小媒体压缩包比 nikpak -m2 快,因为 Deflate 做的功远少于 NAC 编解码器,但它在文本、Office 文件和源代码上更慢。
  • 解压。通用集解压 0.9 秒(nikpak -m2)或 5.6 秒(-m5),7-Zip 6.0 秒,RAR 6.7 秒,ZIP 6.9 秒。带再压缩 Deflate 流的压缩包解压更慢,因为每条流都重建(Office 集 2.1 秒对 0.7 秒)。
  • 此机器上的 RAR。RAR 在此比 11.3 慢(对相近数量的源代码,-m5 -s 72 秒而非 27 秒)。机器和数据不同,因此两节不应混用。在本轮内每个工具条件相同。

这对 nikpak 意味着什么

在最高级别,nikpak 1.2.0 在所测每个集合上都做出四个工具中最小的压缩包,比 7-Zip -mx9 小 2.4 到 31.7 %。代价是速度:-m5 单线程,在 Office 文件和源代码上比 7-Zip 慢。让它更快在路线图上(第 15.2 节)。

12 nikpak 与 RAR 比较

RAR 是优秀且广泛使用的压缩工具,本章并不假装相反。它说明测量和设计在何处偏向 nikpak,RAR 在何处领先,以及谁该选什么。

12.1 nikpak 领先之处

领域nikpak证据
音频、图像、跟踪器音乐在所测每个媒体集上比 RAR 7 -m5 小 12 - 39 %第 11.2 节
源代码,最高级别比 RAR 最佳固实设置小 5.5 %第 11.3 节
快速级别 -m2 的速度197 MB:0.9 秒对 9.7 - 16.7 秒;一个 WAV:1.45 秒对 2.54 秒第 11.3、11.4 节
密码与模式XChaCha20-Poly1305,已认证;RAR5 在 CBC 模式使用 AES-256,未认证且依赖单独的校验和第 8 章
密码散列Argon2id,内存困难(64 MiB);RAR5 使用 PBKDF2-HMAC-SHA256,受 CPU 限制且可在 GPU 上廉价并行第 8.2 节
文件名隐私始终加密;RAR 需要 -hp 开关第 8.4 节
密码生成内置:未提供时生成强随机密码第 3.3 节
篡改证据重排、截断和文件头更改被密码学地检测第 8.6 节
透明性完整源代码和文档化格式;以内存安全 Rust 实现第 5、10 章
分发一个静态可执行文件(拖放窗口和命令行;无窗口约 2.3 MB),或一个小型 Windows 安装程序;共享软件,个人使用免费(3.8)第 13 章



12.2 RAR 领先之处

领域RAR 7如今的 nikpak
恢复记录能修复损坏的压缩包检测损坏,不能修复
分卷压缩包是否
自解压压缩包是否
更新压缩包原地添加、替换和删除文件仅创建或覆盖
图形界面WinRAR,带资源管理器集成用于压缩的拖放窗口(3.7);无资源管理器集成,从命令行解压
最高级别速度通用集 26.7 - 36.2 秒 45.6 s
媒体解压速度17 MB WAV 0.23 秒 0.28 s
成熟度与覆盖面数十年的实地检验;数百万现有 .rar 文件较新;无独立审计;只读 .nik



12.3 抗量子纵观

nikpak 和 RAR5 都用 256 位对称密钥加密,因此两者都不受 Shor 算法影响,都对 Grover 保持约 128 位安全性。任一说法都无法区分二者。nikpak 在其上加的是内存困难的密码步骤(使主导的现实攻击——密码猜测——昂贵得多)和认证加密(使篡改可检测)。它也从一开始就在假设没有任何公钥原语会成为压缩包格式一部分的前提下设计,其路线图承诺若加入公钥共享则采用混合后量子方案。

12.4 该选哪个

  • 选择 nikpak 用于大小和速度都重要的音频、图像、跟踪器音乐和源代码压缩包,用于脚本化备份,用于强大的默认加密,以及当开放、文档化的格式有价值时。
  • 选择 RAR 当你需要恢复记录、分卷或自解压压缩包、原地更新,或必须与只有 RAR 工具的人交换压缩包时。
  • 两者都用:没有什么阻止工作流用 nikpak 处理批量媒体和加密备份,同时保留 RAR 用于分发。

12.5 那 7-Zip 和 ZIP 呢?

第 11.7 节也测量 nikpak 1.2 对 7-Zip 23.01 和 Info-ZIP 3.0。简而言之:

  • 选择 nikpak -m5 获得最小的压缩包。它在所测每个集合上比 7-Zip -mx9 小 2.4 到 31.7 %:Office 文件 31.7 %,文本 15.9 %,源代码 2.4 %。选择 nikpak -m2 用于快速备份(源代码上比 7-Zip 默认快 56 倍)。nikpak 还有更强的加密:7-Zip 用 AES-256,其 SHA-256 密钥派生不内存困难,且它只用 -mhe 隐藏文件名。
  • 选择 7-Zip 当最高级别时间更重要时(它在大型 Office 和源代码集上更快),当你需要其默认级别在文档上就很小,或当你需要打开 7-Zip 可读的众多格式时。
  • 选择 ZIP 只为兼容。每个操作系统无需额外软件即可打开它,但 nikpak -m5 小 19.9 到 60.4 %,且 ZIP 的传统加密很弱。

13 构建、部署与集成

13.1 用 PowerShell 在 Windows 上构建

nikpak 用 Rust 编写,用 Cargo 构建。在 Windows 上,Rust 需要一个来自 Visual Studio Build Tools 的 C/C++ 链接器。在管理员 PowerShell 中,一次性安装两者,然后重新打开 PowerShell:

winget install Rustlang.Rustup
$o = '--wait --passive --add Microsoft.VisualStudio.Workload.VCTools'
$o += ' --includeRecommended'
winget install Microsoft.VisualStudio.2022.BuildTools --override $o



如果 winget 不可用,从 rustup.rs 安装 Rust,并安装带使用 C++ 的桌面开发工作负载的Visual Studio 生成工具。然后,在包含源代码的文件夹中:

.\build.ps1 # builds dist\nikpak.exe and dist\unNik.exe
.\build.ps1 -Test # runs the unit tests first


# if PowerShell refuses to run scripts:
powershell -ExecutionPolicy Bypass -File .\build.ps1



build.ps1 检查 Cargo 是否已安装(若没有则说明如何安装),运行构建,失败时解释常见原因,并把可执行文件复制到 dist\nikpak.exe。

Windows 安装程序

.\build.ps1 -Installer(或 npm run installer)还用 Inno Setup 6.3 或更新版本构建安装程序 dist\nikpak--setup.exe(用 winget install JRSoftware.InnoSetup 一次性安装)。脚本是 installer\nikpak.iss;版本取自 Cargo.toml。这个安装文件正是上传到共享软件和下载站点的东西。

  • 默认只为当前用户安装,无需管理员权限或 UAC 提示,安装到 %LOCALAPPDATA%\Programs\NikPak。其第一页提供“为所有用户安装”(Program Files,需要管理员权限)。
  • 它显示 LICENSE.txt 供接受,并安装 nikpak.exe、unNik.exe、图标、LICENSE.txt、THIRD-PARTY-NOTICES.txt 和 CHANGELOG.md。它添加开始菜单项、可选桌面图标,以及按需把文件夹加入命令行的 PATH。它在设置 > 应用中注册卸载程序。
  • 快捷方式:开始菜单中的 NikPak、unNik 和许可证;桌面上的 NikPak(始终)以及桌面上的 unNik(可选)。
  • .nik 文件始终被注册(自 1.5.2 起):它们获得自己的图标(nikfile.ico,一页带拉链,像 .zip 文件)、类型名“NikPak archive”,双击在 unNik 中打开(右键菜单中的“用 unNik 打开”)。unNik 也列在“打开方式”和默认应用中。卸载程序移除该注册。如果 .nik 文件已设置为用其他程序打开,Windows 保留该选择,直到在“打开方式”中选过一次 unNik。
  • 最后的“Launch NikPak”以普通用户启动 NikPak,绝不以管理员,因此拖放正常。更新会覆盖旧版本安装(固定 AppId)。%APPDATA%\nikpak 中的许可证在更新和卸载时保留。安装程序从不含密钥或密钥文件。
  • 安装程序跟随 Windows 以英语或法语运行。发布前,用代码签名证书为 nikpak.exe 和安装程序签名,以免 Windows SmartScreen 警告未知发布者。

buildall.ps1 一次运行构建所有发布内容——nikpak.exe、unNik.exe、两个安装程序和 SHA256SUMS.txt——复制到 binaries\,重建网站并通过 FTP 上传(-NoUpload 跳过上传)。第二个安装程序 unnik--setup.exe(installer\unnik.iss)只安装 unNik,免费的压缩包浏览器;当 NikPak 已安装时它只安装该程序,因此菜单不会重复。

13.2 用 npm 构建

Rust 项目不使用 npm,因此从不要求 npm。为方便起见,项目包含一个小 package.json,其脚本只是调用 Cargo,因此生活在 Node.js 工作流中的开发者可以输入熟悉的命令:

npm run build # cargo build --release
npm run dist # build.ps1 -> dist\nikpak.exe
npm test # cargo test --release
npm run clean # cargo clean



13.3 自包含可执行文件

文件 .cargo/config.toml 为 Windows MSVC 目标静态链接 C 运行库。因此生成的 nikpak.exe(带图形界面几 MB,无界面约 2.3 MB)没有 DLL 依赖,在运行它的机器上不需要Visual C++ 可再发行组件。把它复制到 PATH 上的文件夹即可在任何地方使用。发布配置启用完全优化、链接时优化、单一代码生成单元、符号剥离和 panic = abort,以获得小而快的二进制文件。

13.4 macOS 和 Linux

./build.sh # builds and copies the result to dist/nikpak
# or: cargo build --release
sudo cp dist/nikpak /usr/local/bin/



在 macOS 上先安装命令行工具(xcode-select --install);在 Debian 或 Ubuntu 上安装 C 编译器(sudo apt install build-essential)。Rust 本身来自 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh。压缩库由 Cargo 从其 C 源编译,因此不需要系统库。

站点的 macOS 和 Linux 下载由 ./package-unix.sh 制作,它构建 nikpak 和 unNik,并用 install.sh 打包成 binaries\nikpak--macos.zip(通用二进制:Intel + Apple 芯片)或 binaries\nikpak--linux-.tar.gz。在 Linux 上设 ZIG=1 以用 cargo-zigbuild 针对 glibc 2.28 构建,使程序也能在较旧发行版上运行。

13.5 依赖

自 1.2 版起 nikpak 需要 Rust 1.89 或更新版本。build.ps1 和 build.sh 检查已安装版本,较旧时运行 rustup update stable。用 npm 时,在 npm run build 之前运行一次 rustup update。

nikpak 依赖一小组成熟的 crate。其确切版本固定在 Cargo.lock 中,它是源包的一部分,因此每次构建都用相同的代码。

Crate用途
zstdZstandard 压缩,带多线程
chacha20poly1305XChaCha20-Poly1305 和 STREAM 构造(RustCrypto)
argon2Argon2id 密钥派生(RustCrypto)
rand访问操作系统的安全随机生成器
zeroize从内存擦除密钥和密码
rpassword在每个平台上无回显地读取密码
crc32fast快速 CRC32
glob通配符展开(PowerShell 不为本机程序展开通配符)
liblzma-m5 的 LZMA2 引擎(liblzma,从 C 源静态编译)
ppmd-rust-m5 文本的 PPMd 变体 I 引擎
preflate-rs逐位精确的 Deflate 再压缩(微软,Apache-2.0);vendor/preflate-rs 中的随附副本,其 LGPL 熵编码器(cabac)被 nikpak 自己的区间编码器替换,因此 nikpak 不含 LGPL 代码。需要 Rust 1.89 或更新版本
eframe / egui图形界面(可选 cargo feature gui,默认开启)
rfd用于 Browse 的原生文件夹对话框
ed25519-dalek许可证密钥签名验证(第 3.8 节)
rustybuzz, unicode-bidi窗口中印地语、孟加拉语和阿拉伯语的整形和从右到左排序(可选 cargo feature gui)



13.6 脚本化

可预测的退出码和环境变量密码使 nikpak 易于脚本化。一个把警告视为可接受、更糟视为失败的 PowerShell 备份:

$archive = "D:\Backups\docs-$(Get-Date -Format yyyy-MM-dd).nik"
# the password comes from a file or secret store, not from the script
$env:NIKPAK_PASSWORD = Get-Content "$env:USERPROFILE\.nikpak-secret"


nikpak a -p -m3 -inul $archive "$env:USERPROFILE\Documents"
if ($LASTEXITCODE -gt 1) { throw "nikpak failed: $LASTEXITCODE" }


nikpak t -inul $archive # verify what was written
if ($LASTEXITCODE -ne 0) { throw "archive verification failed" }



同样的 shell 脚本,用裸列表命令向其他工具供料:

export NIKPAK_PASSWORD="$(cat ~/.nikpak-secret)"
nikpak a -p -m3 -inul "backup-$(date +%F).nik" ~/Documents || [ $? -eq 1 ]
nikpak lb backup-2026-10-16.nik | grep -i '\.wav$'



13.7 Total Commander

第 3.6 节描述的集成依赖 nikpak 遵循 RAR 的语法。简短清单:注册扩展名 nik;打包用 a,解包用 x;让前端为长文件列表传递 @listfile;无人值守解压给 -o+ 或 -y;用 -p 或 NIKPAK_PASSWORD 变量传递密码,因为图形前端没有可供提示的控制台。

14 测试与验证

因为压缩工具保存人们的数据,nikpak 在四个层面测试。

14.1 单元测试

用 cargo test --release 运行三十四个自动化测试:

领域检查什么
加密流在 0、1、100、65,535、65,536、65,537、131,072、131,077 和 327,803 字节(64 KiB 块边界附近)做往返;错误的头(关联数据)、错误的密钥和截断都必须失败。
密钥派生限制恶意的 Argon2id 参数(巨大内存、零遍数、零通道、过多遍数)被拒绝。
NAC 音频编解码器8/16/24/32 位音频、1、2、3 和 6 声道、九种信号类型(静音、带噪正弦、白噪声、满量程方波、斜坡、单脉冲、恒定最大值、浪费的低位、恒定最小值)、从 1 到 5000 采样的八种块长度、三种努力级别和强制原样编码的无损往返。
音频方法NAC、Shuffle 和 Shuffle+Delta 覆盖所有位深、声道数、字节序和有符号性,包括不能整除的块。
检测与文件WAV(单声道、立体声、8/16/24/32 位、可扩展、六声道、尾部块、奇数剩余字节、零和过大的数据长度)、AIFF、MOD(含截断)和 BMP(8/24/32 位、行填充、尾部数据);浮点 WAV、微小文件和文本不得被检测;每个文件都通过每个适用的过滤器往返。
健壮性向音频解码器和媒体解码器输入数千个随机和位翻转的块:它们必须返回错误,从不 panic 或耗尽内存。
分帧与辅助带尾部数据的分块框架、路径清理、通配符和过滤器匹配、日期格式化、密码生成。
Deflate 再压缩Deflate 级别 1、3、6 和 9 的 ZIP 容器、带两条 zlib 流的 PDF、三成员 gzip、非容器;400 个损坏或截断的再压缩流必须干净失败(preflate panic 被捕获)。
多引擎块在 256 KiB 帧边界附近通过 zstd、LZMA2 和 PPMd 块的往返;300 个损坏或截断的主体;恶意引擎参数;PPMd/LZMA2 试算。
图形界面在虚拟显示(Xvfb)上运行:空闲、悬停、进度和完成屏幕目视检查;拖入的文件夹逐字节压缩和解压;压缩中取消和关闭不留文件;无显示时回退到用法文本。
许可证nikpak-keygen 和 PHP 套件制作的密钥相同且被接受;更改的姓名、电子邮件、类型或密钥,以及由另一密钥签名的密钥,被拒绝。在虚拟显示上:两种按钮顺序的提醒、错误密钥报错、成功注册、无提醒重启;文本保持在框内。
语言每种语言用相同占位符翻译每个文本;天城文元音符号和阿拉伯语连写被整形;在虚拟显示上逐屏检查全部十种语言(提醒、空闲、进度、完成、关于、注册、语言菜单);选择一种语言会保存并以它重启 nikpak。
压缩包 API读取索引、从文件夹内部解压一个文件(落地时不带文件夹路径)、删除文件夹及其内容、删除全部,针对 -m5、zstd + 加密和存储压缩包;错误密码被如实报告。
压缩包浏览器带隐含文件夹的树构建、文本检测和解码(UTF-8、UTF-16、Windows-1252)、查看器中的自动换行和搜索。在虚拟显示上:打开、密码、导航、选择、搜索、查看器、解压、删除和测试,用几种语言。
皮肤与文件管理器菜单生成并检查 Linux 菜单文件;一个针对选择的压缩包以三个程序启动;虚拟显示下的 --add-here(三个项目)和 --extract-here / --extract-to;加密压缩包要求密码并解压出相同文件;每个新界面文本都被翻译成十种语言(每种语言翻译每个文本的断言)。



14.2 端到端测试

  • 每个基准压缩包都被解压并在几个级别上与其源逐字节比较(10,949 文件集用 SHA-256)。
  • 创建了一个混合压缩包(WAV、MOD、BMP、文本、随机数据、子文件夹),列出、测试、完全解压,并从中部解压了单个文件。
  • 同样的压缩包以加密创建,用正确和错误密码测试,并解压。
  • 上一格式版本(1.0)写入的压缩包被 1.1 打开。
  • 通过伪终端演练交互式密码输入:匹配的密码、不匹配、空输入(生成)、打开时的正确和错误密码。

14.3 攻击测试

对每个发布候选版本执行篡改、截断、块重排、文件头操纵、垃圾文件以及第 10.1 节的路径穿越压缩包。结果列于第 8.6 和 10.1 节;每种情况下的预期结果都是拒绝、清晰消息和非零退出码,从不 panic,从不写到目标之外。

14.4 测试发现的缺陷

测试活动是发布程序中若干缺陷不存在的原因。两个例子:一个对 32 位音频失败的 Rice 参数搜索,本会写出数 GB 的码(被单元测试矩阵发现并修复),以及一个 zstd 校验和失败却用通用退出码 2 而非 3 报告(在篡改测试中发现并修复)。第三个例子是基准测试方法本身:与 RAR 的第一次比较用了其非固实默认,在源代码上偏袒了 nikpak;已改用固实模式重做,第 11 章的结果是修正后的。

15 局限与路线图

15.1 当前局限

局限细节与变通
无恢复记录、分卷或自解压损坏被检测,不修复(重要压缩包保留第二份副本);如需分卷,用通用工具拆分大压缩包。
无原地更新格式是一条固实流。删除(nikpak d,或 unNik 中的 Delete)会重新写入压缩包;不支持向已有压缩包添加文件:创建新压缩包,或用 -o+ 覆盖。
顺序访问从非常大的压缩包解压一个文件会读取它前面的内容(媒体条目被跳过而不解码);unNik 在最后一个所选文件之后停止读取。
媒体覆盖整数 PCM WAV 和 AIFF、ProTracker MOD、未压缩 BMP。浮点 WAV、ADPCM、XM/S3M/IT 和压缩图像以普通压缩存储。
高级别的内存-m4 和 -m5 压缩包使用 128 MiB 窗口;解压它们需要这么多内存。
最高级别 -m5Single-threaded and the slowest level. Creating needs up to about 1.1 GB of memory, extracting up to about 350 MB.
符号链接与日期链接在压缩时被跳过,解压时被拒绝;日期以 UTC 列出。
安全审查尚未独立审计。
Windows 和 macOS 构建在 Linux 上开发、测试和基准测试。Windows 和 macOS 特定的代码路径经人工审查,但尚未在这些系统上演练:依赖某个构建前先在那里运行 cargo test --release。



15.2 路线图

项目用途
恢复记录Reed-Solomon 奇偶校验,使损坏的压缩包可修复——RAR 用户最想念的功能。
分卷压缩包和更新支持拆分大压缩包,添加和替换文件。
混合方案的公钥共享用 X25519 结合 ML-KEM 把压缩包加密给接收方密钥,任一组件成立即安全。
更多媒体过滤器浮点音频、XM/S3M/IT 跟踪器、PNG 再压缩、用于 TGA 和未压缩 TIFF 的二维过滤器。
读取 ZIP 和 RAR,资源管理器集成在 unNik 中解包常见格式并把文件从 unNik 拖出,使 nikpak 能成为唯一安装的工具。(Explorer 和 Finder 右键菜单在 1.5.5 落地,第 3.11 节。)
更快的最高级别-m5 单线程,在 Office 文件上最多比 7-Zip -mx9 慢三倍:多线程 LZMA2 和 PPMd 块、相同文件去重,以及用于可执行文件的 BCJ 过滤器。
外部安全审查对密码学代码和解压器的独立审计。



许可证:NikPak 是闭源共享软件,© 2026 Nicolas Choukroun,由 Nikkoid LLC 发行(LICENSE.txt,第 3.8 节);unNik 是免费软件(第 3.9 节)。它所含的开源组件及其许可证列于 THIRD-PARTY-NOTICES.txt,每个副本都必须随附。把私有许可证密钥(keys/nikpak-private.key)排除在每个发布的包和 Web 根目录之外。把 Cargo.lock 保留在仓库中以使构建可复现,并在你支持的每个平台的持续集成任务中运行 cargo test --release。

A 附录 A:格式常量与限制

项目值
魔数 89 4E 49 4B 0D 0A 1A 0A
格式版本3(读取器接受 1、2 和 3)
文件头大小12 字节;加密时 59 字节
记录类型0 END, 1 FILE, 2 DIR, 3 MEDIA
压缩包方法0 存储、1 Zstandard、2 多引擎块
块引擎1 Zstandard;2 LZMA2(写入字典最多 128 MiB,接受 1.5 GiB);3 PPMd8(写入阶数 8、192 MiB;接受阶数 2-16、1 GiB)
Deflate 再压缩ZIP、PDF、gzip;文件最多 512 MiB;每条流最多 512 MiB 普通数据和 64 MiB 修正
最大名称长度4096 字节
加密块64 KiB 明文 + 16 字节标签,前面是 u32 大端长度
Argon2id(写入)64 MiB、3 遍、4 通道、16 字节盐、32 字节密钥
Argon2id(接受)内存 8 × 通道数 .. 1 GiB,遍数 1 .. 64,通道数 1 .. 64
流随机数19 字节随机前缀 + 32 位计数器 + 1 字节最终标志 = 24 字节
最小密码长度8 个字符
生成的密码24 个字符、56 符号字母表、约 139 位
媒体最小文件大小8 KiB(区域至少 4 KiB)
NAC 块4096 采样帧;Shuffle 块 32768 帧
试算最多三个 768 KiB 窗口(5 %、45 %、85 %);最多 3 MiB 时取整个区域;2 % 余量
BMP 区域限制256 MiB
Zstandard 窗口默认;-m4 和 -m5 带长距离匹配为 2^27
线程1 .. 256(-mt)



B 附录 B:术语表

术语含义
AEAD带关联数据的认证加密:加密并检测任何修改,也包括明文发送的数据(如文件头)。
Argon2idRFC 9106 标准化的内存困难密码散列函数;以刻意高昂的内存成本把密码变成密钥。
CRC32一种检测意外损坏的 32 位校验和。不是安全功能。
FSE有限状态熵:Zstandard 使用的查表熵编码器。
Grover 算法一种有二次加速的量子搜索算法;把对称密钥的有效长度减半。
LPC线性预测编码:从前几个采样的加权和预测一个采样。
Levinson-Durbin一种从信号的自相关计算每个阶数最优预测器系数的递归。
LZMA27-Zip 和 xz 使用的 Lempel-Ziv-Markov 链算法:带最优解析和二进制区间编码器的 LZ77 匹配。
MED中值边缘检测器:来自 LOCO-I / JPEG-LS 图像编码器的像素预测器。
内存困难成本由内存主导的函数,它限制了 GPU 和定制芯片可获得的加速。
NACnikpak 的无损音频编解码器。
PPMd部分匹配预测(变体 I):从前最多 8 个字节预测每个字节;在文本上非常强。
preflate一种把 Deflate 流变成其普通数据加小修正的方法(和库),从中可重建完全相同的 Deflate 流。
Rice 编码一种用于大致几何分布数字的简单、快速编码:一元商加固定数量的低位。
Shor 算法一种分解整数并计算离散对数的量子算法,可破解 RSA 和椭圆曲线密码学。
固实压缩包作为一条连续流压缩的压缩包,从而可利用文件之间的冗余。
STREAM一种把长消息加密为认证块的构造,随机数中含计数器和最后一块标志。
Zig-zag 映射把有符号整数映射为无符号整数(0、-1、1、-2 ……到 0、1、2、3 ……),使小幅度变成小数字。
Zstandard一种结合 LZ77 匹配与 Huffman 和 FSE 熵编码的现代开放压缩格式。



版本 1.6.2 · 2026 年 10 月 第 19 页