bench-forgejo/routers/api/v1/activitypub/reqsignature.go
Wim cb50375e2b
Add more linters to improve code readability (#19989)
Add nakedret, unconvert, wastedassign, stylecheck and nolintlint linters to improve code readability

- nakedret - https://github.com/alexkohler/nakedret - nakedret is a Go static analysis tool to find naked returns in functions greater than a specified function length.
- unconvert - https://github.com/mdempsky/unconvert - Remove unnecessary type conversions
- wastedassign - https://github.com/sanposhiho/wastedassign -  wastedassign finds wasted assignment statements.
- notlintlint -  Reports ill-formed or insufficient nolint directives
- stylecheck - https://staticcheck.io/docs/checks/#ST - keep style consistent
  - excluded: [ST1003 - Poorly chosen identifier](https://staticcheck.io/docs/checks/#ST1003) and [ST1005 - Incorrectly formatted error string](https://staticcheck.io/docs/checks/#ST1005)
2022-06-20 12:02:49 +02:00

102 lines
2.8 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package activitypub
import (
"crypto"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"net/http"
"net/url"
"code.gitea.io/gitea/modules/activitypub"
gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/setting"
ap "github.com/go-ap/activitypub"
"github.com/go-fed/httpsig"
)
func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) {
person := ap.PersonNew(ap.IRI(keyID.String()))
err = person.UnmarshalJSON(b)
if err != nil {
err = fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %v", err)
return
}
pubKey := person.PublicKey
if pubKey.ID.String() != keyID.String() {
err = fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
return
}
pubKeyPem := pubKey.PublicKeyPem
block, _ := pem.Decode([]byte(pubKeyPem))
if block == nil || block.Type != "PUBLIC KEY" {
err = fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
return
}
p, err = x509.ParsePKIXPublicKey(block.Bytes)
return p, err
}
func fetch(iri *url.URL) (b []byte, err error) {
req := httplib.NewRequest(iri.String(), http.MethodGet)
req.Header("Accept", activitypub.ActivityStreamsContentType)
req.Header("User-Agent", "Gitea/"+setting.AppVer)
resp, err := req.Response()
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status)
return
}
b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
return b, err
}
func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
r := ctx.Req
// 1. Figure out what key we need to verify
v, err := httpsig.NewVerifier(r)
if err != nil {
return
}
ID := v.KeyId()
idIRI, err := url.Parse(ID)
if err != nil {
return
}
// 2. Fetch the public key of the other actor
b, err := fetch(idIRI)
if err != nil {
return
}
pubKey, err := getPublicKeyFromResponse(b, idIRI)
if err != nil {
return
}
// 3. Verify the other actor's key
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
authenticated = v.Verify(pubKey, algo) == nil
return authenticated, err
}
// ReqHTTPSignature function
func ReqHTTPSignature() func(ctx *gitea_context.APIContext) {
return func(ctx *gitea_context.APIContext) {
if authenticated, err := verifyHTTPSignatures(ctx); err != nil {
ctx.ServerError("verifyHttpSignatures", err)
} else if !authenticated {
ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed")
}
}
}