Use cases of fep-8b32: Object Integrity Proofs

Continuing the discussion from FEP-8b32: Object Integrity Proofs:

I’m starting a new topic to collect the cases where using signed documents will be useful. New topic, as I’d like this discussion to be separate from the original topic, that discusses how to implement proofs.

I’m currently aware of three use cases:

  • FEP-c390: Identity Proofs.
  • Forwarding from the inbox if the activity is signed then it can the person that receives the forwarded activity doesn’t need to fetch the activity to check authenticity.
  • Announce activities can contain the signed object. This would require the ability of the server sending the Announce to obtain a signed copy of the object being announced.

Are there more use cases?

Another side note: By signing documents, we turn them from “linked data” into json-ld as a key resolver. I don’t think the signatures survive using another context on the documents, or resolving an URI for the corresponding document. So these signatures are probably similar to HTTP Signatures only good for the transport and forwarding. I haven’t worked out the use cases in sufficient details to have a firm opinion here.

As I was reminded of Should we fork AS/AP specs to Codeberg, create vNext drafts?, let me also make this bridge. Using Fep-8b32 to specify modified Announce / Inbox Forwarding behavior is quite a step to take for evolving AP/AS. I personally think it should be up to the person spending the energy to work out the details if they want to do it via W3C or FEP or something else. However, I think it’s something one should form an opinion on, before submitting a document.

3 Likes

Another interesting use case is client-side signing. This requires FEP-c390 (for linking user’s signing key to the actor) and server needs to accept activity ID provided by the client.

4 Likes

Not sure that’s how I would implement that. If the client were to sign the Note object (not the Create), I would like to see some type of process where first the Note is assigned by the server an id (and possibly reply, shares, etc… collections), and then the client uploads a signed Note.

Disclaimer: I haven’t worked out the details yet. It relates to a lot of stuff somewhere not at the top of my todo list (like actually displaying something like a public timeline).

Yes, there are multiple ways to implement that. I’m using semi-random IDs, so I can generate them on the client and construct object IDs according to the template (https://myserver.example/objects/<uuid>) without requesting ID from the server.
This is on my todo list. I already have a number of API endpoints that enable client-side signing, for example user can sign “profile update” activity and send it as is to followers. It is working but I need to rewrite everything to use AP C2S, FEP-8b32 and FEP-c390.

By using signed objects the mechanics of fep-1b12 should extend to activities only addressed to the group. This will lead to “facebook style groups”, i.e. only members of the group can see what was posted to the group.

This might require some UI work in implementations:

  • It should be possible to address a post to a group. The Mastodon mechanic of “mention” + selecting visibility “mentioned people only” might work in theory, but would lead to users unhappy with the UX if they are supposed to use this for group posting.
  • Announce activities from a group need to interpreted correctly, in particular one should be able to reply with the group being the recipient.

I don’t think any of this is hard. It just needs thinking things through properly.

1 Like

In theory FEP-8b32 may unlock content-addressing, but I haven’t figured out how to introduce it without breaking compatibility with existing software.

In ActivityPub, id property is required, but one can’t know the hash of the object before hashing it. Possible workaround: sign everything except the id:

{
    "type": "Note",
    "attributedTo": "https://test.example/users/alice",
    "content": "Example",
    "proof": {
        "excludedProperties": "id",
        ...FEP-8b32 proof
    }
}

Let’s assume that IPFS CID of this object is bafybeihc4hti5ix4ds2tefhy35qd. The content URI will be ipfs://bafybeihc4hti5ix4ds2tefhy35qd.

The AP server should also work as IPFS gateway and use /ipfs/<cid> template to generate the ID:

{
    "id": "https://test.example/ipfs/bafybeihc4hti5ix4ds2tefhy35qd"
    "type": "Note",
    "attributedTo": "https://test.example/users/alice",
    "content": "Example",
    "proof": {
        "excludedProperties": "id"
        ...FEP-8b32 proof
    }
}

Implementations that support content addressing can ignore the host name and load object directly from IPFS, they will also know how to re-create ID. Everyone else will fetch it via HTTP. The server may even insert id property when serving objects via HTTP to provide backwards compatibility.

That might work. Unfortunately, Data Integrity spec doesn’t support partial signing, and there’s no such parameter as excludedProperties.

1 Like

Federated moderation such as sharing results by PhotoDNA should use cryptographic signatures, so there is better accountability.

Integrity proofs are good for all kinds of approvals and attestations. And while there are ways in which they can be abused (e.g. inserting centralized attestation services into a network), I think there are many legitimate use cases as well. In the past I suggested that FEP-5624 implementations may use integrity proofs to reduce the number of HTTP requests: FEP-5624: Per-object reply control policies - #42 by silverpill.

I’ve written up a draft how one can do “fairer announces”:

You can also consider this just me wanting to draw sequence diagrams and discovering that codeberg can render them in markdown.

I think that this sequence diagram and the one above illustrate why I basically think that we must use signatures to rework the Announce activity.

1 Like

Bad example
The following is a naive embedding of the object. I’m unsure how I would specify a parser for this that is not extremely specific to this one use case. So if one wants to handle more use cases, one needs to write a new parser.

Why a new parser is needed? Is it somehow related to @context being present in embedded post?

1 Like

The first parser is the detection of objects, who need their signature checked. Then comes checking these signatures. Then the actual parsing.

I would like this first parser to be as simple as possible, so use a container that just contains these objects.

Array as a top-level structure is unusual, and likely not supported by many implementations. Perhaps your signature checker could search for nested objects containing proof key and verify each one?

Implementations need to changed anyway to support receiving additional data via an announce. So compatibility with current implementations is not a real factor for me.

I would like to have a simple algorithm that I can actually describe. The algorithm:

Traverse the list and check

  • the first element has the authority the list was signed
  • the following elements are either signed or have the same authority as the first element

is simple. One still needs to check that all subobjects of these elements have the same authority as the parent, but this is the same problem as one currently has (also it can be done by a jsonld.flatten.)

If one suggests using an algorithm that traverses the object, things get quite a bit more complicated. I would claim that this is the exact stuff, I prefer not implementing myself, and like using libraries for!

In regards of the proposed draft FEP (FEP-0008), I’d recommend to instead use the @included keyword of JSON-LD in place of the proposed extension embeddedObject property. To my interpretation of the JSON-LD standard, @included offers a means to include a list of other referenced node objects, which may be embedded in some parsing/expansion/framing operation, meanwhile ‘plain JSON’ implementations can just do the exact same thing without any prerequisite of JSON-LD parsing logic (match a referenced ID in the main object, to the ID of an object listed in @included).

As it simply says in the spec:

@included
Used in a top-level node object to define an included block, for including secondary node objects within another node object.

Or for the more extensive definition and example: JSON-LD 1.1

Simply: it may be best to formulate a broader FEP, proposing to make use of @included, with Announces as one example of usage. Further, it may require changes to FEP-8b32 to recommend omitting that property from any sign/verify operations. It would pretty much be an FEP plainly of “hey, @included exists, let’s use that” rather than recreating it, and needs no additional @context declaration, since it’s already built into the format itself. I can write up a proposed replacement, if desired.

This can also extend to usage in the C2S protocol as well, to let the server be more helpful to the client and hint some referenced objects, rather than the client having to manually individually query every referenced object.

Just a simple kitchen sink example:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Announce",
  "actor": "https://example.social/user/bob",
  "object": "https://elsewhere.example/note/12345",
  "@included": [
    {
      "type": "Note",
      "id": "https://elsewhere.example/note/12345",
      "actor": "https://elsewhere.example/user/alice",
      "content": "Example content"
    },
    {
      "type": "Person",
      "id": "https://elsewhere.example/user/alice",
      "preferredUsername": "alice",
      (...)
    }
  ]
}

Give or take Object Integrity Proofs for each standalone object.

Aside: Was wanting to chime in on the discussions on the fediverse, but for some reason still having difficulty querying anything from your server (mymath.rocks) since almost a year ago, while everyone else shows up fine in discussion threads.

1 Like

See my discussion above. The result of compacting the following against "https://www.w3.org/ns/activitystreams",

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "embeddedObjects": {
        "@id": "https://funfedi.example/embeddedObjects",
        "@type": "@json",
        "@container": "@set"
      }
    }
  ],
  "actor": "https://first.example/users/first",
  "cc": [
      "https://second.example/users/second",
      "https://first.example/users/first/followers"
  ],
  "id": "https://first.example/users/first/statuses/1097854/activity",
  "object": "https://second.example/users/second/statuses/109724234853",
  "published": "2023-01-31T19:11:46Z",
  "to": ["https://www.w3.org/ns/activitystreams#Public"],
  "type": "Announce",
  "@included": [
    {
      "@context": [
          "https://www.w3.org/ns/activitystreams",
          "https://w3id.org/security/data-integrity/v1"
      ],
      "id": "https://second.example/users/second/statuses/109724234853",
      "type": "Note",
      "content": "Hello world",
      "proof": {
          "type": "DataIntegrityProof",
          "cryptosuite": "eddsa-jcs-2022",
          "verificationMethod": "https://second.example/users/alice#ed25519-key",
          "proofPurpose": "assertionMethod",
          "proofValue": "z3sXaxjKs4M3BRicwWA9peyNPJvJqxtGsDmpt1jjoHCjgeUf71TRFz56osPSfDErszyLp5Ks1EhYSgpDaNM977Rg2",
          "created": "2023-02-24T23:36:38Z"
      }
    }
  ]
}

is

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://first.example/users/first/statuses/1097854/activity",
  "@included": {
    "id": "https://second.example/users/second/statuses/109724234853",
    "type": "Note",
    "https://w3id.org/security#proof": {
      "@graph": {
        "type": "https://w3id.org/security#DataIntegrityProof",
        "http://purl.org/dc/terms/created": {
          "type": "xsd:dateTime",
          "@value": "2023-02-24T23:36:38Z"
        },
        "https://w3id.org/security#cryptosuite": "eddsa-jcs-2022",
        "https://w3id.org/security#proofPurpose": {
          "id": "https://w3id.org/security#assertionMethod"
        },
        "https://w3id.org/security#proofValue": {
          "type": "https://w3id.org/security#multibase",
          "@value": "z3sXaxjKs4M3BRicwWA9peyNPJvJqxtGsDmpt1jjoHCjgeUf71TRFz56osPSfDErszyLp5Ks1EhYSgpDaNM977Rg2"
        },
        "https://w3id.org/security#verificationMethod": {
          "id": "https://second.example/users/alice#ed25519-key"
        }
      }
    },
    "content": "Hello world"
  },
  "type": "Announce",
  "actor": "https://first.example/users/first",
  "cc": [
    "https://second.example/users/second",
    "https://first.example/users/first/followers"
  ],
  "object": "https://second.example/users/second/statuses/109724234853",
  "published": "2023-01-31T19:11:46Z",
  "to": "as:Public"
}

which is obviously not what I want. The same should be true for anybody wanting to check the DataIntegrityProof.

Regarding pleroma and mymath.rocks: It might be related to this akkoma bug, I filed 3 weeks ago.

1 Like

In regards of the result having expanded IRIs and expanded graph representation of the proof? It seems if you combine the @context of the main object, and each item in the @included list it should compact again just fine. But then, yes, the result has a different @context value in the output, which would break proof verification. I guess a JSON literal type is probably the only solution then.

And yes, I assume it’s very likely the mentioned bug, I’ll see if I can get that ironed out on my server.

What if we re-define object? How that will affect everything?

"@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "object": {
        "@id": "https://funfedi.example/object",
        "@type": "@json"
      }
    }
  ]

Playground: JSON-LD Playground

It wouldn’t be conformant with AS2 and would break JSON-LD/RDF processing (“break” in the sense that the object would effectively become a string instead of a subgraph).

Implementations may augment the provided @context with additional @context definitions but must not override or change the normative context. — Section 2.1, AS2 Core

2 Likes

Scaling takes precedence over any data format considerations. So if sticking to strict JsonLD doesn’t allow proper scaling, JsonLD dies. As the only approach, I know, to reduce the amount of traffic caused by an Announce activities, breaks pure JsonLD. It means pure JsonLD will die.

So if you want to save JsonLD, it’s time to put on the design hat and come up with a different approach.

Not sure. I think one could in a similar vain declare that Activities must obey some json-schema, with some additional rules related to:

  • If the embedded object has a context and a proof property
  • If the embedded object has no context

Currently, I need to take a look at what steams provides in the context. I haven’t spend the energy yet to figure out what is done, and if I would want to do it differently.

e.g. Mike Macgirvin 🖥️ - mikedev@fediversity.site

2 Likes