如何在Go中实现HashCash算法(类型转换问题)?

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

How to implement the algorithm for HashCash in Go (type conversion issues)?

问题

我一直在尝试在Go中实现HashCash算法!对于那些不了解的人来说 -

> HashCash是一种阻止垃圾邮件的方法。基本上,一个头部由一些环境变量构成,客户端和服务器都知道(例如电子邮件、时间戳等)。一个随机的nonce被附加到头部的末尾。客户端通过更改nonce来尝试强制产生一个部分哈希碰撞(例如,前x位为0)。

> HashCash之所以有效,是因为找到部分哈希碰撞不那么昂贵。当服务器接收到这个头部时,它会验证其中的信息(因此它只能用于一个会话)并计算出结果哈希。如果前x位为0,那么客户端的机器上已经花费了相当多的时间来计算碰撞(这在垃圾邮件机器人上是不会发生的)。

对我来说,我只是想编写一个程序,找出客户端找到x位部分哈希碰撞所需的时间。

我编写了以下代码,如果int64具有x位的哈希碰撞,则返回true/false。

func partialAllZeroes (zeroCount uint8, val int64) (bool, os.Error) {
    setBitString := "1111111111111111111111111111111111111111111111111111111111111111"
    unsetBitString := "0000000000000000000000000000000000000000000000000000000000000000"
    setBitString = setBitString[0:zeroCount-1]
    unsetBitString = unsetBitString[0:zeroCount-1]
    
    zeroTest, e := strconv.Btoi64(setBitString, 2) // 64个0位
    zeroes, e   := strconv.Btoi64(unsetBitString, 2) // 64个1位
    
    if e != nil {
        return false, e
    }
    result := val & zeroTest
    switch {
        case result == zeroes:
            return true, nil
        case result != zeroes:
            return false, nil
    }
    
    return false, os.NewError("")
}

我目前的问题是我遇到了很多类型转换问题。例如,我只能操作int64类型,因为这是strconv.Btoi64返回的类型。另一个问题是哈希函数返回一个字节数组,我不知道如何将其转换为int64类型。

下面是我当前的哈希代码 -

hasher := sha1.New()
baseCollisionString := "BASE COLLISION STRING"
nonce := "12345"
hasher.Write([]byte(strings.Join(baseCollisionString, nonce)))
testCollision := hasher.Sum()
// 不知道如何将testCollision的前x位转换为int64类型,以便我可以在partialAllZeroes中使用它
英文:

I've been trying to implement the HashCash algorithm in Go! For those of you who don't know -

> HashCash is a method to stop spam. Basically, a header is constructed of some environment variables known both to the client and server (email, timestamp etc.). A random nonce is appended to the end of the header. The client tries to bruteforce a partial hash collision (e.g. where the first x bits are 0) by changing the nonce.
>
> HashCash works because it's not as expensive to find partial hash collisions. When the server receives this header, they verify the information in it (so it can be used only for one session) and compute the resulting hash. If the first x bits are 0, then a good amount of time has been expended on the client's machine, computing the collision (which wouldn't happen on a spambot)

For me, I'm just wanting to write a program which finds the time it takes for a client to find a partial hash collision of x bits.

I wrote this code which will return true/false if the int64 has a hash collision of x bits.

func partialAllZeroes (zeroCount uint8, val int64) (bool, os.Error) {
    setBitString := "1111111111111111111111111111111111111111111111111111111111111111"
    unsetBitString := "0000000000000000000000000000000000000000000000000000000000000000"
    setBitString = setBitString[0:zeroCount-1]
    unsetBitString = unsetBitString[0:zeroCount-1]
    
    zeroTest, e := strconv.Btoi64(setBitString, 2) // 64 0bits
    zeroes, e   := strconv.Btoi64(unsetBitString, 2) // 64 1bits
    
    if e != nil {
        return false, e
    }
    result := val & zeroTest
    switch {
        case result == zeroes:
            return true, nil
        case result != zeroes:
            return false, nil
    }
    
    return false, os.NewError("")
}

My current problem is I'm having alot of type conversion issues. For example, I am only able to operate on the int64 type, because that's what strconv.Btoi64 returns. Another issue that I'm also looking at is that the hash function returns as a byte array, and I have no idea how to convert that into an int64.

Below is my current hash code -

hasher := sha1.New()
baseCollisionString := "BASE COLLISION STRING"
nonce := "12345"
hasher.Write([]byte(strings.Join(baseCollisionString, nonce)))
testCollision := hasher.Sum()
// Somehow I must convert the first x bits of testCollision into an int64 type, so I can use partialAllZeroes with it

答案1

得分: 4

我建议使用以下代码(函数partialAllZeroes应该运行得更快):

package main

import "crypto/sha1"

func partialAllZeroes(zeroCount uint8, b []byte) bool {
    i := 0
    for zeroCount >= 8 {
        if b[i] != 0 {
            return false
        }
        i++
        zeroCount -= 8
    }

    var mask byte
    switch zeroCount {
    case 0: mask = 0x00
    case 1: mask = 0x01
    case 2: mask = 0x03
    case 3: mask = 0x07
    case 4: mask = 0x0f
    case 5: mask = 0x1f
    case 6: mask = 0x3f
    case 7: mask = 0x7f
    }

    return (b[i] & mask) == 0
}

func main() {
    hasher := sha1.New()
    baseCollisionString := "BASE COLLISION STRING"
    nonce := "12345"
    hasher.Write([]byte(baseCollisionString + nonce))
    testCollision := hasher.Sum()
    partialAllZeroes(100, testCollision)
}
英文:

I would suggest the following code (the function partialAllZeroes should run much faster):

package main

import "crypto/sha1"

func partialAllZeroes(zeroCount uint8, b []byte) bool {
    i := 0
    for zeroCount >= 8 {
        if b[i] != 0 {
            return false
        }
        i++
        zeroCount -= 8
    }

    var mask byte
    switch zeroCount {
    case 0: mask = 0x00
    case 1: mask = 0x01
    case 2: mask = 0x03
    case 3: mask = 0x07
    case 4: mask = 0x0f
    case 5: mask = 0x1f
    case 6: mask = 0x3f
    case 7: mask = 0x7f
    }

    return (b[i] & mask) == 0
}

func main() {
    hasher := sha1.New()
    baseCollisionString := "BASE COLLISION STRING"
    nonce := "12345"
    hasher.Write([]byte(baseCollisionString + nonce))
    testCollision := hasher.Sum()
    partialAllZeroes(100, testCollision)
}

答案2

得分: 1

我建议使用math/big.Int类型。它可以处理大的位集。它有SetString方法,也可以处理二进制(例如"0b101010")。使用fmt.Printf或Sprintf可以使用%b动词从中获取二进制文本字符串。

英文:

I would suggest to use the math/big.Int type. It can handle large bit sets. It has the SetString method which also handles binary (like "0b101010"). With fmt.Printf or Sprintf one can use the %b verb to get a binary text string back from it.

答案3

得分: 0

我还建议升级到最新的每周版本(自昨天的RC1以来)。
“go fix”命令将帮助您进行过渡。

然后使用strconv包:

package main

import (
        "fmt"
        "strconv"
        "reflect"
)

func main() {
        a := []byte{ '1', '1', '1', '0', '1', '0', '1', '1' }

        b, err := strconv.ParseInt(string(a[:3]), 2, 64)
        if err != nil {
                fmt.Println(err)
                return
        }

        fmt.Println(b, reflect.TypeOf(b))
}
英文:

I also suggest to upgrade to the latest weekly (since yesterday RC1).
The "go fix" command will help you in the transition.

Then using strconv pkg:

package main

import (
        "fmt"
        "strconv"
        "reflect"
)

func main() {
        a := []byte{ '1', '1', '1', '0', '1', '0', '1', '1' }

        b, err := strconv.ParseInt(string(a[:3]), 2, 64)
        if err != nil {
                fmt.Println(err)
                return
        }

        fmt.Println(b, reflect.TypeOf(b))
}

huangapple
  • 本文由 发表于 2012年3月14日 17:39:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/9699307.html
匿名

发表评论

匿名网友

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

确定