[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:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -71,26 +72,31 @@ func Verify(request *http.Request) (bool, error) {
|
|||||||
return false, errors.New("Unable to determine algorithm to verify request")
|
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 {
|
if err != nil {
|
||||||
return false, errors.Wrap(err, "failed to resolve actor from IRI to fetch key")
|
return false, errors.Wrap(err, "failed to resolve actor from IRI to fetch key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if actor.ActorIri == nil {
|
var publicKeyActorIRI *url.URL
|
||||||
return false, errors.New("actor IRI is empty")
|
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.
|
// 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")
|
return false, errors.New("domain is blocked")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If actor is specifically blocked, then fail validation.
|
// 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
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key := actor.W3IDSecurityV1PublicKey.Begin().Get().GetW3IDSecurityV1PublicKeyPem().Get()
|
key := publicKey.GetW3IDSecurityV1PublicKeyPem().Get()
|
||||||
block, _ := pem.Decode([]byte(key))
|
block, _ := pem.Decode([]byte(key))
|
||||||
if block == nil {
|
if block == nil {
|
||||||
log.Errorln("failed to parse PEM block containing the public key")
|
log.Errorln("failed to parse PEM block containing the public key")
|
||||||
@@ -103,15 +109,26 @@ func Verify(request *http.Request) (bool, error) {
|
|||||||
return false, errors.Wrap(err, "failed to parse DER encoded public key")
|
return false, errors.Wrap(err, "failed to parse DER encoded public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
var algorithm httpsig.Algorithm = httpsig.Algorithm(algorithmString)
|
algos := []httpsig.Algorithm{
|
||||||
|
httpsig.Algorithm(algorithmString), // try stated algorithm first then other common algorithms
|
||||||
// The verifier will verify the Digest in addition to the HTTP signature
|
httpsig.RSA_SHA256, // <- used by almost all fedi software
|
||||||
if err := verifier.Verify(parsedKey, algorithm); err != nil {
|
httpsig.RSA_SHA512,
|
||||||
return false, errors.Wrap(err, algorithmString+" http signature verification error for: "+pubKeyID.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
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 {
|
func isBlockedDomain(domain string) bool {
|
||||||
blockedDomains := data.GetBlockedFederatedDomains()
|
blockedDomains := data.GetBlockedFederatedDomains()
|
||||||
|
|||||||
@@ -122,6 +122,72 @@ func GetResolvedActorFromActorProperty(actor vocab.ActivityStreamsActorProperty)
|
|||||||
return apActor, err
|
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.
|
// GetResolvedActorFromIRI will resolve an IRI string to a fully populated actor.
|
||||||
func GetResolvedActorFromIRI(personOrServiceIRI string) (apmodels.ActivityPubActor, error) {
|
func GetResolvedActorFromIRI(personOrServiceIRI string) (apmodels.ActivityPubActor, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
Reference in New Issue
Block a user