Unverified Commit 41fbe8b5 authored by Hong Minhee's avatar Hong Minhee
Browse files

Instrument `verifyProof()`

parent 3ad7569a
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -93,7 +93,6 @@
    "hongminhee",
    "hono",
    "httpsig",
    "httpsignatures",
    "hugoalh",
    "icojs",
    "instanceof",
+2 −0
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ To be released.
     -  Added `VerifyRequestOptions.tracerProvider` option.
     -  Added `SignRequestOptions` interface.
     -  Added the optional fourth parameter to `signRequest()` function.
     -  Added `VerifyProofOptions.tracerProvider` option.
     -  Added `VerifyObjectOptions.tracerProvider` option.

 -  Added `@fedify/fedify/x/sveltekit` module for integrating with [SvelteKit]
    hook.  [[#171], [#183] by Jiyu Park]
+13 −9
Original line number Diff line number Diff line
@@ -118,12 +118,13 @@ Fedify automatically instruments the following operations with OpenTelemetry
spans:

| Span name                        | [Span kind] | Description                           |
|--------------------------------|-------------|---------------------------------------|
|----------------------------------|-------------|---------------------------------------|
| `{method} {template}`            | Server      | Serves the incoming HTTP request.     |
| `activitypub.get_actor_handle`   | Client      | Resolves the actor handle.            |
| `activitypub.lookup_object`      | Client      | Looks up the Activity Streams object. |
| `http_signatures.sign`           | Internal    | Signs the HTTP request.               |
| `http_signatures.verify`         | Internal    | Verifies the HTTP request signature.  |
| `object_integrity_proofs.verify` | Internal    | Verifies the object integrity proof.  |
| `webfinger.handle`               | Server      | Handles the WebFinger request.        |
| `webfinger.lookup`               | Client      | Looks up the WebFinger resource.      |

@@ -158,6 +159,9 @@ for ActivityPub:
| `http_signatures.algorithm`           | string   | The algorithm of the HTTP request signature.                                             | `"rsa-sha256"`                                                       |
| `http_signatures.key_id`              | string   | The public key ID of the HTTP request signature.                                         | `"https://example.com/actor/1#main-key"`                             |
| `http_signatures.digest.{algorithm}`  | string   | The digest of the HTTP request body in hexadecimal.  The `{algorithm}` is the digest algorithm (e.g., `sha`, `sha-256`). | `"d41d8cd98f00b204e9800998ecf8427e"` |
| `object_integrity_proofs.cryptosuite` | string   | The cryptographic suite of the object integrity proof.                                   | `"eddsa-jcs-2022"`                                                   |
| `object_integrity_proofs.key_id`      | string   | The public key ID of the object integrity proof.                                         | `"https://example.com/actor/1#main-key"`                             |
| `object_integrity_proofs.signature`   | string   | The integrity proof of the object in hexadecimal.                                        | `"73a74c990beabe6e59cc68f9c6db7811b59cbb22fd12dcffb3565b651540efe9"` |
| `webfinger.resource`                  | string   | The queried resource URI.                                                                | `"acct:fedify@hollo.social"`                                         |
| `webfinger.resource.scheme`           | string   | The scheme of the queried resource URI.                                                  | `"acct"`                                                             |

+1 −0
Original line number Diff line number Diff line
@@ -476,6 +476,7 @@ export async function handleInbox<TContextData>(
        contextLoader: context.contextLoader,
        documentLoader: context.documentLoader,
        keyCache,
        tracerProvider,
      });
    } catch (error) {
      logger.error("Failed to parse activity:\n{error}", {
+68 −2
Original line number Diff line number Diff line
import { getLogger } from "@logtape/logtape";
import { Activity, Multikey } from "../vocab/vocab.ts";
import {
  type Span,
  SpanStatusCode,
  trace,
  type TracerProvider,
} from "@opentelemetry/api";
import { encodeHex } from "@std/encoding/hex";
// @ts-ignore: json-canon is not typed
import serialize from "json-canon";
import metadata from "../deno.json" with { type: "json" };
import type { DocumentLoader } from "../runtime/docloader.ts";
import { DataIntegrityProof, type Object } from "../vocab/vocab.ts";
import {
  Activity,
  DataIntegrityProof,
  Multikey,
  type Object,
} from "../vocab/vocab.ts";
import {
  fetchKey,
  type FetchKeyResult,
@@ -149,6 +161,13 @@ export interface VerifyProofOptions {
   * @since 0.12.0
   */
  keyCache?: KeyCache;

  /**
   * The OpenTelemetry tracer provider.  If omitted, the global tracer provider
   * is used.
   * @since 1.3.0
   */
  tracerProvider?: TracerProvider;
}

/**
@@ -165,6 +184,53 @@ export async function verifyProof(
  jsonLd: unknown,
  proof: DataIntegrityProof,
  options: VerifyProofOptions = {},
): Promise<Multikey | null> {
  const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
  const tracer = tracerProvider.getTracer(metadata.name, metadata.version);
  return await tracer.startActiveSpan(
    "object_integrity_proofs.verify",
    async (span) => {
      if (span.isRecording()) {
        if (proof.cryptosuite != null) {
          span.setAttribute(
            "object_integrity_proofs.cryptosuite",
            proof.cryptosuite,
          );
        }
        if (proof.verificationMethodId != null) {
          span.setAttribute(
            "object_integrity_proofs.key_id",
            proof.verificationMethodId.href,
          );
        }
        if (proof.proofValue != null) {
          span.setAttribute(
            "object_integrity_proofs.signature",
            encodeHex(proof.proofValue),
          );
        }
      }
      try {
        const key = await verifyProofInternal(jsonLd, proof, options);
        if (key == null) span.setStatus({ code: SpanStatusCode.ERROR });
        return key;
      } catch (error) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: String(error),
        });
        throw error;
      } finally {
        span.end();
      }
    },
  );
}

async function verifyProofInternal(
  jsonLd: unknown,
  proof: DataIntegrityProof,
  options: VerifyProofOptions,
): Promise<Multikey | null> {
  if (
    typeof jsonLd !== "object" ||