master
go 347 lines 7.56 KB
Raw
1 package coreapi
2
3 import (
4 "context"
5 "crypto/rand"
6 "errors"
7 "fmt"
8 "sort"
9
10 "github.com/ipfs/boxo/ipns"
11 "github.com/ipfs/boxo/path"
12 coreiface "github.com/ipfs/kubo/core/coreiface"
13 caopts "github.com/ipfs/kubo/core/coreiface/options"
14 "github.com/ipfs/kubo/tracing"
15 crypto "github.com/libp2p/go-libp2p/core/crypto"
16 peer "github.com/libp2p/go-libp2p/core/peer"
17 "go.opentelemetry.io/otel/attribute"
18 "go.opentelemetry.io/otel/trace"
19 )
20
21 type KeyAPI CoreAPI
22
23 type key struct {
24 name string
25 peerID peer.ID
26 path path.Path
27 }
28
29 func newKey(name string, pid peer.ID) (*key, error) {
30 p, err := path.NewPath("/ipns/" + ipns.NameFromPeer(pid).String())
31 if err != nil {
32 return nil, fmt.Errorf("cannot create new key: %w", err)
33 }
34 return &key{
35 name: name,
36 peerID: pid,
37 path: p,
38 }, nil
39 }
40
41 // Name returns the key name
42 func (k *key) Name() string {
43 return k.name
44 }
45
46 // Path returns the path of the key.
47 func (k *key) Path() path.Path {
48 return k.path
49 }
50
51 // ID returns key PeerID
52 func (k *key) ID() peer.ID {
53 return k.peerID
54 }
55
56 // Generate generates new key, stores it in the keystore under the specified
57 // name and returns a base58 encoded multihash of its public key.
58 func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) {
59 _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "Generate", trace.WithAttributes(attribute.String("name", name)))
60 defer span.End()
61
62 options, err := caopts.KeyGenerateOptions(opts...)
63 if err != nil {
64 return nil, err
65 }
66
67 if name == "self" {
68 return nil, errors.New("cannot create key with name 'self'")
69 }
70
71 _, err = api.repo.Keystore().Get(name)
72 if err == nil {
73 return nil, fmt.Errorf("key with name '%s' already exists", name)
74 }
75
76 var sk crypto.PrivKey
77 var pk crypto.PubKey
78
79 switch options.Algorithm {
80 case "rsa":
81 if options.Size == -1 {
82 options.Size = caopts.DefaultRSALen
83 }
84
85 priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.RSA, options.Size, rand.Reader)
86 if err != nil {
87 return nil, err
88 }
89
90 sk = priv
91 pk = pub
92 case "ed25519":
93 priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
94 if err != nil {
95 return nil, err
96 }
97
98 sk = priv
99 pk = pub
100 default:
101 return nil, fmt.Errorf("unrecognized key type: %s", options.Algorithm)
102 }
103
104 err = api.repo.Keystore().Put(name, sk)
105 if err != nil {
106 return nil, err
107 }
108
109 pid, err := peer.IDFromPublicKey(pk)
110 if err != nil {
111 return nil, err
112 }
113
114 return newKey(name, pid)
115 }
116
117 // List returns a list keys stored in keystore.
118 func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) {
119 _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "List")
120 defer span.End()
121
122 keys, err := api.repo.Keystore().List()
123 if err != nil {
124 return nil, fmt.Errorf("cannot list keys in keystore: %w", err)
125 }
126
127 sort.Strings(keys)
128
129 out := make([]coreiface.Key, 1, len(keys)+1)
130 out[0], err = newKey("self", api.identity)
131 if err != nil {
132 return nil, err
133 }
134
135 for _, k := range keys {
136 privKey, err := api.repo.Keystore().Get(k)
137 if err != nil {
138 log.Errorf("cannot get key from keystore: %s", err)
139 continue
140 }
141
142 pubKey := privKey.GetPublic()
143
144 pid, err := peer.IDFromPublicKey(pubKey)
145 if err != nil {
146 log.Errorf("cannot decode public key: %s", err)
147 continue
148 }
149
150 k, err := newKey(k, pid)
151 if err != nil {
152 return nil, err
153 }
154 out = append(out, k)
155 }
156 return out, nil
157 }
158
159 // Rename renames `oldName` to `newName`. Returns the key and whether another
160 // key was overwritten, or an error.
161 func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, opts ...caopts.KeyRenameOption) (coreiface.Key, bool, error) {
162 _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "Rename", trace.WithAttributes(attribute.String("oldname", oldName), attribute.String("newname", newName)))
163 defer span.End()
164
165 options, err := caopts.KeyRenameOptions(opts...)
166 if err != nil {
167 return nil, false, err
168 }
169 span.SetAttributes(attribute.Bool("force", options.Force))
170
171 ks := api.repo.Keystore()
172
173 if oldName == "self" {
174 return nil, false, errors.New("cannot rename key with name 'self'")
175 }
176
177 if newName == "self" {
178 return nil, false, errors.New("cannot overwrite key with name 'self'")
179 }
180
181 oldKey, err := ks.Get(oldName)
182 if err != nil {
183 return nil, false, fmt.Errorf("no key named %s was found", oldName)
184 }
185
186 pubKey := oldKey.GetPublic()
187
188 pid, err := peer.IDFromPublicKey(pubKey)
189 if err != nil {
190 return nil, false, err
191 }
192
193 // This is important, because future code will delete key `oldName`
194 // even if it is the same as newName.
195 if newName == oldName {
196 k, err := newKey(oldName, pid)
197 return k, false, err
198 }
199
200 overwrite := false
201 if options.Force {
202 exist, err := ks.Has(newName)
203 if err != nil {
204 return nil, false, err
205 }
206
207 if exist {
208 overwrite = true
209 err := ks.Delete(newName)
210 if err != nil {
211 return nil, false, err
212 }
213 }
214 }
215
216 err = ks.Put(newName, oldKey)
217 if err != nil {
218 return nil, false, err
219 }
220
221 err = ks.Delete(oldName)
222 if err != nil {
223 return nil, false, err
224 }
225
226 k, err := newKey(newName, pid)
227 return k, overwrite, err
228 }
229
230 // Remove removes keys from keystore. Returns ipns path of the removed key.
231 func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Key, error) {
232 _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "Remove", trace.WithAttributes(attribute.String("name", name)))
233 defer span.End()
234
235 ks := api.repo.Keystore()
236
237 if name == "self" {
238 return nil, errors.New("cannot remove key with name 'self'")
239 }
240
241 removed, err := ks.Get(name)
242 if err != nil {
243 return nil, fmt.Errorf("no key named %s was found", name)
244 }
245
246 pubKey := removed.GetPublic()
247
248 pid, err := peer.IDFromPublicKey(pubKey)
249 if err != nil {
250 return nil, err
251 }
252
253 err = ks.Delete(name)
254 if err != nil {
255 return nil, err
256 }
257
258 return newKey("", pid)
259 }
260
261 func (api *KeyAPI) Self(ctx context.Context) (coreiface.Key, error) {
262 if api.identity == "" {
263 return nil, errors.New("identity not loaded")
264 }
265
266 return newKey("self", api.identity)
267 }
268
269 const signedMessagePrefix = "libp2p-key signed message:"
270
271 func (api *KeyAPI) Sign(ctx context.Context, name string, data []byte) (coreiface.Key, []byte, error) {
272 var (
273 sk crypto.PrivKey
274 err error
275 )
276 if name == "" || name == "self" {
277 name = "self"
278 sk = api.privateKey
279 } else {
280 sk, err = api.repo.Keystore().Get(name)
281 }
282 if err != nil {
283 return nil, nil, err
284 }
285
286 pid, err := peer.IDFromPrivateKey(sk)
287 if err != nil {
288 return nil, nil, err
289 }
290
291 key, err := newKey(name, pid)
292 if err != nil {
293 return nil, nil, err
294 }
295
296 data = append([]byte(signedMessagePrefix), data...)
297
298 sig, err := sk.Sign(data)
299 if err != nil {
300 return nil, nil, err
301 }
302
303 return key, sig, nil
304 }
305
306 func (api *KeyAPI) Verify(ctx context.Context, keyOrName string, signature, data []byte) (coreiface.Key, bool, error) {
307 var (
308 name string
309 pk crypto.PubKey
310 err error
311 )
312 if keyOrName == "" || keyOrName == "self" {
313 name = "self"
314 pk = api.privateKey.GetPublic()
315 } else if sk, err := api.repo.Keystore().Get(keyOrName); err == nil {
316 name = keyOrName
317 pk = sk.GetPublic()
318 } else if ipnsName, err := ipns.NameFromString(keyOrName); err == nil {
319 // This works for both IPNS names and Peer IDs.
320 name = ""
321 pk, err = ipnsName.Peer().ExtractPublicKey()
322 if err != nil {
323 return nil, false, err
324 }
325 } else {
326 return nil, false, fmt.Errorf("'%q' is not a known key, an IPNS Name, or a valid PeerID", keyOrName)
327 }
328
329 pid, err := peer.IDFromPublicKey(pk)
330 if err != nil {
331 return nil, false, err
332 }
333
334 key, err := newKey(name, pid)
335 if err != nil {
336 return nil, false, err
337 }
338
339 data = append([]byte(signedMessagePrefix), data...)
340
341 valid, err := pk.Verify(data, signature)
342 if err != nil {
343 return nil, false, err
344 }
345
346 return key, valid, nil
347 }