У меня возникла проблема с некоторым кодом Go, который я написал для библиотеки проверки подлинности паролей. Общая идея состоит в том, чтобы предоставить 2 функции: Check() и New(), которым предоставлены пароль и 256-битный ключ HMAC. Функция Check() также предоставляет 256-битную соль и 256-битный хеш и возвращает логическое значение. Функция New() возвращает новую случайную соль и соответствующий хеш. Обе функции полагаются на вспомогательную функцию, hash(), которая использует scrypt для удлинения ключа и выполняет реальную работу по генерации выходного хэша.Многократные вызовы Golang crypto имеют разные ответы
Это работало, когда я изначально написал его (о чем свидетельствует тот факт, что у меня есть тестовые данные, сгенерированные ранее потерянной версией кода).
Проблема, с которой я сталкиваюсь, заключается в том, что функция 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"
)
// 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, и он не работает на обоих.
Любая помощь была бы высоко оценена! Как я уже сказал, у меня есть , но я, кажется, сломал его где-то на этом пути. Я обычно обнаружил, что он не работает при написании модульных тестов ... Полагаю, для этого они нужны!
Thanks,
Mike.
Почему вы используете HMAC и SHA-256, когда в scrypt уже используются оба? – Luke
Я напрямую вызываю функции из обоих этих пакетов в строке 'hmh: = hmac.New (sha256.New, hmk)', поэтому мне нужно их импортировать. – Intermernet
Почему вы используете протокол HMAC? – Luke