@cryptotaxi247 / kubo / commits / a0656402e

Key import and export cli commands

rendaw committed Jul 16, 2020 at 22:25 UTC a0656402ed9cb527aedee902b4e65f4cd63dfb7a
5 files changed +338 -37
core/commands/commands_test.go
+3
@@ -133,6 +133,9 @@ func TestCommands(t *testing.T) {
133 "/id",
134 "/key",
135 "/key/gen",
136 + "/key/export",
137 + "/key/import",
138 + "/key/identify",
139 "/key/list",
140 "/key/rename",
141 "/key/rm",
core/commands/keystore.go
+260 -19
@@ -7,8 +7,13 @@ import (
7
8 cmds "github.com/ipfs/go-ipfs-cmds"
9 cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
10 + "github.com/ipfs/go-ipfs/core/coreapi"
11 + repo "github.com/ipfs/go-ipfs/repo"
12 + fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
13 options "github.com/ipfs/interface-go-ipfs-core/options"
14 + "github.com/libp2p/go-libp2p-core/crypto"
15 peer "github.com/libp2p/go-libp2p-core/peer"
16 + "github.com/mr-tron/base58/base58"
17 mbase "github.com/multiformats/go-multibase"
18 )
19
@@ -30,10 +35,13 @@ publish'.
35 `,
36 },
37 Subcommands: map[string]*cmds.Command{
33 - "gen": keyGenCmd,
34 - "list": keyListCmd,
35 - "rename": keyRenameCmd,
36 - "rm": keyRmCmd,
38 + "gen": keyGenCmd,
39 + "export": keyExportCmd,
40 + "import": keyImportCmd,
41 + "identify": keyIdentifyCmd,
42 + "list": keyListCmd,
43 + "rename": keyRenameCmd,
44 + "rm": keyRmCmd,
45 },
46 }
47
@@ -42,6 +50,16 @@ type KeyOutput struct {
50 Id string
51 }
52
53 +type GenerateKeyOutput struct {
54 + Name string
55 + Id string
56 + Sk string
57 +}
58 +
59 +type ExportKeyOutput struct {
60 + Sk string
61 +}
62 +
63 type KeyOutputList struct {
64 Keys []KeyOutput
65 }
@@ -58,6 +76,8 @@ const (
76 keyStoreTypeOptionName = "type"
77 keyStoreSizeOptionName = "size"
78 keyFormatOptionName = "format"
79 + keyExportOptionName = "export"
80 + keyNoStoreOptionName = "no-store"
81 )
82
83 var keyGenCmd = &cmds.Command{
@@ -68,24 +88,57 @@ var keyGenCmd = &cmds.Command{
88 cmds.StringOption(keyStoreTypeOptionName, "t", "type of the key to create: rsa, ed25519").WithDefault("rsa"),
89 cmds.IntOption(keyStoreSizeOptionName, "s", "size of the key to generate"),
90 cmds.StringOption(keyFormatOptionName, "f", "output format: b58mh or b36cid").WithDefault("b58mh"),
91 + cmds.BoolOption(keyExportOptionName, "e", "return generated key for later re-import").WithDefault(false),
92 + cmds.BoolOption(keyNoStoreOptionName, "n", "don't add the key to the keychain").WithDefault(false),
93 },
94 Arguments: []cmds.Argument{
73 - cmds.StringArg("name", true, false, "name of key to create"),
95 + cmds.StringArg("name", false, false, "name of key to create, required unless -n specified"),
96 },
97 Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
76 - api, err := cmdenv.GetApi(env, req)
77 - if err != nil {
78 - return err
79 - }
80 -
98 typ, f := req.Options[keyStoreTypeOptionName].(string)
99 if !f {
100 return fmt.Errorf("please specify a key type with --type")
101 }
102
86 - name := req.Arguments[0]
87 - if name == "self" {
88 - return fmt.Errorf("cannot create key with name 'self'")
103 + store := !req.Options[keyNoStoreOptionName].(bool)
104 + export := req.Options[keyExportOptionName].(bool)
105 +
106 + var name string
107 + var r repo.Repo = nil
108 + defer func() {
109 + if r != nil {
110 + r.Close()
111 + }
112 + }()
113 + if store {
114 + if len(req.Arguments) == 0 {
115 + return fmt.Errorf("you must specify a key name")
116 + }
117 +
118 + name = req.Arguments[0]
119 +
120 + if name == "self" {
121 + return fmt.Errorf("cannot create key with name 'self'")
122 + }
123 +
124 + cfgRoot, err := cmdenv.GetConfigRoot(env)
125 + if err != nil {
126 + return err
127 + }
128 +
129 + r, err = fsrepo.Open(cfgRoot)
130 + if err != nil {
131 + return err
132 + }
133 +
134 + _, err = r.Keystore().Get(name)
135 + if err == nil {
136 + return fmt.Errorf("key with name '%s' already exists", name)
137 + }
138 + }
139 +
140 + if !store && !export {
141 + return fmt.Errorf("you must export key if not storing")
142 }
143
144 opts := []options.KeyGenerateOption{options.Key.Type(typ)}
@@ -94,28 +147,52 @@ var keyGenCmd = &cmds.Command{
147 if sizefound {
148 opts = append(opts, options.Key.Size(size))
149 }
97 - if err = verifyFormatLabel(req.Options[keyFormatOptionName].(string)); err != nil {
150 + if err := verifyFormatLabel(req.Options[keyFormatOptionName].(string)); err != nil {
151 return err
152 }
153
101 - key, err := api.Key().Generate(req.Context, name, opts...)
154 + sk, pk, err := coreapi.GenerateKey(opts...)
155 + if err != nil {
156 + return err
157 + }
158 +
159 + if store {
160 + err = r.Keystore().Put(name, sk)
161 + if err != nil {
162 + return err
163 + }
164 + }
165
166 + pid, err := peer.IDFromPublicKey(pk)
167 if err != nil {
168 return err
169 }
170
107 - return cmds.EmitOnce(res, &KeyOutput{
171 + var encoded string
172 + if export {
173 + encoded, err = encodeSKForExport(sk)
174 + if err != nil {
175 + return err
176 + }
177 + }
178 +
179 + return cmds.EmitOnce(res, &GenerateKeyOutput{
180 Name: name,
109 - Id: formatID(key.ID(), req.Options[keyFormatOptionName].(string)),
181 + Id: formatID(pid, req.Options[keyFormatOptionName].(string)),
182 + Sk: encoded,
183 })
184 },
185 Encoders: cmds.EncoderMap{
113 - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ko *KeyOutput) error {
186 + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ko *GenerateKeyOutput) error {
187 + if ko.Sk != "" {
188 + _, err := w.Write([]byte(ko.Sk + "\n"))
189 + return err
190 + }
191 _, err := w.Write([]byte(ko.Id + "\n"))
192 return err
193 }),
194 },
118 - Type: KeyOutput{},
195 + Type: GenerateKeyOutput{},
196 }
197
198 func verifyFormatLabel(formatLabel string) error {
@@ -143,6 +220,170 @@ func formatID(id peer.ID, formatLabel string) string {
220 }
221 }
222
223 +func encodeSKForExport(sk crypto.PrivKey) (string, error) {
224 + data, err := crypto.MarshalPrivateKey(sk)
225 + if err != nil {
226 + return "", err
227 + }
228 + return base58.Encode(data), nil
229 +}
230 +
231 +var keyExportCmd = &cmds.Command{
232 + Helptext: cmds.HelpText{
233 + Tagline: "Export a keypair",
234 + },
235 + Arguments: []cmds.Argument{
236 + cmds.StringArg("name", true, false, "name of key to export").EnableStdin(),
237 + },
238 + Options: []cmds.Option{},
239 + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
240 + name := req.Arguments[0]
241 +
242 + if name == "self" {
243 + return fmt.Errorf("exporting key 'self' is not allowed")
244 + }
245 +
246 + cfgRoot, err := cmdenv.GetConfigRoot(env)
247 + if err != nil {
248 + return err
249 + }
250 +
251 + r, err := fsrepo.Open(cfgRoot)
252 + if err != nil {
253 + return err
254 + }
255 + defer r.Close()
256 +
257 + sk, err := r.Keystore().Get(name)
258 + if err != nil {
259 + return fmt.Errorf("key with name '%s' doesn't exist", name)
260 + }
261 +
262 + encoded, err := encodeSKForExport(sk)
263 + if err != nil {
264 + return err
265 + }
266 +
267 + return cmds.EmitOnce(res, &ExportKeyOutput{
268 + Sk: encoded,
269 + })
270 + },
271 + Encoders: cmds.EncoderMap{
272 + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ko *ExportKeyOutput) error {
273 + _, err := w.Write([]byte(ko.Sk + "\n"))
274 + return err
275 + }),
276 + },
277 + Type: ExportKeyOutput{},
278 +}
279 +
280 +var keyImportCmd = &cmds.Command{
281 + Helptext: cmds.HelpText{
282 + Tagline: "Import a key and prints imported key id",
283 + },
284 + Options: []cmds.Option{
285 + cmds.StringOption(keyFormatOptionName, "f", "output format: b58mh or b36cid").WithDefault("b58mh"),
286 + },
287 + Arguments: []cmds.Argument{
288 + cmds.StringArg("name", true, false, "name to associate with key in keychain"),
289 + cmds.StringArg("key", true, false, "key provided by generate or export"),
290 + },
291 + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
292 + name := req.Arguments[0]
293 + encoded := req.Arguments[1]
294 +
295 + data, err := base58.Decode(encoded)
296 + if err != nil {
297 + return err
298 + }
299 +
300 + sk, err := crypto.UnmarshalPrivateKey(data)
301 + if err != nil {
302 + return err
303 + }
304 +
305 + cfgRoot, err := cmdenv.GetConfigRoot(env)
306 + if err != nil {
307 + return err
308 + }
309 +
310 + r, err := fsrepo.Open(cfgRoot)
311 + if err != nil {
312 + return err
313 + }
314 + defer r.Close()
315 +
316 + _, err = r.Keystore().Get(name)
317 + if err == nil {
318 + return fmt.Errorf("key with name '%s' already exists", name)
319 + }
320 +
321 + err = r.Keystore().Put(name, sk)
322 + if err != nil {
323 + return err
324 + }
325 +
326 + pid, err := peer.IDFromPrivateKey(sk)
327 + if err != nil {
328 + return err
329 + }
330 +
331 + return cmds.EmitOnce(res, &KeyOutput{
332 + Name: name,
333 + Id: formatID(pid, req.Options[keyFormatOptionName].(string)),
334 + })
335 + },
336 + Encoders: cmds.EncoderMap{
337 + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ko *KeyOutput) error {
338 + _, err := w.Write([]byte(ko.Id + "\n"))
339 + return err
340 + }),
341 + },
342 + Type: KeyOutput{},
343 +}
344 +
345 +var keyIdentifyCmd = &cmds.Command{
346 + Helptext: cmds.HelpText{
347 + Tagline: "Identify an exported keypair",
348 + },
349 + Options: []cmds.Option{
350 + cmds.StringOption(keyFormatOptionName, "f", "output format: b58mh or b36cid").WithDefault("b58mh"),
351 + },
352 + Arguments: []cmds.Argument{
353 + cmds.StringArg("key", true, false, "key provided by generate or export"),
354 + },
355 + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
356 + encoded := req.Arguments[0]
357 +
358 + data, err := base58.Decode(encoded)
359 + if err != nil {
360 + return err
361 + }
362 +
363 + sk, err := crypto.UnmarshalPrivateKey(data)
364 + if err != nil {
365 + return err
366 + }
367 +
368 + pid, err := peer.IDFromPrivateKey(sk)
369 + if err != nil {
370 + return err
371 + }
372 +
373 + return cmds.EmitOnce(res, &KeyOutput{
374 + Name: "",
375 + Id: formatID(pid, req.Options[keyFormatOptionName].(string)),
376 + })
377 + },
378 + Encoders: cmds.EncoderMap{
379 + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ko *KeyOutput) error {
380 + _, err := w.Write([]byte(ko.Id + "\n"))
381 + return err
382 + }),
383 + },
384 + Type: KeyOutput{},
385 +}
386 +
387 var keyListCmd = &cmds.Command{
388 Helptext: cmds.HelpText{
389 Tagline: "List all local keypairs",
core/coreapi/key.go
+26 -16
@@ -37,21 +37,11 @@ func (k *key) ID() peer.ID {
37 return k.peerID
38 }
39
40 -// Generate generates new key, stores it in the keystore under the specified
41 -// name and returns a base58 encoded multihash of its public key.
42 -func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) {
40 +// GenerateKey generates a new keypair and returns it
41 +func GenerateKey(opts ...caopts.KeyGenerateOption) (crypto.PrivKey, crypto.PubKey, error) {
42 options, err := caopts.KeyGenerateOptions(opts...)
43 if err != nil {
45 - return nil, err
46 - }
47 -
48 - if name == "self" {
49 - return nil, fmt.Errorf("cannot create key with name 'self'")
50 - }
51 -
52 - _, err = api.repo.Keystore().Get(name)
53 - if err == nil {
54 - return nil, fmt.Errorf("key with name '%s' already exists", name)
44 + return nil, nil, err
45 }
46
47 var sk crypto.PrivKey
@@ -65,7 +55,7 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key
55
56 priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.RSA, options.Size, rand.Reader)
57 if err != nil {
68 - return nil, err
58 + return nil, nil, err
59 }
60
61 sk = priv
@@ -73,13 +63,33 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key
63 case "ed25519":
64 priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
65 if err != nil {
76 - return nil, err
66 + return nil, nil, err
67 }
68
69 sk = priv
70 pk = pub
71 default:
82 - return nil, fmt.Errorf("unrecognized key type: %s", options.Algorithm)
72 + return nil, nil, fmt.Errorf("unrecognized key type: %s", options.Algorithm)
73 + }
74 +
75 + return sk, pk, nil
76 +}
77 +
78 +// Generate generates new key, stores it in the keystore under the specified
79 +// name and returns a base58 encoded multihash of its public key.
80 +func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) {
81 + if name == "self" {
82 + return nil, fmt.Errorf("cannot create key with name 'self'")
83 + }
84 +
85 + _, err := api.repo.Keystore().Get(name)
86 + if err == nil {
87 + return nil, fmt.Errorf("key with name '%s' already exists", name)
88 + }
89 +
90 + sk, pk, err := GenerateKey(opts...)
91 + if err != nil {
92 + return nil, err
93 }
94
95 err = api.repo.Keystore().Put(name, sk)
test/sharness/lib/test-lib.sh
+16
@@ -483,6 +483,22 @@ test_check_ed25519_b36cid_peerid() {
483 }
484 }
485
486 +test_check_rsa2048_sk() {
487 + peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
488 + test "$sklen" = "300" || {
489 + echo "Bad RSA2048 sk '$1' with len '$sklen'"
490 + return 1
491 + }
492 +}
493 +
494 +test_check_ed25519_sk() {
495 + sklen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
496 + test "$sklen" = "300" || {
497 + echo "Bad ED25519 sk '$1' with len '$sklen'"
498 + return 1
499 + }
500 +}
501 +
502 convert_tcp_maddr() {
503 echo $1 | awk -F'/' '{ printf "%s:%s", $3, $5 }'
504 }
test/sharness/t0165-keystore.sh
+33 -2
@@ -17,6 +17,11 @@ PEERID=$(ipfs key gen -f=b58mh --type=rsa --size=2048 key_rsa) &&
17 test_check_rsa2048_b58mh_peerid $PEERID
18 '
19
20 +test_expect_success "test RSA key B36CID sk export format" '
21 +PEERID=$(ipfs key export key_rsa) &&
22 +test_check_rsa2048_sk $PEERID
23 +'
24 +
25 test_expect_success "test RSA key B36CID multihash format" '
26 PEERID=$(ipfs key list -f=b36cid -l | grep key_rsa | head -n 1 | cut -d " " -f1) &&
27 test_check_rsa2048_b36cid_peerid $PEERID &&
@@ -28,6 +33,11 @@ PEERID=$(ipfs key gen -f=b36cid --type=ed25519 key_ed25519) &&
33 test_check_ed25519_b36cid_peerid $PEERID
34 '
35
36 +test_expect_success "test RSA key ED25519 sk export format" '
37 +PEERID=$(ipfs key export key_ed25519) &&
38 +test_check_ed25519_sk $PEERID
39 +'
40 +
41 test_expect_success "test ED25519 key B36CID multihash format" '
42 PEERID=$(ipfs key list -f=b36cid -l | grep key_ed25519 | head -n 1 | cut -d " " -f1) &&
43 test_check_ed25519_b36cid_peerid $PEERID &&
@@ -43,11 +53,32 @@ ipfs key rm key_ed25519
53 test_expect_success "create a new ed25519 key" '
54 edhash=$(ipfs key gen -f=b58mh bazed --type=ed25519)
55 '
56 +
57 + test_expect_success "import an rsa key" '
58 + imphash=$(ipfs key import -f=b58mh quxel B9bLmHeKLQU1hX23meSn2kJiNW7AZ31C6PBNSYumejXB13vxSVvViZDkEnchAH4BTs9yVnBNZKZYLwykaCohzxTntesTCVaBhZR2Br2Swav2NXwVBhfrUbaBTrR2248KfbVxSiUdkpFn8kcAmUGwp2KGMGRmq85WreGFDdAvzz8ruN2EFfWSHLc1YeUxHeUgKsQm3N13uF7q5x4qvjWM6yvMWNY7JtZrihT8BZQhgb2ezR4iYPjXZTtPZWsLbrrhUvHhxSn1NsTw6NZ7Jbs84qMXXnH56BmT8J9LugRhQQvgBquSoS1m7aeD2y1L1A7mueVDYGLDzgxRSt5CohY7VVMdheUpPiq44CicuYt5YgbbuE3wMHntn6sd9QSYw7f4SjjKdw7Jhy5fNW29SkHvpLfZKfqzBNhSfHsofXVEASDpfr5mqws1eQTztqvvZhHXQxAoxxP3PK6TfDnATdLdV3Cy5v6nLt7ppsBj54hif5EZneHMLeYP8bYLbELQ2fZdoprpnKVBsMY1nWvgrMKUTpLjoKB6bzAZYuXaYVtmrdhESmKCyE12yEyvCcD8DJQU6JjqaD1DyVSPNkL3ze26Mm3ZyiFEH7M4XirUsPLrsj41Qt1xGaVGNEdkdthihytTZxxnmgyAptZMUNfSviBfH1tVbfoXFtBGU8eYMLdSHFqxSktT1mqeiWatxMQZ8pTeA9VCvAp9RTSRFkTQR9uP6w6qTzRD9cFcH4HyCEc5TJpiZdP7u9RjaEo2S3P9VkHfmqCH4McpLgw7He9nm7rf9JA2Gh7ubTKy5e6dJUWojgYhGS4KGe3yKGFhLaNgRiME63fUEFSnN2ZvCSM9qsrj34q2h8962xBod9hCVEDfk4tfmHu1UHX5AGaW6mk3pzqKKVYTTWXi84JSH7vzKPmQuhwaAR9Ye3Jbdzehp4xhrT5aFCjnz3r5qNv2zz48Fq5bGc1RUh88PUMT3z6kuzv6B1eXTLYpeu9gGdjc5C9DQDTYPfcHWn7dSHr4AGV1sN6SwVy8W5LZdMAZaeXCDn9iXDwbeD2DYd2ozVCEzceygVzpVdnueNx5FmG6zHtGzfuStr4Jj85sbd2jUGh4ES2bMU41jw2gJ6ujjf6CrxZpCWhXz6NJpAS9njcDFXuspf7otbMjCB6TzwokJwEse31nGUZQdhQgXn23vnZtxwCV621uXFbm7xVACRZKeuXgw8VdEVaXGvf2V4DdhjZnjmePBbTeJ7WjABavLcpMqZJH7FgaLxazFqk9RXtnfUEbVAAhuZzxz6L8Z6axHwz3a4EZtALRjfFjn6xjaUtsWXYW8P6F7femM6UHx3qXMo43hKC7oxnd6Tfta972dgyQfSoBwWkWzB8cvaJreNh4bdLNkw6mty86NXGKyijv83LR1HjbnUoTwPbEMX8JyzLfMf3qiWzwf6MHrXprwygmEpNc6w8tNNivmcWyCX3wmPkMKK1bmi5TCHoUtRrxcyXKhmuyo6zzag8KyK6iZaRbMiFiUJBi5VYwidMutkexWo8SRqfSV5yp2kxswknmpeVTnXBhVEy3anMEiD6bV48AnbF6SfKAi2DGBFqxBFfpFEbYtPauHiYYzZX1epqvKxY23xA9J8FosMk4yYN4Ps7Rh)
59 + '
60
47 - test_expect_success "both keys show up in list output" '
61 + test_expect_success "identify an rsa key" '
62 + gothash=$(ipfs key identify B9bLmHeKLQU1hX23meSn2kJiNW7AZ31C6PBNSYumejXB13vxSVvViZDkEnchAH4BTs9yVnBNZKZYLwykaCohzxTntesTCVaBhZR2Br2Swav2NXwVBhfrUbaBTrR2248KfbVxSiUdkpFn8kcAmUGwp2KGMGRmq85WreGFDdAvzz8ruN2EFfWSHLc1YeUxHeUgKsQm3N13uF7q5x4qvjWM6yvMWNY7JtZrihT8BZQhgb2ezR4iYPjXZTtPZWsLbrrhUvHhxSn1NsTw6NZ7Jbs84qMXXnH56BmT8J9LugRhQQvgBquSoS1m7aeD2y1L1A7mueVDYGLDzgxRSt5CohY7VVMdheUpPiq44CicuYt5YgbbuE3wMHntn6sd9QSYw7f4SjjKdw7Jhy5fNW29SkHvpLfZKfqzBNhSfHsofXVEASDpfr5mqws1eQTztqvvZhHXQxAoxxP3PK6TfDnATdLdV3Cy5v6nLt7ppsBj54hif5EZneHMLeYP8bYLbELQ2fZdoprpnKVBsMY1nWvgrMKUTpLjoKB6bzAZYuXaYVtmrdhESmKCyE12yEyvCcD8DJQU6JjqaD1DyVSPNkL3ze26Mm3ZyiFEH7M4XirUsPLrsj41Qt1xGaVGNEdkdthihytTZxxnmgyAptZMUNfSviBfH1tVbfoXFtBGU8eYMLdSHFqxSktT1mqeiWatxMQZ8pTeA9VCvAp9RTSRFkTQR9uP6w6qTzRD9cFcH4HyCEc5TJpiZdP7u9RjaEo2S3P9VkHfmqCH4McpLgw7He9nm7rf9JA2Gh7ubTKy5e6dJUWojgYhGS4KGe3yKGFhLaNgRiME63fUEFSnN2ZvCSM9qsrj34q2h8962xBod9hCVEDfk4tfmHu1UHX5AGaW6mk3pzqKKVYTTWXi84JSH7vzKPmQuhwaAR9Ye3Jbdzehp4xhrT5aFCjnz3r5qNv2zz48Fq5bGc1RUh88PUMT3z6kuzv6B1eXTLYpeu9gGdjc5C9DQDTYPfcHWn7dSHr4AGV1sN6SwVy8W5LZdMAZaeXCDn9iXDwbeD2DYd2ozVCEzceygVzpVdnueNx5FmG6zHtGzfuStr4Jj85sbd2jUGh4ES2bMU41jw2gJ6ujjf6CrxZpCWhXz6NJpAS9njcDFXuspf7otbMjCB6TzwokJwEse31nGUZQdhQgXn23vnZtxwCV621uXFbm7xVACRZKeuXgw8VdEVaXGvf2V4DdhjZnjmePBbTeJ7WjABavLcpMqZJH7FgaLxazFqk9RXtnfUEbVAAhuZzxz6L8Z6axHwz3a4EZtALRjfFjn6xjaUtsWXYW8P6F7femM6UHx3qXMo43hKC7oxnd6Tfta972dgyQfSoBwWkWzB8cvaJreNh4bdLNkw6mty86NXGKyijv83LR1HjbnUoTwPbEMX8JyzLfMf3qiWzwf6MHrXprwygmEpNc6w8tNNivmcWyCX3wmPkMKK1bmi5TCHoUtRrxcyXKhmuyo6zzag8KyK6iZaRbMiFiUJBi5VYwidMutkexWo8SRqfSV5yp2kxswknmpeVTnXBhVEy3anMEiD6bV48AnbF6SfKAi2DGBFqxBFfpFEbYtPauHiYYzZX1epqvKxY23xA9J8FosMk4yYN4Ps7Rh) &&
63 + test $imphash = $gothash
64 + '
65 +
66 + test_expect_success "key import can't export self" '
67 + test_must_fail ipfs key export self 2>&1 | tee key_exp_out &&
68 + grep -q "Error: cannot export key with name" key_exp_out
69 + '
70 +
71 + test_expect_success "key import can't import self" '
72 + test_must_fail ipfs key import self B9bLmHeKLQU1hX23meSn2kJiNW7AZ31C6PBNSYumejXB13vxSVvViZDkEnchAH4BTs9yVnBNZKZYLwykaCohzxTntesTCVaBhZR2Br2Swav2NXwVBhfrUbaBTrR2248KfbVxSiUdkpFn8kcAmUGwp2KGMGRmq85WreGFDdAvzz8ruN2EFfWSHLc1YeUxHeUgKsQm3N13uF7q5x4qvjWM6yvMWNY7JtZrihT8BZQhgb2ezR4iYPjXZTtPZWsLbrrhUvHhxSn1NsTw6NZ7Jbs84qMXXnH56BmT8J9LugRhQQvgBquSoS1m7aeD2y1L1A7mueVDYGLDzgxRSt5CohY7VVMdheUpPiq44CicuYt5YgbbuE3wMHntn6sd9QSYw7f4SjjKdw7Jhy5fNW29SkHvpLfZKfqzBNhSfHsofXVEASDpfr5mqws1eQTztqvvZhHXQxAoxxP3PK6TfDnATdLdV3Cy5v6nLt7ppsBj54hif5EZneHMLeYP8bYLbELQ2fZdoprpnKVBsMY1nWvgrMKUTpLjoKB6bzAZYuXaYVtmrdhESmKCyE12yEyvCcD8DJQU6JjqaD1DyVSPNkL3ze26Mm3ZyiFEH7M4XirUsPLrsj41Qt1xGaVGNEdkdthihytTZxxnmgyAptZMUNfSviBfH1tVbfoXFtBGU8eYMLdSHFqxSktT1mqeiWatxMQZ8pTeA9VCvAp9RTSRFkTQR9uP6w6qTzRD9cFcH4HyCEc5TJpiZdP7u9RjaEo2S3P9VkHfmqCH4McpLgw7He9nm7rf9JA2Gh7ubTKy5e6dJUWojgYhGS4KGe3yKGFhLaNgRiME63fUEFSnN2ZvCSM9qsrj34q2h8962xBod9hCVEDfk4tfmHu1UHX5AGaW6mk3pzqKKVYTTWXi84JSH7vzKPmQuhwaAR9Ye3Jbdzehp4xhrT5aFCjnz3r5qNv2zz48Fq5bGc1RUh88PUMT3z6kuzv6B1eXTLYpeu9gGdjc5C9DQDTYPfcHWn7dSHr4AGV1sN6SwVy8W5LZdMAZaeXCDn9iXDwbeD2DYd2ozVCEzceygVzpVdnueNx5FmG6zHtGzfuStr4Jj85sbd2jUGh4ES2bMU41jw2gJ6ujjf6CrxZpCWhXz6NJpAS9njcDFXuspf7otbMjCB6TzwokJwEse31nGUZQdhQgXn23vnZtxwCV621uXFbm7xVACRZKeuXgw8VdEVaXGvf2V4DdhjZnjmePBbTeJ7WjABavLcpMqZJH7FgaLxazFqk9RXtnfUEbVAAhuZzxz6L8Z6axHwz3a4EZtALRjfFjn6xjaUtsWXYW8P6F7femM6UHx3qXMo43hKC7oxnd6Tfta972dgyQfSoBwWkWzB8cvaJreNh4bdLNkw6mty86NXGKyijv83LR1HjbnUoTwPbEMX8JyzLfMf3qiWzwf6MHrXprwygmEpNc6w8tNNivmcWyCX3wmPkMKK1bmi5TCHoUtRrxcyXKhmuyo6zzag8KyK6iZaRbMiFiUJBi5VYwidMutkexWo8SRqfSV5yp2kxswknmpeVTnXBhVEy3anMEiD6bV48AnbF6SfKAi2DGBFqxBFfpFEbYtPauHiYYzZX1epqvKxY23xA9J8FosMk4yYN4Ps7Rh 2>&1 | tee key_imp_out &&
73 + grep -q "Error: cannot import key with name" key_imp_out
74 + '
75 +
76 + test_expect_success "all keys show up in list output" '
77 echo bazed > list_exp &&
78 echo foobarsa >> list_exp &&
50 - echo self >> list_exp
79 + echo quxel >> list_exp &&
80 + echo self >> list_exp &&
81 + sort -o list_exp list_exp
82 ipfs key list -f=b58mh | sort > list_out &&
83 test_cmp list_exp list_out
84 '