code.gitea.io/gitea@v1.21.7/routers/api/v1/activitypub/reqsignature.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package activitypub 5 6 import ( 7 "crypto" 8 "crypto/x509" 9 "encoding/pem" 10 "fmt" 11 "io" 12 "net/http" 13 "net/url" 14 15 "code.gitea.io/gitea/modules/activitypub" 16 gitea_context "code.gitea.io/gitea/modules/context" 17 "code.gitea.io/gitea/modules/httplib" 18 "code.gitea.io/gitea/modules/setting" 19 20 ap "github.com/go-ap/activitypub" 21 "github.com/go-fed/httpsig" 22 ) 23 24 func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) { 25 person := ap.PersonNew(ap.IRI(keyID.String())) 26 err = person.UnmarshalJSON(b) 27 if err != nil { 28 return nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) 29 } 30 pubKey := person.PublicKey 31 if pubKey.ID.String() != keyID.String() { 32 return nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) 33 } 34 pubKeyPem := pubKey.PublicKeyPem 35 block, _ := pem.Decode([]byte(pubKeyPem)) 36 if block == nil || block.Type != "PUBLIC KEY" { 37 return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type") 38 } 39 p, err = x509.ParsePKIXPublicKey(block.Bytes) 40 return p, err 41 } 42 43 func fetch(iri *url.URL) (b []byte, err error) { 44 req := httplib.NewRequest(iri.String(), http.MethodGet) 45 req.Header("Accept", activitypub.ActivityStreamsContentType) 46 req.Header("User-Agent", "Gitea/"+setting.AppVer) 47 resp, err := req.Response() 48 if err != nil { 49 return nil, err 50 } 51 defer resp.Body.Close() 52 53 if resp.StatusCode != http.StatusOK { 54 return nil, fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status) 55 } 56 b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize)) 57 return b, err 58 } 59 60 func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) { 61 r := ctx.Req 62 63 // 1. Figure out what key we need to verify 64 v, err := httpsig.NewVerifier(r) 65 if err != nil { 66 return false, err 67 } 68 ID := v.KeyId() 69 idIRI, err := url.Parse(ID) 70 if err != nil { 71 return false, err 72 } 73 // 2. Fetch the public key of the other actor 74 b, err := fetch(idIRI) 75 if err != nil { 76 return false, err 77 } 78 pubKey, err := getPublicKeyFromResponse(b, idIRI) 79 if err != nil { 80 return false, err 81 } 82 // 3. Verify the other actor's key 83 algo := httpsig.Algorithm(setting.Federation.Algorithms[0]) 84 authenticated = v.Verify(pubKey, algo) == nil 85 return authenticated, err 86 } 87 88 // ReqHTTPSignature function 89 func ReqHTTPSignature() func(ctx *gitea_context.APIContext) { 90 return func(ctx *gitea_context.APIContext) { 91 if authenticated, err := verifyHTTPSignatures(ctx); err != nil { 92 ctx.ServerError("verifyHttpSignatures", err) 93 } else if !authenticated { 94 ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed") 95 } 96 } 97 }