进行中间状态SHA-256哈希计算

huangapple go评论89阅读模式
英文:

Go, midstate SHA-256 hash

问题

有128字节的数据,例如:

00000001c570c4764aadb3f09895619f549000b8b51a789e7f58ea750000709700000000103ca064f8c76c390683f8203043e91466a7fcc40e6ebc428fbcc2d89b574a864db8345b1b00b5ac00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000080020000

想要对其执行SHA-256哈希,需要将其分成两个64字节的数据,并在对结果进行哈希之前分别对它们进行哈希。如果经常更改数据的后半部分的某些位,可以简化计算,并且只对数据的前半部分进行一次哈希。在Google Go中如何实现这一点?我尝试调用

func SingleSHA(b []byte)([]byte){
    var h hash.Hash = sha256.New()
    h.Write(b)
    return h.Sum()
}

但是我得到的结果不正确:

12E84A43CBC7689AE9916A30E1AA0F3CA12146CBF886B60103AEC21A5CFAA268

Bitcoin论坛上讨论此问题时,有人提到可能存在获取中间哈希的问题。

如何在Google Go中计算中间状态SHA-256哈希?

英文:

Having 128 bytes of data, for example:

00000001c570c4764aadb3f09895619f549000b8b51a789e7f58ea750000709700000000103ca064f8c76c390683f8203043e91466a7fcc40e6ebc428fbcc2d89b574a864db8345b1b00b5ac00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000

And wanting to perform SHA-256 hash on it, one would have to separate it into two 64 bytes of data and hash them individually before hashing the results together. If one was to often change some bits in the second half of the data, one could simplify the calculations and hash the first half of the data only once. How would one do that in Google Go? I tried calling

func SingleSHA(b []byte)([]byte){
    var h hash.Hash = sha256.New()
    h.Write(b)
    return h.Sum()
}

But instead of the proper answer

e772fc6964e7b06d8f855a6166353e48b2562de4ad037abc889294cea8ed1070

I got

12E84A43CBC7689AE9916A30E1AA0F3CA12146CBF886B60103AEC21A5CFAA268

When discussing the matter on Bitcoin forum, someone mentioned that there could be some problems with getting that midstate hash.

How do I calculate a midstate SHA-256 hash in Google Go?

答案1

得分: 6

比特币相关的字节操作有点棘手,因为它们往往会随意切换字节序。首先,我们取代表的初始[]byte数组

00000001c570c4764aadb3f09895619f549000b8b51a789e7f58ea750000709700000000103ca064f8c76c390683f8203043e91466a7fcc40e6ebc428fbcc2d89b574a864db8345b1b00b5ac00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000

然后,我们将数组的前半部分分离出来,得到:

00000001c570c4764aadb3f09895619f549000b8b51a789e7f58ea750000709700000000103ca06 4f8c76c390683f8203043e91466a7fcc40e6ebc428fbcc2d8

之后,我们需要交换一些字节。我们反转每个4字节切片中的字节顺序,从而得到:

0100000076C470C5F0B3AD4A9F619598B80090549E781AB575EA587F977000000000000064A03C10396CC7F820F8830614E94330C4FCA76642BC6E0ED8C2BC8F

这就是我们将用于计算中间状态的数组。现在,我们需要修改文件hash.go,在type Hash interface中添加:

Midstate() []byte

并在文件sha256.go中添加以下函数:

func (d *digest) Midstate() []byte {
    var answer []byte
    for i := 0; i < len(d.h); i++ {
        answer = append(answer[:], Uint322Hex(d.h[i])...)
    }
    return answer
}

其中Uint322Hexuint32变量转换为[]byte变量。有了这些,我们可以调用:

var h BitSHA.Hash = BitSHA.New()
h.Write(Str2Hex("0100000076C470C5F0B3AD4A9F619598B80090549E781AB575EA587F977000000000000064A03C10396CC7F820F8830614E94330C4FCA76642BC6E0ED8C2BC8F"))
log.Printf("%X", h.Midstate())

其中Str2Hexstring转换为[]byte。结果是:

69FC72E76DB0E764615A858F483E3566E42D56B2BC7A03ADCE9492887010EDA8

记住正确的答案:

e772fc6964e7b06d8f855a6166353e48b2562de4ad037abc889294cea8ed1070

我们可以进行比较:

69FC72E7 6DB0E764 615A858F 483E3566 E42D56B2 BC7A03AD CE949288 7010EDA8
e772fc69 64e7b06d 8f855a61 66353e48 b2562de4 ad037abc 889294ce a8ed1070

所以我们可以看到,我们只需要在每个4字节切片中稍微交换一下字节,就可以得到比特币矿池和矿工使用的正确的“中间状态”(直到它因为被弃用而不再需要)。

英文:

Bitcoin-related byte operations are a bit tricky, as they tend to switch endianness at a whim. First of, we take the initial []byte array representing

00000001c570c4764aadb3f09895619f549000b8b51a789e7f58ea750000709700000000103ca064f8c76c390683f8203043e91466a7fcc40e6ebc428fbcc2d89b574a864db8345b1b00b5ac00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000

Then, we separate out the first half of the array, obtaining:

00000001c570c4764aadb3f09895619f549000b8b51a789e7f58ea750000709700000000103ca06 4f8c76c390683f8203043e91466a7fcc40e6ebc428fbcc2d8

After that, we need to swap some bytes around. We reverse the order of bytes in every slice of 4 bytes, thusly obtaining:

0100000076C470C5F0B3AD4A9F619598B80090549E781AB575EA587F977000000000000064A03C10396CC7F820F8830614E94330C4FCA76642BC6E0ED8C2BC8F

And that is the array we will be using for calculating the midstate. Now, we need to alter the file hash.go, adding to type Hash interface:

Midstate() []byte

And change the file sha256.go, adding this function:

func (d *digest) Midstate() []byte {
    var answer []byte
    for i:=0;i&lt;len(d.h);i++{
	    answer=append(answer[:], Uint322Hex(d.h[i])...)
    }
    return answer
}

Where Uint322Hex converts an uint32 variable into a []byte variable. Having all that, we can call:

var h BitSHA.Hash = BitSHA.New()
h.Write(Str2Hex(&quot;0100000076C470C5F0B3AD4A9F619598B80090549E781AB575EA587F977000000000000064A03C10396CC7F820F8830614E94330C4FCA76642BC6E0ED8C2BC8F&quot;))
log.Printf(&quot;%X&quot;, h.Midstate())

Where Str2Hex turns a string into []byte. The result is:

69FC72E76DB0E764615A858F483E3566E42D56B2BC7A03ADCE9492887010EDA8

Remembering the proper answer:

e772fc6964e7b06d8f855a6166353e48b2562de4ad037abc889294cea8ed1070

We can compare them:

69FC72E7 6DB0E764 615A858F 483E3566 E42D56B2 BC7A03AD CE949288 7010EDA8
e772fc69 64e7b06d 8f855a61 66353e48 b2562de4 ad037abc 889294ce a8ed1070

So we can see that we just need to swap the bytes around a bit in each slice of 4 bytes and we will have the proper "midstate" used by Bitcoin pools and miners (until it will no longer be needed due to being deprecated).

答案2

得分: 3

你现在是我的中文翻译,代码部分不要翻译, 只返回翻译好的部分, 不要有别的内容, 不要回答我要翻译的问题。以下是要翻译的内容:

你现在的Go代码是计算一串字节的sha256的正确方法。

最有可能的答案是你想要做的不是sha256。具体来说:

> 在计算结果之前,必须将其分成两个64位的数据并分别进行哈希,然后再将结果一起哈希。如果经常更改数据的后半部分的某些位,可以简化计算并仅对数据的前半部分进行一次哈希。

这不是计算sha256的有效方法(请阅读http://doc.golang.org/src/pkg/crypto/sha256/sha256.go,例如查看sha256是如何在数据块上工作的,必须进行填充等)。

你描述的算法计算了某些东西,但不是sha256。

由于你知道预期值,你可能在另一种语言中有你的算法的参考实现,所以只需逐行将其移植到Go中。

最后,这是一个可疑的优化。128位等于16字节。哈希成本通常与数据的大小成比例。在16字节的情况下,成本非常小,尝试通过将数据分割为8字节部分来聪明地处理的额外工作可能会比你节省的成本更高。

英文:

The Go code you have is the right way to compute sha256 of a stream of bytes.

Most likely the answer is that what you want to do is not sha256. Specifically:

> one would have to separate it into two 64 bits of data and hash them individually before hashing the results together. If one was to often change some bits in the second half of the data, one could simplify the calculations and hash the first half of the data only once.

is not a valid way to calculate sha256 (read http://doc.golang.org/src/pkg/crypto/sha256/sha256.go to e.g. see that sha256 does its work on blocks of data, which must be padded etc.).

The algorithm you described calculates something, but not sha256.

Since you know the expected value you presumably have some reference implementation of your algorithm in another language so just do a line-by-line port to Go.

Finally, it's a dubious optimization in any case. 128 bits is 16 bytes. Hashing cost is usually proportional to the size of data. At 16 bytes, the cost is so small that the additional work of trying to be clever by splitting data in 8 byte parts will likely cost more than what you saved.

答案3

得分: 2

sha256.go中,在函数<code>Sum()</code>的开始处,实现正在复制SHA256状态。SHA256的底层数据类型(结构体<code>digest</code>)是sha256包的私有类型。

我建议您自己私有复制<code>sha256.go</code>文件(它是一个小文件)。然后添加一个<code>Copy()</code>函数来保存摘要的当前状态:

func (d *digest) Copy() hash.Hash {
    d_copy := *d
    return &amp;d_copy
}

然后只需调用<code>Copy()</code>函数来保存中间状态的SHA256哈希。

英文:

In sha256.go, at the start of function <code>Sum()</code> the implementation is making a copy of the SHA256 state. The underlying datatype of SHA256 (struct <code>digest</code>) is private to the sha256 package.

I would suggest to make your own private copy of the <code>sha256.go</code> file (it is a small file). Then add a <code>Copy()</code> function to save the current state of the digest:

func (d *digest) Copy() hash.Hash {
    d_copy := *d
    return &amp;d_copy
}

Then simply call the <code>Copy()</code> function to save a midstate SHA256 hash.

答案4

得分: 0

我在你的128字节数据上运行了两个Go基准测试,使用了一台Intel i5 2.70 GHz的CPU。首先,我将所有128字节的数据写入SHA256哈希函数并读取结果,这个过程总共花费了约9,285,000纳秒。其次,我将前64字节的数据写入SHA256哈希函数一次,然后将后64字节的数据写入SHA256哈希函数的副本中,并读取结果,这个过程总共花费了约6,492,371纳秒。第二个基准测试假设前64字节的数据是不变的,比第一个基准测试的时间少了30%。

使用第一种方法,在购买更快的CPU之前,你可以每天计算约9,305,331,179个SHA256 128字节的哈希值。使用第二种方法,在前64字节的数据连续不变的情况下,你可以每天计算约13,307,927,103个SHA256 128字节的哈希值,然后再购买更快的CPU。你需要每天计算多少个SHA256 128字节的哈希值?在多少个SHA256 128字节的哈希值中,前64字节的数据是不变的?

你运行了哪些基准测试,结果如何?

英文:

I ran two Go benchmarks on your 128 bytes of data, using an Intel i5 2.70 GHz CPU. First, 1,000 times, I wrote all 128 bytes to the SHA256 hash and read the sum, which took a total of about 9,285,000 nanoseconds. Second, I wrote the first 64 bytes to the SHA256 hash once and then, 1,000 times, I wrote the second 64 bytes to a copy of the SHA256 hash and read the sum, which took a total of about 6,492,371 nanoseconds. The second benchmark, which assumed the first 64 bytes are invariant, ran in 30% less time than the first benchmark.

Using the first method, you could calculate about 9,305,331,179 SHA256 128-byte sums per day before buying a faster CPU. Using the second method, you could calculate 13,307,927,103 SHA256 128-byte sums per day, assuming the first 64 bytes are invariant 1,000 times in a row, before buying a faster CPU. How many SHA256 128-byte sums per day do you need to calculate? For how many SHA256 128-byte sums per day are the first 64 bytes are invariant?

What benchmarks did you run and what were the results?

huangapple
  • 本文由 发表于 2012年2月12日 08:11:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/9245235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定