Repository navigation
Add initial support for using ssh keys for signing - #18
Conversation
cjslep
left a comment
There was a problem hiding this comment.
This is awesome! I learned a lot reading this review.
Looking at your sample program, I really like the simplicity of it. The httpsig is basically "construct, sign". The only thing that stuck out to me was the comment "using nil for privatekey". I think that's a great warning sign that, somehow that method needs to change so that when a client wants to use a SSHSigner they don't need to pass a nil. We can do that internally though as a shortcut.
I think there's a great opportunity to put SSH Signing on the road to being even more of a first-class supported feature. In that spirit, I have some suggested changes and additions, if you're willing & able to do them. I also include optional bullets if you'd like to extend it to all private key types supported by both SSH & HTTPSignatures, not just ed25519. Those optional bullets don't have to be tackled in this review; I just need someplace to park the vision of fully supporting SSH signing.
What do you think about the following (numbered) adjustments?
- Making a
type SSHSigner interfacededicated type (so nowhttpsig.SSHSignerexists) - It has
SignRequestandSignResponsesimilar toSignerbut w/o thecrypto.PrivateKeyparameter. Now this fixes thenilprivate key parameter being exposed: it no longer exists in the interface. - Now
NewSignerFromSSHSignerneeds to return aSSHSignerinstead of aSigner. It'll do so by passing the buck and callingnewSignerFromSSHSigner- Optional: Pull out the switch/case statement for the various SSH
s.PublicKey().Type()to anAlgorithmstring (ex:"ssh-ed25519" => ED25519) to be a tiny function, use that tiny function when internally callingnewSignerFromSSHSigner.
- Optional: Pull out the switch/case statement for the various SSH
- Now
newSignerFromSSHSignerneeds to return aSSHSigner. Let's create a new type that simply wraps the existingSignRequestfunctionality, passes thenilfor us, plus hides the existing non-SSHSignRequestfor the new type:
type asymmSSHSigner struct {
*asymmSigner
}
func (a *asymmSSHSigner) SignRequest(pubKeyId string, r *http.Request, body []byte) error {
return a.asymmSigner.SignRequest(nil, pubKeyId, r, body)
} //etc, similar for SignResponse
var _ SSHSigner = &asymmSSHSigner{} // This compiles
var _ Signer = &asymmSSHSigner{} // This is a compilation error
- Wrap the
asymmSignerinnewSignerFromSSHSignerwithasymmSSHSigner.- Optional: Do a similar wrapping plus delegation for the algorithms, something like:
type sshAlgorithm struct {
sshSigner ssh.Signer
delegate signer // our internal type
}
func (s *sshAlgorithm) Sign(rand io.Reader, p crypto.PrivateKey, sig []byte) ([]byte, error) {
// Your use of s.sshSigner goes here
}
func (s *sshAlgorithm) Verify(pub crypto.PublicKey, toHash, signature []byte) error {
return s.delegate.Verify(pub, toHash, signature)
}
func (s *sshAlgorithm) String() string {
return s.delegate.String()
}
- Optional: This means
signerFromSSHSignercan change: get any signer fromsignerFromString(you got the string from the "tiny-function" in step 3's optional) and then just wrap it with asshAlgorithmand your sshSigner. No need to hardcode ed25519 insignerFromSSHSignerany longer.
Again, no pressure to do the Optional bullets. The numbered steps I think are worth considering to keep the API for SSH users happy, and welcome your thoughts & feedback!
|
I also thought about doing something like you're suggesting, but didn't do it because it'd wouldn't plug-in as transparent as now (and I thought it would be more easily merged ;-) But I have no issue with implementing it like you suggest, try to do it later this week. |
2840785 to
23b7ccd
Compare
At the moment only ed25519 keys are supported. The NewSSHSigner method takes a ssh.Signer which will be used when actually signing the request/responses.
|
@cjslep I've updated the PR with your requested changes, didn't do all the optional ones yet, will be for a next PR. |
This PR builds on #16 and #17 and also fixes #10.
At the moment only ed25519 keys are supported.
The NewSignerFromSSHSigner method takes a ssh.Signer which will be used when actually signing the request/responses.
With this PR you can also use your ssh-agent to sign requests.
Simple example below which will work if you have a ed25519 key in your ssh agent.