-
Notifications
You must be signed in to change notification settings - Fork 23
Signing CLI #566
Signing CLI #566
Conversation
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.
Implemented a sign command which allows to sign a given digest using key shares in the provided directory.
Shadowfiend
left a comment
There was a problem hiding this 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", | ||
| }, |
There was a problem hiding this comment.
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", | ||
| }, |
There was a problem hiding this comment.
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(), | ||
| ) |
There was a problem hiding this comment.
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, | ||
| ) |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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, | ||
| ) |
There was a problem hiding this comment.
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]", |
There was a problem hiding this comment.
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 andexit 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) andexit 1. - If there are
len(signers)signatures and they are all the same, print the signature tostdoutand exit normally (which is to say, return).
I think the error to stderr + exit 1 is doable by just returning an error.
|
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 |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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", | ||
| ) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
Shadowfiend
left a comment
There was a problem hiding this 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 😎
|
|
||
| keepRegistry := registry.NewKeepsRegistry(persistence) | ||
|
|
||
| keepRegistry.LoadExistingKeeps() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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 tomlsign-digest: allows signing a given digest using provided key shares