/
crypto.go
115 lines (104 loc) · 2.84 KB
/
crypto.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* crypto.go
*
* @Author: Nanang Suryadi
* @Date: November 28, 2019
* @Last Modified by: @suryakencana007
* @Last Modified time: 28/11/19 18:20
*/
package mimir
import (
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"fmt"
"hash"
"strconv"
"strings"
"golang.org/x/crypto/pbkdf2"
)
const (
RecommendedRoundsSHA1 = 131000
RecommendedRoundsSHA256 = 29000
RecommendedRoundsSHA512 = 25000
)
var b64 = base64.RawStdEncoding
// PassLibBase64Encode encodes using a variant of base64, like Passlib.
// Check https://pythonhosted.org/passlib/lib/passlib.utils.html#passlib.utils.ab64_encode
func PassLibBase64Encode(src []byte) (dst string) {
dst = b64.EncodeToString(src)
dst = strings.Replace(dst, "+", ".", -1)
return
}
// PassLibBase64Decode decodes using a variant of base64, like Passlib.
// Check https://pythonhosted.org/passlib/lib/passlib.utils.html#passlib.utils.ab64_decode
func PassLibBase64Decode(src string) (dst []byte, err error) {
src = strings.Replace(src, ".", "+", -1)
dst, err = b64.DecodeString(src)
return
}
// Base64Encode encodes using a Standard of base64.
// return string base64 encode
func Base64Encode(src []byte) (dst string) {
return base64.StdEncoding.EncodeToString(src)
}
// Base64Encode decodes using a Standard of base64.
// return string base64 encode
func Base64Decode(src string) (dst []byte, err error) {
return base64.StdEncoding.DecodeString(src)
}
func HashPassword(password, salt string) string {
return fmt.Sprintf(
"$pbkdf2-sha512$%d$%s$%v",
RecommendedRoundsSHA512,
PassLibBase64Encode([]byte(salt)),
PassLibBase64Encode(
pbkdf2.Key(
[]byte(password),
[]byte(salt),
RecommendedRoundsSHA512,
sha512.Size, sha512.New,
),
),
)
}
func VerifyPassword(hashpassword, password string) (bool, error) {
// only pbkdf2 supported
if !strings.HasPrefix(hashpassword, "$pbkdf2-") {
return false, fmt.Errorf("invalid hashPass")
}
// five fields expected: $pbkdf2-digest$rounds$salt$checksum
fields := strings.Split(hashpassword, "$")
if len(fields) != 5 {
return false, fmt.Errorf("invalid hashPass format")
}
// extract digest
hdr := strings.Split(fields[1], "-")
if len(hdr) != 2 {
return false, fmt.Errorf("invalid digest")
}
var (
keyLen int
hashFunc func() hash.Hash
)
switch hdr[1] {
case "sha256":
keyLen = sha256.Size
hashFunc = sha256.New
case "sha512":
keyLen = sha512.Size
hashFunc = sha512.New
default:
return false, fmt.Errorf("invalid hashPass func")
}
// get remaining fields
rounds, err := strconv.Atoi(fields[2])
if err != nil {
return false, fmt.Errorf("invalid hashPass roound")
}
salt, err := PassLibBase64Decode(fields[3])
if err != nil {
return false, fmt.Errorf("invalid hashPass salt")
}
key := pbkdf2.Key([]byte(password), salt, rounds, keyLen, hashFunc)
return fields[4] == PassLibBase64Encode(key), nil
}