函数应返回sha256 / sha384 / sha512结果作为字节切片

时间:2018-01-02 20:18:21

标签: arrays go cryptography

我正在编写一个函数,它将输入数据作为字符串,并调用SHA算法的位大小。它应该将结果哈希作为字节切片返回(第一次尝试):

package main

import (
    "crypto/sha256"
    "crypto/sha512"
    "errors"
    "fmt"
)

func main() {
    input := "This is a test."
    sha256, _ := shaSum(input, 256)
    sha384, _ := shaSum(input, 384)
    sha512, _ := shaSum(input, 512)
    fmt.Println(input, sha256, sha384, sha512)
}

func shaSum(data string, size uint) ([]byte, error) {
    input := []byte(data)
    switch size {
    case 256:
        return sha256.Sum256(input), nil
    case 384:
        return sha512.Sum384(input), nil
    case 512:
        return sha512.Sum512(input), nil
    default:
        return nil, errors.New("unsupported sha size")
    }
}

当然,这不起作用:

$ go run shasum.go
# command-line-arguments
./shasum.go:22:23: cannot use sha256.Sum256(input) (type [32]byte) as type []byte in return argument
./shasum.go:24:23: cannot use sha512.Sum384(input) (type [48]byte) as type []byte in return argument
./shasum.go:26:23: cannot use sha512.Sum512(input) (type [64]byte) as type []byte in return argument

所以我试图从哈希函数的返回值中获取切片,在每次调用(第二次尝试)后添加[:]

func shaSum(data string, size uint) ([]byte, error) {
    input := []byte(data)
    switch size {
    case 256:
        return sha256.Sum256(input)[:], nil
    case 384:
        return sha512.Sum384(input)[:], nil
    case 512:
        return sha512.Sum512(input)[:], nil
    default:
        return nil, errors.New("unsupported sha size")
    }
}

这不起作用:

$ go run shasum.go
# command-line-arguments
./shasum.go:22:30: invalid operation sha256.Sum256(input)[:] (slice of unaddressable value)
./shasum.go:24:30: invalid operation sha512.Sum384(input)[:] (slice of unaddressable value)
./shasum.go:26:30: invalid operation sha512.Sum512(input)[:] (slice of unaddressable value)

所以我试图获取返回值的地址,使用括号确保首先采用表达式的地址然后切片(第三次尝试):

func shaSum(data string, size uint) ([]byte, error) {
    input := []byte(data)
    switch size {
    case 256:
        return (&(sha256.Sum256(input)))[:], nil
    case 384:
        return (&(sha512.Sum384(input)))[:], nil
    case 512:
        return (&(sha512.Sum512(input)))[:], nil
    default:
        return nil, errors.New("unsupported sha size")
    }
}

产生此错误消息:

$ go run shasum.go
# command-line-arguments
./shasum.go:22:10: cannot take the address of sha256.Sum256(input)
./shasum.go:24:10: cannot take the address of sha512.Sum384(input)
./shasum.go:26:10: cannot take the address of sha512.Sum512(input)

所以我放弃并使用额外的线条进行解决(第四次尝试):

func shaSum(data string, size uint) ([]byte, error) {
    input := []byte(data)
    switch size {
    case 256:
        bytes := sha256.Sum256(input)
        return bytes[:], nil
    case 384:
        bytes := sha512.Sum384(input)
        return bytes[:], nil
    case 512:
        bytes := sha512.Sum512(input)
        return bytes[:], nil
    default:
        return nil, errors.New("unsupported sha size")
    }
}

最终编译并运行。现在我想知道:为什么第四次尝试起作用而其他人(特别是第三次尝试)不合适?一个好的解决方案怎么样?有没有办法避免像第四次尝试那样的额外线?

编辑:我的问题的根本问题不是如何从字节数组转换为字节切片,而是我对可寻址概念缺乏了解,以及如何表达解决方案我在惯用Go中的问题。

2 个答案:

答案 0 :(得分:1)

重要的是,请注意sha256.Sum256 function(和其他SHA函数)的签名会返回数组,这与 slice 完全不同。 。本文明确解释了差异:Go Slices: usage and internals

为了说明,请考虑以下事项:

x := [3]int{1, 2, 3} // x has type [3]int
y := []int(x) // ERROR: cannot convert x (type [3]int) to type []int

这就是为什么你的每次尝试都不起作用的原因:

  1. shaSum函数返回([]byte, error)的元组,但每个return语句都会尝试返回([Size]byte, error),因此编译器会抱怨类型不匹配。

  2. 每个sha*.Sum*函数返回一个([Size]byte, error)元组;你不能在这样的元组上使用[:]运算符,只能在arrays and slices上使用。

  3. 与上面的#2类似,您只能使用addressable types上的&运算符,该运算符不包含函数调用(或它们返回的值,直到它们存储在本地)。

  4. 这个工作示例是惯用的。语言设计者故意选择优化读者的清晰度而不是作者的简写。

答案 1 :(得分:1)

语言规范states this about the slice operator

  

如果切片操作数是一个数组,则它必须是可寻址的,切片操作的结果是一个与数组具有相同元素类型的切片。

this about addressability

  

操作数必须是可寻址的,即,变量,指针间接或切片索引操作;或可寻址结构操作数的字段选择器;或者可寻址数组的数组索引操作。

由此可见,第四次尝试是唯一有效的尝试。

这是使用hash.Hash界面的替代方法。

var hashFactory = map[int]func() hash.Hash{
    256: sha256.New,
    384: sha512.New384,
    512: sha512.New,
}

func shaSum(data string, size int) ([]byte, error) {
    f := hashFactory[size]
    if f == nil {
        return nil, errors.New("unsupported sha size")
    }
    h := f()
    io.WriteString(h, data)
    return h.Sum(nil), nil
}