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)
		
			
				
	
	
		
			103 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			103 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")
 | |
| 		}
 | |
| 	}
 | |
| }
 |