Skip to content
This repository was archived by the owner on May 22, 2023. It is now read-only.

Conversation

@lukasz-zimnoch
Copy link
Member

@lukasz-zimnoch lukasz-zimnoch commented Sep 30, 2020

This pull request introduces an out-of-band signing CLI.

That signing CLI contains the following commands:

  • decrypt-key-share: allows to decrypt a key share for given keep and store it in a file using the client config toml
  • sign-digest: allows signing a given digest using provided key shares

Implemented a decrypt key share command
as part of the new signing cli. That command
allows to decrypt a key share for given keep
and store it in a file using the provided
client config toml.
@lukasz-zimnoch lukasz-zimnoch marked this pull request as ready for review September 30, 2020 11:53
Implemented a sign command which allows
to sign a given digest using key shares
in the provided directory.
Copy link
Contributor

@Shadowfiend Shadowfiend left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some thoughts on how to make this a little more “command”-y.

cli.StringFlag{
Name: "keep-address,k",
Usage: "address of the keep",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a flag or just a parameter? Feels like ./keep-ecdsa signing decrypt-key-share <keep-address> makes more sense since the command without the flag can't do anything.

cmd/signing.go Outdated
Name: "digest,d",
Usage: "digest to sign in hex format without " +
"the `0x` prefix",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with decrypt-key-share above, how about we just make this an argument rather than a flag?

cmd/signing.go Outdated
"key_share_%.10s_%.10s",
keepAddress.String(),
keyFile.Address.String(),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than try to generate a target file, since this is meant to be invoked manually, what do you think about just spitting the bytes out to stdout? Then the caller can redirect to a file as desired, and we're not trying to guess filenames. Longer term or alternatively, we could provide a -o/--output-file flag to specify a filename.

cmd/signing.go Outdated
logger.Infof(
"key share has been decrypted successfully and written to file [%s]",
targetFilePath,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also probably avoid the logger here IMO. This is a CLI utility, rather than a running system---if we have user-facing messages to convey, we should be using simple fmt.Printf/fmt.Println calls. Logging should be reserved for some sort of debug or verbose flag, though I don't know that we need to go that far right now.

I think if we rework this to function more like a command-line utility, e.g. by outputting the decrypted data to stdout and potentially allowing a flag that specifies an output file, no additional output is needed. If there's an error, we can print it to stderr and exit 1 or so. The mental model for me is what happens when you call gpg to decrypt something.

cmd/signing.go Outdated
"the `0x` prefix",
},
cli.StringFlag{
Name: "key-shares-dir,k",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the above change, we could make this flag a more standard and guessable directory,d as well? Alternatively, since it's not optional, I wonder if we make this an argument rather than a flag as well 🤔 We can leave it as a flag initially, I'm just always hesitant to put optional parameters behind flags---it's not typically how commands work.

"[signer:%v] error: [%v]",
signingOutcome.signerIndex,
signingOutcome.err,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, this feels like something we should do as not-a-log, printing to stderr with something solely for human consumption.

cmd/signing.go Outdated
}

logger.Infof(
"[signer:%v] signature: [%+v]",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the outcome here a single signature? I don't want to slow us down too much, but I would expect this to:

  • Gather all available signatures.
  • If there are <len(signers), print an error to stderr and exit 1.
  • If there are len(signers) signatures and they are not all the same, print an error to stderr (possibly listing the three signatures by signer id) and exit 1.
  • If there are len(signers) signatures and they are all the same, print the signature to stdout and exit normally (which is to say, return).

I think the error to stderr + exit 1 is doable by just returning an error.

@lukasz-zimnoch
Copy link
Member Author

Thanks for review @Shadowfiend! Ready for the next look.

Name: "signing",
Usage: "Provides several tools useful for out-of-band signing",
Before: func(c *cli.Context) error {
_ = log.SetLogLevel("*", "fatal") // disable the regular logger
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on going through our existing keep-common logging abstraction here and doing a logging.Configure("*", "fatal")? My thinking is it'll be easier to bridge this if and when we bump our logging dependencies to a different major version or so (e.g. with keep-network/keep-common#30).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in the follow-up PR: #568

}

err = ioutil.WriteFile(targetFilePath, signerBytes, 0444) // read-only
_, err = os.Stdout.Write(signerBytes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do support -o, we shouldn't write to stdout if the output file is specified.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in the follow-up PR: #568

SigningCommand = cli.Command{
Name: "signing",
Usage: "Provides several tools useful for out-of-band signing",
Before: func(c *cli.Context) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also use Before here and below to check the Args length and make sure it matches what we expect, returning an error if it does not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, but I feel it's a little bit clearer if we validate the arguments in the subcommand right in the place where we use them.

if signersCount != len(signers) {
return fmt.Errorf(
"signing failed; all signers should support the signature",
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out I was totally wrong about returning an error. It does do us the favor of exiting with a non-zero status, but it prints the error out to a logger. This… Feels like a mistake, but probably not one we need to block this PR on. In particular, it feels like main.go should be fmt.Printf-ing an error from app.Run rather than using a logger.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in the follow-up PR: #568

Copy link
Contributor

@Shadowfiend Shadowfiend left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready to try on a real keep 😎

@Shadowfiend Shadowfiend merged commit 88b54e9 into master Oct 1, 2020
@Shadowfiend Shadowfiend deleted the signing-cli branch October 1, 2020 20:18

keepRegistry := registry.NewKeepsRegistry(persistence)

keepRegistry.LoadExistingKeeps()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually a random note I missed making: we will need to make this work more flexibly. Right now you need a full config file and to have your path set up just right to decrypt the keyshare. We should be able to just point this to a keyshare and, if we have the password configured properly, have that keyshare decrypted to stdout. Otherwise if we have a snapshot that didn't get properly persisted, for example, you'd have to move it into the current directory to make this work.

For followup work though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree but from the other side, seems we won't be able to use the existing registry code as we do here and we will be forced to copy-paste relevant parts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I figured structuring it all right will be a bit more complicated.

@pdyraga pdyraga added this to the v1.3.0 milestone Oct 2, 2020
@pdyraga pdyraga modified the milestones: v1.4.0, v1.3.0 Oct 2, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants