0

[bugfix] Fix http signatures trying to derive actor (#1956)

* add GetResolvedPublicKeyFromIRI

* verify public key using key not actor w/key

* try most common algos first

* try stated algo first

* make sure not to try an algo twice

* return errors per algorithm

* linting
This commit is contained in:
tobi 2022-06-19 00:29:10 +02:00 committed by GitHub
parent 34629d0f5d
commit 05a134c64e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 12 deletions

View File

@ -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,
}
// 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 {

View File

@ -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