-
Notifications
You must be signed in to change notification settings - Fork 4
/
example_credential_store_test.go
115 lines (99 loc) · 3.31 KB
/
example_credential_store_test.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
package passhash_test
import (
"bytes"
"context"
"fmt"
"strconv"
"strings"
)
import (
"github.com/dhui/passhash"
)
const (
storeCredentialFormat string = "%d %s %x %x" //nolint:gosec // Using space as a separator for Sscanf compatibility
)
// StringCredentialStore is an example CredentialStore that stores the Credential as a string
type StringCredentialStore struct {
StoredCredential string
}
func (store *StringCredentialStore) Store(credential *passhash.Credential) error {
cfParams, _ := credential.WorkFactor.Marshal()
cfStrParams := make([]string, 0, len(cfParams))
for _, param := range cfParams {
cfStrParams = append(cfStrParams, strconv.Itoa(param))
}
cfStore := strings.Join(cfStrParams, ",")
store.StoredCredential = fmt.Sprintf(storeCredentialFormat, credential.Kdf, cfStore, string(credential.Salt), string(credential.Hash))
return nil
}
func (store *StringCredentialStore) StoreContext(ctx context.Context, credential *passhash.Credential) error {
return store.Store(credential)
}
func (store *StringCredentialStore) Load(passhash.UserID) (*passhash.Credential, error) {
credential := passhash.Credential{}
var cfStore string
fmt.Sscanf(store.StoredCredential, storeCredentialFormat, &credential.Kdf, &cfStore, &credential.Salt, &credential.Hash)
cfStrParams := strings.Split(cfStore, ",")
cfParams := make([]int, 0, len(cfStrParams))
for _, paramStr := range cfStrParams {
i, err := strconv.Atoi(paramStr)
if err != nil {
return nil, err
}
cfParams = append(cfParams, i)
}
wf, err := passhash.NewWorkFactorForKdf(credential.Kdf)
if err != nil {
return nil, err
}
if err := wf.Unmarshal(cfParams); err != nil {
return nil, err
}
credential.WorkFactor = wf
return &credential, nil
}
func (store *StringCredentialStore) LoadContext(ctx context.Context, userID passhash.UserID) (*passhash.Credential,
error) {
return store.Load(userID)
}
// nolint: dupl
func ExampleCredentialStore() {
userID := passhash.UserID(0)
password := "insecurepassword"
origCredential, err := passhash.NewCredential(userID, password)
if err != nil {
fmt.Println("Error creating credential.", err)
return
}
store := StringCredentialStore{}
if err := store.Store(origCredential); err != nil {
fmt.Println("Error storing credential.", err)
return
}
newCredential, err := store.Load(userID)
if err != nil {
fmt.Println("Error loading credential.", err)
return
}
credentialEqual := newCredential == origCredential
kdfEqual := newCredential.Kdf == origCredential.Kdf
cfEqual := newCredential.WorkFactor == origCredential.WorkFactor // Not equal due to pointer comparison
saltEqual := bytes.Equal(newCredential.Salt, origCredential.Salt)
hashEqual := bytes.Equal(newCredential.Hash, origCredential.Hash)
matched, updated := newCredential.MatchesPassword(password)
fmt.Println("credentialEqual:", credentialEqual)
fmt.Println("kdfEqual:", kdfEqual)
fmt.Println("cfEqual:", cfEqual)
fmt.Println("saltEqual:", saltEqual)
fmt.Println("hashEqual:", hashEqual)
fmt.Println("newCredential.MatchesPassword (matched):", matched)
fmt.Println("newCredential.MatchesPassword (updated):", updated)
// Output:
// credentialEqual: false
// kdfEqual: true
// cfEqual: false
// saltEqual: true
// hashEqual: true
// newCredential.MatchesPassword (matched): true
// newCredential.MatchesPassword (updated): false
}