From 4298a6a307f46b3241f794c76f9e185c5303288a Mon Sep 17 00:00:00 2001 From: Ricardo Date: Sun, 22 Mar 2026 15:00:14 +0100 Subject: [PATCH] fix: remove RSA Multikey from assertionMethod to fix tags.pub signature verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- lib/federation-setup.js | 18 ++++++++++++++++-- package.json | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/federation-setup.js b/lib/federation-setup.js index faaaf51..7eb075e 100644 --- a/lib/federation-setup.js +++ b/lib/federation-setup.js @@ -171,7 +171,16 @@ export function setupFederation(options) { }; if (keyPairs.length > 0) { 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); } @@ -809,7 +818,12 @@ export async function buildPersonActor( if (keyPairs.length > 0) { 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). diff --git a/package.json b/package.json index 4aa72a5..9603821 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "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.", "keywords": [ "indiekit",