4

我为密码身份验证库编写的一些 Go 代码有问题。总体思路是提供 2 个函数,Check() 和 New(),它们都提供了密码和 256 位 HMAC 密钥。Check() 函数还提供了 256 位盐和 256 位哈希,并返回一个布尔值。New() 函数返回一个新的随机盐,以及它对应的哈希值。这两个函数都依赖于一个辅助函数 hash(),它使用 scrypt 来延长密钥,并完成生成输出哈希的实际工作。

当我最初编写它时,这是有效的(事实证明,我有由早期丢失的代码修订生成的工作测试数据)。

我现在遇到的问题是,当提供旧版本代码生成的数据时,Check() 函数似乎可以正常工作,但现在代码自己的 New() 函数生成的任何数据似乎都失败了(两者都使用底层的 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 上都试过了,但都失败了。

任何帮助将非常感激!正如我所说,我确实在某些时候有过这个工作,但我似乎在某个地方把它弄坏了。我通常只是在编写单元测试时才发现它不起作用......我想这就是他们的目的!

谢谢,

麦克风。

4

1 回答 1

6

您似乎已经hash()在您的一个函数中反转了参数。在Check()中,您有:

hchk, err := hash(hmk, pw, s)

New()你有:

h, err = hash(pw, hmk, s)

这些显然不会产生导致验证失败的相同结果。

有了三个具有相同类型的类似参数,这样的错误就不足为奇了。也许值得看看您是否可以重组事物以让类型系统捕获此类错误?

于 2013-05-09T01:48:12.190 回答