git - Git 正在转向新的哈希算法 SHA-256 但为什么 git 社区选择了 SHA-256
问题描述
我刚刚从这个HN-post了解到 git 正在转向新的哈希算法( from SHA-1
to SHA-256
)
我想知道什么SHA-256
最适合 git 的用例。是否有任何/许多强有力的技术原因,或者SHA-256
受欢迎程度是否可能是一个重要因素?(我在猜测)查看https://en.wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions页面,我发现存在许多现代和较旧的替代方案。其中一些比SHA-256
(例如https://crypto.stackexchange.com/q/26336)性能更高(如果不是更多,几乎相同)和更强
解决方案
我在2018 年 8 月的“为什么 Git 不使用更现代的 SHA? ”中介绍了这一举措
Brian M. Carlson在此讨论了原因:
我已经实现并测试了以下算法,所有这些算法都是 256 位的(按字母顺序排列):
- BLAKE2b (libb2)
- BLAKE2bp (libb2)
- KangarooTwelve(从 Keccak 代码包导入)
- SHA-256 (OpenSSL)
- SHA-512/256 (OpenSSL)
- SHA3-256 (OpenSSL)
- SHAKE128 (OpenSSL)
我还拒绝了其他一些候选人。
我找不到任何 SHA256×16 的参考或实现,所以我没有实现它。
我没有考虑 SHAKE256,因为它几乎在所有特性(包括性能)上都与 SHA3-256 相同。SHA-256 和 SHA-512/256
这些是 256 位大小的 32 位和 64 位 SHA-2 算法。
我注意到以下好处:
- 这两种算法都是众所周知的并且经过大量分析。
- 两种算法都提供 256 位原像电阻。
概括
实现可用性最高的算法是 SHA-256、SHA3-256、BLAKE2b 和 SHAKE128。
就命令行可用性而言,BLAKE2b、SHA-256、SHA-512/256 和 SHA3-256 应该在不久的将来在相当小的 Debian、Ubuntu 或 Fedora 安装中可用。
就安全性而言,最保守的选择似乎是 SHA-256、SHA-512/256 和 SHA3-256。
性能优胜者是未加速的 BLAKE2b 和加速的 SHA-256。
建议的结论基于:
人气
在其他条件相同的情况下,我们应该偏向于最广泛使用并推荐给新项目的任何东西。
硬件加速
唯一广泛部署的硬件加速是针对SHA-2 系列的 SHA-1 和 SHA-256 ,但值得注意的是较新的 SHA-3 系列(2015 年发布)中没有。
年龄
与“流行度”类似,将事物偏向于已经存在一段时间的哈希似乎更好,即选择 SHA-3 还为时过早。
哈希转换计划一旦实施,将来也更容易切换到其他东西,所以我们不应该急于选择一些更新的哈希,因为我们需要永远保留它,我们总是可以做另一个再过10-15年的过渡期。
结果:提交 0ed8d8d,Git v2.19.0-rc0,2018 年 8 月 4 日。
SHA-256 有许多优点:
它已经存在了一段时间,被广泛使用,几乎每个加密库(OpenSSL、mbedTLS、CryptoNG、SecureTransport 等)都支持它。
与 SHA1DC 进行比较时,大多数矢量化 SHA-256 实现确实更快,即使没有加速也是如此。
如果我们使用 OpenPGP(或者甚至,我想是 CMS)进行签名,我们将使用 SHA-2,因此当我们的安全性依赖于两个单独的算法时,它们中的任何一个都是没有意义的当我们只能依靠一个人时,一个人可能会破坏安全性。
所以 SHA-256 就是这样。
这个想法仍然存在:SHA1 的任何概念都将从 Git 代码库中删除,并由通用的“散列”变量代替。
明天,该散列将是 SHA2,但代码将来会支持其他散列。
正如Linus Torvalds 巧妙地指出的那样(强调我的):
老实说,可观测宇宙中的粒子数量在 2**256 的数量级上。这是一个非常非常大的数字。
不要让代码库变得比它需要的更复杂。
做出明智的技术决定,并说“256 位很多”。工程学和理论之间的区别在于工程学会做出权衡。
好的软件是精心设计的,而不是理论化的。另外,我建议 git 默认为“
abbrev-commit=40
”,这样默认情况下没有人真正看到新位。
因此,使用“[0-9a-f]{40}
”作为哈希模式的 perl 脚本等只会默默地继续工作。因为向后兼容性很重要 (*)
(*) 而且 2**160 仍然是一个很大的数字,并没有真正成为一个实际问题,而 SHA1DC 很可能是未来十年或更长时间的一个很好的哈希值。
(SHA1DC,用于“Detecting(?) Collision”,在碰撞攻击shattered.io实例之后于 2017 年初进行了讨论:参见提交 28dc98e,Git v2.13.0-rc0,2017 年 3 月,来自Jeff King和“ Hash collision in混帐")
查看更多内容Documentation/technical/hash-function-transition.txt
一次可以在一个本地存储库中转换到 SHA-256。
一个。不需要任何其他方采取任何行动。
湾。SHA-256 存储库可以与 SHA-1 Git 服务器通信(推送/获取)。
C。用户可以交替使用对象的 SHA-1 和 SHA-256 标识符(请参阅下面的“命令行上的对象名称”)。
d。新的签名对象使用比 SHA-1 更强大的散列函数来保证其安全性。
Git 2.27(2020 年第二季度)和它的git fast-import --rewrite-submodules-from/to=<name>:<file>
参见提交1BDCA81,提交D9DB599,提交11D8EF3,提交ABE0CC5 ,提交DDDDDF8D,提交42D4E1D ,COMMIT E02A714, COMMIT EFA7AE3,COMMIT 3C93331A,提交3C9331A,提交8B8F718,提交CFE 3917 ,提交3917 ,commit Bff5517,154.a,8d.1a, 8dd.1a,8dd.a,8dd.1a,8dd.a,8dd.a,a,8dd.1a,8dd.1a,8dd.1a,8dd.a,y.a ,y.a,y.1a,y.a,y.a,y.a,.an,。a 192b517,提交 9412759,提交 61e2a70,提交 dadacf1,提交 768e30e,提交 2078991(2020 年 2 月 22 日),作者:brian m。卡尔森 ( bk2204
) .
(由Junio C Hamano 合并 -- gitster
--在提交 f8cb64e中,2020 年 3 月 27 日)
fast-import
: 添加重写子模块的选项签字人:brian m. 卡尔森
当使用子模块将存储库从一种哈希算法转换为另一种时,有必要将子模块从旧算法重写为新算法,因为只有对子模块的引用,而不是它们的内容,才会写入快速导出流。在不重写子模块的情况下,当遇到另一个算法中
的子模块时,快速导入会失败并出现“ ”错误。Invalid dataref
添加一对选项
--rewrite-submodules-from
和,它们在处理子模块时分别--rewrite-submodules-to
采用由fast-export
和产生的标记列表。 使用这些标记将子模块提交从旧算法映射到新算法。fast-import
我们将标记读入两个对应的结构
mark_set
对象,然后使用哈希表执行从旧到新的映射。这让我们可以重用在其他地方使用的相同的标记解析代码,并允许我们根据它们的 ID 有效地读取和匹配标记,因为不需要对标记文件进行排序。请注意,因为我们使用一个
khash
表作为对象 ID,并且该表复制 struct 的值object_id
而不是引用它们,所以有必要将object_id
我们用于在表中插入和查找的 struct 值归零。否则,我们最终会得到不匹配的 SHA-1 值,因为任何堆栈垃圾都可能留在未使用的区域中。
该git fast-import
文档现在包括:
子模块重写
--rewrite-submodules-from=<name>:<file> --rewrite-submodules-to=<name>:<file>
<name>
将from 中使用的值重写为<file>
to 中使用的子模块的对象 ID<file>
。标记应该是由 创建的
,并且标记应该是在导入同一个子模块时创建的。from
git fast-export
to
git fast-import
<name>
可以是任何不包含冒号字符的任意字符串,但在指定相应标记时,两个选项必须使用相同的值。
可以为多个子模块指定不同的值。不成对使用这些选项是错误的。这些选项主要在将存储库从一种哈希算法转换为另一种时有用;没有它们,如果遇到子模块,快速导入将失败,因为它无法将对象 ID 写入新的哈希算法。
和:
commit
: 使用 SHA-256 的预期签名标头签字人:brian m. 卡尔森
过渡计划预计我们将允许在一次提交中使用多种算法的签名。
为了做到这一点,我们需要为每个算法使用不同的标头,以便计算签名的数据是显而易见的。转换计划指定我们应该使用“
gpgsig-sha256
”,因此连接提交代码以便它可以编写和解析当前算法,并且可以在创建新提交时删除任何算法的标头。
添加测试以确保我们使用正确的标头进行编写并且git fsck
不会拒绝这些提交。
注意:最后的快速导入进化有一个讨厌的副作用:“ git fast-import
” (man)在使用许多标记时浪费了大量内存。
这应该通过 Git 2.30(2020 年第一季度)修复
请参阅Jeff King ( ) 的提交 3f018ec(2020 年 10 月 15 日)。(由Junio C Hamano 合并 -- --在cd47bbe 提交中,2020 年 11 月 2 日)peff
gitster
fast-import
:修复标记存储的过度分配报告人:Sergey Brester
签字人:Jeff King
快速导入将其标记存储在由结构组成的类似 trie 的
mark_set
结构中。
(Trie:数字树)
每个结构都有固定的大小(1024)。如果我们的 id 号太大而无法放入结构中,那么我们分配一个新的结构,它将 id 号移动 10 位。我们原来的 struct 成为这个新层的子节点,新的 struct 成为 trie 的顶层。该方案被ddddf8d7e2 破坏(“
fast-import
:允许读取多个标记文件”,2020-02-22,Git v2.27.0-rc0 --合并在批次 #2中列出)。在此之前,我们有一个顶级“marks”指针,并且通过将新的顶级结构分配给“marks”来实现下推。但是在那次提交之后,insert_mark()
需要一个指向mark_set,
而不是使用全局“标记”的指针。它在下推期间继续分配给全局“标记”变量,这是错误的,原因有两个:
- 我们添加了一个
option_rewrite_submodules()
使用单独标记集的调用;在这里按下“标记”是完全错误的。我们会破坏“标记”集,并且我们无法正确存储任何 id 超过 1024 的子模块映射。- 其他呼叫者通过了“标记”,但下推仍然是错误的。在
read_mark_file()
中,我们将指向 的指针mark_set
作为参数。因此,即使insert_mark()
正在更新全局“标记”,我们所拥有的本地指针也read_mark_file()
没有更新。结果,我们会在需要时添加一个新关卡,但是下一次调用insert_mark()
就看不到它了!然后它会分配一个新的层,它也不会被看到,等等。查找丢失的层显然是行不通的,但在我们到达任何查找阶段之前,我们通常会耗尽内存并死掉。我们的测试没有注意到这两种情况,因为它们没有足够的标记来触发下推行为。t9304 中的新测试涵盖了这两种情况(没有这个补丁就会失败)。
insert_mark()
我们可以通过获取集合顶层的指针来解决这个问题。然后我们的下推可以以调用者实际看到的方式分配给它。请注意option_rewrite_submodules()
. 我们的调用read_mark_file()
可能会修改我们的顶级 set 指针,因此我们必须等到它返回后才能将其值分配给string_list
.
推荐阅读
- java - Stream.forEach() 中的 `return;` 将控制中断到哪里?
- r - glmer() 以一个因子水平作为基线收敛,但在将基线重新调整到另一个水平时无法收敛
- java - Spring Boot MVC - 不支持请求方法“POST”
- python - 在生成梯度下降函数和成本方面需要帮助
- ios - 每次我尝试构建应用程序时,nodemediaclient 都会收到此错误,反应原生 IOS
- parameters - 为什么以及何时我要在 Lua 中使用 os.exit() 函数的参数“代码”
- azure - 微软 Azure 笔记本
- flutter - 如何在我的设备上运行初始应用程序测试?
- python - 如何从 PyqtChart 中的条形图中获取值
- flutter - 如何从任何一个访问失败的值?