/
scrypt.go
89 lines (74 loc) · 2.12 KB
/
scrypt.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
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm)
// go-aah/security source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package acrypto
import (
"crypto/subtle"
"encoding/base64"
"fmt"
"strconv"
"strings"
"aahframework.org/essentials.v0"
"golang.org/x/crypto/scrypt"
)
// ScryptEncoder struct implements `PasswordEncoder` interface for `scrypt`
// hashing.
type ScryptEncoder struct {
n int // cpu/memory cost
r int // blocksize
p int // parallelization
saltLen int // random salt bytes length
dkLen int // derived key length
}
// Generate method returns the `scrypt` password hash based on configured
// values at `security.password_encoder.scrypt.*`.
func (se *ScryptEncoder) Generate(password []byte) ([]byte, error) {
salt := ess.GenerateSecureRandomKey(se.saltLen)
dkHash, err := scrypt.Key(password, salt, se.n, se.r, se.p, se.dkLen)
if err != nil {
return nil, err
}
// Format: n-cpu/mem-cost$r-blocksize$p-parallelization$salt$derived-key-hash
return []byte(fmt.Sprintf("%d$%d$%d$%s$%s", se.n, se.r, se.p,
base64.URLEncoding.EncodeToString(salt),
base64.URLEncoding.EncodeToString(dkHash))), err
}
// Compare method compares given hash password and password using `scrypt`.
func (se *ScryptEncoder) Compare(hash, password []byte) bool {
parts := strings.Split(string(hash), hashDelim)
if len(parts) != 5 {
// invalid hash
return false
}
n, err := strconv.Atoi(parts[0])
if err != nil {
// invalid hash
return false
}
r, err := strconv.Atoi(parts[1])
if err != nil {
// invalid hash
return false
}
p, err := strconv.Atoi(parts[2])
if err != nil {
// invalid hash
return false
}
salt, err := base64.URLEncoding.DecodeString(parts[3])
if err != nil {
// invalid hash
return false
}
dkHash, err := base64.URLEncoding.DecodeString(parts[4])
if err != nil {
// invalid hash
return false
}
otherHash, err := scrypt.Key(password, salt, n, r, p, len(dkHash))
if err != nil {
// unable to generate other hash from cleartext
return false
}
return (subtle.ConstantTimeCompare(dkHash, otherHash) == 1)
}