-
Notifications
You must be signed in to change notification settings - Fork 879
/
fs_keystore.go
145 lines (117 loc) · 3.34 KB
/
fs_keystore.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package keystore
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
)
// ErrNotFound is returned when the key does not exist.
var ErrNotFound = errors.New("keystore: key not found")
// fsKeystore implements persistent Keystore over OS filesystem.
type fsKeystore struct {
path string
ring keyring.Keyring
}
// NewFSKeystore creates a new Keystore over OS filesystem.
// The path must point to a directory. It is created automatically if necessary.
func NewFSKeystore(path string, ring keyring.Keyring) (Keystore, error) {
err := os.Mkdir(path, 0o755)
if err != nil && !os.IsExist(err) {
return nil, fmt.Errorf("keystore: failed to make a dir: %w", err)
}
return &fsKeystore{
path: path,
ring: ring,
}, nil
}
func (f *fsKeystore) Put(n KeyName, pk PrivKey) error {
path := f.pathTo(n.Base32())
_, err := os.Stat(path)
if err == nil {
return fmt.Errorf("keystore: key '%s' already exists", n)
} else if !os.IsNotExist(err) {
return fmt.Errorf("keystore: check before writing key '%s' failed: %w", n, err)
}
data, err := json.Marshal(pk)
if err != nil {
return fmt.Errorf("keystore: failed to marshal key '%s': %w", n, err)
}
err = os.WriteFile(path, data, 0o600)
if err != nil {
return fmt.Errorf("keystore: failed to write key '%s': %w", n, err)
}
return nil
}
func (f *fsKeystore) Get(n KeyName) (PrivKey, error) {
path := f.pathTo(n.Base32())
st, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return PrivKey{}, fmt.Errorf("%w: %s", ErrNotFound, n)
}
return PrivKey{}, fmt.Errorf("keystore: check before reading key '%s' failed: %w", n, err)
}
if err := checkPerms(st.Mode()); err != nil {
return PrivKey{}, fmt.Errorf("keystore: permissions of key '%s' are too relaxed: %w", n, err)
}
data, err := os.ReadFile(path)
if err != nil {
return PrivKey{}, fmt.Errorf("keystore: failed read key '%s': %w", n, err)
}
var key PrivKey
err = json.Unmarshal(data, &key)
if err != nil {
return PrivKey{}, fmt.Errorf("keystore: failed to unmarshal key '%s': %w", n, err)
}
return key, nil
}
func (f *fsKeystore) Delete(n KeyName) error {
path := f.pathTo(n.Base32())
_, err := os.Stat(path)
if os.IsNotExist(err) {
return fmt.Errorf("keystore: key '%s' not found", n)
} else if err != nil {
return fmt.Errorf("keystore: check before reading key '%s' failed: %w", n, err)
}
err = os.Remove(path)
if err != nil {
return fmt.Errorf("keystore: failed to delete key '%s': %w", n, err)
}
return nil
}
func (f *fsKeystore) List() ([]KeyName, error) {
entries, err := fs.ReadDir(os.DirFS(filepath.Dir(f.path)), filepath.Base(f.path))
if err != nil {
return nil, err
}
names := make([]KeyName, len(entries))
for i, e := range entries {
kn, err := KeyNameFromBase32(e.Name())
if err != nil {
return nil, err
}
if err := checkPerms(e.Type()); err != nil {
return nil, fmt.Errorf("keystore: permissions of key '%s' are too relaxed: %w", kn, err)
}
names[i] = kn
}
return names, nil
}
func (f *fsKeystore) Path() string {
return f.path
}
func (f *fsKeystore) Keyring() keyring.Keyring {
return f.ring
}
func (f *fsKeystore) pathTo(file string) string {
return filepath.Join(f.path, file)
}
func checkPerms(perms os.FileMode) error {
if perms&0o077 != 0 {
return fmt.Errorf("required: 0600, got: %#o", perms)
}
return nil
}