Fix http signature verification for deleted profiles.

ap-profile-handling
Alain St-Denis 2023-04-06 09:37:24 -04:00
rodzic 24dcbb3d51
commit b3d5e8629c
2 zmienionych plików z 30 dodań i 28 usunięć

Wyświetl plik

@ -8,10 +8,13 @@ from Crypto.PublicKey.RSA import RsaKey
from federation.entities.activitypub.enums import ActorType
from federation.entities.mixins import BaseEntity
from federation.entities.utils import get_profile
from federation.protocols.activitypub.signing import verify_request_signature
from federation.types import UserType, RequestType
from federation.utils.activitypub import retrieve_and_parse_document
from federation.utils.text import decode_if_bytes
logger = logging.getLogger('federation')
PROTOCOL_NAME = "activitypub"
@ -88,9 +91,32 @@ class Protocol:
if not skip_author_verification:
try:
# Verify the HTTP signature
pubkey = sender_key_fetcher(self.actor) if sender_key_fetcher else ''
self.sender = verify_request_signature(self.request, pubkey=pubkey)
self.verify()
except (ValueError, KeyError, InvalidSignature) as exc:
logger.warning('HTTP signature verification failed: %s', exc)
return self.actor, {}
return self.sender, self.payload
def verify(self):
sig_struct = self.request.headers.get("Signature", None)
if not sig_struct:
raise ValueError("A signature is required but was not provided")
# this should return a dict populated with the following keys:
# keyId, algorithm, headers and signature
sig = {i.split("=", 1)[0]: i.split("=", 1)[1].strip('"') for i in sig_struct.split(",")}
signer = get_profile(key_id=sig.get('keyId'))
if not signer:
signer = retrieve_and_parse_document(sig.get('keyId'))
self.sender = signer.id if signer else self.actor
key = getattr(signer, 'public_key', None)
if not key:
key = self.get_contact_key(self.actor) if self.get_contact_key else ''
if key:
# fallback to the author's key the client app may have provided
logger.warning("Failed to retrieve keyId for %s, trying the actor's key", sig.get('keyId'))
else:
raise ValueError(f"No public key for {sig.get('keyId')}")
verify_request_signature(self.request, key=key, algorithm=sig.get('algorithm',""))

Wyświetl plik

@ -13,7 +13,6 @@ from httpsig.sign_algorithms import PSS
from httpsig.requests_auth import HTTPSignatureAuth
from httpsig.verify import HeaderVerifier
from federation.entities.utils import get_profile
from federation.types import RequestType
from federation.utils.network import parse_http_date
from federation.utils.text import encode_if_text
@ -36,31 +35,10 @@ def get_http_authentication(private_key: RsaKey, private_key_id: str, digest: bo
)
def verify_request_signature(request: RequestType, pubkey: str=""):
def verify_request_signature(request: RequestType, key: str="", algorithm: str=""):
"""
Verify HTTP signature in request against a public key.
"""
from federation.utils.activitypub import retrieve_and_parse_document
sig_struct = request.headers.get("Signature", None)
if not sig_struct:
raise ValueError("A signature is required but was not provided")
# this should return a dict populated with the following keys:
# keyId, algorithm, headers and signature
sig = {i.split("=", 1)[0]: i.split("=", 1)[1].strip('"') for i in sig_struct.split(",")}
signer = get_profile(key_id=sig.get('keyId'))
if not signer:
signer = retrieve_and_parse_document(sig.get('keyId'))
key = getattr(signer, 'public_key', None)
if not key:
if pubkey:
# fallback to the author's key the client app may have provided
logger.warning("Failed to retrieve keyId for %s, trying the actor's key", sig.get('keyId'))
key = pubkey
else:
raise ValueError(f"No public key for {sig.get('keyId')}")
key = encode_if_text(key)
date_header = request.headers.get("Date")
if not date_header:
@ -77,7 +55,5 @@ def verify_request_signature(request: RequestType, pubkey: str=""):
path = getattr(request, 'path', urlsplit(request.url).path)
if not HeaderVerifier(request.headers, key, method=request.method,
path=path, sign_header='signature',
sign_algorithm=PSS() if sig.get('algorithm',None) == 'hs2019' else None).verify():
sign_algorithm=PSS() if algorithm == 'hs2019' else None).verify():
raise ValueError("Invalid signature")
return signer.id