package tools import ( "context" "crypto/x509" "encoding/json" "encoding/pem" "fmt" "io" "net/http" "github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams/vocab" "github.com/go-fed/httpsig" "go.uber.org/zap" ) func VerifyRequest(ctx context.Context, req *http.Request, log *zap.SugaredLogger) (vocab.ActivityStreamsPerson, error) { logger := log.With("type", "verifier") verifier, err := httpsig.NewVerifier(req) if err != nil { return nil, err } keyId := verifier.KeyId() logger.With("keyId", keyId).Debugf("fetching key page") req, err = http.NewRequest("GET", keyId, nil) req.Header.Set("Accept", "application/json; charset=UTF-8") client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() keyPage, err := io.ReadAll(resp.Body) logger.With("keyId", keyId).With("response", string(keyPage)).Debugf("received response") var keyPageData map[string]interface{} err = json.Unmarshal(keyPage, &keyPageData) if err != nil { return nil, err } var person vocab.ActivityStreamsPerson resolver, err := streams.NewJSONResolver(func(c context.Context, p vocab.ActivityStreamsPerson) error { // Store the person in the enclosing scope, for later. person = p return nil }, func(c context.Context, note vocab.ActivityStreamsNote) error { //TODO not needed, need to figure out how to only pass one func return nil }) err = resolver.Resolve(ctx, keyPageData) if err != nil { return nil, err } pubKeyProp := person.GetW3IDSecurityV1PublicKey() iter := pubKeyProp.At(0) // TODO not safe, use a for instead pubKey := iter.Get() pemProp := pubKey.GetW3IDSecurityV1PublicKeyPem() pemStr := pemProp.Get() logger.With("keyId", keyId).With("pem", pemStr).Debugf("extracted pem") pemObj, _ := pem.Decode([]byte(pemStr)) if pemObj == nil { return nil, fmt.Errorf("no PEM block found") } if pemObj.Type != "PUBLIC KEY" { return nil, fmt.Errorf("no public key found in PEM block") } decodedKey, err := x509.ParsePKIXPublicKey(pemObj.Bytes) if err != nil { return nil, err } logger.With("keyId", keyId).With("pem", pemStr).Debugf("got %T", decodedKey) algo := httpsig.RSA_SHA256 return person, verifier.Verify(decodedKey, algo) }