OPAM 仓库签名

注意 (2016 年 9 月):OCaml 2016 研讨会更新的提案 已发布,包括原型实现的链接。

这是一个关于 OPAM 仓库签名的初步提案。欢迎在 platform 邮件列表 上发表评论和讨论。

本提案的目的是实现 OCaml 包的安全分发。如果包开发者对他们的发布进行签名,则无需信任包仓库。

类似于 Python 的 pipRuby 的 gems 或最近的 Haskell 的 hackage,我们将实现 The Upgrade Framework (TUF) 的一种变体。这样做的好处是

  • 它是由比我们更了解这方面内容的人设计的 相关知识
  • 它基于一个包含各种攻击的威胁模型,其中一些攻击并不明显(请参阅 规范 和下文)
  • 它经过了彻底的审查
  • 遵循它可以帮助我们避免许多错误

重要的是,它不强制使用任何特定的加密算法,允许我们使用目前在原生 OCaml 中 可用的算法,并在以后进行演进,例如允许使用 ed25519。

TUF 的目标和 opam 之间存在一些差异,即 TUF 分发包含代码归档文件的目录结构,而 opam 分发关于 OCaml 包的元数据。Opam 将 git(目前是 GitHub)作为一等公民:新包由已经拥有 GitHub 账户的开发者以拉取请求的形式提交。

请注意,TUF 指定了签名层次结构以及传递和检查签名的格式,但在原始文件如何签名方面提供了很大的灵活性:我们可以让包在官方仓库中自动签名,或者由开发者单独签名。或者根据包的不同,两者都允许。

下面,我们尝试解释我们实现的具体细节,主要是一些用户和开发者可见的变化。即使没有事先了解 TUF,也应该能够理解。

我们受到 Haskell 的调整(以及 端到端)使用 git 仓库存储包的 TUF 的启发。签名仓库和签名包是正交的。在本提案中,我们的目标是两者兼顾,但会分别描述它们。

威胁模型

  • 攻击者可以至少破坏包分发系统在线可信密钥中的一个。

  • 攻击者破坏多个密钥可能会同时进行,也可能在一段时间内进行。

  • 攻击者可以在下载仓库、包以及上传新包版本期间响应客户端请求(中间人攻击或服务器入侵)。

  • 攻击者知道一个或多个包的历史版本中存在漏洞,但不知道任何当前版本中是否存在漏洞(明确地不包括针对零日漏洞的防护)。

  • 脱机密钥是安全的,并被安全地存储。

如果攻击者能够导致客户端构建和安装(或保留已安装的)与客户端正在更新的软件的最新版本不同的软件,则认为攻击成功。如果攻击者阻止安装更新,他们希望客户端没有意识到有任何问题。

攻击

  • 任意包:攻击者不应该能够提供他们创建的包来代替用户想要安装的包(通过在包上传、包下载或服务器入侵期间进行中间人攻击)。

  • 回滚攻击:攻击者不应该能够欺骗客户端安装比客户端之前知道可用的软件更旧的软件。

  • 无限冻结攻击:攻击者不应该能够在客户端没有意识到问题的情况下,使用相同的过时元数据响应客户端请求。

  • 无限数据攻击:攻击者不应该能够使用大量数据(极大的文件)来响应客户端请求,从而干扰客户端的系统。

  • 缓慢检索攻击:攻击者不应该能够通过缓慢地响应客户端请求(使得自动更新永远无法完成)来阻止客户端意识到接收更新受到干扰。

  • 额外依赖项攻击:攻击者不应该能够导致客户端下载或安装并非预期依赖项的软件依赖项。

  • 混合匹配攻击:攻击者不应该能够欺骗客户端使用仓库中从未同时存在过的元数据组合。

  • 恶意仓库镜像:不应该能够阻止来自良好镜像的更新。

  • 错误开发者攻击:攻击者不应该能够上传他们并非真正开发者的包的新版本。

信任

密码系统中的一个难题是密钥分发。在 TUF 和本提案中,一组根密钥与 opam 一起分发。这些根密钥中的一个阈值需要(传递地)对用于验证 opam 仓库及其包的所有密钥进行签名。

根密钥

信任根存储在一组根密钥中。对于官方的 opam OCaml 仓库,公钥将存储在 opam 源代码中,允许它验证整个信任链。私钥将由 opam 和仓库维护者持有,并以密码加密的方式安全地脱机存储,最好存储在断开连接的存储设备上。

它们用于使用法定人数对所有顶级密钥进行签名。法定人数具有以下几个优点:

  • 少于法定人数的根密钥被破坏是无害的
  • 即使密钥丢失,它也允许安全地撤销和替换密钥

增加的成本是更多的维护负担,但这仍然很小,因为这些密钥很少使用(仅在密钥即将过期、被破坏或需要添加新的顶级密钥时)。

初始根密钥可以这样分发

  • Louis Gesbert,opam 维护者,OCamlPro
  • Anil Madhavapeddy,主要仓库维护者,OCaml Labs
  • Thomas Gazagnaire,主要仓库维护者,OCaml Labs
  • Grégoire Henry,OCamlPro 密钥保管人
  • OCaml 团队中的某个人?

密钥将设置过期日期,以便每年依次过期一个密钥,为顺利更新预留空间。

对于其他仓库,将有三个选项

  • 无签名(向后兼容?),例如用于本地网络仓库。这应该允许,但需要适当的警告。
  • 首次使用时信任:在首次访问时获取根密钥,让用户确认其指纹,然后完全信任它们。
  • 让用户手动提供根密钥。

端到端签名

这要求最终用户能够验证原始开发者做出的签名。信任链有两个信任路径(其中“→”表示“为…签名”)

  • () 根密钥 → 仓库维护者密钥 → (单独签名) 包委托 + 开发者密钥 → 包文件
  • () 根密钥 → 快照密钥 → (作为快照的一部分进行签名) 包委托 + 开发者密钥 → 包文件

预期包最初可能遵循信任路径,在添加新包时尽可能减少负担和延迟,然后在仓库维护者验证后,通过手动干预将其提升到信任路径。这样,大多数知名且广泛使用的包将获得更高的信任,并且对低信任路径的攻击范围将减少到新的、实验性的或很少使用的包。

仓库签名

这提供了仓库的一致、最新的快照,并防止了与端到端签名完全不同的攻击类别(例如回滚、混合匹配、冻结等)。

这是由一个快照机器人(可能在仓库服务器上运行)自动完成的,它使用快照密钥,该密钥由根密钥直接签名,因此形成了信任链

  • 根密钥 → 快照密钥 → 提交哈希

其中“提交哈希”是仓库 git 仓库的头部(因此是完整仓库状态及其历史记录的有效加密哈希)

仓库维护者 (RM) 密钥

仓库维护者在监控仓库并保证其安全性方面发挥着核心作用,无论是否签名。他们的密钥(在 TUF 框架中称为目标密钥)由根密钥直接签名。由于它们具有很高的安全潜力,为了减少入侵的后果,我们将要求对敏感操作进行法定人数签名

这些密钥存储在 RM 计算机上,并使用密码加密。

快照密钥

此密钥由快照机器人持有,并由根密钥直接签名。它用于保证仓库快照的一致性和新鲜度,并通过对 git 提交哈希和时间戳进行签名来实现。

它在线上运行,并由快照机器人用于自动签名:它的安全性低于 RM 密钥,但潜在风险也较低:它不能直接用于在任何现有软件包中注入恶意代码或元数据。

委托开发人员密钥

这些密钥由软件包开发人员用于端到端签名。新的打包程序(例如,通过 opam-publish 工具)可以根据需要在本地生成它们,并且应该以密码加密的方式存储。它们可以通过拉取请求添加到存储库中,等待签名 (i) 作为快照的一部分(这也防止它们以后被修改,但我们稍后会详细介绍)和 (ii) 由 RM 直接签名。

初始引导

我们需要从某个地方开始,而当前存储库没有签名。一个额外的密钥,initial-bootstrap,将用于保证现有但尚未验证的软件包的完整性。

这是一个一次性密钥,由根密钥签名,然后将被销毁。它允许在没有委托的情况下对软件包进行签名。

信任链和撤销

为了构建信任链,opam 客户端最初在每次更新操作之前下载 keys/root 密钥文件。此文件由根密钥签名,并且客户端可以使用其内置密钥(或上面提到的非官方存储库的方法之一)对其进行验证。它必须由一组已知的根密钥签名,并且包含根、RM、快照和初始引导密钥的完整集合:任何缺少的密钥都被隐式撤销。opam 客户端存储新的根密钥集,并在后续运行中使用它而不是内置的密钥。

开发人员密钥存储在文件中 keys/dev/,自签名,可能由 RM 签名(并且,显然,由快照签名)。它们的验证、删除或替换条件包含在我们对元数据更新的验证中(见下文)。

文件格式和层次结构

签名文件和标签

这些文件遵循 opam 语法:一系列的字段 fieldname: 后跟内容。格式在opam 的文档中详细说明。

opam 中文件的签名是在规范文本表示形式上完成的,遵循以下规则

  • 删除任何现有的 signature: 字段
  • 每行一个字段,以换行符结尾
  • 按字段名称的字典顺序对字段进行排序
  • 在字符串文字中转义换行符、反斜杠和双引号
  • 空格限制为一个,并且仅限于以下情况:字段引导符 fieldname: 后面、列表中的元素之间、大括号选项之前、运算符及其操作数之间
  • 删除注释
  • 删除包含空列表或包含空列表的单元素列表的字段

signature: 字段是一个列表,其元素格式为字符串三元组 [ "<keyid>" "<algorithm>" "<signature>" ]。例如

opam-version: "1.2"
name: "opam"
signature: [
  [ "[email protected]" "RSASSA-PSS" "048b6fb4394148267df..." ]
]

签名标签是 git 注释标签,其内容遵循相同的规则。在这种情况下,格式应包含字段 commit:,指向正在签名和标记的提交哈希。

文件层次结构

存储库格式通过添加以下内容进行了更改

  • 根目录下的 keys/ 目录
  • 委托文件 packages//delegatecompilers/.delegate
  • 位于 packages//./signature 的签名校验和文件

以下是一个示例

repository root /
|--packages/
|  |--pkgname/
|  |  |--delegation                    - signed by developer, repo maintainer
|  |  |--pkgname.version1/
|  |  |  |--opam
|  |  |  |--descr
|  |  |  |--url
|  |  |  `--signature                  - signed by developer1
|  |  `--pkgname.version2/ ...
|  `--pkgname2/ ...
|--compilers/
|  |--version/
|  |  |--version+patch/
|  |  |  |--version+patch.comp
|  |  |  |--version+patch.descr
|  |  |  `--version+patch.signature
|  |  `--version+patch2/ ...
|  |--patch.delegate
|  |--patch2.delegate
|  `--version2/ ...
`--keys/
   |--root
   `--dev/
      |--developer1-email              - signed by developer1,
      `--developer2-email ...            and repo maint. once verified

密钥以不同的文件形式提供,作为字符串三元组 [ [ "keyid" "algo" "key" ] ]keyid 必须不与任何先前定义的密钥冲突,algo 可以是“rsa”,密钥以 PEM 格式编码,稍后可以提供更多选项。

例如,keys/root 文件将具有以下格式

date: "2015-06-04T13:53:00Z"
root-keys: [ [ "keyid" "{expire-date}" "algo" "key" ] ]
snapshot-keys: [ [ "keyid" "algo" "key" ] ]
repository-maintainer-keys: [ [ "keyid" "algo" "key" ] ]

此文件由当前和过去的根密钥签名 - 以允许客户端更新。date: 字段提供了针对回滚攻击的进一步保护:任何客户端都不能接受日期早于其当前日期的文件。日期采用 ISO 8601 标准,UTC 偏移量为 0,如 TUF 中建议的那样。

委托文件

/packages/pkgname/delegation 将软件包 pkgname 版本的所有权委托出去。该文件包含与密钥 ID 关联的版本约束,例如

name: pkgname
delegates: [
  "[email protected]"
  "[email protected]" {>= "1.0"}
]

该文件由以下人员签名

  • 提交它的原始开发人员
  • 或先前拥有所有版本委托的开发人员,用于更改
  • 或由存储库维护人员直接验证委托,并提高信任级别

开发人员委托信任的每个密钥也必须由开发人员签名。

compilers/patch.delegate 文件遵循类似的格式(我们正在考虑更改编译器的层次结构以匹配软件包的层次结构,以简化操作)。

delegates: 字段可以为空:在这种情况下,存储库中不允许使用此名称的任何软件包。这对于标记已弃用软件包的删除很有用,并确保新的不同软件包不会错误地或恶意地使用相同的名称。

软件包签名文件

这些文件保证软件包的完整性:这包括元数据和软件包存档本身(可能在 opam 存储库服务器上镜像,也可能不在)。

除了软件包名称和版本之外,该文件还有一个字段 package-files:,其中包含 packages//. 下的文件列表,以及它们的文件大小(以字节为单位)和一个或多个哈希值(以其类型为前缀),以及一个字段 archive:,其中包含上游存档的相同详细信息。例如

name: pkgname
version: pkgversion
package-files: [
  "opam" {901 [ sha1 "7f9bc3cc8a43bd8047656975bec20b578eb7eed9" md5 "1234567890" ]}
  "descr" {448 [ sha1 "8541f98524d22eeb6dd669f1e9cddef302182333" ]}
  "url" {112 [ sha1 "0a07dd3208baf4726015d656bc916e00cd33732c" ]}
  "files/ocaml.4.02.patch" {17243 [ sha1 "b3995688b9fd6f5ebd0dc4669fc113c631340fde" ]}
]
archive: [ 908460 [ sha1 "ec5642fd2faf3ebd9a28f9de85acce0743e53cc2" ] ]

此文件由以下人员签名

  • 仅在最初由 initial-bootstrap 密钥签名
  • 由委托密钥(即由委托的开发人员)签名
  • 由存储库维护人员的法定人数签名

后者需要在存储库上热修复软件包:存储库维护人员经常需要这样做。仍然需要法定人数,以防止单个 RM 密钥泄露允许对每个软件包进行任意更改。最初不需要法定人数来签名委托,但对于对现有签名委托的任何更改,始终需要法定人数。

编译器签名文件 +.signature 类似,其中 compiler-files 字段包含 +.* 的校验和、相同的字段 archive: 以及一个额外的可选字段 patches:,其中包含此编译器使用的上游补丁的大小和哈希值。

如果无法验证委托或签名,则忽略软件包或编译器。如果任何文件与其大小或哈希值不对应,则也会忽略它。签名文件中未提及的任何文件都将被忽略。

快照和线性

主要快照角色

快照密钥自动将 signed 注释标签添加到存储库服务分支的顶部。此标签包含提交哈希和当前时间戳,有效地确保了整个存储库的新鲜度和一致性。这可以防止混合匹配、回滚和冻结攻击。

在定期检查更新的有效性以及每次更改后,快照机器人都会删除并重新创建 signed 注释标签。

线性

存储库使用 git 提供服务:这意味着,不仅知道最新版本,还知道更改的完整历史记录。这有几个好处,其中包括“免费”的增量下载;以及一种非常简单的签名快照的方法。另一个优点是我们有一个可用的完整 OCaml 实现。

我们在上面提到,我们使用快照签名不仅用于存储库签名,还作为提交的开发人员密钥和委托的初始保证。您可能还注意到,在上面,我们对委托、密钥等进行了单独签名,但没有一个捆绑文件可以确保没有恶意删除任何签名文件。

所有这些问题都通过存储库 git 的线性条件得到解决:快照机器人不仅检查并签名存储库的给定状态,还检查自上次已知的签名状态以来存储库的每个单独更改:补丁必须遵循该 git 提交(同一分支上的后代),并且经过验证以符合某些条件:没有签名文件在没有签名的情况下被删除或更改,等等。

此外,每次客户端更新时也会进行此检查:它稍微弱一些,因为客户端不会持续更新(攻击者可能已在上次更新后重写了提交),但仍然提供了非常好的保证。

开发人员提交并合并的密钥和委托,即使没有 RM 签名,也会作为快照的一部分进行签名:git 和线性条件允许我们保证此委托不会被随后更改或删除,即使没有单独的签名。即使存储库被破坏,攻击者也无法推出破坏这些条件的恶意更新到客户端。

线性不变量是

  1. 任何密钥、委托或软件包版本(签名文件)都不能被删除
  2. 新密钥由自身签名,并可选地由 RM 签名
  3. 新委托由委托密钥签名,并可选地由 RM 签名。签名密钥还必须签名委托密钥
  4. 新软件包或软件包版本由拥有此软件包版本有效委托的有效密钥签名
  5. 密钥只能使用来自先前密钥或 RM 密钥法定人数的签名进行修改
  6. 委托只能使用 RM 密钥法定人数的签名进行修改,或者如果先前没有 RM 签名,则可能由以前的委托密钥(没有版本约束)进行修改
  7. 任何软件包修改都由相应的委托密钥或 RM 密钥法定人数签名

有时需要执行密钥撤销等操作,这些操作不受上述规则的允许。这些操作通过以下其他规则启用,这些规则要求包含更改的提交由存储库维护人员使用注释标签签名

  1. 软件包或软件包版本删除
  2. 开发人员密钥删除(撤销)
  3. 软件包委托删除(通常最好保留空委托)

keys/root文件的更改(可能添加、修改或撤销根密钥、RM密钥和快照密钥)以常规方式进行验证,但需要进行线性检查,因为它决定了RM签名的有效性。由于在检查signed标记之前可能需要此文件,因此它拥有自己的时间戳以防止回滚攻击。

如果线性不变性检查失败

  • 在GitHub存储库上,这会被标记出来,并建议RM不要合并(或完成缺少的标记签名)
  • 在客户端上,更新会被拒绝,并告知用户发生了什么情况(此时存储库可能已被入侵)
  • 在存储库上(由快照机器人检查),更新会被暂停,并立即警告所有存储库维护者。要恢复,需要修改损坏的提交(上次signed标记和master之间)。

涉及的工作和更改

通用

编写用于密钥处理、opam文件签名和验证的模块。

编写带有线性检查的git同步模块。

opam

重写默认的HTTP存储库同步模块以使用git fetch、verify和git pull。这应该是完全透明的,除了

  • 当然,在出现错误的情况下
  • 注册非官方存储库时
  • 对于一些禁用签名的功能(例如源代码固定)的警告(可能只在第一次使用时会很好)

包含默认存储库的公共根密钥,并在~/.opam/repo/name中实现更新密钥的管理。

处理校验和和未重新打包的归档文件的新格式。

允许每个存储库的安全阈值(例如允许所有、仅允许已签名的包、仅允许由已验证的密钥签名的包、仅允许由其已验证的开发者签名的包)。添加本地网络、未签名的存储库应该很容易但明确。除了git之外的后端不会被签名(本地、rsync...)。

opam-publish

生成密钥、处理本地存储的密钥、生成signature文件、处理签名、提交签名、检查委托、提交新委托、请求委托更改(如果RM签署了委托,则可能需要存储库维护者的干预)、删除开发者、删除包。

管理本地密钥。可能包括重新生成和请求撤销。

opam-admin

大多数关于签名和密钥的操作将作为独立的模块实现(以便能够从例如在存储库上工作的Unikernels中使用)。我们还应该使它们能够从opam-admin中使用,用于测试和手动管理。RM也将需要特殊的工具。

  • 获取归档文件(但不再重新打包为pkg+opam.tar.gz
  • 允许存储库维护者执行所有有用的操作(可能在不同的工具中?)
    • 管理他们的密钥
    • 直接列出和签署更改的包
    • 列出和签署等待委托给开发者密钥的委托
    • 验证签名,打印报告
    • 签署标记,包括向现有标记添加签名以满足法定人数
    • 列出在给定分支上等待满足的法定人数
  • 生成已签名的快照(与快照机器人相同,用于测试)

签名机器人

如果我们不想在公开可见的主机(提供存储库的服务)上处理此操作,我们将需要一种机制来获取存储库并将已签名的标记提交回存储库服务器。

通过Mirage Unikernels执行此操作将很酷,并提供良好的隔离。我们可以想象定期运行此过程

  • 从存储库的git(GitHub)获取更改
  • 检查一致性(线性)
  • 生成并签署signed标记
  • 将标记推送到发布存储库

Travis

在RM决定将提交合并到存储库之前,所有安全信息和检查结果都应提供给RM。这意味着在Travis上运行或类似地对存储库的每个拉取请求运行的过程中包含签名和线性检查,并在GitHub跟踪器中显示结果。

这应该避免大多数快照机器人验证失败的情况,使快照机器人陷入停滞(以及任何存储库更新),直到重写错误的提交。

一些更详细的场景

opam initupdate场景

init时,客户端将克隆存储库并到达signed标记,获取并检查关联的keys/root文件,并根据新的密钥集验证signed标记。如果一切顺利,则会注册新的根密钥、RM密钥和快照密钥集。

然后根据信任链检查所有文件的签名,并将它们复制到opam将使用的内部存储库镜像(~/.opam/repo/<name>)。当需要软件包归档文件时,下载操作要么从存储库进行(如果文件已镜像),要么从其上游进行,在这两种情况下,文件大小和上限都是已知的:如果超过预期大小,则停止下载,如果文件与两者都不匹配,则将其删除。

在随后的更新中,该过程与之前相同,除了对现有克隆执行提取操作,并且只有在线性检查通过后(否则更新将中止),存储库才会转发到新的signed标记。

opam-publish场景

  • 开发者第一次运行opam-publish submit时,会生成一个开发者密钥并将其存储在本地。
  • opam-publish submit时,使用密钥对包进行签名,并将签名包含在提交中。
  • 如果密钥已知,并且此包的委托匹配,则一切正常
  • 如果密钥尚未注册,则将其添加到拉取请求中的/keys/dev/中,并进行自签名。
  • 如果包没有委托,则会添加/packages/pkgname/delegation文件,将委托给开发者密钥并由其签名。
  • 如果存在一个不包含作者密钥的现有委托,则需要存储库管理员手动干预。我们仍然可以提交一个拉取请求,将新密钥添加为该包的委托,并要求存储库维护者——或以前的开发者——对其进行签名。

安全分析

我们声称上述措施可以防止

  • 任意包:如果现有包未签名,则不会安装(甚至对用户不可见)。任何人都可以提交新的未认领包(但在当前设置中,仍然需要对存储库具有GitHub写入权限,或绕过GitHub的安全措施)。

  • 回滚攻击:git更新必须遵循当前已知的signed标记。如果快照机器人检测到包被删除,它将拒绝签名,并且客户端会对此进行二次检查。keys/root文件包含一个时间戳。

  • 无限冻结攻击:快照机器人定期使用时间戳签署signed标记,如果客户端接收到的标记比预期年龄旧,它会注意到。

  • 无限数据攻击:我们依赖于git协议,这无法防御无限数据攻击。但是,软件包归档文件的下载(其来源可能是任何镜像)是受保护的。在我们的设置中,攻击范围得到了缓解,因为没有无人参与的更新:程序是手动和交互式运行的,因此用户很可能会注意到。

  • 缓慢检索攻击:同上。

  • 无关依赖项攻击:元数据已签名,如果签名不匹配,则不予接受。

    注意:provides字段——尚未实现,请参阅opam/doc/design中的文档——在这种情况下可能提供了一个载体,通过为流行的包宣传一个替代品。在实现此功能时将采取其他措施,例如要求提供包的签名。

  • 混合匹配攻击:存储库具有线性条件,并且不可能存在部分存储库。

  • 恶意存储库镜像:如果签名不匹配,则拒绝。

  • 错误的开发者攻击:如果开发者不在委托中,则拒绝。

GitHub存储库

GitHub(opam-repository)和签名机器人之间的链接是否特殊?如果此链接上存在MITM,它们可以添加任意的新包,但由于缺少签名,因此只有自定义的Universe。没有现有包可以被更改或删除,否则上面的一致性条件不再成立,签名机器人将不会签名。

当然,访问权限可以被冻结,因此签名机器人不会收到更新,但会继续签署旧的存储库版本。

快照密钥

如果快照密钥被泄露,攻击者能够

  • 添加任意(非已存在的)包,如上所述。

  • 冻结,通过永远使用更新的时间戳重新签署signed标记。

最重要的是,攻击者将无法篡改现有包。即使快照密钥被泄露,这也极大地降低了攻击的可能性。

上述攻击还需要存储库和客户端之间的MITM,或者opam存储库被泄露:在后一种情况下,由于线性检查即使在客户端上也能重现

  • 任何篡改都可以在很短时间内被检测到,并采取措施。
  • 只要开发者检查他们的包是否确实在线,就会检测到冻结。目前,这种情况每天发生几次

然后,存储库只需要重置到攻击之前的状态,git 使得这变得尽可能容易,并且根密钥的持有者将签署一个新的/auth/root,撤销被泄露的快照密钥并引入一个新的密钥。

在快照机器人可以使用新快照密钥重新上线之前——漏洞已被发现并修复——开发者可以在标记过期之前手动签署时间戳标记(例如每天一次),以免阻碍更新。

存储库维护者密钥

存储库维护者功能强大,他们可以修改现有的opam文件并对其进行签名(作为热修复)、为包引入新的委托等。

但是,通过要求对敏感操作进行法定人数,我们将单个RM密钥泄露的范围限制在新开发者密钥或委托的验证上(这应该是RM执行的最常见操作):这使得能够提高新恶意包的安全级别,但在其他方面与仅访问git存储库所能做的事情没有太大区别。

进一步泄露RM密钥的法定人数将允许删除或篡改任何开发者密钥、委托或包:任何这些都相当于能够用受损版本替换任何包。清理将需要替换除根密钥之外的所有密钥,并将存储库重置到任何恶意提交之前的状态。

与TUF的区别

  • 我们使用git
  • 因此免费获得线性
  • 并且已经拥有整个存储库的哈希值
  • TUF提供了一种委托机制,但它既更重也不足以满足我们的需求——直接委托给包。
  • 我们将文件分成更多的小文件,以及每个包一个文件,以适应并很好地扩展使opam成功的基于git的工作流程。原始的TUF将拥有大型的json文件,用于签署大量文件,并且可能发生冲突。开发者和存储库维护者应该能够在没有问题的情况下安全地并行工作。在TUF中签署捆绑包可以额外保证没有文件在没有适当签名的情况下被删除,但这是由git和已签名的标记处理的。
  • 不是单个包含特定开发者签署的所有包的文件,而是每个包一个文件

与Haskell的区别

  • 使用TUF密钥,而不是gpg
  • 端到端签名