Skip to content

cmd/clef: add newaccount command #20782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cmd/clef/intapi_changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the:

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

### 7.0.1

Added `clef_New` to the internal API calleable from a UI.

> `New` creates a new password protected Account. The private key is protected with
> the given password. Users are responsible to backup the private key that is stored
> in the keystore location that was specified when this API was created.
> This method is the same as New on the external API, the difference being that
> this implementation does not ask for confirmation, since it's initiated by
> the user

### 7.0.0

- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value.
Expand Down
48 changes: 46 additions & 2 deletions cmd/clef/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,21 @@ The setpw command stores a password for a given address (keyfile).
Description: `
The delpw command removes a password for a given address (keyfile).
`}
newAccountCommand = cli.Command{
Action: utils.MigrateFlags(newAccount),
Name: "newaccount",
Usage: "Create a new account",
ArgsUsage: "",
Flags: []cli.Flag{
logLevelFlag,
keystoreFlag,
utils.LightKDFFlag,
},
Description: `
The newaccount command creates a new keystore-backed account. It is a convenience-method
which can be used in lieu of an external UI.`,
}

gendocCommand = cli.Command{
Action: GenDoc,
Name: "gendoc",
Expand Down Expand Up @@ -222,7 +237,12 @@ func init() {
advancedMode,
}
app.Action = signer
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
app.Commands = []cli.Command{initCommand,
attestCommand,
setCredentialCommand,
delCredentialCommand,
newAccountCommand,
gendocCommand}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}

Expand Down Expand Up @@ -382,6 +402,31 @@ func removeCredential(ctx *cli.Context) error {
return nil
}

func newAccount(c *cli.Context) error {
if err := initialize(c); err != nil {
return err
}
// The newaccount is meant for users using the CLI, since 'real' external
// UIs can use the UI-api instead. So we'll just use the native CLI UI here.
var (
ui = core.NewCommandlineUI()
pwStorage storage.Storage = &storage.NoStorage{}
ksLoc = c.GlobalString(keystoreFlag.Name)
lightKdf = c.GlobalBool(utils.LightKDFFlag.Name)
)
log.Info("Starting clef", "keystore", ksLoc, "light-kdf", lightKdf)
am := core.StartClefAccountManager(ksLoc, true, lightKdf, "")
// This gives is us access to the external API
apiImpl := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage)
// This gives us access to the internal API
internalApi := core.NewUIServerAPI(apiImpl)
addr, err := internalApi.New(context.Background())
if err == nil {
fmt.Printf("Generated account %v\n", addr.String())
}
return err
}

func initialize(c *cli.Context) error {
// Set up the logger to print everything
logOutput := os.Stdout
Expand Down Expand Up @@ -457,7 +502,6 @@ func signer(c *cli.Context) error {
api core.ExternalAPI
pwStorage storage.Storage = &storage.NoStorage{}
)

configDir := c.GlobalString(configdirFlag.Name)
if stretchedKey, err := readMasterKey(c, ui); err != nil {
log.Warn("Failed to open master, rules disabled", "err", err)
Expand Down
14 changes: 11 additions & 3 deletions signer/core/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const (
// ExternalAPIVersion -- see extapi_changelog.md
ExternalAPIVersion = "6.0.0"
// InternalAPIVersion -- see intapi_changelog.md
InternalAPIVersion = "7.0.0"
InternalAPIVersion = "7.0.1"
)

// ExternalAPI defines the external API through which signing requests are made.
Expand Down Expand Up @@ -395,16 +395,24 @@ func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) {
// the given password. Users are responsible to backup the private key that is stored
// in the keystore location thas was specified when this API was created.
func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
be := api.am.Backends(keystore.KeyStoreType)
if len(be) == 0 {
if be := api.am.Backends(keystore.KeyStoreType); len(be) == 0 {
return common.Address{}, errors.New("password based accounts not supported")
}
if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil {
return common.Address{}, err
} else if !resp.Approved {
return common.Address{}, ErrRequestDenied
}
return api.newAccount()
}

// newAccount is the internal method to create a new account. It should be used
// _after_ user-approval has been obtained
func (api *SignerAPI) newAccount() (common.Address, error) {
be := api.am.Backends(keystore.KeyStoreType)
if len(be) == 0 {
return common.Address{}, errors.New("password based accounts not supported")
}
// Three retries to get a valid password
for i := 0; i < 3; i++ {
resp, err := api.UI.OnInputRequired(UserInputRequest{
Expand Down
10 changes: 10 additions & 0 deletions signer/core/uiapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,16 @@ func (api *UIServerAPI) Import(ctx context.Context, keyJSON json.RawMessage, old
return be[0].(*keystore.KeyStore).Import(keyJSON, oldPassphrase, newPassphrase)
}

// New creates a new password protected Account. The private key is protected with
// the given password. Users are responsible to backup the private key that is stored
// in the keystore location that was specified when this API was created.
// This method is the same as New on the external API, the difference being that
// this implementation does not ask for confirmation, since it's initiated by
// the user
func (api *UIServerAPI) New(ctx context.Context) (common.Address, error) {
return api.extApi.newAccount()
}

// Other methods to be added, not yet implemented are:
// - Ruleset interaction: add rules, attest rulefiles
// - Store metadata about accounts, e.g. naming of accounts