diff --git a/activitypub/inbox/worker.go b/activitypub/inbox/worker.go index a427cad08..ef8aef9f3 100644 --- a/activitypub/inbox/worker.go +++ b/activitypub/inbox/worker.go @@ -4,6 +4,7 @@ import ( "context" "crypto/x509" "encoding/pem" + "fmt" "net/http" "net/url" "strings" @@ -71,26 +72,31 @@ func Verify(request *http.Request) (bool, error) { return false, errors.New("Unable to determine algorithm to verify request") } - actor, err := resolvers.GetResolvedActorFromIRI(pubKeyID.String()) + publicKey, err := resolvers.GetResolvedPublicKeyFromIRI(pubKeyID.String()) if err != nil { return false, errors.Wrap(err, "failed to resolve actor from IRI to fetch key") } - if actor.ActorIri == nil { - return false, errors.New("actor IRI is empty") + var publicKeyActorIRI *url.URL + if ownerProp := publicKey.GetW3IDSecurityV1Owner(); ownerProp != nil { + publicKeyActorIRI = ownerProp.Get() + } + + if publicKeyActorIRI == nil { + return false, errors.New("public key owner IRI is empty") } // Test to see if the actor is in the list of blocked federated domains. - if isBlockedDomain(actor.ActorIri.Hostname()) { + if isBlockedDomain(publicKeyActorIRI.Hostname()) { return false, errors.New("domain is blocked") } // If actor is specifically blocked, then fail validation. - if blocked, err := isBlockedActor(actor.ActorIri); err != nil || blocked { + if blocked, err := isBlockedActor(publicKeyActorIRI); err != nil || blocked { return false, err } - key := actor.W3IDSecurityV1PublicKey.Begin().Get().GetW3IDSecurityV1PublicKeyPem().Get() + key := publicKey.GetW3IDSecurityV1PublicKeyPem().Get() block, _ := pem.Decode([]byte(key)) if block == nil { log.Errorln("failed to parse PEM block containing the public key") @@ -103,14 +109,25 @@ func Verify(request *http.Request) (bool, error) { return false, errors.Wrap(err, "failed to parse DER encoded public key") } - var algorithm httpsig.Algorithm = httpsig.Algorithm(algorithmString) - - // The verifier will verify the Digest in addition to the HTTP signature - if err := verifier.Verify(parsedKey, algorithm); err != nil { - return false, errors.Wrap(err, algorithmString+" http signature verification error for: "+pubKeyID.String()) + algos := []httpsig.Algorithm{ + httpsig.Algorithm(algorithmString), // try stated algorithm first then other common algorithms + httpsig.RSA_SHA256, // <- used by almost all fedi software + httpsig.RSA_SHA512, } - return true, nil + // The verifier will verify the Digest in addition to the HTTP signature + triedAlgos := make(map[httpsig.Algorithm]error) + for _, algorithm := range algos { + if _, tried := triedAlgos[algorithm]; !tried { + err := verifier.Verify(parsedKey, algorithm) + if err == nil { + return true, nil + } + triedAlgos[algorithm] = err + } + } + + return false, fmt.Errorf("http signature verification error(s) for: %s: %+v", pubKeyID.String(), triedAlgos) } func isBlockedDomain(domain string) bool { diff --git a/activitypub/resolvers/resolve.go b/activitypub/resolvers/resolve.go index 060cb217d..522bf136c 100644 --- a/activitypub/resolvers/resolve.go +++ b/activitypub/resolvers/resolve.go @@ -122,6 +122,72 @@ func GetResolvedActorFromActorProperty(actor vocab.ActivityStreamsActorProperty) return apActor, err } +// GetResolvedPublicKeyFromIRI will resolve a publicKey IRI string to a vocab.W3IDSecurityV1PublicKey. +func GetResolvedPublicKeyFromIRI(publicKeyIRI string) (vocab.W3IDSecurityV1PublicKey, error) { + var err error + var pubkey vocab.W3IDSecurityV1PublicKey + resolved := false + + personCallback := func(c context.Context, person vocab.ActivityStreamsPerson) error { + if pkProp := person.GetW3IDSecurityV1PublicKey(); pkProp != nil { + for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() { + if iter.IsW3IDSecurityV1PublicKey() { + pubkey = iter.Get() + resolved = true + return nil + } + } + } + return errors.New("error deriving publickey from activitystreamsperson") + } + + serviceCallback := func(c context.Context, service vocab.ActivityStreamsService) error { + if pkProp := service.GetW3IDSecurityV1PublicKey(); pkProp != nil { + for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() { + if iter.IsW3IDSecurityV1PublicKey() { + pubkey = iter.Get() + resolved = true + return nil + } + } + } + return errors.New("error deriving publickey from activitystreamsservice") + } + + applicationCallback := func(c context.Context, app vocab.ActivityStreamsApplication) error { + if pkProp := app.GetW3IDSecurityV1PublicKey(); pkProp != nil { + for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() { + if iter.IsW3IDSecurityV1PublicKey() { + pubkey = iter.Get() + resolved = true + return nil + } + } + } + return errors.New("error deriving publickey from activitystreamsapp") + } + + pubkeyCallback := func(c context.Context, pk vocab.W3IDSecurityV1PublicKey) error { + pubkey = pk + resolved = true + return nil + } + + if e := ResolveIRI(context.Background(), publicKeyIRI, personCallback, serviceCallback, applicationCallback, pubkeyCallback); e != nil { + err = e + } + + if err != nil { + err = errors.Wrap(err, "error resolving publickey from iri") + } + + if !resolved { + err = errors.New("error resolving publickey from iri") + } + + return pubkey, err +} + // GetResolvedActorFromIRI will resolve an IRI string to a fully populated actor. func GetResolvedActorFromIRI(personOrServiceIRI string) (apmodels.ActivityPubActor, error) { var err error