@@ -12,6 +12,8 @@ import (
1212 "fmt"
1313 "net/http"
1414 "time"
15+
16+ "golang.org/x/crypto/ssh"
1517)
1618
1719// Algorithm specifies a cryptography secure algorithm for signing HTTP requests
@@ -170,6 +172,70 @@ func NewSigner(prefs []Algorithm, dAlgo DigestAlgorithm, headers []string, schem
170172 return s , defaultAlgorithm , err
171173}
172174
175+ // Signers will sign HTTP requests or responses based on the algorithms and
176+ // headers selected at creation time.
177+ //
178+ // Signers are not safe to use between multiple goroutines.
179+ //
180+ // Note that signatures do set the deprecated 'algorithm' parameter for
181+ // backwards compatibility.
182+ type SSHSigner interface {
183+ // SignRequest signs the request using ssh.Signer.
184+ // The public key id is used by the HTTP server to identify which key to use
185+ // to verify the signature.
186+ //
187+ // A Digest (RFC 3230) will be added to the request. The body provided
188+ // must match the body used in the request, and is allowed to be nil.
189+ // The Digest ensures the request body is not tampered with in flight,
190+ // and if the signer is created to also sign the "Digest" header, the
191+ // HTTP Signature will then ensure both the Digest and body are not both
192+ // modified to maliciously represent different content.
193+ SignRequest (pubKeyId string , r * http.Request , body []byte ) error
194+ // SignResponse signs the response using ssh.Signer. The public key
195+ // id is used by the HTTP client to identify which key to use to verify
196+ // the signature.
197+ //
198+ // A Digest (RFC 3230) will be added to the response. The body provided
199+ // must match the body written in the response, and is allowed to be
200+ // nil. The Digest ensures the response body is not tampered with in
201+ // flight, and if the signer is created to also sign the "Digest"
202+ // header, the HTTP Signature will then ensure both the Digest and body
203+ // are not both modified to maliciously represent different content.
204+ SignResponse (pubKeyId string , r http.ResponseWriter , body []byte ) error
205+ }
206+
207+ // NewwSSHSigner creates a new Signer using the specified ssh.Signer
208+ // At the moment only ed25519 ssh keys are supported.
209+ // The headers specified will be included into the HTTP signatures.
210+ //
211+ // The Digest will also be calculated on a request's body using the provided
212+ // digest algorithm, if "Digest" is one of the headers listed.
213+ //
214+ // The provided scheme determines which header is populated with the HTTP
215+ // Signature.
216+ func NewSSHSigner (s ssh.Signer , dAlgo DigestAlgorithm , headers []string , scheme SignatureScheme , expiresIn int64 ) (SSHSigner , Algorithm , error ) {
217+ sshAlgo := getSSHAlgorithm (s .PublicKey ().Type ())
218+ if sshAlgo == "" {
219+ return nil , "" , fmt .Errorf ("key type: %s not supported yet." , s .PublicKey ().Type ())
220+ }
221+
222+ signer , err := newSSHSigner (s , sshAlgo , dAlgo , headers , scheme , expiresIn )
223+ if err != nil {
224+ return nil , "" , err
225+ }
226+
227+ return signer , sshAlgo , nil
228+ }
229+
230+ func getSSHAlgorithm (pkType string ) Algorithm {
231+ switch pkType {
232+ case "ssh-ed25519" :
233+ return ED25519
234+ }
235+
236+ return ""
237+ }
238+
173239// Verifier verifies HTTP Signatures.
174240//
175241// It will determine which of the supported headers has the parameters
@@ -225,6 +291,34 @@ func NewResponseVerifier(r *http.Response) (Verifier, error) {
225291 })
226292}
227293
294+ func newSSHSigner (sshSigner ssh.Signer , algo Algorithm , dAlgo DigestAlgorithm , headers []string , scheme SignatureScheme , expiresIn int64 ) (SSHSigner , error ) {
295+ var expires , created int64 = 0 , 0
296+
297+ if expiresIn != 0 {
298+ created = time .Now ().Unix ()
299+ expires = created + expiresIn
300+ }
301+
302+ s , err := signerFromSSHSigner (sshSigner , string (algo ))
303+ if err != nil {
304+ return nil , fmt .Errorf ("no crypto implementation available for ssh algo %q" , algo )
305+ }
306+
307+ a := & asymmSSHSigner {
308+ asymmSigner : & asymmSigner {
309+ s : s ,
310+ dAlgo : dAlgo ,
311+ headers : headers ,
312+ targetHeader : scheme ,
313+ prefix : scheme .authScheme (),
314+ created : created ,
315+ expires : expires ,
316+ },
317+ }
318+
319+ return a , nil
320+ }
321+
228322func newSigner (algo Algorithm , dAlgo DigestAlgorithm , headers []string , scheme SignatureScheme , expiresIn int64 ) (Signer , error ) {
229323
230324 var expires , created int64 = 0 , 0
0 commit comments