Documentation ¶
Index ¶
- Constants
- Variables
- func NewHTTPClient(hc *http.Client, signer *Signer, verifier *Verifier) *http.Client
- func NewHandler(h http.Handler, verifier *Verifier) http.Handler
- func NewTransport(rt http.RoundTripper, signer *Signer, verifier *Verifier) http.RoundTripper
- func Sign(req *http.Request, params SigningOptions, mdp ...MetadataProvider) error
- type Algorithm
- type CreatedScheme
- type Digest
- type ErrCode
- type ExpiresScheme
- type InvalidSignature
- type KeyError
- type KeyErrorReason
- type KeyFetcher
- type KeySpec
- type Metadata
- type MetadataProvider
- type NonceScheme
- type SignatureError
- type SignedField
- type Signer
- type SigningOptions
- type VerifiedSignature
- type Verifier
- type VerifyHandler
- type VerifyProfile
- type VerifyResult
Examples ¶
Constants ¶
const ( // Supported signing algorithms Algo_RSA_PSS_SHA512 Algorithm = "rsa-pss-sha512" Algo_RSA_v1_5_sha256 Algorithm = "rsa-v1_5-sha256" Algo_HMAC_SHA256 Algorithm = "hmac-sha256" Algo_ECDSA_P256_SHA256 Algorithm = "ecdsa-p256-sha256" Algo_ECDSA_P384_SHA384 Algorithm = "ecdsa-p384-sha384" Algo_ED25519 Algorithm = "ed25519" DigestSHA256 Digest = "sha-256" DigestSHA512 Digest = "sha-512" // Signature metadata parameters MetaCreated Metadata = "created" MetaExpires Metadata = "expires" MetaNonce Metadata = "nonce" MetaAlgorithm Metadata = "alg" MetaKeyID Metadata = "keyid" MetaTag Metadata = "tag" // DefaultSignatureLabel is the label that will be used for a signature if not label is provided in the parameters. // A request can contain multiple signatures therefore each signature is labeled. DefaultSignatureLabel = "sig1" // Nonce schemes NonceRandom32 = iota // 32 bit random nonce. Base64 encoded )
Variables ¶
var ( DefaultVerifyProfile = VerifyProfile{ AllowedAlgorithms: []Algorithm{Algo_ECDSA_P256_SHA256, Algo_ECDSA_P384_SHA384, Algo_ED25519, Algo_HMAC_SHA256}, RequiredFields: DefaultRequiredFields, RequiredMetadata: []Metadata{MetaCreated, MetaKeyID}, DisallowedMetadata: []Metadata{MetaAlgorithm}, DisableMultipleSignatures: true, CreatedValidDuration: time.Minute * 5, DateFieldSkew: time.Minute, } // DefaultRequiredFields covers the request body with 'content-digest' the method and full URI. // As per the specification Date is not covered in favor of using the 'created' metadata parameter. DefaultRequiredFields = Fields("content-digest", "@method", "@target-uri") )
Functions ¶
func NewHTTPClient ¶
NewSigningHTTPClient creates an *http.Client that signs requests before sending. If hc is nil a new *http.Client is created. If signer is not nil all requests will be signed. If verifier is not nil all requests will be verified.
func NewHandler ¶
NewHandler wraps an http.Handler with a an http.Handler that verifies each request. verifier cannot be nil.
Example ¶
myhandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Lookup the results of verification if veriftyResult, ok := httpsig.GetVerifyResult(r.Context()); ok { keyid, _ := veriftyResult.Signature().Metadata.KeyID() fmt.Fprintf(w, "Hello, %s", keyid) } else { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) } }) // Create a verifier verifier, _ := httpsig.NewVerifier(nil, httpsig.DefaultVerifyProfile) mux := http.NewServeMux() // Wrap the handler with the a signature verification handler. mux.Handle("/", httpsig.NewHandler(myhandler, verifier))
Output:
func NewTransport ¶
func NewTransport(rt http.RoundTripper, signer *Signer, verifier *Verifier) http.RoundTripper
NewTransport returns an http.RoundTripper implementation that signs requests and verifies responses if signer and verifier are not nil. If rt is nil http.DefaultTransport is used.
func Sign ¶
func Sign(req *http.Request, params SigningOptions, mdp ...MetadataProvider) error
Example ¶
pkeyEncoded := `-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgNTK6255ubaaj1i/c ppuLouTgjAVyHGSxI0pYX8z1e2GhRANCAASkbVuWv1KXXs2H8b0ruFLyv2lKJWtT BznPJ5sSI1Jn+srosJB/GbEZ3Kg6PcEi+jODF9fdpNEaHGbbGdaVhJi1 -----END PRIVATE KEY-----` pkey, _ := keyutil.ReadPrivateKey([]byte(pkeyEncoded)) req := httptest.NewRequest("GET", "https://example.com/data", nil) params := httpsig.SigningOptions{ PrivateKey: pkey, Algorithm: httpsig.Algo_ECDSA_P256_SHA256, Fields: httpsig.DefaultRequiredFields, Metadata: []httpsig.Metadata{httpsig.MetaKeyID}, MetaKeyID: "key123", } signer, _ := httpsig.NewSigner(params) signer.Sign(req)
Output:
Types ¶
type CreatedScheme ¶
type CreatedScheme int
type ErrCode ¶
type ErrCode string
ErrCode enumerates the reasons a signing or verification can fail
const ( // Errors related to not being able to extract a valid signature. ErrNoSigInvalidHeader ErrCode = "nosig_invalid_header" ErrNoSigUnsupportedDigest ErrCode = "nosig_unsupported_digest" ErrNoSigWrongDigest ErrCode = "nosig_wrong_digest" ErrNoSigMissingSignature ErrCode = "nosig_missing_signature" ErrNoSigInvalidSignature ErrCode = "nosig_invalid_signature" ErrNoSigMessageBody ErrCode = "nosig_message_body" // Could not read message body // Errors related to an individual signature. ErrSigInvalidSignature ErrCode = "sig_invalid_signature" // The signature is unparseable or in the wrong format. ErrSigKeyFetch ErrCode = "sig_key_fetch" // Failed to the key for a signature ErrSigVerification ErrCode = "sig_failed_algo_verification" // The signature did not verify according to the algorithm. ErrSigPublicKey ErrCode = "sig_public_key" // The public key for the signature is invalid or missing. ErrSigSecretKey ErrCode = "sig_secret_key" // The secret key for the signature is invalid or missing. ErrSigUnsupportedAlgorithm ErrCode = "sig_unsupported_algorithm" // unsupported or invalid algorithm. ErrSigProfile ErrCode = "sig_failed_profile" // The signature was valid but failed the verify profile check // Signing ErrInvalidSignatureOptions ErrCode = "invalid_signature_options" ErrInvalidComponent ErrCode = "invalid_component" ErrInvalidMetadata ErrCode = "invalid_metadata" // Accept Signature ErrInvalidAcceptSignature ErrCode = "invalid_accept_signature" ErrMissingAcceptSignature ErrCode = "missing_accept_signature" // The Accept-Signature field was present but had an empty value. // General ErrInternal ErrCode = "internal_error" ErrUnsupported ErrCode = "unsupported" // A particular feature of the spec is not supported )
type ExpiresScheme ¶
type ExpiresScheme int
type InvalidSignature ¶
type InvalidSignature struct { Label string // Signature label Raw string // Raw string value of the signature Error SignatureError // HasMetadata is true if the signature parsed successfully. HasMetadata bool // contains filtered or unexported fields }
func NewInvalidSignature ¶
func NewInvalidSignature(md MetadataProvider) InvalidSignature
func (InvalidSignature) Metadata ¶
func (is InvalidSignature) Metadata() (mp MetadataProvider, found bool)
type KeyError ¶
type KeyError struct { Reason KeyErrorReason Message string // contains filtered or unexported fields }
type KeyErrorReason ¶
type KeyErrorReason string
type KeyFetcher ¶
type MetadataProvider ¶
type MetadataProvider interface { Created() (int, error) Expires() (int, error) Nonce() (string, error) Alg() (string, error) KeyID() (string, error) Tag() (string, error) }
MetadataProvider allows customized functions for metadata parameter values. Not needed for default usage.
type NonceScheme ¶
type NonceScheme int
type SignatureError ¶
func (*SignatureError) Error ¶
func (se *SignatureError) Error() string
func (*SignatureError) GoString ¶
func (se *SignatureError) GoString() string
func (*SignatureError) Unwrap ¶
func (se *SignatureError) Unwrap() error
type SignedField ¶
type SignedField struct { Name string Parameters map[string]any // Parameters are modifiers applied to the field that changes the way the signature is calculated. }
SignedField indicates which part of the request or response to use for signing. This is the 'message component' in the specification.
func Fields ¶
func Fields(fields ...string) []SignedField
Fields turns a list of fields into the full specification. Used when the signed fields/components do not need to specify any parameters
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
func NewSigner ¶
func NewSigner(params SigningOptions, mdp ...MetadataProvider) (*Signer, error)
func (*Signer) Sign ¶
Sign signs the request and adds the signature headers to the request. If the signature fields includes Content-Digest and Content-Digest is not already included in the request then Sign will read the request body to calculate the digest and set the header. The request body will be replaced with a new io.ReaderCloser.
type SigningOptions ¶
type SigningOptions struct { PrivateKey crypto.PrivateKey // Required for asymetric algorithms Secret []byte // Required for HMAC signing Algorithm Algorithm Digest Digest // The http digest algorithm to apply. Defaults to sha-256. Fields []SignedField // Fields and Derived components to sign Metadata []Metadata // Metadata parameters to add to the signature Label string // The signature label. Defaults to DefaultSignatureLabel // Signature metadata settings. // These are only added to the signature if the parameter is listed in the Metadata list. MetaKeyID string // 'keyid' - No default. A value must be provided if the parameter is in Metadata. MetaTag string // 'tag' - No default. A value must be provided if the parameter is in Metadata. MetaExpiresDuration time.Duration // 'expires' - Current time plus this duration. Default duration 5 minutes. MetaNonce NonceScheme // 'nonce' - Defaults to NonceRandom32 }
func ParseAcceptSignature ¶
func ParseAcceptSignature(acceptHeader string) (SigningOptions, error)
func (SigningOptions) Alg ¶
func (so SigningOptions) Alg() (string, error)
func (SigningOptions) Created ¶
func (so SigningOptions) Created() (int, error)
func (SigningOptions) Expires ¶
func (so SigningOptions) Expires() (int, error)
func (SigningOptions) KeyID ¶
func (so SigningOptions) KeyID() (string, error)
func (SigningOptions) Nonce ¶
func (so SigningOptions) Nonce() (string, error)
func (SigningOptions) Tag ¶
func (so SigningOptions) Tag() (string, error)
type VerifiedSignature ¶
type VerifiedSignature struct { Label string // Label should not be used for the identity of the caller. Use keyid or tag instead. Label can be set by an attacker. Metadata MetadataProvider }
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
func NewVerifier ¶
func NewVerifier(kf KeyFetcher, profile VerifyProfile) (*Verifier, error)
func (*Verifier) Verify ¶
func (ver *Verifier) Verify(req *http.Request) (VerifyResult, error)
Verify verifies the signature(s) in an http request. Any invalid signature will return an error. A valid VerifyResult is returned even if error is also returned.
func (*Verifier) VerifyResponse ¶
func (ver *Verifier) VerifyResponse(resp *http.Response) (VerifyResult, error)
type VerifyHandler ¶
type VerifyHandler struct {
// contains filtered or unexported fields
}
VerifyHandler verifies the http signature of each request. If not verified it returns a 401 Unauthorized HTTP error. If verified it puts the verification result in the requests context. Use GetVerifyResult to read the context.
func (VerifyHandler) ServeHTTP ¶
func (vh VerifyHandler) ServeHTTP(rw http.ResponseWriter, inReq *http.Request)
type VerifyProfile ¶
type VerifyProfile struct { RequiredFields []SignedField RequiredMetadata []Metadata DisallowedMetadata []Metadata AllowedAlgorithms []Algorithm // Which algorithms are allowed, either from keyid meta or in the KeySpec // True to only allow one signatures per message. // WARNING: An attacker can DoS the requester if it has an ability to add bad signatures. DisableMultipleSignatures bool // Timing enforcement options DisableTimeEnforcement bool // If true do no time enforcement on any fields DisableExpirationEnforcement bool // If expiration is present default to enforce CreatedValidDuration time.Duration // Duration allowed for between time.Now and the created time ExpiredSkew time.Duration // Maximum duration allowed between time.Now and the expired time DateFieldSkew time.Duration // Maximum duration allowed between Date field and created }
VerifyProfile sets the parameters for a fully valid request or response. A valid signature is a relatively easy accomplishment. Did the signature include all the important parts of the request? Did it use a strong enough algorithm? Was it signed 41 days ago? There are choices to make about what constitutes a valid signed request or response beyond just a verified signature.
type VerifyResult ¶
type VerifyResult struct { Signatures map[string]VerifiedSignature // Signatures is a map of label to signature. // InvalidSignatures is a map of label to InvalidSignature. // It is provided to allow introspection of attempted verifications. InvalidSignatures map[string]InvalidSignature }
func GetVerifyResult ¶
func GetVerifyResult(ctx context.Context) (v VerifyResult, found bool)
GetVerifyResult returns the results of a successful request signature verification.
func Verify ¶
func Verify(req *http.Request, kf KeyFetcher, profile VerifyProfile) (VerifyResult, error)
Verify validates the signatures in a request and ensured the signature meets the required profile.
Example ¶
pubkeyEncoded := `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIUctKvU5L/eEYxua5Zlz0HIQJRQq MTQ7eYQXwqpTvTJkuTffGXKLilT75wY2YZWfybv9flu5d6bCfw+4UB9+cg== -----END PUBLIC KEY-----` pubkey, _ := keyutil.ReadPublicKey([]byte(pubkeyEncoded)) req := httptest.NewRequest("GET", "https://example.com/data", nil) kf := keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ "key123": { KeyID: "key123", Algo: httpsig.Algo_ECDSA_P256_SHA256, PubKey: pubkey, }, }) httpsig.Verify(req, kf, httpsig.DefaultVerifyProfile)
Output:
func VerifyResponse ¶
func VerifyResponse(resp *http.Response, kf KeyFetcher, profile VerifyProfile) (VerifyResult, error)
func (*VerifyResult) NumSignatures ¶
func (vr *VerifyResult) NumSignatures() int
func (*VerifyResult) Signature ¶
func (vr *VerifyResult) Signature() VerifiedSignature
Signature is for messages with single signatures. It will return the first Signature in the map of signatures.