ITADN
casey/filepack
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈
目录↗️

filepack


Filepack 是一种具有自描述元数据的内容寻址包格式。

一个包是一个包含文件及其 BLAKE3 哈希值的清单的目录。文件和目录的哈希值构成一棵 Merkle 树,其根哈希值,即 包指纹,唯一标识该包。包可以使用 Ed25519 进行签名,并针对清单、指纹或签名进行验证。

包可以包含描述其内容的机器可读元数据, 从而支持程序化搜索、预览、播放和转换。

filepack 是一个用于创建、签名和验证 包的命令行工具,并包含一个用于包上传、下载和 显示的 HTTP 服务器。

一个包含示例包选择的演示服务器可在 filepack.com 获取。

Filepack 目前处于实验阶段。filepack 接口和包格式 可能会随时更改。

快速入门

可选地,添加描述您包的元数据:

echo "title: Packaging Guide" > metadata.yaml

创建 manifest.filepack:

filepack create

根据 manifest.filepack 中的哈希值验证当前目录:

filepack verify

打印软件包指纹:

filepack fingerprint

打印软件包元数据:

filepack metadata

安装

filepack 使用 Rust 编写,可以从源码构建, 并通过从本仓库签出的副本进行安装:

cargo install --path .

或者从 crates.io 使用:

cargo install filepack

请参阅 rustup.rs 获取 Rust 的安装说明。

预构建二进制文件

Linux、MacOS 和 Windows 的预构建二进制文件可在 发布页面 上找到。

您可以在 Linux、MacOS 或 Windows 上使用以下命令下载 最新版本,只需将 DEST 替换为您希望放置 filepack 的目录即可:

curl --proto '=https' --tlsv1.2 -sSf https://filepack.com/install.sh | bash -s -- --to DEST

例如,要将 filepack 安装到 ~/bin

# create ~/bin
mkdir -p ~/bin

# download and extract filepack to ~/bin/filepack
curl --proto '=https' --tlsv1.2 -sSf https://filepack.com/install.sh | bash -s -- --to ~/bin

# add `~/bin` to the paths that your shell searches for executables
# this line should be added to your shell's initialization file,
# e.g. `~/.bashrc` or `~/.zshrc`
export PATH="$PATH:$HOME/bin"

# filepack should now be executable
filepack --help

请注意,install.sh 在 GitHub Actions 或许多机器共享 IP 地址的其他环境中可能会失败。install.sh 会调用 GitHub API 以确定要安装的 filepack 的最新版本,而这些 API 调用是按 IP 地址进行速率限制的。为了使 install.sh 在此类情况下更加可靠,请使用 --tag 传递要安装的具体标签。

Usage

Filepack 支持多个子命令,包括用于创建清单的 filepack create、用于验证清单的 filepack verify,以及用于启动 HTTP 包服务器的 filepack serve

请参阅 filepack help 了解支持的子命令,并参阅 filepack help SUBCOMMAND 了解特定子命令的信息。

filepack create

创建一个 manifest。

可通过以下方式启用推荐的 lints:

filepack create --deny distribution

filepack verify

验证目录内容与清单是否一致。

要验证 DIR 的内容是否与 DIR/manifest.filepack 一致:

filepack verify DIR

如果当前目录包含 manifest.filepack,则可以省略 DIR

filepack verify

filepack verify 接受一个可选的 --print 标志,如果验证成功,则会将清单打印到标准输出。这可以在管道中使用,以确保在继续之前清单已被验证:

filepack verify --print | jq

filepack serve

启动 HTTP 服务器。filepack 服务器的身份验证功能有限,且没有磁盘使用配额,应被视为实验性功能。

要在 0.0.0.0:80 上提供 HTTP 服务:

filepack serve

要在 0.0.0.0:443 上使用 ACME TLS 证书提供 HTTPS 服务:

filepack serve --https --domain filepack.example

有关更多详细信息,请参阅 filepack serve --help

文件可以使用 filepack upload 上传,并使用 filepack download 下载。

数据目录

Filepack 将本地数据(包括私钥)存储在 filepack 数据 目录中,该目录位于 ~/.filepack,或者如果 $XDG_DATA_HOME 已设置且非空,则位于 $XDG_DATA_HOME/filepack

Filepack 将私钥存储在 filepack 数据目录的 keychain 子目录中,默认位于 ~/.filepack/keychain

命令所使用的 filepack 数据目录的位置可以通过 --data-dir 选项或 FILEPACK_DATA_DIR 环境变量进行覆盖。

清单

filepack 清单通常命名为 manifest.filepack,并 放置在它们所引用的文件旁边。

清单是 CBOR,可以使用 filepack manifest 将其转换为 JSON 以便检查或操作。

清单在转换为 JSON 后,是一个包含三个必需键的对象, embeddedpackagesignatures

embedded

embedded 键的值为一个对象,将 BLAKE3 哈希映射到十六进制编码的文件内容。package 对象仅包含文件名、哈希和大小,但文件可以嵌入在清单归档的 embedded 映射中。

目前这仅用于嵌入 metadata.filemeta

package

必填键 package 的值是一个将路径组件映射到目录条目的对象。目录条目可以是子目录或文件。文件是具有键 hash(文件的十六进制编码 BLAKE3 哈希值)和 size(文件的字节长度)的对象。

路径组件为 UTF-8 编码,不得为 ...,不得包含路径分隔符 /\,不得包含控制字符,长度不得超过 255 字节,且不得以 Windows 驱动器前缀开头,例如 C:

signatures

signatures 键的值为一个签名数组。 签名是 Bech32m 字符串,包含 Ed25519 密钥、签名所针对的软件包 指纹、可选的时间戳以及签名本身。

公钥是 Curve25519 点,签名是针对包含软件包指纹的序列化 CBOR 语句的哈希值 生成的 Ed25519 签名,该指纹承诺了 package 的内容。

示例

一个清单转换为 JSON,针对包含文件 README.mdsrc/main.c 的目录,由公钥 public1a67dndhhmae7p6fsfnj0z37zf78cde6mwqgtms0y87h8ldlvvflyqcxnd63 签名:

{
  "embedded": {},
  "package": {
    "README.md": {
      "hash": "fc253b84551ce6b00e820a826ac18054dc7f63a318ce62f3175315f5c467a62a",
      "size": 11883
    },
    "src": {
      "main.rs": {
        "hash": "1fa48b95ed335369d45b91af8138bdccd1413364bcdbfa6e9034e8a2cfd6e17f",
        "size": 33
      }
    }
  },
  "signatures": ["…"]
}

签名因篇幅所限而省略。签名是 Bech32m 编码的字符串,同时包含公钥和 Ed25519 签名。

密钥、签名、指纹和哈希

公钥、私钥、签名和包指纹均为 Bech32m 编码的 字符串,分别以 public1…private1…signature1…package1… 开头。

BLAKE3 文件哈希是 64 位小写十六进制字符串。

元数据

Filepack 包可能包含一个名为 metadata.filemeta 的文件,用于描述 包及其内容。如果存在,metadata.filemeta 也会嵌入到 manifest.filepack 归档中。

元数据通过在包根目录创建一个名为 metadata.yaml 的文件来编写。filepack create 随后加载 metadata.yaml(如果存在),检查 其有效性和未知字段,并将 CBOR 序列化写入 包根目录中的 metadata.filemeta

metadata.yaml 作为人类可读的参考保留,并用于修改 元数据,但 metadata.filemeta 是元数据的权威来源。为了 供脚本和工具使用,filepack metadata 以 JSON 格式打印 metadata.filemeta 的内容。

Filepack 元数据旨在作为包内容的广泛有用的机器和人类可读 描述,涵盖个人、分发和 归档用例。

元数据遵循固定模式,且不可由用户扩展。filepack 的未来版本 可能定义新的元数据字段,如果这些 字段存在且根据新模式无效,则会导致验证错误。

请随时提交 issue,提出新元数据字段的建议。

Schema

此 schema 适用于 YAML 编写格式以及 filepack metadata 的 JSON 输出。CBOR schema 目前尚无文档。

字段以 NAME: TYPE 形式给出。所有字段均为可选。

顶层字段:

  • artwork: component.{jpeg,png}: 包含内容艺术作品的 JPEG 或 PNG 文件的文件名,例如专辑的封面艺术或电影的 关键艺术。

  • creator: component: 创建该内容的人员或团体。

  • description: markdown: 对内容的描述。

  • homepage: url: 内容的主要 URL。应为内容的官方主页(如果存在),而不是例如维基百科或媒体数据库 链接。

  • language: language: 内容的主要语言。

  • media: object: 媒体元数据。

  • package: object: 包元数据。

  • readme: component.md: 内容 readme 的文件名。

  • time: time: 内容创建或发布的时间。

  • title: component: 内容的人类可读标题。

media 中包含媒体特定元数据的字段:

  • type: {audio,image,video,web}: 媒体类型。

如果媒体类型为 audioimagevideo,则媒体对象包含一个 名为 items 的字段,该字段是一个对象列表,包含包中各个项目的元数据。

在编写元数据 YAML 时,每个项目都是包含包项目文件名的字符串。例如,对于 audio 包:

media:
  type: audio
  items:
  - foo.flac
  - bar.flac

filepack create 将用包含从每个条目中提取的元数据的对象替换文件名。

描述包本身而非其内容的 package 字段:

  • colophon: component.md: 包尾注的文件名。

  • creator: component: 创建该包的人员或团体。

  • description: markdown: 包的描述。

  • homepage: url: 包的主要 URL。

  • time: time: 包创建的时间。

  • title: component: 包的标题。可能包含与来源、打包者或编码相关的详细信息。如果主标题字段存在歧义,保存包时可能将其用作目录名。

类型:

  • component: 一个字符串,具有与清单 package 对象中路径组件相同的限制,允许它们用作 unix 文件系统路径。 请注意,Windows 施加了额外的限制,但这些限制并未强制执行,因此组件在 Windows 上可能不是有效的路径。

  • component.EXTENSION: 必须以 .EXTENSION 结尾的组件。

  • language: 包含 ISO 639-1 两字符语言代码的字符串。请参阅 filepack languages 了解有效的语言代码。

  • markdown: 包含 CommonMark markdown 的字符串。

  • time: 包含具有两种精度之一的字符串,即仅年份,或年、月和日,写作 Y-MM-DD。时间使用天文纪年法的 前推格里高利历。

  • url: 包含方案为 httphttps 的 URL 的字符串。

时间示例:

0
-44
1929
-13787000000
0-01-01
929-01-01
1970-01-01
-44-03-15

示例

title: Tobin's Spirit Guide
creator: John Horace Tobin
artwork: cover.png
time: 1929
description: A compilation of supernatural occurrences, entities, and facts.
homepage: https://tobin-society.org/spirit-guide
language: en
readme: README.md
package:
  title: Tobin's Spirit Guide - First Edition
  creator: Egon Spengler
  time: 1984-07-08
  description: >
    First edition on loan from NYPL Main Branch research stacks. Captured via
    Microtek MS-300A flatbed scanner.
  homepage: https://ghost-busters.net/~egon
  colophon: COLOPHON.md

homepage 中的 URL 当然是时代错位的,因为万维网创建于 1989 年,比 Egon 首次打包 Tobin's Spirit Guide 晚了好几年。

Lints

filepack create 支持可按组启用的可选 lints:

filepack create --deny distribution

distribution lint 组用于检查可能导致分发问题的情况,例如在 Windows 上非法的非可移植路径、在大小写不敏感的文件系统上会产生冲突的路径,以及包含 .DS_Store 等垃圾文件。

Lint 组名称及其涵盖的 lint 可通过以下方式打印:

filepack lints

密钥与签名

filepack 支持生成 Curve25519 公钥/私钥密钥对, 以及对清单进行 EdDSA 签名的创建与验证。

密钥对生成

密钥对通过以下方式生成:

filepack keygen

这会在文件包数据目录的 keychain 子目录中创建 master.publicmaster.private 文件。

公钥打印

生成的公钥可以通过以下方式打印:

filepack key

签名

签名通过以下方式创建:

filepack sign

使用主密钥对当前目录中的清单进行签名,并将签名添加到清单的 signatures 数组中。签名基于从清单内容递归计算的指纹哈希值。

签名验证

嵌入在清单中的签名在验证清单时始终会被验证。可以使用以下命令断言特定公钥签名的存在:

filepack verify --key PUBLIC_KEY

如果清单内容中不存在 PUBLIC_KEY 的有效签名,这将失败。

指纹

Filepack 签名基于包指纹,即清单中包含的文件和目录所对应的 CBOR 对象的哈希值。

指纹是 BLAKE3 哈希,其构造方式使得不可能生成内容不同但指纹相同的包。

指纹可用作全局唯一标识符。如果两个包具有相同的指纹,则它们具有相同的内容。

指南

示例中的 [DIRECTORY] 参数可以省略,在这种情况下,它默认为当前目录。

创建包

要创建 filepack 清单:

filepack create [DIRECTORY]

这将创建 manifest.filepack,其中包含 DIRECTORY 及其子目录中所有文件的哈希值和文件大小。

要启用 linting,请在 lint 或 lint 组中使用 --deny 标志:

filepack create --deny <LINT> [DIRECTORY]

要查看所有 lints:

filepack lints

请随时提交 issue 以请求新的 lint。

创建带有元数据的包

要创建一个包含描述该包的元数据的包,请在包目录中创建一个名为 metadata.yaml 的文件,例如:

title: The Necronomicon
creator: Abdul Alhazred
description: >
  The Old Ones, their history, and the rites by which they may be summoned.
language: la

然后,创建该包:

filepack create [DIRECTORY]

如果存在元数据,将对其进行验证并写入 metadata.filemeta, 同时将其包含在 manifest.filepack 中。

请参阅 metadata 以获取完整的架构。

签名软件包

在创建软件包时对其进行签名,首先创建一个新的公钥和私钥 对:

filepack keygen

这会在 filepack 密钥链目录中创建一个新的公钥,master.public,以及对应的私钥, master.private

filepack info 命令将打印 filepack 数据目录、 密钥链目录以及密钥链目录中任何公钥的路径。

您的公钥可以通过以下方式打印:

filepack key

要签署一个新软件包:

filepack create --sign [DIRECTORY]

要为现有软件包添加签名:

filepack sign

获取包指纹

要打印包的指纹:

filepack fingerprint [DIRECTORY]

发布清单

清单仅包含哈希值而不包含文件内容,因此可以发布在任何位置,包括因技术或法律限制而无法发布内容本身的地方。

验证软件包

软件包内容可通过以下方式验证:

filepack verify [DIRECTORY]

任何多余的文件、缺失的文件或已修改的文件都会导致错误。

此操作会根据清单中存储的哈希值验证文件。

单独运行 filepack verify 可以检测意外的损坏,但无法检测 故意的篡改,因为攻击者可以修改软件包内容, 然后修改清单以使其与修改后的内容匹配,从而通过 验证。

但是,如果清单是从可信来源获取的,验证将 捕获任何修改,无论是故意的还是无意的,即使软件包内容 是从不可信来源获取的。

要检测故意的篡改,您可以将软件包与 已知的良好指纹进行验证:

filepack verify --fingerprint <FINGERPRINT> [DIRECTORY]

这验证了包的内容以及清单是否具有预期的指纹。前者可防止意外损坏,后者可防止故意篡改。

还可以通过检查已知公钥的签名来验证包。只要与公钥对应的私钥受到保护,并且仅为您或您信任的人所知,这也可以防止故意篡改。

要验证包是否具有来自特定公钥的签名:

filepack verify --key <PUBLIC_KEY> [DIRECTORY]

--key 选项可以重复使用,以要求来自多个公钥的签名。

检测意外损坏

使用以下命令创建 filepack 清单:

filepack create <PACKAGE>

这将创建 <PACKAGE>/manifest.filepack

之后,用于根据清单验证该包:

filepack verify <PACKAGE>

由于清单包含加密哈希值,对文件或清单的意外损坏总是会被 filepack verify 检测到。

对于故意的恶意损坏则并非如此,因为攻击者可以修改文件,并用修改后文件的哈希值替换清单中的哈希值。

检测恶意损坏

由于攻击者可以修改文件并用修改后文件的哈希值替换清单中的哈希值,您必须确保清单未被篡改。

这可以通过多种方式实现,例如将清单保存到安全位置、保存包指纹或对包进行签名。

清单

要将清单保存到安全位置,请使用 --manifest 选项将清单保存到包以外的位置:

filepack create <PACKAGE> --manifest <MANIFEST>

然后,根据已保存的清单验证该包:

filepack verify <PACKAGE> --manifest <MANIFEST>

由于清单受到保护,对软件包的任何修改都将被 检测到。其优势在于,不仅任何修改都将被 检测到,还可以检测到哪些文件被修改了。

指纹

在软件包根目录中创建清单:

filepack create <PACKAGE>

打印包指纹:

filepack fingerprint <PACKAGE>

将指纹保存在安全的位置。

然后,根据已保存的指纹验证该包:

filepack verify <PACKAGE> --fingerprint <FINGERPRINT>

由于指纹受到保护,对软件包的任何修改都将被检测到。其优点是您只需保存一个小的文本字符串,但缺点是虽然任何修改都会被检测到,但您无法确定哪些文件已更改。

签名

在软件包根目录中创建清单,并使用您的 master 密钥对其进行签名:

filepack create <PACKAGE> --sign

然后,验证该包及其签名:

filepack verify <PACKAGE> --key master

对包或清单的任何修改都会使签名失效, 并且会被检测到。这具有无需保存要验证的包的 清单或指纹的优势。然而,你需要 生成并妥善保管你的私钥。

确定真实性

要检查由他人创建的包的真实身份,获取他们的 公钥,并验证该包是否包含由该密钥生成的签名:

filepack verify <PACKAGE> --key <KEY>

替代方案与现有技术

filepack 与诸如 shasum 之类的程序具有相同的目的,这些程序会对文件进行哈希处理, 并输出一个包含文件哈希值和路径的文本文件,之后可以使用同一程序来验证文件是否未发生更改。

它们每行输出一个哈希值和路径,以空白字符分隔,主要区别在于它们使用的哈希函数。

以下是一些示例,包含指向实现及其所用哈希函数的链接:

二进制文件哈希函数
b2sumBLAKE2
b3sumBLAKE3
cksfvCRC-32
hashdeep多种
hashdir多种
sha3sumSHA-3
shasumSHA-1SHA-2

CRC-32 不是加密哈希函数,无法用于检测故意修改。同样,SHA-1 曾被认为是加密 哈希函数,但现在已知其不安全。

filepackb3sum 都使用 BLAKE3,这是一种快速、通用的加密 哈希函数。

filepack 还可以创建和验证签名。其他签名和 验证工具包括:

二进制文件简介
gpg通用,OpenPGP 实现
ssh-keygen通用,随 OpenSSH 一起提供
minisign通用
signifiy通用
SignToolWindows 代码签名
codesignmacOS 代码签名
jarsignerJDK 代码签名

SFV 爱好者的 Filepack

如果你打包内容用于分发,与使用 .sfv 文件进行简单的文件验证相比,Filepack 提供了许多优势。

  • Filepack 能够检测意外的损坏和故意的修改, 而 .sfv 文件只能检测意外的损坏。

  • 由于 manifest.filepack 清单包含文件大小,Filepack 可以告诉 用户不仅文件是否已被修改,还可以判断它是否 为空、被截断或过长。

  • Filepack 包具有指纹,一个以 package1… 开头的短文本字符串,保证全局唯一,允许仅通过 指纹来识别和引用包。

  • 指纹可用于验证 manifest.filepack 清单本身 是否未被篡改,无论其来源如何,都能证明包的真实性。

  • 包可以被签名,允许用户通过公钥验证来自打包者的任何包的真实性, 公钥是一个以 public1… 开头的短字符串。

  • 如果包的文件名可能在其他 操作系统或文件系统上引起问题,Filepack 可以发出警告。

  • Filepack 包是 Merkle 树,既针对文件,也针对文件内部。拥有 清单、包指纹或受信任公钥的用户可以增量 流式传输并验证文件,或随机访问并验证文件内容。 此外,通过仅传输文件中损坏的部分,可以检测并恢复错误。

  • 软件包可以包含遵循 filepack 定义的模式(schema)的机器可读元数据,从而允许通过功能丰富的接口对软件包进行搜索、索引和展示。

为什么元数据很重要?

元数据是良好用户体验与糟糕用户体验之间的区别。

在 Netflix 上,电影以吸引人的方式呈现,配有艺术图、标题、演员及其他信息。所有内容都可以在你正在使用的任何设备上播放,流媒体即时启动,搜索实用,推荐引擎会展示你可能喜欢的电影。任何人都可以使用它,无论年龄大小或技术熟练程度如何。

在 BitTorrent 上,电影以晦涩难懂的文字列表形式呈现,标题格式怪异,信息零散或缺失,且没有艺术图。内容可能无法在你的设备上播放,流媒体播放是不可能的,搜索是无结构的,且没有推荐功能。它只能被人口中一小部分且不断减少的人群使用,而邀请某人过来“用 BitTorrent 放松一下”只会换来茫然的眼神,随后当你像操作数字蒸汽机一样摆弄你的 torrent 客户端,拼命寻找想看电影的种子时,你会遭到嘲笑。

这两个系统之间的差异并非源于底层 数据的差异,任何可以想象的内容在 BitTorrent 上均可获取。这是元数据可用性的差异。

Netflix 拥有一个标准化的、机器可读的元数据库,支持 系统几乎每一项功能。BitTorrent 则是一组分散的 文件文件夹集合,其元数据不一致且不完整,系统的 用户体验和功能正是由此直接导致的。

随着由元数据驱动的精致服务日益普及,基于文件 文件夹的系统所能提供的用户体验变得愈发 不可接受且格格不入,无论成本或底层内容的 可用性如何。

Filepack 旨在通过标准化机器可读的元数据来纠正这一问题, 这些元数据可以随文件文件夹内容一起包含。

Filepack 元数据存储在一个文件中,metadata.filemeta,位于 filepack 包的根目录以及 manifest.filepack 归档中,并包含 有关包内容的信息,包中哪些文件 包含包内容及其文件格式,以及谁创建了该 包。此元数据可作为创建丰富的本地和 分布式应用与服务的基础,其用户体验可与 封闭的集中式替代品相媲美并超越之。

设计

清单格式

文件包清单包含验证目录内容所需的所有信息。清单的 package 键是一个目录对象,将文件名映射到目录条目,这些条目本身可以是目录,也可以是文件,如果是文件,则包含文件内容的哈希值以及文件的长度。

文件的长度对于验证并非严格必要,但包含它以便识别被截断的、空的和过长的文件,这可能有助于理解验证失败的原因。

文件内容可以嵌入在 manifest.filepack 归档中的 embedded 键下。

文件哈希

文件内容使用 BLAKE3 进行哈希,采用官方的 Rust 实现。选择 BLAKE3 既是因为其速度,也是因为它 利用了 Merkle 树结构。Merkle 树允许进行经过验证的文件 流式传输和子范围包含证明,这两者在文件 哈希和验证的上下文中似乎都有用。

签名

Filepack 允许对清单的内容创建 Ed25519 签名,从而承诺清单所覆盖的目录的内容。签名是对包含清单的规范化 CBOR 序列化的“指纹”哈希的声明进行的。这使得签名独立于清单格式,避免了清单 JSON 规范化问题,避免了由于在清单本身中包含签名而导致的哈希循环,并允许证明签名所覆盖的文件的存在。

指纹

包指纹是清单内容的规范化 CBOR 序列化的 BLAKE3 哈希。指纹被构建为唯一,这意味着两个具有不同内容的不同包不可能具有相同的指纹。

目前,不存在指纹测试向量,最好的文档是代码本身。

特别是,参见:

以及用于编码的 cbor 模块。