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

huangapple go评论71阅读模式
英文:

Golang crypto multiple calls have different responses

问题

我对我为密码验证库编写的一些Go代码遇到了问题。总体思路是提供两个函数Check()和New(),它们都提供了一个密码和一个256位的HMAC密钥。Check()函数还提供了一个256位的盐和一个256位的哈希值,并返回一个布尔值。New()函数返回一个新的、随机的盐和它对应的哈希值。这两个函数都依赖于一个辅助函数hash(),该函数使用scrypt进行密钥长度扩展,并完成生成输出哈希的真正工作。

当我最初编写它时,它是正常工作的(通过我之前版本的代码生成的工作测试数据可以证明这一点)。

我现在遇到的问题是,当提供由代码自己的New()函数生成的任何数据时,Check()函数似乎完美地工作,但是现在似乎无法使用旧版本代码生成的数据(这两个函数都使用底层的hash()函数)。

我知道,我应该从一开始就使用git进行版本控制!我现在已经吸取了教训。

我将函数和问题的一个快速演示放在一个.go文件中,如下所示,并添加了一些用于调试的输出:

package main

import (
	"code.google.com/p/go.crypto/scrypt"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"errors"
	"fmt"
	"io"
)

// scrypt的常量。参见code.google.com/p/go.crypto/scrypt
const (
	KEYLENGTH = 32
	N         = 16384
	R         = 8
	P         = 1
)

// hash接受一个HMAC密钥、一个密码和一个盐(作为字节切片)
// scrypt转换密码和盐,然后HMAC转换结果。
// 返回生成的256位哈希值。
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() // 可能不是必要的
	return h, nil
}

// Check接受一个HMAC密钥、一个要检查的哈希值、一个密码和一个盐(作为字节切片)
// 调用hash()函数。
// 将生成的256位哈希值与检查哈希值进行比较,并返回一个布尔值。
func Check(hmk, h, pw, s []byte) (chk bool, err error) {
	// 打印输入的哈希值
	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
	}
	// 打印要比较的哈希值
	fmt.Printf("Hchk: %x\n", hchk)
	if subtle.ConstantTimeCompare(h, hchk) != 1 {
		return false, errors.New("Error: Hash verification failed")
	}
	return true, nil
}

// New接受一个HMAC密钥和一个密码(作为字节切片)
// 使用"crypto/rand"生成一个新的盐
// 调用hash()函数。
// 返回生成的256位哈希值和盐。
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() {

	// 已知的有效值
	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,
	}

	// 检查已知的有效值。这个是有效的。
	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()

	// 从已知的HMAC和盐创建新的哈希和盐
	fmt.Println("Creating new hash and salt values...")
	h, s, err := New(hmac, []byte(pass))
	if err != nil {
		fmt.Printf("%s\n", err)
	}

	// 检查新的值。这个是无效的!
	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)
}

我在64位的Linux和64位的Windows8上都尝试过,但都失败了。

非常感谢任何帮助!正如我所说的,我在某个时候确实使它工作了,但在某个地方似乎出了问题。我通常只有在编写单元测试时才发现它不起作用...我想这就是它们的用途!

谢谢,

Mike。

英文:

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\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)
}

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

得分: 6

你似乎在其中一个函数中颠倒了hash()的参数。在Check()中,你有:

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

而在New()中,你有:

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

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

当有三个相似类型的参数时,这样的错误并不令人意外。也许值得看看是否可以重构代码,让类型系统捕捉到这类错误?

英文:

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?

huangapple
  • 本文由 发表于 2013年5月8日 19:59:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/16440131.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定