Use correct keyId with legacy fallback

Bookwyrm keyIds are at `userpath/#main-key`, however when signing AP objects we have claimed in the headers that the keyId is at `userpath#main-key`.
This is incorrect, and makes GoToSocial's strict checking break.
Simply updating the signatures to use the correct KeyId breaks legacy Bookwyrm's signature checks, becuase it assumes that the keyId path is the same as the user path plus a fragment.
This commit allows for either option, by sending the request a second time with the incorrect keyId if sending with the correct one causes an error.
This commit is contained in:
Hugh Rundle 2023-04-11 15:45:06 +10:00
parent c9dcd4f7ad
commit 03f21b0f35
3 changed files with 18 additions and 4 deletions

View file

@ -540,10 +540,11 @@ async def sign_and_send(
digest = make_digest(data)
headers = {
"Date": now,
"Digest": digest,
"Signature": make_signature("post", sender, destination, now, digest),
"Signature": make_signature("post", sender, destination, now, digest, False),
"Content-Type": "application/activity+json; charset=utf-8",
"User-Agent": USER_AGENT,
}
@ -554,6 +555,16 @@ async def sign_and_send(
logger.exception(
"Failed to send broadcast to %s: %s", destination, response.reason
)
logger.info("Trying again with legacy keyId")
# try with incorrect keyId to enable communication with legacy Bookwyrm servers
legacy_signature = make_signature("post", sender, destination, now, digest, True)
headers["Signature"] = legacy_signature
async with session.post(destination, data=data, headers=headers) as response:
if not response.ok:
logger.exception(
"Failed to send broadcast with legacy keyId to %s: %s", destination, response.reason
)
return response
except asyncio.TimeoutError:
logger.info("Connection timed out for url: %s", destination)

View file

@ -22,7 +22,7 @@ def create_key_pair():
return private_key, public_key
def make_signature(method, sender, destination, date, digest=None):
def make_signature(method, sender, destination, date, digest=None, use_legacy_key=False):
"""uses a private key to sign an outgoing message"""
inbox_parts = urlparse(destination)
signature_headers = [
@ -38,8 +38,10 @@ def make_signature(method, sender, destination, date, digest=None):
message_to_sign = "\n".join(signature_headers)
signer = pkcs1_15.new(RSA.import_key(sender.key_pair.private_key))
signed_message = signer.sign(SHA256.new(message_to_sign.encode("utf8")))
# For legacy reasons we need to use an incorrect keyId for older Bookwyrm versions
key_id = f"{sender.remote_id}#main-key" if use_legacy_key else f"{sender.remote_id}/#main-key"
signature = {
"keyId": f"{sender.remote_id}/#main-key",
"keyId": key_id,
"algorithm": "rsa-sha256",
"headers": headers,
"signature": b64encode(signed_message).decode("utf8"),

View file

@ -137,7 +137,8 @@ def has_valid_signature(request, activity):
return False
if signature.key_id != remote_user.key_pair.remote_id:
raise ValueError("Wrong actor created signature.")
if signature.key_id != f"{remote_user.remote_id}#main-key": # legacy Bookwyrm
raise ValueError("Wrong actor created signature.")
try:
signature.verify(remote_user.key_pair.public_key, request)