OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / tendermint / go-crypto / keys / storage / filestorage / main.go
1 /*
2 package filestorage provides a secure on-disk storage of private keys and
3 metadata.  Security is enforced by file and directory permissions, much
4 like standard ssh key storage.
5 */
6 package filestorage
7
8 import (
9         "fmt"
10         "io/ioutil"
11         "os"
12         "path"
13         "strings"
14
15         "github.com/pkg/errors"
16         crypto "github.com/tendermint/go-crypto"
17         keys "github.com/tendermint/go-crypto/keys"
18 )
19
20 const (
21         BlockType = "Tendermint Light Client"
22
23         // PrivExt is the extension for private keys.
24         PrivExt = "tlc"
25         // PubExt is the extensions for public keys.
26         PubExt = "pub"
27
28         keyPerm = os.FileMode(0600)
29         // pubPerm = os.FileMode(0644)
30         dirPerm = os.FileMode(0700)
31 )
32
33 type FileStore struct {
34         keyDir string
35 }
36
37 // New creates an instance of file-based key storage with tight permissions
38 //
39 // dir should be an absolute path of a directory owner by this user. It will
40 // be created if it doesn't exist already.
41 func New(dir string) FileStore {
42         err := os.MkdirAll(dir, dirPerm)
43         if err != nil {
44                 panic(err)
45         }
46         return FileStore{dir}
47 }
48
49 // assert FileStore satisfies keys.Storage
50 var _ keys.Storage = FileStore{}
51
52 // Put creates two files, one with the public info as json, the other
53 // with the (encoded) private key as gpg ascii-armor style
54 func (s FileStore) Put(name string, key []byte, info keys.Info) error {
55         pub, priv := s.nameToPaths(name)
56
57         // write public info
58         err := writeInfo(pub, info)
59         if err != nil {
60                 return err
61         }
62
63         // write private info
64         return write(priv, name, key)
65 }
66
67 // Get loads the info and (encoded) private key from the directory
68 // It uses `name` to generate the filename, and returns an error if the
69 // files don't exist or are in the incorrect format
70 func (s FileStore) Get(name string) ([]byte, keys.Info, error) {
71         pub, priv := s.nameToPaths(name)
72
73         info, err := readInfo(pub)
74         if err != nil {
75                 return nil, info, err
76         }
77
78         key, _, err := read(priv)
79         return key, info.Format(), err
80 }
81
82 // List parses the key directory for public info and returns a list of
83 // Info for all keys located in this directory.
84 func (s FileStore) List() (keys.Infos, error) {
85         dir, err := os.Open(s.keyDir)
86         if err != nil {
87                 return nil, errors.Wrap(err, "List Keys")
88         }
89         defer dir.Close()
90
91         names, err := dir.Readdirnames(0)
92         if err != nil {
93                 return nil, errors.Wrap(err, "List Keys")
94         }
95
96         // filter names for .pub ending and load them one by one
97         // half the files is a good guess for pre-allocating the slice
98         infos := make([]keys.Info, 0, len(names)/2)
99         for _, name := range names {
100                 if strings.HasSuffix(name, PubExt) {
101                         p := path.Join(s.keyDir, name)
102                         info, err := readInfo(p)
103                         if err != nil {
104                                 return nil, err
105                         }
106                         infos = append(infos, info.Format())
107                 }
108         }
109
110         return infos, nil
111 }
112
113 // Delete permanently removes the public and private info for the named key
114 // The calling function should provide some security checks first.
115 func (s FileStore) Delete(name string) error {
116         pub, priv := s.nameToPaths(name)
117         err := os.Remove(priv)
118         if err != nil {
119                 return errors.Wrap(err, "Deleting Private Key")
120         }
121         err = os.Remove(pub)
122         return errors.Wrap(err, "Deleting Public Key")
123 }
124
125 func (s FileStore) nameToPaths(name string) (pub, priv string) {
126         privName := fmt.Sprintf("%s.%s", name, PrivExt)
127         pubName := fmt.Sprintf("%s.%s", name, PubExt)
128         return path.Join(s.keyDir, pubName), path.Join(s.keyDir, privName)
129 }
130
131 func writeInfo(path string, info keys.Info) error {
132         return write(path, info.Name, info.PubKey.Bytes())
133 }
134
135 func readInfo(path string) (info keys.Info, err error) {
136         var data []byte
137         data, info.Name, err = read(path)
138         if err != nil {
139                 return
140         }
141         pk, err := crypto.PubKeyFromBytes(data)
142         info.PubKey = pk
143         return
144 }
145
146 func read(path string) ([]byte, string, error) {
147         f, err := os.Open(path)
148         if err != nil {
149                 return nil, "", errors.Wrap(err, "Reading data")
150         }
151         defer f.Close()
152
153         d, err := ioutil.ReadAll(f)
154         if err != nil {
155                 return nil, "", errors.Wrap(err, "Reading data")
156         }
157         block, headers, key, err := crypto.DecodeArmor(string(d))
158         if err != nil {
159                 return nil, "", errors.Wrap(err, "Invalid Armor")
160         }
161         if block != BlockType {
162                 return nil, "", errors.Errorf("Unknown key type: %s", block)
163         }
164         return key, headers["name"], nil
165 }
166
167 func write(path, name string, key []byte) error {
168         f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, keyPerm)
169         if err != nil {
170                 return errors.Wrap(err, "Writing data")
171         }
172         defer f.Close()
173         headers := map[string]string{"name": name}
174         text := crypto.EncodeArmor(BlockType, headers, key)
175         _, err = f.WriteString(text)
176         return errors.Wrap(err, "Writing data")
177 }