为啥 Git 不使用更现代的 SHA?

Posted

技术标签:

【中文标题】为啥 Git 不使用更现代的 SHA?【英文标题】:Why doesn't Git use more modern SHA?为什么 Git 不使用更现代的 SHA? 【发布时间】:2015-03-25 09:27:50 【问题描述】:

我了解到 Git 使用 SHA-1 摘要作为修订的 ID。为什么它不使用更现代的 SHA 版本?

【问题讨论】:

性能是我能想到的唯一原因,SHA-1 比 SHA-2 快。我个人认为这是一个糟糕的决定,因为 SHA-1 的抗碰撞能力相当弱。 ***.com/questions/9392365/… - 不完全匹配,但涵盖了相似的领域 这在 2006 年的 git 邮件列表中进行了讨论。请参阅 the whole thread。总而言之,Linus 当时说 SHA-1 只需要足够独特,这样就不会发生冲突,事实就是如此。 SHA-1 不是 git 的安全功能。 “任何只是盲目地接受来自不受信任来源的数据的人都会以许多其他方式被搞砸,以至于哈希攻击甚至都没有引起人们的注意。” -- Linus 更新:SHA-1 冲突现在在野外shattered.it 2018 年第一季度:支持替代 SHA 的努力正在进行中:请参阅 my answer below 【参考方案1】:

为什么不使用更现代的 SHA 版本?

十二月。 2017:会的。 Git 2.16(2018 年第一季度)是第一个说明和实现这一意图的版本。

注意:请参阅下面的 Git 2.19:它将是 SHA-256

Git 2.16 将提出一个基础架构来定义在 Git 中使用什么哈希函数,并将开始努力在各种代码路径中进行探测。

见commit c250e02(2017 年 11 月 28 日)Ramsay Jones (``)。 请参阅brian m. carlson (bk2204) 的commit eb0ccfd、commit 78a6766、commit f50e766、commit abade65(2017 年 11 月 12 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 721cc43,2017 年 12 月 13 日)


添加表示哈希算法的结构

由于将来我们希望支持额外的哈希算法,请添加 a structure that represents a hash algorithm and all the data that must go along with it。 添加constant to allow easy enumeration of hash algorithms。 实现function typedefs 以创建可供任何哈希算法使用的抽象 API,以及符合此 API 的现有 SHA1 函数的包装器。

公开value for hex size as well as binary size。 虽然一个永远是另一个的两倍,但这两个值都非常使用 普遍存在于整个代码库中,同时提供这两种方法可以提高可读性。

不要在空对象 ID 的哈希算法结构中包含条目。 由于此值全为零,因此可以使用任何大小合适的全零对象 ID,并且无需在每个哈希的基础上存储给定的 ID。

当前的哈希函数转换计划设想我们将接受来自用户的输入,该输入可能采用 SHA-1 或 NewHash 格式。 由于我们无法知道用户提供了哪个值,请添加 constant representing the unknown algorithm 以指示我们必须查找正确的值。


将哈希算法支持与 repo 设置集成

在未来的 Git 版本中,我们计划支持额外的哈希 算法。 将哈希算法的枚举与存储库设置相结合,并在结构存储库中存储指向枚举数据的指针。 当然,we currently only support SHA-1, so hard-code this value in read_repository_format。 以后,我们会从配置中枚举这个值。

添加a constant, the_hash_algo,它指向存储库全局中的hash_algo结构指针。 请注意,这是用于将数据序列化到磁盘的哈希值,而不是用于向用户显示项目的哈希值。 过渡计划预计这些可能会有所不同。 我们可以在未来添加一个额外的元素(例如,ui_hash_algo)来提供这种情况。


2018 年 8 月更新,对于 Git 2.19(2018 年第三季度),Git 似乎选择 SHA-256 作为 NewHash。

见commit 0ed8d8d(2018 年 8 月 4 日)Jonathan Nieder (artagnon)。 请参阅 Ævar Arnfjörð Bjarmason (avar) 的 commit 13f5e09(2018 年 7 月 25 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 34f2297,2018 年 8 月 20 日)

doc hash-function-transition: 选择 SHA-256 作为 NewHash

从安全角度来看,似乎 SHA-256、BLAKE2、SHA3-256、K12 等都被认为具有相似的安全属性。 从安全的角度来看,所有这些都是不错的选择。

SHA-256 有很多优点:

它已经存在了一段时间,被广泛使用,几乎每个加密库(OpenSSL、mbedTLS、CryptoNG、SecureTransport 等)都支持它。

与 SHA1DC 相比,大多数矢量化 SHA-256 实现确实更快,即使没有加速。

如果我们使用 OpenPGP(甚至,我想是 CMS)进行签名,我们将使用 SHA-2,因此让我们的安全性依赖于两个独立的当我们可以只依赖一个时,其中任何一个都可能破坏安全性的算法。

所以是 SHA-256。 更新 hash-function-transition 设计文档来说明。

在这个补丁之后,没有剩余的字符串实例 “NewHash”,除了从 2008 年开始与 a variable name in t/t9700/test.pl 无关的用法。


您可以通过 Git 2.20(2018 年第四季度)看到向 SHA 256 的过渡:

见commit 0d7c419、commit dda6346、commit eccb5a5、commit 93eb00f、commit d8a3a69、commit fbd0e37、commit f690b6b、commit 49d1660、commit 268babd、@9876545356、@、@987654356 @、commit 2f0c9e9、commit 825544a(2018 年 10 月 15 日)brian m. carlson (bk2204)。 请参阅 SZEDER Gábor (szeder) 的 commit 6afedba(2018 年 10 月 15 日)。(由 Junio C Hamano -- gitster -- 合并于 commit d829d49,2018 年 10 月 30 日)

替换硬编码常量

GIT_MAX_HEXSZ 的引用替换几个基于 40 的常量或 the_hash_algo,视情况而定。 将GIT_SHA1_HEXSZ 的所有用法转换为the_hash_algo,以便它们 适用于任何给定的哈希长度。 而不是使用硬编码常量来表示十六进制对象 ID 的大小, 切换到使用来自parse_oid_hex 的计算指针,该指针指向之后 解析后的对象 ID。

GIT_SHA1_HEXSZ 被 Git 2.22(2019 年第二季度)和 commit d4e568b 进一步删除/替换。


Git 2.21(2019 年第一季度)继续这种过渡,它添加了 sha-256 哈希并将其插入代码以允许使用“NewHash”构建 Git。

参见commit 4b4e291、commit 27dc04c、commit 13eeedb、commit c166599、commit 37649b7、commit a2ce0a7、commit 50c817e、commit 9a3a0ff、commit 9a3a0ff、commit 0dab712、@987654374 和@(11 月 14 日) commit 2f90b9d、commit 1ccf07c(2018 年 10 月 22 日)brian m. carlson (bk2204)。(由 Junio C Hamano -- gitster -- 合并于 commit 33e4ae9,2019 年 1 月 29 日)

添加 SHA-256 支持的基本实现(2019 年 2 月)

SHA-1 很弱,我们需要转换到新的哈希函数。 一段时间以来,我们将这个新功能称为 NewHash。 最近,我们决定选择 SHA-256 作为 NewHash。The reasons behind the choice of SHA-256 are outlined in this thread 并在哈希函数转换文档的提交历史记录中。

添加基于 libtomcrypt 的 SHA-256 的基本实现,位于 公有领域。 对其进行优化和重组以符合我们的编码标准。 从 SHA-1 块实现中提取更新和最终函数,因为我们在所有编译器中都正确知道这些函数。此实现比 SHA-1 慢,但会在未来的提交中引入更高性能的实现。

在哈希算法列表中连接 SHA-256,并添加一个测试 算法正常工作。

请注意,使用此补丁后,仍然无法在 Git 中切换为使用 SHA-256。 需要额外的补丁来准备代码以处理更大的哈希算法,并且需要进一步的测试修复。

hash:使用 OpenSSL 添加 SHA-256 实现

我们已经有可用于 SHA-1 的 OpenSSL 例程,因此请添加例程 也适用于 SHA-256。

在 Core i7-6600U 上,此 SHA-256 实现优于 SHA1DC SHA-1 实现:

SHA-1: 157 MiB/s (64 byte chunks); 337 MiB/s (16 KiB chunks)
SHA-256: 165 MiB/s (64 byte chunks); 408 MiB/s (16 KiB chunks)

sha256:使用 libgcrypt 添加一个 SHA-256 实现

一般来说,用汇编语言编写的加密例程比 C 语言获得更好的性能,对于 SHA-256 也是如此。 此外,大多数 Linux 发行版无法分发链接到的 Git 出于许可原因的 OpenSSL。

大多数带有 GnuPG 的系统也会有 libgcrypt,因为它是 GnuPG 的依赖项。libgcrypt 对于几 KiB 或更大的消息也比 SHA1DC 实现更快。

作为比较,在 Core i7-6600U 上,此实现处理 16 KiB 以 355 MiB/s 的速度处理块,而 SHA1DC 以 337 处理等效块 MiB/s。

此外,libgcrypt 在 LGPL 2.1 下获得许可,即 与 GPL 兼容。添加一个使用 SHA-256 的实现 libgcrypt.


Git 2.24(2019 年第四季度)继续升级

见commit aaa95df、commit be8e172、commit 3f34d70、commit fc06be3、commit 69fa337、commit 3a4d7aa、commit e0cb7cd、commit 8d4d86b、commit f6ca67d、@987654391、@、@987654391 @、commit 95518fa、commit e84f357、commit fe9fec4、commit 976ff7e、commit 703d2d4、commit 9d958cc、commit 7962e04、commit fee4930(2019 年 8 月 18 日)by brian m. carlson (bk2204)。由Junio C Hamano -- gitster -- 在commit 676278f 中合并,2019 年 10 月 11 日)

不要使用GIT_SHA1_HEXSZ 和硬编码常量,而是切换到 使用the_hash_algo


在 Git 2.26(2020 年第一季度)中,测试脚本已经为对象名称使用 SHA-256 的那一天做好了准备。

见commit 277eb5a、commit 44b6c05、commit 7a868c5、commit 1b8f39f、commit a8c17e3、commit 8320722、commit 74ad99b、commit ba1be1a、commit cba472d、@9876544413、@、@987654414 @、commit 1d86c8f、commit 525a7f1、commit 7a1bcb2、commit cb78f4f、commit 717c939、commit 08a9dd8、commit 215b60b、commit 194264c(2019 年 12 月 21 日)by brian m. carlson (bk2204)。由Junio C Hamano -- gitster -- 合并于commit f52ab33,2020 年 2 月 5 日)

例子:

t4204:使哈希大小独立

签字人:brian m.卡尔森

使用$OID_REGEX 而不是硬编码的正则表达式。

所以,不要使用:

grep "^[a-f0-9]\40\ $(git rev-parse HEAD)$" output

测试正在使用

grep "^$OID_REGEX $(git rev-parse HEAD)$" output

OID_REGEX 来自brian m. carlson (bk2204) 的commit bdee9cd(2018 年5 月13 日)。(由Junio C Hamano -- gitster -- 在commit 9472b13 中合并,2018 年5 月30 日,Git v2.18.0-rc0)

t/test-lib:介绍OID_REGEX

签字人:brian m.卡尔森

目前我们有一个变量$_x40,,其中包含一个匹配完整的 40 个字符的十六进制常量的正则表达式。

但是,对于NewHash,我们将拥有超过 40 个字符的对象 ID。

在这种情况下,$_x40 将是一个令人困惑的名称。

创建一个$OID_REGEX 变量,该变量将始终反映匹配适当对象 ID 的正则表达式,而不管当前哈希的长度如何。

而且,还在测试中:

见commit f303765、commit edf0424、commit 5db24dc、commit d341e08、commit 88ed241、commit 48c10cc、commit f7ae8e6、commit e70649b、commit a30f93b、@9876544442、@、@987654444 @、commit dfa5f53、commit f743e8f、commit 72f936b、commit 5df0f11、commit 07877f3、commit 6025e89、commit 7b1a182、commit 94db7e3、commit db12505(2020 年 2 月 7 日)@987654@4(2020 年 2 月 17 日由 Junio C Hamano -- gitster -- 在 commit 5af345a 中合并)

t5703: 使用 SHA-256 进行测试

签字人:brian m.卡尔森

此测试使用长度为 40 个十六进制字符的对象 ID,当使用 SHA-256 作为哈希运行时,导致测试不仅无法通过,而且会挂起。

使用test_oid_inittest_oid 将此值更改为固定的虚拟对象ID。

此外,请确保我们使用带有字段的剪切而不是固定长度来提取适当长度的对象 ID。


一些代码路径被赋予了一个存储库实例作为在存储库中工作的参数,但将 the_repository 实例传递给了它的被调用者,这已被 Git 2.26(2020 年第一季度)(在某种程度上)清理了。

参见commit b98d188、commit 2dcde20、commit 7ad5c44、commit c8123e7、commit 5ec9b8a、commit a651946、commit eb999b3(2020 年 1 月 30 日)Matheus Tavares (matheustavares)。(合并Junio C Hamano -- gitster --commit 78e67cd,2020 年 2 月 14 日)

sha1-file:允许check_object_signature() 处理任何回购

签字人:Matheus Tavares

check_object_signature() 的某些调用者可以在任意存储库上工作,但存储库不会传递给此函数。相反,the_repository 始终在内部使用。 要修复可能的不一致,请允许函数接收结构存储库并让这些调用者传递正在处理的存储库。

基于:

sha1-file:将@​​987654541@ 传递给hash_object_file()

签字人:Matheus Tavares

通过引入git_hash_algo 参数,允许hash_object_file() 处理任意repos。更改在其范围内具有结构存储库指针的调用者,以从所述存储库传递git_hash_algo。 对于所有其他调用者,请传递 the_hash_algo,它已在 hash_object_file() 内部使用。 此功能将在以下补丁中使用,以使 check_object_signature() 能够在任意 repos 上工作(反过来,这将用于修复 object.c:parse_object() 的不一致)。

【讨论】:

另外,不要忘记 Git v2.13.0 和后来默认迁移到强化的 SHA-1 实现,它不容易受到 SHAttered 攻击。见***.com/a/43355918/6309 1:谷歌在 2017 年 2 月产生了碰撞shattered.io(估计成本为 110,000 美元) 2:南洋理工大学在 2019 年 1 月产生了碰撞sha-mbles.github.io(估计成本在 $11k - $45k 之间) Git 是时候超越 SHA1 了 @bristweb “现在是 Git 超越 SHA1 的时候了”:我同意,并且对于 Git 2.25(今天发布),这一举措是合二为一的。 git rev-parse 现在能够打印将使用的哈希值:***.com/a/58862319/6309。并且空树有一个新的 SHA2 id:***.com/a/9766506/6309【参考方案2】:

现在有一个transition plan 来表示更强的哈希,因此看起来将来它将使用比 SHA-1 更现代的哈希。来自current transition plan:

考虑中的一些哈希值是 SHA-256、SHA-512/256、SHA-256x16、K12 和 BLAKE2bp-256

【讨论】:

【参考方案3】:

这是关于从 SHA1 迁移到 Mercurial 的紧迫性的讨论,但它也适用于 Git:https://www.mercurial-scm.org/wiki/mpm/SHA1

简而言之:如果您今天不是非常勤奋,那么您的漏洞比 sha1 严重得多。但尽管如此,Mercurial 早在 10 多年前就开始为从 sha1 迁移做准备。

多年来,为 SHA1 的继任者改造 Mercurial 的数据结构和协议的工作一直在进行。 10 多年前,随着 RevlogNG 的引入,在 Mercurial 0.9 中,我们为 revlog 结构中的更大哈希分配了存储空间。最近引入的 bundle2 格式支持通过网络交换不同的散列类型。唯一剩下的部分是替换函数的选择和向后兼容策略的选择。

如果 git 没有在 Mercurial 之前从 sha1 迁移出去,您总是可以通过使用 hg-git 保持本地 Mercurial 镜像来增加另一个安全级别。

【讨论】:

【参考方案4】:

更新:上面的问题和这个答案是从 2015 年开始的。从那时起,谷歌宣布了第一次 SHA-1 冲突:https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html


显然我只能从外部推测为什么 Git 继续使用 SHA-1,但这些可能是其中的原因:

    Git 是 Linus Torvald 的创作,Linus 目前显然不想用另一种散列算法替换 SHA-1。 他合理地声称,针对 Git 的成功基于 SHA-1 冲突的攻击比实现冲突本身要困难得多,并且考虑到 SHA-1 比它应有的弱,没有完全破坏,这使得它实质上是至少在今天远非可行的攻击。此外,他指出,如果碰撞对象晚于现有对象到达,“成功”攻击将收效甚微,因为后者将被假定与有效对象相同并被忽略(尽管其他人指出反之亦然)。 更改软件既费时又容易出错,尤其是当存在必须迁移的现有基础架构和基于现有协议的数据时。即使是那些生产以加密安全为系统唯一要点的软件和硬件产品的人,也仍在从 SHA-1 和其他地方的弱算法迁移的过程中。试想一下所有这些硬编码的unsigned char[20] 缓冲区;-),一开始就为加密敏捷性编程要容易得多,而不是以后再对其进行改造。 SHA-1 的性能优于各种 SHA-2 哈希(现在可能不会成为交易破坏者,但可能是 10 年前的症结所在),并且 SHA- 的存储大小2 更大。

一些链接:

*** question on what would happen if a collision did occur in Git Newsgroup post showing a brief comment from Linus on the subject a couple of months after the main SHA-1 weakness became known in 2005 A thread discussing the weakness and possible move to sha-256 (with replies from Linus) in 2006 NIST statement on SHA-1 deprecation and recommending "to transition rapidly to the stronger SHA-2 family of hash functions"

我个人的观点是,虽然实际的攻击可能需要一段时间,但即使它们确实发生了,人们最初也可能会通过改变哈希算法本身以外的方式来缓解它们,如果你确实关心你的安全性在选择算法时应该谨慎行事,并不断提高安全强度,因为攻击者的能力也只会朝着一个方向发展,因此将 Git 作为榜样是不明智的,尤其是作为它使用 SHA-1 的目的并不是为了加密安全。

【讨论】:

"你可以让那些试图作恶的人。他们不会成功。N̶o̶b̶o̶d̶y̶̶h̶a̶s̶̶b̶e̶e̶n̶̶a̶b̶l̶e̶̶t̶o̶̶b̶r̶e̶a̶k̶̶S̶H̶A̶-̶1̶,但重点是 SHA-1,就 Git 而言,甚至不是安全功能。它纯粹是一种一致性检查。” -Linus Torvalds Git 的哈希值必须是安全的,以便人们在其代码上放置的安全签名来验证任何内容。这些签名签署了这些哈希的巨大树。如果该树的某个分支发生冲突,则可以在签名通过时插入恶意代码。 Git 现在被广泛使用。需要进行哈希升级。 鉴于“破碎”需要考虑两件事:1. SHA-1 使用。 - SHA-1 用作美化校验和以检查意外损坏。 - SHA-1 用作生成器函数,以提供(有点小)方便的十六进制数来指定其内容可寻址存储中的对象(即:美化文件名生成器)。 - 它是负责安全性的签名提交(即:公钥密码签名。不是 sha-1) 2.可行性——经过大量的 GPU 时间,谷歌已经成功地生成了一对块系列。 - 两者都哈希到相同的 SHA-1 总和(这就是冲突) - 它们完全是垃圾(这将是 hard 来证明为什么你的提交在中间有一个巨大的二进制垃圾块)。 - 破碎的演示依赖于有一种方法来呈现不同的行为,具体取决于存在的随机二进制垃圾。使用 PDF(其背后隐藏着嵌入脚本语言)可以做到这一点。在纯源代码上这将变得更加困难(想想Underhanded C Contest) @DrYak For 2:假设您正在跟踪带有注释字段的 Photoshop 文档。或其他带有元标记的媒体文件。这是游戏开发中的典型案例。但通常不会在每次更改时检查它们:如果提交消息显示“优化大小”,​​为什么还要检查元标记?

以上是关于为啥 Git 不使用更现代的 SHA?的主要内容,如果未能解决你的问题,请参考以下文章

为啥现代 JavaScript 框架不鼓励与 DOM 直接交互

为啥在现代浏览器中禁用流水线?

Git 或 Hg 或任何现代 VCS 中的 WebDAV 自动版本控制

更现代的方式来动态创建 HTML 内容?

现代软件工程 第二章 作业 2 学习git用法与心得——孙雪莹

Emacs 开发者讨论如何构建更“现代”的 Emacs