Skip to content

FEP-ef61 proof policy for portable objects #832

Description

@dahlia

Background

FEP-ef61 uses FEP-8b32 Object Integrity Proofs as the main authentication mechanism for portable objects. Fedify already has lower-level proof verification for eddsa-jcs-2022, but FEP-ef61 requires additional policy checks.

For a portable actor, activity, or object identified by an ap: or ap+ef61: URI, it is not enough for the proof signature to be cryptographically valid. The proof's verificationMethod must be a DID URL, and the DID must match the authority component of the portable object's canonical URI.

For example, a Note with this ID:

ap+ef61://did:key:z6MkAlice/objects/1

must not be accepted as a valid portable object if its proof is signed by:

did:key:z6MkMallory#z6MkMallory

even if the signature itself is valid for Mallory's key.

This issue is about adding the policy layer above the existing Object Integrity Proof verifier. It depends on the did:key verification method resolver and FEP-fe34 cryptographic origin work under #288.

Proposed work

Add a helper in @fedify/fedify for validating the FEP-ef61 proof policy for portable objects.

The helper should:

  • detect whether an actor, activity, object, or collection is identified by an ap: or ap+ef61: URI;
  • require actors, activities, and non-collection objects identified by portable URIs to contain a FEP-8b32 integrity proof;
  • verify the proof using the existing verifyProof() path;
  • require the proof's verificationMethod to be a DID URL;
  • compare the DID from verificationMethod with the decoded DID authority of the portable object ID;
  • reject proofs where the DID does not match the portable object authority;
  • use the shared FEP-fe34 origin helper where appropriate, so the comparison stays consistent with cryptographic origin handling;
  • keep lower-level verifyProof() focused on cryptographic signature verification rather than FEP-ef61 policy enforcement.

The intended API can be decided during implementation, but it could look roughly like:

verifyPortableObjectProof(
  jsonLd: unknown,
  options?: VerifyPortableObjectProofOptions,
): Promise<PortableObjectProofResult>;

The result type should make it possible to distinguish common outcomes, such as:

  • not a portable object;
  • missing proof;
  • invalid proof;
  • unsupported verification method;
  • verification method DID mismatch;
  • verified.

Scope

This issue is only about enforcing the FEP-ef61 proof policy for portable actors, activities, and objects.

It does not include:

  • implementing did:key resolution itself;
  • implementing portable URI canonicalization;
  • implementing FEP-fe34 cryptographic origin helpers;
  • gateway dereferencing;
  • compatible HTTP identifier conversion;
  • inbox or outbox forwarding;
  • FEP-ae97 client-side signing;
  • collection trust policy for unsecured portable collections.

Collection handling should be designed separately because FEP-ef61 allows portable collections to be served without integrity proofs when they are retrieved from a trusted gateway.

Tests

Add regression tests for the policy helper.

The tests should cover:

  • a portable object with a valid proof whose verificationMethod DID matches the object ID authority;
  • a portable actor with a valid matching proof;
  • a portable activity with a valid matching proof;
  • a portable object with no proof;
  • a portable object with an invalid proof;
  • a portable object whose proof is signed by a different DID;
  • a portable object whose verificationMethod is not a DID URL;
  • non-portable HTTP(S) objects being reported as outside this policy rather than rejected;
  • collection objects being treated according to the scoped behavior chosen for this issue.

This should be added as a sub-issue of #288.

Metadata

Metadata

Assignees

Priority

High

Effort

Medium

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions