forked from gebi/scryptauth
/
scryptauth.go
95 lines (81 loc) · 2.51 KB
/
scryptauth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* scryptauth is a GO library for secure password handling using scrypt
It uses sha256_hmac(scrypt(user_password, salt), server_key) to protect against
both dictionary attacks and DB leaks.
scryptauth additionally provides encode/decode routines using base64 to create strings
for storing into a DB.
Copyright: Michael Gebetsroither 2012 (michael \x40 mgeb \x2e org)
License: BSD 2 clause
*/
package scryptauth
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"errors"
"golang.org/x/crypto/scrypt"
)
type ScryptAuth struct {
HmacKey []byte // HMAC key used to secure scrypt hash
PwCost uint // PwCost parameter used to calculate N parameter of scrypt (1<<PwCost == N)
// scrypt parameter
R int
P int
}
const (
// Key length and salt length are 32 bytes (256 bits)
KEYLENGTH = 32
// scrypt default parameters
SCRYPT_CONST_R = 8
SCRYPT_CONST_P = 1
)
// Initialise ScryptAuth struct
func New(pw_cost uint, hmac_key []byte) (*ScryptAuth, error) {
if pw_cost > 32 {
return nil, errors.New("scryptauth new() - invalid pw_cost specified")
}
if len(hmac_key) != KEYLENGTH {
return nil, errors.New("scryptauth new() - unsupported hmac_key length")
}
return &ScryptAuth{HmacKey: hmac_key, PwCost: pw_cost, R: SCRYPT_CONST_R, P: SCRYPT_CONST_P}, nil
}
// Create hash_ref suitable for later invocation of Check()
func (s ScryptAuth) Hash(pw_cost uint, user_password, salt []byte) (hash_ref []byte, err error) {
scrypt_hash, err := scrypt.Key(user_password, salt, 1<<pw_cost, s.R, s.P, KEYLENGTH)
if err != nil {
return
}
hmac := hmac.New(sha256.New, s.HmacKey)
if _, err = hmac.Write(scrypt_hash); err != nil {
return
}
hash_ref = hmac.Sum(nil)
return
}
// Check / Verify user_password against hash_ref/salt
func (s ScryptAuth) Check(pw_cost uint, hash_ref, user_password, salt []byte) (chk bool, err error) {
result_hash, err := s.Hash(pw_cost, user_password, salt)
if err != nil {
return false, err
}
if subtle.ConstantTimeCompare(result_hash, hash_ref) != 1 {
return false, errors.New("Error: Hash verification failed")
}
return true, nil
}
// Generate hash_ref and create new salt from crypto.rand
func (s ScryptAuth) Gen(user_password []byte) (hash, salt []byte, err error) {
salt = make([]byte, KEYLENGTH)
salt_length, err := rand.Read(salt)
if salt_length != KEYLENGTH {
return nil, nil, errors.New("Insufficient random bytes for salt")
}
if err != nil {
return nil, nil, err
}
hash, err = s.Hash(s.PwCost, user_password, salt)
if err != nil {
return nil, nil, err
}
return
}