Documentation
¶
Overview ¶
Package httpsign signs HTTP requests and responses as defined in draft-ietf-httpbis-message-signatures. See https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-07.html.
For client-side message signing, use SignRequest, VerifyResponse etc. For server-side operation, WrapHandler installs a wrapper around a normal HTTP message handler.
Index ¶
- func RequestKeyID(signatureName string, req *http.Request) (string, error)
- func ResponseKeyID(signatureName string, res *http.Response) (string, error)
- func SignRequest(signatureName string, signer Signer, req *http.Request) (signatureInput, signature string, err error)
- func SignResponse(signatureName string, signer Signer, res *http.Response) (signatureInput, signature string, err error)
- func VerifyRequest(signatureName string, verifier Verifier, req *http.Request) (verified bool, err error)
- func VerifyResponse(signatureName string, verifier Verifier, res *http.Response) (verified bool, err error)
- func WrapHandler(h http.Handler, config *HandlerConfig) http.Handler
- type Fields
- type HandlerConfig
- func (h *HandlerConfig) SetFetchSigner(f func(res http.Response, r *http.Request) (sigName string, signer *Signer)) *HandlerConfig
- func (h *HandlerConfig) SetFetchVerifier(f func(r *http.Request) (sigName string, verifier *Verifier)) *HandlerConfig
- func (h *HandlerConfig) SetReqNotVerified(f func(w http.ResponseWriter, r *http.Request, err error)) *HandlerConfig
- func (h *HandlerConfig) SetSignResponse(b bool) *HandlerConfig
- func (h *HandlerConfig) SetVerifyRequest(b bool) *HandlerConfig
- type SignConfig
- type Signer
- func NewHMACSHA256Signer(keyID string, key []byte, config *SignConfig, fields Fields) (*Signer, error)
- func NewP256Signer(keyID string, key *ecdsa.PrivateKey, config *SignConfig, fields Fields) (*Signer, error)
- func NewRSAPSSSigner(keyID string, key *rsa.PrivateKey, config *SignConfig, fields Fields) (*Signer, error)
- func NewRSASigner(keyID string, key *rsa.PrivateKey, config *SignConfig, fields Fields) (*Signer, error)
- type Verifier
- func NewHMACSHA256Verifier(keyID string, key []byte, config *VerifyConfig, fields Fields) (*Verifier, error)
- func NewP256Verifier(keyID string, key *ecdsa.PublicKey, config *VerifyConfig, fields Fields) (*Verifier, error)
- func NewRSAPSSVerifier(keyID string, key *rsa.PublicKey, config *VerifyConfig, fields Fields) (*Verifier, error)
- func NewRSAVerifier(keyID string, key *rsa.PublicKey, config *VerifyConfig, fields Fields) (*Verifier, error)
- type VerifyConfig
- func (v *VerifyConfig) SetAllowedAlgs(allowedAlgs []string) *VerifyConfig
- func (v *VerifyConfig) SetNotNewerThan(notNewerThan time.Duration) *VerifyConfig
- func (v *VerifyConfig) SetNotOlderThan(notOlderThan time.Duration) *VerifyConfig
- func (v *VerifyConfig) SetVerifyAlg(verifyAlg bool) *VerifyConfig
- func (v *VerifyConfig) SetVerifyCreated(verifyCreated bool) *VerifyConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RequestKeyID ¶
RequestKeyID parses a signed request and returns the key ID used in the given signature.
func ResponseKeyID ¶
ResponseKeyID parses a signed response and returns the key ID used in the given signature.
func SignRequest ¶
func SignRequest(signatureName string, signer Signer, req *http.Request) (signatureInput, signature string, err error)
SignRequest signs an HTTP request. Returns the Signature-Input and the Signature header values.
Example ¶
config := NewSignConfig().SignCreated(false) // SignCreated should be "true" to protect against replay attacks
fields := HeaderList([]string{"@authority", "date", "@method"})
signer, _ := NewHMACSHA256Signer("my-shared-secret", bytes.Repeat([]byte{0x77}, 64), config, fields)
reqStr := `GET /foo HTTP/1.1
Host: example.org
Date: Tue, 20 Apr 2021 02:07:55 GMT
Cache-Control: max-age=60
`
req, _ := http.ReadRequest(bufio.NewReader(strings.NewReader(reqStr)))
signatureInput, signature, _ := SignRequest("sig77", *signer, req)
fmt.Printf("Signature-Input: %s\n", signatureInput)
fmt.Printf("Signature: %s", signature)
Output: Signature-Input: sig77=("@authority" "date" "@method");alg="hmac-sha256";keyid="my-shared-secret" Signature: sig77=:3e9KqLP62NHfHY5OMG4036+U6tvBowZF35ALzTjpsf0=:
func SignResponse ¶
func SignResponse(signatureName string, signer Signer, res *http.Response) (signatureInput, signature string, err error)
SignResponse signs an HTTP response. Returns the Signature-Input and the Signature header values.
func VerifyRequest ¶
func VerifyRequest(signatureName string, verifier Verifier, req *http.Request) (verified bool, err error)
VerifyRequest verifies a signed HTTP request. Returns true if verification was successful.
Example ¶
config := NewVerifyConfig().SetVerifyCreated(false) // for testing only
verifier, _ := NewHMACSHA256Verifier("my-shared-secret", bytes.Repeat([]byte{0x77}, 64), config,
HeaderList([]string{"@authority", "date", "@method"}))
reqStr := `GET /foo HTTP/1.1
Host: example.org
Date: Tue, 20 Apr 2021 02:07:55 GMT
Cache-Control: max-age=60
Signature-Input: sig77=("@authority" "date" "@method");alg="hmac-sha256";keyid="my-shared-secret"
Signature: sig77=:3e9KqLP62NHfHY5OMG4036+U6tvBowZF35ALzTjpsf0=:
`
req, _ := http.ReadRequest(bufio.NewReader(strings.NewReader(reqStr)))
verified, _ := VerifyRequest("sig77", *verifier, req)
fmt.Printf("verified: %t", verified)
Output: verified: true
func VerifyResponse ¶
func VerifyResponse(signatureName string, verifier Verifier, res *http.Response) (verified bool, err error)
VerifyResponse verifies a signed HTTP response. Returns true if verification was successful.
func WrapHandler ¶
func WrapHandler(h http.Handler, config *HandlerConfig) http.Handler
WrapHandler wraps a server's HTTP request handler so that the incoming request is verified and the response is signed. Both operations are optional. If config is nil, the default configuration is applied: requests are verified and responses are signed.
Example (ClientSigns) ¶
// Callback to let the server locate its verifying key and configuration
fetchVerifier := func(r *http.Request) (string, *Verifier) {
sigName := "sig1"
verifier, _ := NewHMACSHA256Verifier("key", bytes.Repeat([]byte{0x99}, 64), nil,
HeaderList([]string{"@method"}))
return sigName, verifier
}
// The basic handler (HTTP server) that gets wrapped
simpleHandler := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Header().Set("bar", "baz")
fmt.Fprintln(w, "Hey client, your message verified just fine")
}
// Configure the wrapper and set it up
config := NewHandlerConfig().SetSignResponse(false).SetFetchVerifier(fetchVerifier)
ts := httptest.NewServer(WrapHandler(http.HandlerFunc(simpleHandler), config))
defer ts.Close()
// HTTP client code
signer, _ := NewHMACSHA256Signer("key", bytes.Repeat([]byte{0x99}, 64), nil,
*NewFields().AddHeader("content-type").AddQueryParam("pet").AddHeader("@method"))
client := &http.Client{}
body := `{"hello": "world"}`
host := ts.URL // test server
path := "/foo?param=value&pet=dog"
req, _ := http.NewRequest("POST", "http://ignore.me", bufio.NewReader(strings.NewReader(body)))
req.RequestURI = "" // the http package wants this field to be unset for client responses, instead...
u, _ := url.Parse(host + path)
req.URL = u
req.Header.Set("Content-Type", "application/json")
// Request is ready, sign it
sigInput, sig, err := SignRequest("sig1", *signer, req)
if err != nil {
log.Fatalf("Failed to sign request: %v", err)
}
req.Header.Add("Signature", sig)
req.Header.Add("Signature-Input", sigInput)
// Send the request, receive response
res, _ := client.Do(req)
serverText, _ := io.ReadAll(res.Body)
res.Body.Close()
fmt.Println("Status: ", res.Status)
fmt.Println("Server sent: ", string(serverText))
Output: Status: 200 OK Server sent: Hey client, your message verified just fine
Example (ServerSigns) ¶
// Callback to let the server locate its verification key and configuration
fetchSigner := func(res http.Response, r *http.Request) (string, *Signer) {
sigName := "sig1"
signer, _ := NewHMACSHA256Signer("key", bytes.Repeat([]byte{0}, 64), nil,
HeaderList([]string{"@status", "bar", "date"}))
return sigName, signer
}
simpleHandler := func(w http.ResponseWriter, r *http.Request) { // this handler gets wrapped
w.WriteHeader(200)
w.Header().Set("bar", "baz")
fmt.Fprintln(w, "Hello, client")
}
// Configure the wrapper and set it up
config := NewHandlerConfig().SetVerifyRequest(false).SetFetchSigner(fetchSigner)
ts := httptest.NewServer(WrapHandler(http.HandlerFunc(simpleHandler), config))
defer ts.Close()
// HTTP client code
res, err := http.Get(ts.URL)
if err != nil {
log.Fatal(err)
}
_, err = io.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
res.Body.Close()
verifier, _ := NewHMACSHA256Verifier("key", bytes.Repeat([]byte{0}, 64), NewVerifyConfig(), *NewFields())
verified, _ := VerifyResponse("sig1", *verifier, res)
fmt.Println("verified: ", verified)
Output: verified: true
Types ¶
type Fields ¶
type Fields []field
Fields is a list of fields to be signed. To initialize, use HeaderList or for more complex cases, NewFields followed by a chain of Add... methods.
func HeaderList ¶
HeaderList is a simple way to generate a Fields list, where only simple header names and specialty headers are needed.
func (*Fields) AddDictHeader ¶
AddDictHeader indicates that a specific instance of a header is to be signed (TODO: unimplemented)
func (*Fields) AddQueryParam ¶
AddQueryParam indicates a request for a specific query parameter to be signed
type HandlerConfig ¶
type HandlerConfig struct {
// contains filtered or unexported fields
}
HandlerConfig contains additional configuration for the HTTP message handler wrapper.
func NewHandlerConfig ¶
func NewHandlerConfig() *HandlerConfig
NewHandlerConfig generates a default configuration. When verification or respectively, signing is required, the respective "fetch" callback must be supplied.
func (*HandlerConfig) SetFetchSigner ¶
func (h *HandlerConfig) SetFetchSigner(f func(res http.Response, r *http.Request) (sigName string, signer *Signer)) *HandlerConfig
SetFetchSigner defines a callback that looks at the incoming request and the response, just before it is sent, and provides a Signer structure. In the simplest case, the signature name is a constant, and the key ID and key value are fetched based on the sender's identity. To simplify this logic, it is recommended to use the request's ctx (Context) member to store this information. If a signer cannot be determined, the function should return signer as nil.
func (*HandlerConfig) SetFetchVerifier ¶
func (h *HandlerConfig) SetFetchVerifier(f func(r *http.Request) (sigName string, verifier *Verifier)) *HandlerConfig
SetFetchVerifier defines a callback that looks at the incoming request and provides a Verifier structure. In the simplest case, the signature name is a constant, and the key ID and key value are fetched based on the sender's identity, which in turn is gleaned from a header or query parameter. If a verifier cannot be determined, the function should return verifier as nil.
func (*HandlerConfig) SetReqNotVerified ¶
func (h *HandlerConfig) SetReqNotVerified(f func(w http.ResponseWriter, r *http.Request, err error)) *HandlerConfig
SetReqNotVerified defines a callback to be called when a request fails to verify. The default callback sends a 401 status code with an error message that includes the error string.
func (*HandlerConfig) SetSignResponse ¶
func (h *HandlerConfig) SetSignResponse(b bool) *HandlerConfig
SetSignResponse indicates that all HTTP responses must be signed.
func (*HandlerConfig) SetVerifyRequest ¶
func (h *HandlerConfig) SetVerifyRequest(b bool) *HandlerConfig
SetVerifyRequest indicates that all incoming requests for this handler must be verified.
type SignConfig ¶
type SignConfig struct {
// contains filtered or unexported fields
}
SignConfig contains additional configuration for the signer.
func NewSignConfig ¶
func NewSignConfig() *SignConfig
NewSignConfig generates a default configuration.
func (*SignConfig) SetRequestResponse ¶
func (c *SignConfig) SetRequestResponse(name, signature string) *SignConfig
SetRequestResponse allows the server to indicate signature name and signature that it had received from a client and include it in the signature input.
func (*SignConfig) SignAlg ¶
func (c *SignConfig) SignAlg(b bool) *SignConfig
SignAlg indicates that an "alg" signature parameters must be generated and signed (default: true).
func (*SignConfig) SignCreated ¶
func (c *SignConfig) SignCreated(b bool) *SignConfig
SignCreated indicates that a "created" signature parameters must be generated and signed (default: true).
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer includes a cryptographic key and configuration of what needs to be signed.
func NewHMACSHA256Signer ¶
func NewHMACSHA256Signer(keyID string, key []byte, config *SignConfig, fields Fields) (*Signer, error)
NewHMACSHA256Signer returns a new Signer structure. Key must be at least 64 bytes long. Field names must be all lowercase, config may be nil for a default configuration.
func NewP256Signer ¶
func NewP256Signer(keyID string, key *ecdsa.PrivateKey, config *SignConfig, fields Fields) (*Signer, error)
NewP256Signer returns a new Signer structure. Key is an elliptic curve P-256 private key. Field names must be all lowercase, config may be nil for a default configuration.
func NewRSAPSSSigner ¶
func NewRSAPSSSigner(keyID string, key *rsa.PrivateKey, config *SignConfig, fields Fields) (*Signer, error)
NewRSAPSSSigner returns a new Signer structure. Key is an RSA private key. Field names must be all lowercase, config may be nil for a default configuration.
func NewRSASigner ¶
func NewRSASigner(keyID string, key *rsa.PrivateKey, config *SignConfig, fields Fields) (*Signer, error)
NewRSASigner returns a new Signer structure. Key is an RSA private key. Field names must be all lowercase, config may be nil for a default configuration.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier includes a cryptographic key (typically a public key) and configuration of what needs to be verified.
func NewHMACSHA256Verifier ¶
func NewHMACSHA256Verifier(keyID string, key []byte, config *VerifyConfig, fields Fields) (*Verifier, error)
NewHMACSHA256Verifier generates a new verifier for HMAC-SHA256 signatures. Set config to nil for a default configuration. Fields is the list of required headers and fields, which may be empty (but this is typically insecure).
func NewP256Verifier ¶
func NewP256Verifier(keyID string, key *ecdsa.PublicKey, config *VerifyConfig, fields Fields) (*Verifier, error)
NewP256Verifier generates a new verifier for ECDSA (P-256) signatures. Set config to nil for a default configuration. Fields is the list of required headers and fields, which may be empty (but this is typically insecure).
func NewRSAPSSVerifier ¶
func NewRSAPSSVerifier(keyID string, key *rsa.PublicKey, config *VerifyConfig, fields Fields) (*Verifier, error)
NewRSAPSSVerifier generates a new verifier for RSA-PSS signatures. Set config to nil for a default configuration. Fields is the list of required headers and fields, which may be empty (but this is typically insecure).
func NewRSAVerifier ¶
func NewRSAVerifier(keyID string, key *rsa.PublicKey, config *VerifyConfig, fields Fields) (*Verifier, error)
NewRSAVerifier generates a new verifier for RSA signatures. Set config to nil for a default configuration. Fields is the list of required headers and fields, which may be empty (but this is typically insecure).
type VerifyConfig ¶
type VerifyConfig struct {
// contains filtered or unexported fields
}
VerifyConfig contains additional configuration for the verifier.
func NewVerifyConfig ¶
func NewVerifyConfig() *VerifyConfig
NewVerifyConfig generates a default configuration.
func (*VerifyConfig) SetAllowedAlgs ¶
func (v *VerifyConfig) SetAllowedAlgs(allowedAlgs []string) *VerifyConfig
SetAllowedAlgs defines what are the allowed values of the "alg" parameter. This is useful if the actual algorithm used in verification is taken from the message - not a recommended practice. Default: all supported asymmetric algorithms.
func (*VerifyConfig) SetNotNewerThan ¶
func (v *VerifyConfig) SetNotNewerThan(notNewerThan time.Duration) *VerifyConfig
SetNotNewerThan sets the window for messages that appear to be newer than the current time, which can only happen if clocks are out of sync. Default: 1,000 ms.
func (*VerifyConfig) SetNotOlderThan ¶
func (v *VerifyConfig) SetNotOlderThan(notOlderThan time.Duration) *VerifyConfig
SetNotOlderThan sets the window for messages that are older than the current time, because of network latency. Default: 10,000 ms.
func (*VerifyConfig) SetVerifyAlg ¶
func (v *VerifyConfig) SetVerifyAlg(verifyAlg bool) *VerifyConfig
SetVerifyAlg indicates that the "alg" parameter exist. Use SetAllowedAlgs to specify allowed values. Default: false.
func (*VerifyConfig) SetVerifyCreated ¶
func (v *VerifyConfig) SetVerifyCreated(verifyCreated bool) *VerifyConfig
SetVerifyCreated indicates that the "created" parameter must be within some time window, defined by NotNewerThan and NotOlderThan. Default: true.