dqkf49487 2013-05-08 11:59
浏览 143
已采纳

Golang加密多次呼叫有不同的响应

I'm having a problem with some Go code I've written for a password authentication library. The general idea is to provide 2 functions, Check() and New(), which are both provided a password and a 256 bit HMAC key. The Check() function is also provided a 256 bit salt and a 256 bit hash, and returns a boolean. The New() function returns a new, random salt, and it's corresponding hash. Both functions rely on a helper function, hash(), that uses scrypt for key lengthening, and does the real work of generating an output hash.

This was working when I originally wrote it (as evidenced by the fact that I have working test data generated by an earlier, lost revision of the code).

The problem I'm now having is that the Check() function seems to work perfectly when provided with data generated by the old version of the code, but now seems to fail with any data generated by the code's own New() function (which both use the underlying hash() function).

I know, I should have had git version controlling the code from the start! I've learnt my lesson now.

I've grouped the functions, and a quick demo of the problem into one .go file, as below, and added some output for debugging:

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
HMAC: %x
Salt: %x
Pass: %x
", 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
", 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
Salt: %x
Pass: %x
", 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
", err)
    }
    fmt.Printf("%t
", 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
", 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
", err)
    }
    fmt.Printf("%t
", chk)
}

I've tried this on both Linux 64bit and Windows8 64bit and it fails on both.

Any help would be much appreciated! As I said, I did have this working at some point, but I seem to have broken it somewhere along the way. I typically only discovered it wasn't working when writing unit tests... I suppose that's what they're for!

Thanks,

Mike.

  • 写回答

1条回答 默认 最新

  • dougua9328 2013-05-09 01:48
    关注

    You seem to have reversed the arguments to hash() in one of your functions. In Check(), you have:

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

    While in New() you have:

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

    These obviously won't produce the same result leading to the verification failure.

    With three similar arguments with the same types like this, mistakes like this aren't too surprising. Perhaps it would be worth seeing whether you could restructure things to let the type system catch this class of error?

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#python#的问题:求帮写python代码
  • ¥15 LiBeAs的带隙等于0.997eV,计算阴离子的N和P
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 来真人,不要ai!matlab有关常微分方程的问题求解决,
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?