-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeychain.go
83 lines (72 loc) · 2.21 KB
/
keychain.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
package main
import (
b64 "encoding/base64"
"errors"
keychain "github.com/keybase/go-keychain"
"github.com/vmihailenco/msgpack/v5"
)
const SERVICE_NAME = "aws-google-go"
type CredentialStorage[T any] interface {
StoreEntry(name string, data *T) error
GetEntry(name string) (*T, bool, error)
DeleteEntry(name string) error
}
type KeyringStorage[T any] struct{}
func (k *KeyringStorage[T]) StoreEntry(name string, data *T) error {
encoded, err := msgpack.Marshal(data)
if err != nil {
return err
}
s := b64.StdEncoding.EncodeToString(encoded)
item := keychain.NewItem()
item.SetSecClass(keychain.SecClassGenericPassword)
item.SetService(SERVICE_NAME)
item.SetAccount(name)
item.SetData([]byte(s))
item.SetSynchronizable(keychain.SynchronizableNo)
item.SetAccessible(keychain.AccessibleWhenUnlockedThisDeviceOnly)
err = keychain.AddItem(item)
if err == keychain.ErrorDuplicateItem {
err = keychain.UpdateItem(item, item)
}
return err
}
func (k *KeyringStorage[_]) DeleteEntry(name string) error {
item := keychain.NewItem()
item.SetSecClass(keychain.SecClassGenericPassword)
item.SetService(SERVICE_NAME)
item.SetAccount(name)
item.SetSynchronizable(keychain.SynchronizableNo)
item.SetAccessible(keychain.AccessibleWhenUnlockedThisDeviceOnly)
err := keychain.DeleteItem(item)
return err
}
func (k *KeyringStorage[T]) GetEntry(name string) (*T, bool, error) {
query := keychain.NewItem()
query.SetSecClass(keychain.SecClassGenericPassword)
query.SetService("aws-google-go")
query.SetAccount(name)
query.SetMatchLimit(keychain.MatchLimitOne)
query.SetReturnData(true)
results, err := keychain.QueryItem(query)
if err != nil {
return nil, false, err
} else if len(results) == 0 {
// Not found
return nil, false, nil
} else if len(results) > 1 {
// More than one entry found, requires user to intervene
return nil, false, errors.New("obtained more than one keychain entry for '" + SERVICE_NAME + "', cleanup your keychain")
} else {
var data T
b := results[0].Data
l := b64.StdEncoding.DecodedLen(len(b))
o := make([]byte, l)
rlen, err := b64.StdEncoding.Decode(o, b)
if err != nil {
return nil, false, err
}
err = msgpack.Unmarshal(o[0:rlen], &data)
return &data, true, err
}
}