Add initial support for using ssh keys for signing
このコミットが含まれているのは:
コミット
10b10290b7
|
@ -21,6 +21,7 @@ import (
|
|||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -219,9 +220,19 @@ func (r *rsaAlgorithm) String() string {
|
|||
|
||||
var _ signer = &ed25519Algorithm{}
|
||||
|
||||
type ed25519Algorithm struct{}
|
||||
type ed25519Algorithm struct {
|
||||
sshSigner ssh.Signer
|
||||
}
|
||||
|
||||
func (r *ed25519Algorithm) Sign(rand io.Reader, p crypto.PrivateKey, sig []byte) ([]byte, error) {
|
||||
if r.sshSigner != nil {
|
||||
sshsig, err := r.sshSigner.Sign(rand, sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sshsig.Blob, nil
|
||||
}
|
||||
ed25519K, ok := p.(ed25519.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("crypto.PrivateKey is not ed25519.PrivateKey")
|
||||
|
@ -418,6 +429,15 @@ func newAlgorithm(algo string, key []byte) (hash.Hash, crypto.Hash, error) {
|
|||
return h, c, err
|
||||
}
|
||||
|
||||
func signerFromSSHSigner(sshSigner ssh.Signer, s string) (signer, error) {
|
||||
if !strings.HasPrefix(s, ed25519Prefix) {
|
||||
return nil, fmt.Errorf("no signer matching %q", s)
|
||||
}
|
||||
return &ed25519Algorithm{
|
||||
sshSigner: sshSigner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// signerFromString is an internally public method constructor
|
||||
func signerFromString(s string) (signer, error) {
|
||||
s = strings.ToLower(s)
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,5 +1,5 @@
|
|||
module github.com/go-fed/httpsig
|
||||
|
||||
require golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
||||
require golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
|
||||
go 1.13
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,6 +1,6 @@
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
|
|
94
httpsig.go
94
httpsig.go
|
@ -12,6 +12,8 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Algorithm specifies a cryptography secure algorithm for signing HTTP requests
|
||||
|
@ -170,6 +172,70 @@ func NewSigner(prefs []Algorithm, dAlgo DigestAlgorithm, headers []string, schem
|
|||
return s, defaultAlgorithm, err
|
||||
}
|
||||
|
||||
// Signers will sign HTTP requests or responses based on the algorithms and
|
||||
// headers selected at creation time.
|
||||
//
|
||||
// Signers are not safe to use between multiple goroutines.
|
||||
//
|
||||
// Note that signatures do set the deprecated 'algorithm' parameter for
|
||||
// backwards compatibility.
|
||||
type SSHSigner interface {
|
||||
// SignRequest signs the request using ssh.Signer.
|
||||
// The public key id is used by the HTTP server to identify which key to use
|
||||
// to verify the signature.
|
||||
//
|
||||
// A Digest (RFC 3230) will be added to the request. The body provided
|
||||
// must match the body used in the request, and is allowed to be nil.
|
||||
// The Digest ensures the request body is not tampered with in flight,
|
||||
// and if the signer is created to also sign the "Digest" header, the
|
||||
// HTTP Signature will then ensure both the Digest and body are not both
|
||||
// modified to maliciously represent different content.
|
||||
SignRequest(pubKeyId string, r *http.Request, body []byte) error
|
||||
// SignResponse signs the response using ssh.Signer. The public key
|
||||
// id is used by the HTTP client to identify which key to use to verify
|
||||
// the signature.
|
||||
//
|
||||
// A Digest (RFC 3230) will be added to the response. The body provided
|
||||
// must match the body written in the response, and is allowed to be
|
||||
// nil. The Digest ensures the response body is not tampered with in
|
||||
// flight, and if the signer is created to also sign the "Digest"
|
||||
// header, the HTTP Signature will then ensure both the Digest and body
|
||||
// are not both modified to maliciously represent different content.
|
||||
SignResponse(pubKeyId string, r http.ResponseWriter, body []byte) error
|
||||
}
|
||||
|
||||
// NewwSSHSigner creates a new Signer using the specified ssh.Signer
|
||||
// At the moment only ed25519 ssh keys are supported.
|
||||
// The headers specified will be included into the HTTP signatures.
|
||||
//
|
||||
// The Digest will also be calculated on a request's body using the provided
|
||||
// digest algorithm, if "Digest" is one of the headers listed.
|
||||
//
|
||||
// The provided scheme determines which header is populated with the HTTP
|
||||
// Signature.
|
||||
func NewSSHSigner(s ssh.Signer, dAlgo DigestAlgorithm, headers []string, scheme SignatureScheme, expiresIn int64) (SSHSigner, Algorithm, error) {
|
||||
sshAlgo := getSSHAlgorithm(s.PublicKey().Type())
|
||||
if sshAlgo == "" {
|
||||
return nil, "", fmt.Errorf("key type: %s not supported yet.", s.PublicKey().Type())
|
||||
}
|
||||
|
||||
signer, err := newSSHSigner(s, sshAlgo, dAlgo, headers, scheme, expiresIn)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return signer, sshAlgo, nil
|
||||
}
|
||||
|
||||
func getSSHAlgorithm(pkType string) Algorithm {
|
||||
switch pkType {
|
||||
case "ssh-ed25519":
|
||||
return ED25519
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Verifier verifies HTTP Signatures.
|
||||
//
|
||||
// It will determine which of the supported headers has the parameters
|
||||
|
@ -225,6 +291,34 @@ func NewResponseVerifier(r *http.Response) (Verifier, error) {
|
|||
})
|
||||
}
|
||||
|
||||
func newSSHSigner(sshSigner ssh.Signer, algo Algorithm, dAlgo DigestAlgorithm, headers []string, scheme SignatureScheme, expiresIn int64) (SSHSigner, error) {
|
||||
var expires, created int64 = 0, 0
|
||||
|
||||
if expiresIn != 0 {
|
||||
created = time.Now().Unix()
|
||||
expires = created + expiresIn
|
||||
}
|
||||
|
||||
s, err := signerFromSSHSigner(sshSigner, string(algo))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("no crypto implementation available for ssh algo %q", algo)
|
||||
}
|
||||
|
||||
a := &asymmSSHSigner{
|
||||
asymmSigner: &asymmSigner{
|
||||
s: s,
|
||||
dAlgo: dAlgo,
|
||||
headers: headers,
|
||||
targetHeader: scheme,
|
||||
prefix: scheme.authScheme(),
|
||||
created: created,
|
||||
expires: expires,
|
||||
},
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func newSigner(algo Algorithm, dAlgo DigestAlgorithm, headers []string, scheme SignatureScheme, expiresIn int64) (Signer, error) {
|
||||
|
||||
var expires, created int64 = 0, 0
|
||||
|
|
14
signing.go
14
signing.go
|
@ -179,6 +179,20 @@ func (a *asymmSigner) signatureStringResponse(r http.ResponseWriter) (string, er
|
|||
return signatureString(r.Header(), a.headers, requestTargetNotPermitted, a.created, a.expires)
|
||||
}
|
||||
|
||||
var _ SSHSigner = &asymmSSHSigner{}
|
||||
|
||||
type asymmSSHSigner struct {
|
||||
*asymmSigner
|
||||
}
|
||||
|
||||
func (a *asymmSSHSigner) SignRequest(pubKeyId string, r *http.Request, body []byte) error {
|
||||
return a.asymmSigner.SignRequest(nil, pubKeyId, r, body)
|
||||
}
|
||||
|
||||
func (a *asymmSSHSigner) SignResponse(pubKeyId string, r http.ResponseWriter, body []byte) error {
|
||||
return a.asymmSigner.SignResponse(nil, pubKeyId, r, body)
|
||||
}
|
||||
|
||||
func setSignatureHeader(h http.Header, targetHeader, prefix, pubKeyId, algo, enc string, headers []string, created int64, expires int64) {
|
||||
if len(headers) == 0 {
|
||||
headers = defaultHeaders
|
||||
|
|
読み込み中…
新しいイシューから参照