keystore.go 6.2 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2 3 4 5
package commands

import (
	"fmt"
	"io"
6
	"text/tabwriter"
Jeromy's avatar
Jeromy committed
7

8
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
9 10
	"github.com/ipfs/go-ipfs/core/commands/e"
	"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
Jeromy's avatar
Jeromy committed
11

12 13
	"gx/ipfs/QmPTfgFTo9PFr1PvPKyKoeMgBvYPh6cX3aDP7DHKVbnCbi/go-ipfs-cmds"
	"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
Jeromy's avatar
Jeromy committed
14 15 16
)

var KeyCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
17
	Helptext: cmdkit.HelpText{
18 19
		Tagline: "Create and list IPNS name keypairs",
		ShortDescription: `
20 21
'ipfs key gen' generates a new keypair for usage with IPNS and 'ipfs name
publish'.
22 23 24 25 26 27 28 29 30 31

  > ipfs key gen --type=rsa --size=2048 mykey
  > ipfs name publish --key=mykey QmSomeHash

'ipfs key list' lists the available keys.

  > ipfs key list
  self
  mykey
		`,
Jeromy's avatar
Jeromy committed
32 33
	},
	Subcommands: map[string]*cmds.Command{
Michael Muré's avatar
Michael Muré committed
34 35 36 37
		"gen":    keyGenCmd,
		"list":   keyListCmd,
		"rename": keyRenameCmd,
		"rm":     keyRmCmd,
Jeromy's avatar
Jeromy committed
38 39 40 41 42 43 44 45
	},
}

type KeyOutput struct {
	Name string
	Id   string
}

46 47 48 49
type KeyOutputList struct {
	Keys []KeyOutput
}

Michael Muré's avatar
Michael Muré committed
50
// KeyRenameOutput define the output type of keyRenameCmd
Michael Muré's avatar
Michael Muré committed
51 52 53 54 55 56 57
type KeyRenameOutput struct {
	Was       string
	Now       string
	Id        string
	Overwrite bool
}

58
var keyGenCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
59
	Helptext: cmdkit.HelpText{
Jeromy's avatar
Jeromy committed
60 61
		Tagline: "Create a new keypair",
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
62 63 64
	Options: []cmdkit.Option{
		cmdkit.StringOption("type", "t", "type of the key to create [rsa, ed25519]"),
		cmdkit.IntOption("size", "s", "size of the key to generate"),
Jeromy's avatar
Jeromy committed
65
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
66 67
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("name", true, false, "name of key to create"),
Jeromy's avatar
Jeromy committed
68
	},
69
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
70
		api, err := cmdenv.GetApi(env)
Jeromy's avatar
Jeromy committed
71
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
72
			res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
73 74 75
			return
		}

76
		typ, f := req.Options["type"].(string)
Jeromy's avatar
Jeromy committed
77
		if !f {
Jan Winkelmann's avatar
Jan Winkelmann committed
78
			res.SetError(fmt.Errorf("please specify a key type with --type"), cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
79 80 81
			return
		}

82
		name := req.Arguments[0]
Jeromy's avatar
Jeromy committed
83
		if name == "self" {
Jan Winkelmann's avatar
Jan Winkelmann committed
84
			res.SetError(fmt.Errorf("cannot create key with name 'self'"), cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
85 86 87
			return
		}

88
		opts := []options.KeyGenerateOption{options.Key.Type(typ)}
Jeromy's avatar
Jeromy committed
89

90 91 92
		size, sizefound := req.Options["size"].(int)
		if sizefound {
			opts = append(opts, options.Key.Size(size))
Jeromy's avatar
Jeromy committed
93 94
		}

95
		key, err := api.Key().Generate(req.Context, name, opts...)
Jeromy's avatar
Jeromy committed
96 97

		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
98
			res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
99 100 101
			return
		}

102
		cmds.EmitOnce(res, &KeyOutput{
Jeromy's avatar
Jeromy committed
103
			Name: name,
104
			Id:   key.ID().Pretty(),
Jeromy's avatar
Jeromy committed
105 106
		})
	},
107 108
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
109
			k, ok := v.(*KeyOutput)
Jeromy's avatar
Jeromy committed
110
			if !ok {
111
				return e.TypeErr(k, v)
Jeromy's avatar
Jeromy committed
112 113
			}

114 115 116
			_, err := w.Write([]byte(k.Id + "\n"))
			return err
		}),
Jeromy's avatar
Jeromy committed
117 118 119 120
	},
	Type: KeyOutput{},
}

121
var keyListCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
122
	Helptext: cmdkit.HelpText{
Jeromy's avatar
Jeromy committed
123 124
		Tagline: "List all local keypairs",
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
125 126
	Options: []cmdkit.Option{
		cmdkit.BoolOption("l", "Show extra information about keys."),
127
	},
128
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
129
		api, err := cmdenv.GetApi(env)
Jeromy's avatar
Jeromy committed
130
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
131
			res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
132 133 134
			return
		}

135
		keys, err := api.Key().List(req.Context)
Jeromy's avatar
Jeromy committed
136
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
137
			res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
138 139 140
			return
		}

141
		list := make([]KeyOutput, 0, len(keys))
142 143

		for _, key := range keys {
144
			list = append(list, KeyOutput{Name: key.Name(), Id: key.ID().Pretty()})
145 146
		}

147
		cmds.EmitOnce(res, &KeyOutputList{list})
Jeromy's avatar
Jeromy committed
148
	},
149 150
	Encoders: cmds.EncoderMap{
		cmds.Text: keyOutputListMarshaler(),
Jeromy's avatar
Jeromy committed
151
	},
152 153 154
	Type: KeyOutputList{},
}

Michael Muré's avatar
Michael Muré committed
155
var keyRenameCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
156
	Helptext: cmdkit.HelpText{
Michael Muré's avatar
Michael Muré committed
157 158
		Tagline: "Rename a keypair",
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
159 160 161
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("name", true, false, "name of key to rename"),
		cmdkit.StringArg("newName", true, false, "new name of the key"),
Michael Muré's avatar
Michael Muré committed
162
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
163 164
	Options: []cmdkit.Option{
		cmdkit.BoolOption("force", "f", "Allow to overwrite an existing key."),
Michael Muré's avatar
Michael Muré committed
165
	},
166
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
167
		api, err := cmdenv.GetApi(env)
Michael Muré's avatar
Michael Muré committed
168
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
169
			res.SetError(err, cmdkit.ErrNormal)
Michael Muré's avatar
Michael Muré committed
170 171 172
			return
		}

173 174
		name := req.Arguments[0]
		newName := req.Arguments[1]
175
		force, _ := req.Options["force"].(bool)
Michael Muré's avatar
Michael Muré committed
176

177
		key, overwritten, err := api.Key().Rename(req.Context, name, newName, options.Key.Force(force))
Michael Muré's avatar
Michael Muré committed
178
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
179
			res.SetError(err, cmdkit.ErrNormal)
Michael Muré's avatar
Michael Muré committed
180 181 182
			return
		}

183
		cmds.EmitOnce(res, &KeyRenameOutput{
Michael Muré's avatar
Michael Muré committed
184 185
			Was:       name,
			Now:       newName,
186
			Id:        key.ID().Pretty(),
187
			Overwrite: overwritten,
Michael Muré's avatar
Michael Muré committed
188 189
		})
	},
190 191
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
192
			k, ok := v.(*KeyRenameOutput)
Michael Muré's avatar
Michael Muré committed
193
			if !ok {
194
				return fmt.Errorf("expected a KeyRenameOutput as command result")
Michael Muré's avatar
Michael Muré committed
195 196 197
			}

			if k.Overwrite {
198
				fmt.Fprintf(w, "Key %s renamed to %s with overwriting\n", k.Id, k.Now)
Michael Muré's avatar
Michael Muré committed
199
			} else {
200
				fmt.Fprintf(w, "Key %s renamed to %s\n", k.Id, k.Now)
Michael Muré's avatar
Michael Muré committed
201
			}
202 203
			return nil
		}),
Michael Muré's avatar
Michael Muré committed
204 205 206 207
	},
	Type: KeyRenameOutput{},
}

208
var keyRmCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
209
	Helptext: cmdkit.HelpText{
Michael Muré's avatar
Michael Muré committed
210 211
		Tagline: "Remove a keypair",
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
212 213
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("name", true, true, "names of keys to remove").EnableStdin(),
Michael Muré's avatar
Michael Muré committed
214
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
215 216
	Options: []cmdkit.Option{
		cmdkit.BoolOption("l", "Show extra information about keys."),
Michael Muré's avatar
Michael Muré committed
217
	},
218
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
219
		api, err := cmdenv.GetApi(env)
Michael Muré's avatar
Michael Muré committed
220
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
221
			res.SetError(err, cmdkit.ErrNormal)
Michael Muré's avatar
Michael Muré committed
222 223 224
			return
		}

225
		names := req.Arguments
Michael Muré's avatar
Michael Muré committed
226

Michael Muré's avatar
Michael Muré committed
227 228
		list := make([]KeyOutput, 0, len(names))
		for _, name := range names {
229
			key, err := api.Key().Remove(req.Context, name)
Michael Muré's avatar
Michael Muré committed
230
			if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
231
				res.SetError(err, cmdkit.ErrNormal)
Michael Muré's avatar
Michael Muré committed
232 233 234
				return
			}

235
			list = append(list, KeyOutput{Name: name, Id: key.ID().Pretty()})
Michael Muré's avatar
Michael Muré committed
236 237
		}

238
		cmds.EmitOnce(res, &KeyOutputList{list})
Michael Muré's avatar
Michael Muré committed
239
	},
240 241
	Encoders: cmds.EncoderMap{
		cmds.Text: keyOutputListMarshaler(),
Michael Muré's avatar
Michael Muré committed
242
	},
Michael Muré's avatar
Michael Muré committed
243
	Type: KeyOutputList{},
Michael Muré's avatar
Michael Muré committed
244 245
}

246 247
func keyOutputListMarshaler() cmds.EncoderFunc {
	return cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
248
		withID, _ := req.Options["l"].(bool)
249 250 251 252 253 254 255 256

		list, ok := v.(*KeyOutputList)
		if !ok {
			return e.TypeErr(list, v)
		}

		tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
		for _, s := range list.Keys {
257
			if withID {
258 259 260 261
				fmt.Fprintf(tw, "%s\t%s\t\n", s.Id, s.Name)
			} else {
				fmt.Fprintf(tw, "%s\n", s.Name)
			}
262
		}
263 264 265
		tw.Flush()
		return nil
	})
Jeromy's avatar
Jeromy committed
266
}