我遇到了一些我为密码认证库编写的Go代码的问题。一般的想法是提供2个函数,Check()和New(),它们都提供了密码和256位HMAC密钥。 Check()函数还提供256位salt和256位散列,并返回一个布尔值。 New()函数返回一个新的随机salt,它是相应的哈希值。这两个函数都依赖于一个辅助函数hash(),它使用scrypt进行键延长,并且可以生成输出哈希值。
当我最初编写它时,这是有效的(事实证明我有一个早期的,丢失的代码修订版生成的测试数据)。
我现在遇到的问题是Check()函数在提供由旧版本代码生成的数据时似乎工作得很好,但现在似乎失败了代码自己的New()生成的任何数据function(两者都使用底层的hash()函数)。
我知道,我应该从一开始就有git版本来控制代码!我现在已经吸取了教训。
我已将功能分组,并将问题快速演示到一个.go文件中,如下所示,并添加了一些输出用于调试:
package main
import (
"code.google.com/p/go.crypto/scrypt"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"errors"
"fmt"
"io"
)
// Constants for scrypt. See code.google.com/p/go.crypto/scrypt
const (
KEYLENGTH = 32
N = 16384
R = 8
P = 1
)
// hash takes an HMAC key, a password and a salt (as byte slices)
// scrypt transforms the password and salt, and then HMAC transforms the result.
// Returns the resulting 256 bit hash.
func hash(hmk, pw, s []byte) (h []byte, err error) {
sch, err := scrypt.Key(pw, s, N, R, P, KEYLENGTH)
if err != nil {
return nil, err
}
hmh := hmac.New(sha256.New, hmk)
hmh.Write(sch)
h = hmh.Sum(nil)
hmh.Reset() // Probably not necessary
return h, nil
}
// Check takes an HMAC key, a hash to check, a password and a salt (as byte slices)
// Calls hash().
// Compares the resulting 256 bit hash against the check hash and returns a boolean.
func Check(hmk, h, pw, s []byte) (chk bool, err error) {
// Print the input hash
fmt.Printf("Hash: %x\nHMAC: %x\nSalt: %x\nPass: %x\n", h, hmk, s, []byte(pw))
hchk, err := hash(hmk, pw, s)
if err != nil {
return false, err
}
// Print the hash to compare against
fmt.Printf("Hchk: %x\n", hchk)
if subtle.ConstantTimeCompare(h, hchk) != 1 {
return false, errors.New("Error: Hash verification failed")
}
return true, nil
}
// New takes an HMAC key and a password (as byte slices)
// Generates a new salt using "crypto/rand"
// Calls hash().
// Returns the resulting 256 bit hash and salt.
func New(hmk, pw []byte) (h, s []byte, err error) {
s = make([]byte, KEYLENGTH)
_, err = io.ReadFull(rand.Reader, s)
if err != nil {
return nil, nil, err
}
h, err = hash(pw, hmk, s)
if err != nil {
return nil, nil, err
}
fmt.Printf("Hash: %x\nSalt: %x\nPass: %x\n", h, s, []byte(pw))
return h, s, nil
}
func main() {
// Known values that work
pass := "pleaseletmein"
hash := []byte{
0x6f, 0x38, 0x7b, 0x9c, 0xe3, 0x9d, 0x9, 0xff,
0x6b, 0x1c, 0xc, 0xb5, 0x1, 0x67, 0x1d, 0x11,
0x8f, 0x72, 0x78, 0x85, 0xca, 0x6, 0x50, 0xd0,
0xe6, 0x8b, 0x12, 0x9c, 0x9d, 0xf4, 0xcb, 0x29,
}
salt := []byte{
0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20,
0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x4, 0x97,
0x48, 0x44, 0xe3, 0x7, 0x4a, 0xe8, 0xdf, 0xdf,
0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42, 0xfc, 0xd0,
}
hmac := []byte{
0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48,
0x46, 0x1c, 0x6, 0xcd, 0x81, 0xfd, 0x38, 0xeb,
0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e,
0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2,
}
// Check the known values. This Works.
fmt.Println("Checking known values...")
chk, err := Check(hmac, hash, []byte(pass), salt)
if err != nil {
fmt.Printf("%s\n", err)
}
fmt.Printf("%t\n", chk)
fmt.Println()
// Create new hash and salt from the known HMAC and Salt
fmt.Println("Creating new hash and salt values...")
h, s, err := New(hmac, []byte(pass))
if err != nil {
fmt.Printf("%s\n", err)
}
// Check the new values. This Fails!
fmt.Println("Checking new hash and salt values...")
chk, err = Check(hmac, h, []byte(pass), s)
if err != nil {
fmt.Printf("%s\n", err)
}
fmt.Printf("%t\n", chk)
}
我在Linux 64bit和Windows8 64bit上都尝试过这种方法,但两者都失败了。
任何帮助将不胜感激!正如我所说的那样,我确实在某些时候有这个工作,但我似乎已经在某个地方打破了它。我通常只发现它在编写单元测试时不起作用......我想这就是他们的目的!
谢谢,
麦克
答案 0 :(得分:6)
您似乎已将某个功能中的hash()
的参数撤消了。在Check()
中,您有:
hchk, err := hash(hmk, pw, s)
在New()
中你有:
h, err = hash(pw, hmk, s)
这些显然不会产生导致验证失败的相同结果。
对于像这样相同类型的三个相似论点,这样的错误并不太令人惊讶。也许值得一看,你是否可以重组一些东西让类型系统捕获这类错误?