我已经在Aug. 2018中的“Why doesn't Git use more modern SHA?”中展示了这个动作
原因是 Brian M. Carlson 的 discussed here:
我已经实现并测试了以下算法,所有这些算法都是
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 加速。
suggested conclusion was based on:
人气
在其他条件相同的情况下,我们应该偏向于
最广泛的用途并推荐用于新项目。
硬件加速
唯一广泛部署的硬件加速是针对 SHA-1 和 SHA-256 from the SHA-2 family,但值得注意的是较新的 SHA-3 系列(2015 年发布)没有。
年龄
类似于“流行度”,将事物偏向哈希似乎更好
这已经有一段时间了,即现在选择还为时过早
SHA-3。
哈希转换计划一旦实施,将来也更容易切换到其他东西,所以我们不应该急于选择一些更新的哈希,因为我们需要为了永远保持它,我们总是可以在另外 10 到 15 年内再进行一次过渡。
结果:commit 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 delicately puts it(强调我的):
老实说,可观测宇宙中的粒子数量在 2**256 的数量级上。这是一个非常非常大的数字。
不要使代码库过于复杂。
做出明智的技术决策,并说“256 位是一个很多”。
工程学和理论的区别在于工程学
权衡取舍。
好的软件是精心设计的,而不是理论化的。
另外,我建议git默认为“abbrev-commit=40”,这样
默认情况下,实际上没有人看到新位。
因此,使用“[0-9a-f]{40}”作为哈希模式的 perl 脚本等只会默默地继续工作。
因为向后兼容性很重要 (*)
(*) 而且 2**160 仍然是一个很大的数字,而且还不是一个真正的数字
实际问题,SHA1DC 可能是下一个很好的哈希
十年或更长时间。
(SHA1DC, for "Detecting(?) Collision", 是discussed in early 2017,在碰撞攻击后shattered.io 实例:见commit 28dc98e, Git v2.13.0-rc0, March 2017, from Jeff King, and "Hash collision in git")
在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> 促进了这种转变
见commit 1bdca81、commit d9db599、commit 11d8ef3、commit abe0cc5、commit ddddf8d、commit 42d4e1d、commit e02a714、commit efa7ae3、commit 3c9331a、@9876543443、@、@987654344 @、commit 8dca7f3、commit 6946e52、commit 8bd5a29、commit 1f5f8f3、commit 192b517、commit 9412759、commit 61e2a70、commit dadacf1、commit 768e30e、commit 2078991987654355@ (27654355@987654355commit dadacf1987654354@,@987654355) .
(由Junio C Hamano -- gitster -- 合并于commit f8cb64e,2020 年 3 月 27 日)
签字人:brian m.卡尔森
将使用子模块的存储库从一种哈希算法转换为另一种哈希算法时,有必要将子模块从旧算法重写为新算法,因为只有对子模块的引用,而不是它们的内容,才会写入快- 导出流。
在不重写子模块的情况下,当遇到另一个算法中的子模块时,快速导入会失败并出现“Invalid dataref”错误。
添加一对选项,--rewrite-submodules-from 和 --rewrite-submodules-to,它们在处理子模块时分别采用由 fast-export 和 fast-import 生成的标记列表。
使用这些标记将子模块提交从旧算法映射到新算法。
我们将标记读入两个对应的结构mark_set 对象,然后使用哈希表执行从旧到新的映射。这让我们可以重用在其他地方使用的相同的标记解析代码,并允许我们根据标记的 ID 有效地读取和匹配标记,因为标记文件不需要排序。
请注意,因为我们使用 khash 表作为对象 ID,并且此表复制 struct object_id 的值而不是引用它们,所以有必要将我们使用的 struct object_id 值归零插入并在表中查找。否则,我们最终会得到不匹配的 SHA-1 值,因为任何堆栈垃圾都可能留在未使用的区域中。
git fast-import documentation 现在包括:
子模块重写
--rewrite-submodules-from=<name>:<file>
--rewrite-submodules-to=<name>:<file>
将<name>指定的子模块的对象ID从<file>中使用的值重写为<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 (peff) 的commit 3f018ec(2020 年 10 月 15 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit cd47bbe,2020 年 11 月 2 日)
报告人:Sergey Brester
签字人:Jeff King
Fast-import 将其标记存储在由 mark_set 结构组成的类似 trie 的结构中。
(Trie: digital tree)
每个结构都有一个固定的大小(1024)。如果我们的 id 号太大而无法放入结构中,那么我们分配一个新的结构,它将 id 号移动 10 位。我们原来的struct成为这个新层的子节点,新的struct成为trie的顶层。
该方案被ddddf8d7e2 破坏(“fast-import: 允许读取多个标记文件”,2020-02-22,Git v2.27.0-rc0 -- merge 在batch #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() 的调用可能会修改我们的顶级集合指针,因此我们必须等到它返回后才能将其值赋给string_list。