乐趣区

关于golang:gosum中特殊hash如何计算

Golang 为了依赖的平安思考,在 go.mod 的根底上引入了 go.sum,go.sum 文件的作用次要是记录我的项目依赖的 hash 值,避免被人批改。

在剖析具体我的项目的 go.sum 文件后能够发现 go.sum 中不仅记录了 go.mod 等的 hash 值,也记录了整个模块的 hash 值,这是为什么呢?

这样作的目标次要是在下载整个模块外部的时候可找到子依赖,使得能够并行下载多个依赖。

起初我认为 go.sum 中记录的 hash 值是通过 sha256 间接计算再进行 base64 编码后的后果,然而在实际操作验证时失去的 base64 值和 go.sum 中记录的总是对不上,因而通过查看 go 的源码(/usr/local/go/src/cmd/go/ 上面对 /usr/local/go/src/cmd/vendor/golang.org/x/mod/sumdb/dirhash 包下有援用依赖,这里也是实现 go.sum 的底层算法外围)发现 Golang 对文件的 hash 和整个我的项目的 hash 计算并不是简略的 sha256 计算和 base64 编码。

案例剖析

cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
# 下面的大抵意思是
<module> <version>/go.mod h1:<sha256hash+base64>
# 第一段是模块依赖门路
# 第二段是版本信息 / 具体文件
# 第三段是针对该文件内容计算的 sha256 哈希值再进行 bash64 编码的值
# 其中 h1 代表的意思就是 sha256+base64

非凡 hash 计算

go.mod 的非凡 hash 计算

# 输出:go.mod 的文件门路
# 步骤:# 1. 关上 go.mod 文件读取文件内容进行 sha256 哈希计算,失去 sha256hash
# 2. 构建新的字符串 base64in = "sha256hash  go.mod\n" , 两头用两个空格分隔,最初必须有一个环行符
# 3. 将 base64in 作为输出给 base64 进行编码失去 base64encode
# 4. 字符串拼接失去 go.sum 中一样的后果 h1:base64encode

go.mod 的 hash 计算能够通过 shell 模仿得出后果,然而对于整个模块的 hash 计算就无能为力了,上面通过 shell 命令模仿上述过程

$ sha256sum go.mod 
5a93925e1efdeecd8b5755d089fdba6dfb3c04eb85447e8dec8b31cdb44203ab  go.mod    #sha256hash
$ vim base64in.txt  
5a93925e1efdeecd8b5755d089fdba6dfb3c04eb85447e8dec8b31cdb44203ab  go.mod   # base64in 字符串,留神上面的环行符不能少,不然和 Golang 中的后果对不上

$ sha256sum base64in.txt  | xxd -r -ps | base64
+DbmgtsW3Ksw3QccfHlswRDLj07woKf4ku0C0xYA7u0=  #base64encode
# 最终的后果通过字符串拼接即可失去 h1:+DbmgtsW3Ksw3QccfHlswRDLj07woKf4ku0C0xYA7u0= 
#在写入 go.sum 时须要同时写上 <module> <version>/go.mod h1:+DbmgtsW3Ksw3QccfHlswRDLj07woKf4ku0C0xYA7u0= 

整个模块的非凡 hash 计算

对整个模块进行 hash 计算时不是间接对打包好的 zip 包求 hash,而是对解压后的文件进行遍历 hash 计算后再进行一次总的 hash 计算,这样作的目标是防止因为 zip 算法进行打包时因为字节的差别导致对整个 zip 包的 hash 后果不统一

# 输出:模块所在目录和模块在的导入门路 (在源码中应用时的那个导入门路)
# 步骤:# 1. 遍历模块中所有文件
# 只思考文件,不思考目录
# 疏忽.git 目录内的所有文件
# 拼接每个文件相对路径与导入门路到一起
# 例如:导入门路 "github.com/spf13/cobra", 该包中 command.go 文件通过拼接后为:github.com/spf13/cobra/command.go
# 将遍历的后果存储在一个列表中不便前面计算 hash
# 2. 对上一步失去的列表进行排序 (排序主是保障 hash 后果统一)
# 3. 而后进行遍历 hash,其计算过程是在排序后的列表中读取一个文件进行 sha256 hash 将 "ha256hash github.com/spf13/cobra/command.go\n" 字符串拼接在后一个文件 hash 后果后面,以此类推最初失去一个所有文件 hash 后果的字符串
# 4. 对下面的长字符串再进行 sha256 hash 计算失去后果 sha256hash 进行 base64 编码失去 base64encode
# 5. 在写入 go.sum 时相似如下:github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
# 第一行是对整个包的 hash 后果
# 第二行是对 go.mod 的 hash 后果

下面的过程都能够在 Golang 源码中找到,在 github 找到了一位大神也对这种非凡的 hash 进行了复现:https://hub.fastgit.org/vikyd…

退出移动版