ANSSI-FR/MLA
GitHub: ANSSI-FR/MLA
MLA 是一个纯 Rust 实现的多层存档格式,提供加密、压缩、数字签名和后量子支持,以解决安全数据存储和传输的需求。
Stars: 369 | Forks: 22
[](https://github.com/ANSSI-FR/MLA/actions)
[](https://crates.io/crates/mla)
[](https://docs.rs/mla)
[](https://crates.io/crates/mlar)
[](https://pypi.org/project/mla-archive/)
# 多层存档 (MLA)
### 法国国家网络安全局 (ANSSI)

[](https://github.com/ANSSI-FR#types-de-projets)
[](https://documentation.ouvert.numerique.gouv.fr/les-parcours-de-documentation/ouvrir-un-projet-num%C3%A9rique/#niveau-ouverture)
*此项目由 [ANSSI](https://cyber.gouv.fr/) 管理。要了解更多信息,您可以访问 ANSSI 开源策略的专用[页面](https://cyber.gouv.fr/enjeux-technologiques/open-source/)(法语)。您也可以点击上面的徽章了解其含义。*
# 简介
MLA 是一种存档文件格式,具有以下特点:
* 支持使用非对称密钥进行传统加密与后量子加密的混合(基于 X25519 和后量子 ML-KEM 1024 混合的 KEM,采用 AES256-GCM 的 HPKE)
* 支持传统签名与后量子签名的混合
* 支持压缩(基于 [`rust-brotli`](https://github.com/dropbox/rust-brotli/))
* 可流式创建存档:
* 即使在单向数据二极管上也能构建存档
* 可以通过数据块添加条目,无需预先知道最终大小
* 条目数据块可以交错添加(可以添加一个条目的开头,开始第二个条目,然后继续添加第一个条目的后续部分)
* 架构无关且在一定程度上可移植(完全用 Rust 编写)
* 存档读取是可定位的,即使已压缩或加密。可以直接访问存档中间的条目,无需从头开始读取
* 如果存档被截断,在一定程度上可以恢复。提供两种模式:
* 经认证的恢复(默认):只检索经认证(如 AEAD,无签名验证)的加密数据块
* 未认证的恢复:检索经认证和未经认证的加密数据块。使用风险自负。
* 可以说更不容易出现 bug,尤其是在解析不受信任的存档时(Rust 的安全性)
# 仓库
此仓库包含:
* `mla`:实现 MLA 读写器的 Rust 库
* `mlar`:一个封装 `mla` 以执行常见操作(创建、列表、提取等)的 Rust 命令行工具
* `doc`:与 MLA 相关的文档(例如格式规范、密码学)
* [MLA 手册](https://anssi-fr.github.io/MLA)
* `bindings`:其他语言的绑定
* `samples`:测试资源
* `mla-fuzz-afl`:用于对 `mla` 进行模糊测试的 Rust 工具
* `.github`:持续集成需求
# 快速命令行使用
以下是一些使用 ``mlar`` 来处理 MLA 格式存档的命令。
```
# 生成 MLA 密钥对。
mlar keygen sender
mlar keygen receiver
# 创建包含某些文件的归档。
mlar create -k sender.mlapriv -p receiver.mlapub -o my_archive.mla /boot/./grub/locale/en@quot.mo /etc/security/../issue ../file.txt
# 列出归档的内容。
# 注意顺序可能不同,根目录被去除,
# 路径已规范化,且列表内容按照
# `doc/src/ENTRY_NAME.md` 中的描述进行编码(因此输出中有百分号)。
# 输出结果为:
# ``
# etc/issue
# boot/grub/locale/en%40quot.mo
# file.txt
# ``
mlar list -k receiver.mlapriv -p sender.mlapub -i my_archive.mla
# 将归档内容提取到一个新目录中。
# 在此示例中,这将创建两个文件:
# extracted_content/etc/issue 和 extracted_content/etc/os-release
mlar extract -k receiver.mlapriv -p sender.mlapub -i my_archive.mla -o extracted_content
# 显示归档中一个文件的内容。
mlar cat -k receiver.mlapriv -p sender.mlapub -i my_archive.mla etc/os-release
# 将归档转换为长期格式,主要用于存档目的。
# 以下操作还会移除加密并应用
# the highest (but slowest) compression level.
mlar convert -k receiver.mlapriv -p sender.mlapub -i my_archive.mla -o longterm.mla --unencrypted --unsigned -q 11
# 创建包含多个接收者且不带签名也不压缩的归档。
mlar create --unsigned --uncompressed -p archive.mlapub -p client1.mlapub -o my_archive.mla ...
# 列出一个包含无法解释为路径的条目名称的归档。
# 输出结果为:
# `c%3a%2f%00%3b%e2%80%ae%0ac%0dd%1b%5b1%3b31ma%3cscript%3eevil%5c..%2f%d8%01%c2%85%e2%88%95`
# 对应于一个包含以下内容的条目名称:ASCII 字符、c:、/、..、\、
# NUL、RTLO、换行符、终端转义序列、回车符、
# HTML、代理代码单元、U+0085 奇怪换行符、伪造的 Unicode 斜杠。
# 请注意,其中一些字符可能出现在有效的路径中。
mlar list -k samples/test_mlakey_archive_v2_receiver.mlapriv -p samples/test_mlakey_archive_v2_sender.mlapub -i samples/archive_weird.mla --raw-escaped-names
# 获取其内容。
# 显示内容为:
# `' OR 1=1`
mlar cat -k samples/test_mlakey_archive_v2_receiver.mlapriv -p samples/test_mlakey_archive_v2_sender.mlapub -i samples/archive_weird.mla --raw-escaped-names c%3a%2f%00%3b%e2%80%ae%0ac%0dd%1b%5b1%3b31ma%3cscript%3eevil%5c..%2f%d8%01%c2%85%e2%88%95
# 创建一个网页文件的归档,不加密且不带签名
curl https://raw.githubusercontent.com/ANSSI-FR/MLA/refs/heads/main/LICENSE.md | mlar create --unencrypted --unsigned -o my_archive.mla --stdin-data
# 创建一个网页文件和任意字节串的归档,不加密且不带签名(选择的分隔符不应出现在两个条目中)
(curl https://raw.githubusercontent.com/ANSSI-FR/MLA/refs/heads/main/LICENSE.md; echo "SEPARATOR"; echo -n "All Hail MLA") | mlar create --unencrypted --unsigned -o my_archive.mla --stdin-data --stdin-data-separator "SEPARATOR" --stdin-data-entry-names great_license.md,hello.txt
# 创建一个通过标准输入传递文件列表(而非数据)的归档
echo -n -e "/etc/issue\n/etc/os-release" | mlar create -unencrypted --unsigned -o my_archive.mla --stdin-file-list
```
`mlar` 可以通过以下方式获取:
* 通过 Cargo:`cargo install mlar`
* 使用支持的操作系统的[最新版本](https://github.com/ANSSI-FR/MLA/releases)
* 发布的二进制文件使用 `opt-level = 3` 构建,性能出色
为了获得更高性能,您可以构建一个针对本机优化的二进制文件(不可移植),例如在 Linux 机器上:
```
RUSTFLAGS="-Ctarget-cpu=native" cargo build --release --target x86_64-unknown-linux-musl
```
注意:本机构建针对您机器的 CPU 进行了优化,**并且不可移植**。仅当在构建它的同一台机器上运行时才使用它们。
# API 使用
参见 [https://docs.rs/mla](https://docs.rs/mla/2.0.0-beta/mla/index.html)
# 在其他语言中使用 MLA
以下语言的绑定可用:
* [C/C++](bindings/C/README.md)
* [Python](bindings/python/README.md)
## 安全
您应该在使用前阅读 [API 文档](https://github.com/ANSSI-FR/MLA#api-usage) 和 `mlar --help`。它们有时会提供重要的安全警告。[`doc/src/ENTRY_NAME.md`](doc/src/ENTRY_NAME.md) 对于理解条目命名约定和安全影响也至关重要。
**潜在问题**
* 将存档提取到至少一个上级目录可被其他用户写入的目录(例如 `/tmp` 或其他共享目录)通常是不安全的,因为存在符号链接攻击。
* 除了符号链接攻击外,`mlar` 不会提取到指定输出目录之外。
* 即使使用经过认证的密码加密,未签名的存档也无法认证其作者。任何拥有您公钥的人都可以创建这样的存档,因此它可能包含任意(可能恶意的)数据。
* `mlar` 在输出时会转义条目名称以避免安全问题。
**保证与限制**
* MLA 不提供任何防止侧信道攻击的保证。但是,如果您发现这方面的问题,请联系我们,我们将评估是否修复。
* MLA Python 绑定的安全性和维护级别明确不保证。
* 在 MLA 2.0.0-beta 上进行了安全评估,记录了此后应已修复的低严重性问题(参见 [issue #465](https://github.com/ANSSI-FR/MLA/issues/465))。报告位于 `doc/20260130-mla-security-assessment.pdf`。
## 常见问题
**`MLAArchiveWriter` 是 `Send` 的吗?**
默认情况下,`MLAArchiveWriter` 不是 `Send` 的。如果内部的可写类型也是 `Send`,可以在 `Cargo.toml` 中为 `mla` 启用 `send` 特性,例如:
```
[dependencies]
mla = { version = "...", default-features = false, features = ["send"]}
```
**真的需要一种新格式吗?**
由于现有的存档格式众多,可能不需要。
但据作者所知,没有一种格式支持上述特性(当然,它们更适合其他用途)。
例如(基于作者的理解):
* `tar` 格式在添加文件前需要知道其大小,并且不可定位
* `zip` 格式在删除页脚后可能会丢失文件信息
* `7zip` 格式在向存档添加文件时需要重建整个存档(不可流式)。它也相当复杂,因此在解包未知存档时更难审计/信任
* `journald` 格式不可流式。这里也不需要单写/多读,从而释放了 `journald` 格式的一些约束
* 任何存档 + `age`:[age](https://age-encryption.org/) 截至 MLA 2.0 发布时,不支持后量子加密或签名。
* 备份格式通常被设计来避免重复等问题,因此它们需要在内存中保留更大的结构,或者不可流式
调整这些格式可能会产生类似的特性。做出的选择是为了更好地控制格式的能力,并(尝试)保持简单。
## 性能
您可以通过基于 [Criterion](https://github.com/bheisler/criterion.rs) 的嵌入式基准测试来评估性能。
已经嵌入了几个场景,例如:
* 文件添加,具有不同的大小和层配置
* 文件添加,改变压缩质量
* 文件读取,具有不同的大小和层配置
* 随机文件读取,具有不同的大小和层配置
* 线性存档提取,具有不同的大小和层配置
在 "Intel(R) Core(TM) i7-1255U CPU @ 2.60GHz" 上:
```
$ cargo bench
...
multiple_layers_multiple_block_size/compression: true, encryption: true, signature: true/1048576
time: [7.0850 ms 7.1179 ms 7.1586 ms]
thrpt: [139.69 MiB/s 140.49 MiB/s 141.14 MiB/s]
...
chunk_size_decompress_multifiles_random/compression: true, encryption: true, signature: true/1048576
time: [11.285 ms 11.494 ms 11.663 ms]
thrpt: [85.745 MiB/s 87.005 MiB/s 88.616 MiB/s]
...
reader_multiple_layers_multiple_block_size_multifiles_linear/compression: true, encryption: true, signature: true/1048576
time: [4.6197 ms 4.6383 ms 4.6604 ms]
thrpt: [214.58 MiB/s 215.60 MiB/s 216.47 MiB/s]
...
```
Criterion.rs 文档解释了如何获取 HTML 报告、比较结果等。
### AES-NI 支持
如 [aes crate 文档](https://docs.rs/aes/0.8.4/aes/index.html#x86x86_64-intrinsics-aes-ni) 所述,此 crate 在 `i686` 和 `x86_64` 目标上使用**运行时检测**来检查 AES-NI 是否可用。如果未检测到 AES-NI,它会自动回退到一个常数时间的软件实现。
# 贡献
感谢您的帮助!要贡献,请阅读我们的[贡献说明](.github/CONTRIBUTING.md)。
### 法国国家网络安全局 (ANSSI)

[](https://github.com/ANSSI-FR#types-de-projets)
[](https://documentation.ouvert.numerique.gouv.fr/les-parcours-de-documentation/ouvrir-un-projet-num%C3%A9rique/#niveau-ouverture)
*此项目由 [ANSSI](https://cyber.gouv.fr/) 管理。要了解更多信息,您可以访问 ANSSI 开源策略的专用[页面](https://cyber.gouv.fr/enjeux-technologiques/open-source/)(法语)。您也可以点击上面的徽章了解其含义。*
# 简介
MLA 是一种存档文件格式,具有以下特点:
* 支持使用非对称密钥进行传统加密与后量子加密的混合(基于 X25519 和后量子 ML-KEM 1024 混合的 KEM,采用 AES256-GCM 的 HPKE)
* 支持传统签名与后量子签名的混合
* 支持压缩(基于 [`rust-brotli`](https://github.com/dropbox/rust-brotli/))
* 可流式创建存档:
* 即使在单向数据二极管上也能构建存档
* 可以通过数据块添加条目,无需预先知道最终大小
* 条目数据块可以交错添加(可以添加一个条目的开头,开始第二个条目,然后继续添加第一个条目的后续部分)
* 架构无关且在一定程度上可移植(完全用 Rust 编写)
* 存档读取是可定位的,即使已压缩或加密。可以直接访问存档中间的条目,无需从头开始读取
* 如果存档被截断,在一定程度上可以恢复。提供两种模式:
* 经认证的恢复(默认):只检索经认证(如 AEAD,无签名验证)的加密数据块
* 未认证的恢复:检索经认证和未经认证的加密数据块。使用风险自负。
* 可以说更不容易出现 bug,尤其是在解析不受信任的存档时(Rust 的安全性)
# 仓库
此仓库包含:
* `mla`:实现 MLA 读写器的 Rust 库
* `mlar`:一个封装 `mla` 以执行常见操作(创建、列表、提取等)的 Rust 命令行工具
* `doc`:与 MLA 相关的文档(例如格式规范、密码学)
* [MLA 手册](https://anssi-fr.github.io/MLA)
* `bindings`:其他语言的绑定
* `samples`:测试资源
* `mla-fuzz-afl`:用于对 `mla` 进行模糊测试的 Rust 工具
* `.github`:持续集成需求
# 快速命令行使用
以下是一些使用 ``mlar`` 来处理 MLA 格式存档的命令。
```
# 生成 MLA 密钥对。
mlar keygen sender
mlar keygen receiver
# 创建包含某些文件的归档。
mlar create -k sender.mlapriv -p receiver.mlapub -o my_archive.mla /boot/./grub/locale/en@quot.mo /etc/security/../issue ../file.txt
# 列出归档的内容。
# 注意顺序可能不同,根目录被去除,
# 路径已规范化,且列表内容按照
# `doc/src/ENTRY_NAME.md` 中的描述进行编码(因此输出中有百分号)。
# 输出结果为:
# ``
# etc/issue
# boot/grub/locale/en%40quot.mo
# file.txt
# ``
mlar list -k receiver.mlapriv -p sender.mlapub -i my_archive.mla
# 将归档内容提取到一个新目录中。
# 在此示例中,这将创建两个文件:
# extracted_content/etc/issue 和 extracted_content/etc/os-release
mlar extract -k receiver.mlapriv -p sender.mlapub -i my_archive.mla -o extracted_content
# 显示归档中一个文件的内容。
mlar cat -k receiver.mlapriv -p sender.mlapub -i my_archive.mla etc/os-release
# 将归档转换为长期格式,主要用于存档目的。
# 以下操作还会移除加密并应用
# the highest (but slowest) compression level.
mlar convert -k receiver.mlapriv -p sender.mlapub -i my_archive.mla -o longterm.mla --unencrypted --unsigned -q 11
# 创建包含多个接收者且不带签名也不压缩的归档。
mlar create --unsigned --uncompressed -p archive.mlapub -p client1.mlapub -o my_archive.mla ...
# 列出一个包含无法解释为路径的条目名称的归档。
# 输出结果为:
# `c%3a%2f%00%3b%e2%80%ae%0ac%0dd%1b%5b1%3b31ma%3cscript%3eevil%5c..%2f%d8%01%c2%85%e2%88%95`
# 对应于一个包含以下内容的条目名称:ASCII 字符、c:、/、..、\、
# NUL、RTLO、换行符、终端转义序列、回车符、
# HTML、代理代码单元、U+0085 奇怪换行符、伪造的 Unicode 斜杠。
# 请注意,其中一些字符可能出现在有效的路径中。
mlar list -k samples/test_mlakey_archive_v2_receiver.mlapriv -p samples/test_mlakey_archive_v2_sender.mlapub -i samples/archive_weird.mla --raw-escaped-names
# 获取其内容。
# 显示内容为:
# `' OR 1=1`
mlar cat -k samples/test_mlakey_archive_v2_receiver.mlapriv -p samples/test_mlakey_archive_v2_sender.mlapub -i samples/archive_weird.mla --raw-escaped-names c%3a%2f%00%3b%e2%80%ae%0ac%0dd%1b%5b1%3b31ma%3cscript%3eevil%5c..%2f%d8%01%c2%85%e2%88%95
# 创建一个网页文件的归档,不加密且不带签名
curl https://raw.githubusercontent.com/ANSSI-FR/MLA/refs/heads/main/LICENSE.md | mlar create --unencrypted --unsigned -o my_archive.mla --stdin-data
# 创建一个网页文件和任意字节串的归档,不加密且不带签名(选择的分隔符不应出现在两个条目中)
(curl https://raw.githubusercontent.com/ANSSI-FR/MLA/refs/heads/main/LICENSE.md; echo "SEPARATOR"; echo -n "All Hail MLA") | mlar create --unencrypted --unsigned -o my_archive.mla --stdin-data --stdin-data-separator "SEPARATOR" --stdin-data-entry-names great_license.md,hello.txt
# 创建一个通过标准输入传递文件列表(而非数据)的归档
echo -n -e "/etc/issue\n/etc/os-release" | mlar create -unencrypted --unsigned -o my_archive.mla --stdin-file-list
```
`mlar` 可以通过以下方式获取:
* 通过 Cargo:`cargo install mlar`
* 使用支持的操作系统的[最新版本](https://github.com/ANSSI-FR/MLA/releases)
* 发布的二进制文件使用 `opt-level = 3` 构建,性能出色
为了获得更高性能,您可以构建一个针对本机优化的二进制文件(不可移植),例如在 Linux 机器上:
```
RUSTFLAGS="-Ctarget-cpu=native" cargo build --release --target x86_64-unknown-linux-musl
```
注意:本机构建针对您机器的 CPU 进行了优化,**并且不可移植**。仅当在构建它的同一台机器上运行时才使用它们。
# API 使用
参见 [https://docs.rs/mla](https://docs.rs/mla/2.0.0-beta/mla/index.html)
# 在其他语言中使用 MLA
以下语言的绑定可用:
* [C/C++](bindings/C/README.md)
* [Python](bindings/python/README.md)
## 安全
您应该在使用前阅读 [API 文档](https://github.com/ANSSI-FR/MLA#api-usage) 和 `mlar --help`。它们有时会提供重要的安全警告。[`doc/src/ENTRY_NAME.md`](doc/src/ENTRY_NAME.md) 对于理解条目命名约定和安全影响也至关重要。
**潜在问题**
* 将存档提取到至少一个上级目录可被其他用户写入的目录(例如 `/tmp` 或其他共享目录)通常是不安全的,因为存在符号链接攻击。
* 除了符号链接攻击外,`mlar` 不会提取到指定输出目录之外。
* 即使使用经过认证的密码加密,未签名的存档也无法认证其作者。任何拥有您公钥的人都可以创建这样的存档,因此它可能包含任意(可能恶意的)数据。
* `mlar` 在输出时会转义条目名称以避免安全问题。
**保证与限制**
* MLA 不提供任何防止侧信道攻击的保证。但是,如果您发现这方面的问题,请联系我们,我们将评估是否修复。
* MLA Python 绑定的安全性和维护级别明确不保证。
* 在 MLA 2.0.0-beta 上进行了安全评估,记录了此后应已修复的低严重性问题(参见 [issue #465](https://github.com/ANSSI-FR/MLA/issues/465))。报告位于 `doc/20260130-mla-security-assessment.pdf`。
## 常见问题
**`MLAArchiveWriter` 是 `Send` 的吗?**
默认情况下,`MLAArchiveWriter` 不是 `Send` 的。如果内部的可写类型也是 `Send`,可以在 `Cargo.toml` 中为 `mla` 启用 `send` 特性,例如:
```
[dependencies]
mla = { version = "...", default-features = false, features = ["send"]}
```
**真的需要一种新格式吗?**
由于现有的存档格式众多,可能不需要。
但据作者所知,没有一种格式支持上述特性(当然,它们更适合其他用途)。
例如(基于作者的理解):
* `tar` 格式在添加文件前需要知道其大小,并且不可定位
* `zip` 格式在删除页脚后可能会丢失文件信息
* `7zip` 格式在向存档添加文件时需要重建整个存档(不可流式)。它也相当复杂,因此在解包未知存档时更难审计/信任
* `journald` 格式不可流式。这里也不需要单写/多读,从而释放了 `journald` 格式的一些约束
* 任何存档 + `age`:[age](https://age-encryption.org/) 截至 MLA 2.0 发布时,不支持后量子加密或签名。
* 备份格式通常被设计来避免重复等问题,因此它们需要在内存中保留更大的结构,或者不可流式
调整这些格式可能会产生类似的特性。做出的选择是为了更好地控制格式的能力,并(尝试)保持简单。
## 性能
您可以通过基于 [Criterion](https://github.com/bheisler/criterion.rs) 的嵌入式基准测试来评估性能。
已经嵌入了几个场景,例如:
* 文件添加,具有不同的大小和层配置
* 文件添加,改变压缩质量
* 文件读取,具有不同的大小和层配置
* 随机文件读取,具有不同的大小和层配置
* 线性存档提取,具有不同的大小和层配置
在 "Intel(R) Core(TM) i7-1255U CPU @ 2.60GHz" 上:
```
$ cargo bench
...
multiple_layers_multiple_block_size/compression: true, encryption: true, signature: true/1048576
time: [7.0850 ms 7.1179 ms 7.1586 ms]
thrpt: [139.69 MiB/s 140.49 MiB/s 141.14 MiB/s]
...
chunk_size_decompress_multifiles_random/compression: true, encryption: true, signature: true/1048576
time: [11.285 ms 11.494 ms 11.663 ms]
thrpt: [85.745 MiB/s 87.005 MiB/s 88.616 MiB/s]
...
reader_multiple_layers_multiple_block_size_multifiles_linear/compression: true, encryption: true, signature: true/1048576
time: [4.6197 ms 4.6383 ms 4.6604 ms]
thrpt: [214.58 MiB/s 215.60 MiB/s 216.47 MiB/s]
...
```
Criterion.rs 文档解释了如何获取 HTML 报告、比较结果等。
### AES-NI 支持
如 [aes crate 文档](https://docs.rs/aes/0.8.4/aes/index.html#x86x86_64-intrinsics-aes-ni) 所述,此 crate 在 `i686` 和 `x86_64` 目标上使用**运行时检测**来检查 AES-NI 是否可用。如果未检测到 AES-NI,它会自动回退到一个常数时间的软件实现。
# 贡献
感谢您的帮助!要贡献,请阅读我们的[贡献说明](.github/CONTRIBUTING.md)。标签:ANSSI, CVE, DNS解析, Rust语言, 加密, 压缩, 可视化界面, 后量子密码学, 存档格式, 安全存储, 密码学算法, 开源项目, 操作系统检测, 数字签名, 数据二极管, 文件格式, 流式处理, 混合加密, 漏洞扫描器, 网络安全, 蓝队防御, 逆向工具, 通知系统, 隐私保护