fix: remove RSA Multikey from assertionMethod to fix tags.pub signature verification

The Fedify 2.0 migration added assertionMethods = keyPairs.map(k => k.multikey),
which places the RSA Multikey (id: #main-key) into assertionMethod alongside the
Ed25519 Multikey (id: #key-2).

This creates a keyId collision: the RSA CryptographicKey in publicKey and the RSA
Multikey in assertionMethod both use #main-key. Servers that traverse JSON-LD
properties alphabetically (assertionMethod before publicKey) find the Multikey
first — which lacks publicKeyPem — and return "public key not found".

Fix: filter assertionMethods to only Ed25519 keys (Object Integrity Proofs).
RSA keys already have their correct representation in publicKey (HTTP Signatures).
This matches Mastodon's behavior and is semantically correct per the two key systems.
This commit is contained in:
Ricardo
2026-03-22 15:00:14 +01:00
committed by svemagie
parent cb6c1b5bbc
commit 4298a6a307
2 changed files with 17 additions and 3 deletions

View File

@@ -171,7 +171,16 @@ export function setupFederation(options) {
}; };
if (keyPairs.length > 0) { if (keyPairs.length > 0) {
appOptions.publicKey = keyPairs[0].cryptographicKey; appOptions.publicKey = keyPairs[0].cryptographicKey;
appOptions.assertionMethods = keyPairs.map((k) => k.multikey); // Only include Ed25519 keys in assertionMethod (Object Integrity Proofs).
// RSA keys belong only in publicKey (HTTP Signatures). Putting the RSA
// Multikey in assertionMethod with the same #main-key id as the
// CryptographicKey in publicKey causes id collisions — servers that
// traverse JSON-LD properties alphabetically (assertionMethod before
// publicKey) find the Multikey first, which has no publicKeyPem,
// and fail signature verification.
appOptions.assertionMethods = keyPairs
.filter((k) => k.privateKey.algorithm.name !== "RSASSA-PKCS1-v1_5")
.map((k) => k.multikey);
} }
return new Application(appOptions); return new Application(appOptions);
} }
@@ -809,7 +818,12 @@ export async function buildPersonActor(
if (keyPairs.length > 0) { if (keyPairs.length > 0) {
personOptions.publicKey = keyPairs[0].cryptographicKey; personOptions.publicKey = keyPairs[0].cryptographicKey;
personOptions.assertionMethods = keyPairs.map((k) => k.multikey); // Only include Ed25519 keys in assertionMethod (Object Integrity Proofs).
// RSA keys belong only in publicKey (HTTP Signatures). See instance actor
// above for the full explanation of why this filter is necessary.
personOptions.assertionMethods = keyPairs
.filter((k) => k.privateKey.algorithm.name !== "RSASSA-PKCS1-v1_5")
.map((k) => k.multikey);
} }
// Build profile field attachments (PropertyValue). // Build profile field attachments (PropertyValue).

View File

@@ -1,6 +1,6 @@
{ {
"name": "@rmdes/indiekit-endpoint-activitypub", "name": "@rmdes/indiekit-endpoint-activitypub",
"version": "3.8.2", "version": "3.8.3",
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.", "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
"keywords": [ "keywords": [
"indiekit", "indiekit",