Golang加密多个调用有不同的响应

时间:2013-05-08 11:59:55

标签: cryptography go hmac sha256 scrypt

我遇到了一些我为密码认证库编写的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上都尝试过这种方法,但两者都失败了。

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

谢谢,

麦克

1 个答案:

答案 0 :(得分:6)

您似乎已将某个功能中的hash()的参数撤消了。在Check()中,您有:

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

New()中你有:

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

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

对于像这样相同类型的三个相似论点,这样的错误并不太令人惊讶。也许值得一看,你是否可以重组一些东西让类型系统捕获这类错误?