This repository has been archived by the owner on Sep 12, 2021. It is now read-only.
/
argon2.go
107 lines (87 loc) · 2.88 KB
/
argon2.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
package argon2
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"strconv"
"strings"
"fyne.io/fyne"
"golang.org/x/crypto/argon2"
)
// Params controls structural values for the argon2 implementation.
type Params struct {
Salt []byte
Time uint32
Memory uint32
Threads uint8
KeyLen uint32
}
// randSalt generates a random salt value using crypto/rand.
func randSalt() ([]byte, error) {
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
fyne.LogError("Error on generating random salt", err)
return nil, err
}
return salt, nil
}
// encode takes the hash and parameters to encode a string with it all (not in accordance to the argon2 specs).
func encode(hash []byte, p Params) string {
return fmt.Sprintf("$%d$m=%d,t=%d,p=%d$%s$%s",
argon2.Version, p.Memory, p.Time, p.Threads,
base64.RawStdEncoding.EncodeToString(p.Salt),
base64.RawStdEncoding.EncodeToString(hash),
)
}
// decode takes the encoded hash string and extracts hash and parameters from it.
func decode(encoded string) (hashed []byte, p Params, err error) {
data := strings.Split(encoded, "$")
if len(data) != 5 {
return nil, p, errors.New("invalid hash")
}
if version, err := strconv.ParseInt(data[1], 10, 0); err != nil {
return nil, p, err
} else if version != argon2.Version {
return nil, p, errors.New("invalid argon2 version")
}
_, err = fmt.Sscanf(data[2], "m=%d,t=%d,p=%d", &p.Memory, &p.Time, &p.Threads)
if err != nil {
fyne.LogError("Error on scanning parameters", err)
return nil, p, err
}
p.Salt, err = base64.RawStdEncoding.DecodeString(data[3])
if err != nil {
fyne.LogError("Error on decoding base64 salt", err)
return nil, p, err
}
hashed, err = base64.RawStdEncoding.DecodeString(data[4])
if err != nil {
fyne.LogError("Error on decoding base64 hash", err)
return nil, p, err
}
p.KeyLen = 2 * uint32(len(hashed))
return hashed, p, nil
}
// GenerateFromPasswordAES256 generates a hash and returns the first part for use as an AES-256 key, the second part for storage and lastly an error.
func GenerateFromPasswordAES256(password []byte, p Params) (key []byte, verific string, err error) {
p.Salt, err = randSalt()
if err != nil {
return nil, "", err
}
hash := argon2.IDKey(password, p.Salt, p.Time, p.Memory, p.Threads, p.KeyLen)
return hash[:p.KeyLen/2], encode(hash[p.KeyLen/2:], p), nil
}
// CompareHashAndPasswordAES256 generates a hash from the password with the same parameters as the hash and then compares the second part of it.
func CompareHashAndPasswordAES256(encoded string, password []byte) (key []byte, err error) {
stored, p, err := decode(encoded)
if err != nil {
return nil, err
}
hash := argon2.IDKey(password, p.Salt, p.Time, p.Memory, p.Threads, p.KeyLen)
if subtle.ConstantTimeCompare(stored, hash[p.KeyLen/2:]) != 1 {
return nil, errors.New("the hashes are not equal")
}
return hash[:p.KeyLen/2], nil
}