Skip to content

SHA-256 digestMultibase helpers for portable media #831

Description

@dahlia

Background

FEP-ef61 requires portable objects that reference external resources, such as media attachments, to include a digestMultibase property. The digest must be computed using SHA-256. The spec also says that the URI of an external resource should be a hashlink.

Hashlinks are defined by draft-sporny-hashlink-07, an expired Internet-Draft. The draft describes a broader format for cryptographic hyperlinks, including metadata-bearing hashlinks and legacy URL parameter encodings. FEP-ef61 only appears to need the simple hl:<resource-hash> form, where the resource hash is a multibase-encoded multihash.

There is an existing JavaScript implementation, digitalbazaar/hashlink, but it does not look like a good fit as a Fedify runtime dependency:

  • the underlying hashlink specification is an expired draft, so we should avoid committing to more of the format than FEP-ef61 actually needs;
  • the package predates Fedify's current runtime requirements and documents Node.js 8.3+/CommonJS-style usage;
  • Fedify needs code that works consistently across Deno, Node.js, Bun, and browser-like WebCrypto environments;
  • adding the package would require dependency updates for both Deno and Node.js/Bun, while the needed subset is small;
  • Fedify already has multibase and multicodec helpers in @fedify/vocab-runtime.

For this reason, Fedify should implement a small FEP-ef61-oriented helper layer instead of depending on digitalbazaar/hashlink.

Proposed work

Add helper functions in @fedify/vocab-runtime for the digest format needed by FEP-ef61 portable media.

The helpers should:

  • compute a SHA-256 digest for a byte sequence;
  • encode that digest as the digestMultibase value expected by FEP-ef61;
  • parse and validate digestMultibase values;
  • parse simple hashlink URIs of the form hl:<resource-hash>;
  • create simple hashlink URIs from a digestMultibase value;
  • verify that a byte sequence matches a given digestMultibase value;
  • verify that a byte sequence matches a simple hl: URI;
  • reject unsupported hash algorithms, malformed multibase values, malformed multihash values, and metadata-bearing hashlinks that are outside the initial scope.

The implementation should build on the existing multibase/multicodec support in @fedify/vocab-runtime instead of introducing a second encoding stack.

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

computeDigestMultibase(bytes: Uint8Array): Promise<string>;
parseDigestMultibase(value: string): { algorithm: "sha2-256"; digest: Uint8Array };
parseHashlink(value: string | URL): { digestMultibase: string };
createHashlink(digestMultibase: string): string;
verifyDigestMultibase(bytes: Uint8Array, digestMultibase: string): Promise<boolean>;
verifyHashlink(bytes: Uint8Array, hashlink: string | URL): Promise<boolean>;

Scope

This issue is only about the digest and simple hashlink helper layer needed for FEP-ef61 portable media.

It does not include:

  • full implementation of draft-sporny-hashlink-07;
  • hashlink metadata parsing or serialization;
  • legacy ?hl= URL parameter support;
  • gateway media upload, serving, or deletion endpoints;
  • access control for media;
  • adding the digestMultibase vocabulary property.

The digestMultibase vocabulary property is handled separately under #288.

Tests

Add regression tests for digest and hashlink helpers.

The tests should cover:

  • computing a SHA-256 digestMultibase value from bytes;
  • creating a simple hl: URI from that digest;
  • parsing a simple hl: URI back to the same digest;
  • verifying matching bytes against digestMultibase;
  • rejecting non-matching bytes;
  • rejecting unsupported multihash algorithms;
  • rejecting malformed multibase and multihash values;
  • rejecting metadata-bearing hashlinks for now;
  • confirming that the implementation works without network access.

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

Metadata

Metadata

Assignees

Fields

Priority

Medium

Effort

Medium

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions