summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-fed/httpsig/verifying.go
diff options
context:
space:
mode:
authorLibravatar Tobi Smethurst <31960611+tsmethurst@users.noreply.github.com>2021-08-12 21:03:24 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-12 21:03:24 +0200
commit98263a7de64269898a2f81207e38943b5c8e8653 (patch)
tree743c90f109a6c5d27832d1dcef2388d939f0f77a /vendor/github.com/go-fed/httpsig/verifying.go
parentText duplication fix (#137) (diff)
downloadgotosocial-98263a7de64269898a2f81207e38943b5c8e8653.tar.xz
Grand test fixup (#138)
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
Diffstat (limited to 'vendor/github.com/go-fed/httpsig/verifying.go')
-rw-r--r--vendor/github.com/go-fed/httpsig/verifying.go184
1 files changed, 184 insertions, 0 deletions
diff --git a/vendor/github.com/go-fed/httpsig/verifying.go b/vendor/github.com/go-fed/httpsig/verifying.go
new file mode 100644
index 000000000..6d8df02ce
--- /dev/null
+++ b/vendor/github.com/go-fed/httpsig/verifying.go
@@ -0,0 +1,184 @@
+package httpsig
+
+import (
+ "crypto"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var _ Verifier = &verifier{}
+
+type verifier struct {
+ header http.Header
+ kId string
+ signature string
+ created int64
+ expires int64
+ headers []string
+ sigStringFn func(http.Header, []string, int64, int64) (string, error)
+}
+
+func newVerifier(h http.Header, sigStringFn func(http.Header, []string, int64, int64) (string, error)) (*verifier, error) {
+ scheme, s, err := getSignatureScheme(h)
+ if err != nil {
+ return nil, err
+ }
+ kId, sig, headers, created, expires, err := getSignatureComponents(scheme, s)
+ if created != 0 {
+ //check if created is not in the future, we assume a maximum clock offset of 10 seconds
+ now := time.Now().Unix()
+ if created-now > 10 {
+ return nil, errors.New("created is in the future")
+ }
+ }
+ if expires != 0 {
+ //check if expires is in the past, we assume a maximum clock offset of 10 seconds
+ now := time.Now().Unix()
+ if now-expires > 10 {
+ return nil, errors.New("signature expired")
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &verifier{
+ header: h,
+ kId: kId,
+ signature: sig,
+ created: created,
+ expires: expires,
+ headers: headers,
+ sigStringFn: sigStringFn,
+ }, nil
+}
+
+func (v *verifier) KeyId() string {
+ return v.kId
+}
+
+func (v *verifier) Verify(pKey crypto.PublicKey, algo Algorithm) error {
+ s, err := signerFromString(string(algo))
+ if err == nil {
+ return v.asymmVerify(s, pKey)
+ }
+ m, err := macerFromString(string(algo))
+ if err == nil {
+ return v.macVerify(m, pKey)
+ }
+ return fmt.Errorf("no crypto implementation available for %q", algo)
+}
+
+func (v *verifier) macVerify(m macer, pKey crypto.PublicKey) error {
+ key, ok := pKey.([]byte)
+ if !ok {
+ return fmt.Errorf("public key for MAC verifying must be of type []byte")
+ }
+ signature, err := v.sigStringFn(v.header, v.headers, v.created, v.expires)
+ if err != nil {
+ return err
+ }
+ actualMAC, err := base64.StdEncoding.DecodeString(v.signature)
+ if err != nil {
+ return err
+ }
+ ok, err = m.Equal([]byte(signature), actualMAC, key)
+ if err != nil {
+ return err
+ } else if !ok {
+ return fmt.Errorf("invalid http signature")
+ }
+ return nil
+}
+
+func (v *verifier) asymmVerify(s signer, pKey crypto.PublicKey) error {
+ toHash, err := v.sigStringFn(v.header, v.headers, v.created, v.expires)
+ if err != nil {
+ return err
+ }
+ signature, err := base64.StdEncoding.DecodeString(v.signature)
+ if err != nil {
+ return err
+ }
+ err = s.Verify(pKey, []byte(toHash), signature)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func getSignatureScheme(h http.Header) (scheme SignatureScheme, val string, err error) {
+ s := h.Get(string(Signature))
+ sigHasAll := strings.Contains(s, keyIdParameter) ||
+ strings.Contains(s, headersParameter) ||
+ strings.Contains(s, signatureParameter)
+ a := h.Get(string(Authorization))
+ authHasAll := strings.Contains(a, keyIdParameter) ||
+ strings.Contains(a, headersParameter) ||
+ strings.Contains(a, signatureParameter)
+ if sigHasAll && authHasAll {
+ err = fmt.Errorf("both %q and %q have signature parameters", Signature, Authorization)
+ return
+ } else if !sigHasAll && !authHasAll {
+ err = fmt.Errorf("neither %q nor %q have signature parameters", Signature, Authorization)
+ return
+ } else if sigHasAll {
+ val = s
+ scheme = Signature
+ return
+ } else { // authHasAll
+ val = a
+ scheme = Authorization
+ return
+ }
+}
+
+func getSignatureComponents(scheme SignatureScheme, s string) (kId, sig string, headers []string, created int64, expires int64, err error) {
+ if as := scheme.authScheme(); len(as) > 0 {
+ s = strings.TrimPrefix(s, as+prefixSeparater)
+ }
+ params := strings.Split(s, parameterSeparater)
+ for _, p := range params {
+ kv := strings.SplitN(p, parameterKVSeparater, 2)
+ if len(kv) != 2 {
+ err = fmt.Errorf("malformed http signature parameter: %v", kv)
+ return
+ }
+ k := kv[0]
+ v := strings.Trim(kv[1], parameterValueDelimiter)
+ switch k {
+ case keyIdParameter:
+ kId = v
+ case createdKey:
+ created, err = strconv.ParseInt(v, 10, 64)
+ if err != nil {
+ return
+ }
+ case expiresKey:
+ expires, err = strconv.ParseInt(v, 10, 64)
+ if err != nil {
+ return
+ }
+ case algorithmParameter:
+ // Deprecated, ignore
+ case headersParameter:
+ headers = strings.Split(v, headerParameterValueDelim)
+ case signatureParameter:
+ sig = v
+ default:
+ // Ignore unrecognized parameters
+ }
+ }
+ if len(kId) == 0 {
+ err = fmt.Errorf("missing %q parameter in http signature", keyIdParameter)
+ } else if len(sig) == 0 {
+ err = fmt.Errorf("missing %q parameter in http signature", signatureParameter)
+ } else if len(headers) == 0 { // Optional
+ headers = defaultHeaders
+ }
+ return
+}