FEP-8b32: Object Integrity Proofs

Update: #700 - FEP-8b32: Update proposal - fediverse/fep - Codeberg.org

  • The recommendation of same-origin check was removed in favor of same-owner check.
  • Data integrity context was changed to v2 in examples and test vectors: https://w3id.org/security/data-integrity/v2.
  • Added “Privacy considerations” section discussing the possibility of exposing private data.
  • New implementation: Gush.
1 Like

I’ve implemented this in squidcity (along with its partner, fep-521a), and pushed it live a few weeks ago with no problems – tho that’s probably because very few servers have implemented it yet. But I had at least one successful verification! :slight_smile:

Some notes:

In my testing, base58 was awkward to implement and seemed to provide no meaningful benefit over base64 at these data sizes. I don’t want to pile on, but I agree with the others that an updated spec should recommend base64 and require both encodings to be understood.

I know there was debate about dropping HTTP signatures once this FEP becomes ubiquitous, but: HTTP signatures and proofs don’t completely overlap, so I don’t think HTTP signatures have become redundant. The ability of a proof to “stay attached” across boosts and quotes as it bounces around the network solves a real problem (yay!). But HTTP signatures, especially now that we have “server actors”, are asserting the identity of the server itself, and can be useful if you want a closed allow-list network. It acts as a kind of sealed envelope before you even get to the enclosed event.

Speaking of which, I noticed that the long-suffering HTTP signature RFC was finished. Is there a FEP describing how to use them (and especially: migrate to them)?

2 Likes

Your implementation of FEP-521a appears to be correct :+1: (I was able to fetch lobsters actor you mentioned in readme; can’t verify FEP-8b32 because my Follow activity is being rejected with FST_ERR_CTP_INVALID_MEDIA_TYPE).

The upstream specification needs to be changed first - base58 is required by eddsa-jcs-2022 cryptosuite (section 3.3.1).

I am in favor of base58, anyway, because it is easier to read and because it is already used in many places - integrity proofs, multikeys, did:key, etc.

Not yet, but there’s a detailed description in FEDERATION.md of tootik project: https://github.com/dimkr/tootik/blob/d6fecfefd80a445b27f589250bb19ebcd95acee2/FEDERATION.md#http-signatures

Thanks for checking it out! It looks like the follow requests were getting dropped by fastify because the content-type was “application/ld+json” instead of json or activity+json. I’ve just added that to the list of accepted content types.

1 Like

I am trying to verify the proof on this object: https://bots.grilledcheese.social/ap/post/17u8fu93k5tq51kg4g15. It has @context, but it doesn’t match the top-level @context.

eddsa-jcs-2022 has the following requirement:

If proofOptions.@context exists:
Check that the securedDocument.@context starts with all values contained in the proofOptions.@context in the same order. Otherwise, set verified to false and skip to the last step.

Your object has ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"] and its proof has ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v2"].

(my implementation doesn’t actually check the order, it simply compares contexts)

I see, so "https://w3id.org/security/data-integrity/v2" should replace "https://w3id.org/security/v1" in the standard context block at the top? We should probably call that out explicitly in the next revision of the FEP. (I think most servers write the boilerplate template once and then ignore it.)

Looking at https://bots.grilledcheese.social/ap/post/17u8fu93k5tq51kg4g15 I see no reason that https://w3id.org/security/v1 should be included at all. You’re not using any properties on the Note from that context. Every property on that document is from https://www.w3.org/ns/activitystreams except for proof, which comes from https://w3id.org/security/data-integrity/v2. Within the proof sub-document, you are not using any properties from https://www.w3.org/ns/activitystreams either. So I think the document should actually look like this:

{
  "@context": [
    "https://w3id.org/security/data-integrity/v2",
    "https://www.w3.org/ns/activitystreams"
  ],
  "proof": {
    "@context": "https://w3id.org/security/data-integrity/v2",
    "//": "the proof"
  },
  "//": "the rest of the document"
}

It can be simplified even further by taking out the @context on the proof, since this is all one document and you already imported it at the top-level (with no need for any overrides):

{
  "@context": [
    "https://w3id.org/security/data-integrity/v2",
    "https://www.w3.org/ns/activitystreams"
  ],
  "proof": {
    "//": "the proof"
  },
  "//": "the rest of the document"
}

The https://w3id.org/security/v1 context only matters when using terms from that context, such as publicKey, publicKeyPem, owner, signature, signatureValue, and so on. (A lot of these properties have been deprecated or removed from the Security Vocabulary, but are still used in current fedi implementations like Mastodon et al.)


Regarding the requirement from eddsa-jcs-2022, the order is important because later context declarations override earlier declarations.

For example, if the document declares ["https://w3id.org/security/data-integrity/v2", "https://www.w3.org/ns/activitystreams"] then it is possible that terms defined in https://w3id.org/security/data-integrity/v2 may be redefined in https://www.w3.org/ns/activitystreams, which would override the earlier term definitions and change the semantics of the proof. if the proof re-declares https://w3id.org/security/data-integrity/v2 then it ensures that the terms used within the proof are not semantically confused – any potential re-definitions within https://www.w3.org/ns/activitystreams would be overridden back to whatever is defined in https://w3id.org/security/data-integrity/v2.

A bit of a contrived example: suppose that the activitystreams context defined created to be a boolean of whether the current object was created or not. AS2 processors are required to inject the normative activitystreams context if it is missing. Doing so would cause the proof options created to map to whatever is defined in the activitystreams context (our hypothetical boolean here) instead of what is defined in the data-integrity/v2 context (dc:created, the timestamp of when the proof was created). Rather than requiring eddsa-jcs-2022 implementers to understand JSON-LD and re-inject the data-integrity/v2 context into the proof options, the algorithm simply asks eddsa-jcs-2022 verifiers to check that no terms were overridden like this. The order is important for ensuring this check. (In practice the data-integrity/v2 context is @protected, so terms defined there cannot be redefined/overridden later. JSON-LD processors will catch this and throw an error, but JSON processors will not.)

The @context should be copied to proof. This is stated more clearly in the proof generation algorithm:

If unsecuredDocument.@context is present, set proof.@context to unsecuredDocument.@context.

https://www.w3.org/TR/vc-di-eddsa/#create-proof-eddsa-jcs-2022

I know, I was trying to say that my implementation checks if contexts match exactly, instead of doing what the specification prescribes:

Check that the securedDocument.@context starts with all values contained in the proofOptions.@context in the same order.

Okay, so it probably came from publicKey which I still use in actor records for cavage-draft signature compatibility. (I use the same minimal context block for all AP records to avoid overhead.)

I’ve added the new data-integrity url to the top context, and now copy that into the proof. Hopefully that will satisfy the verifiers.

@robey Did you change the proof.@context? I saw an activity from your test actor a few days ago, and it still had wrong context.

Ope, thanks, I missed the most important code block! I think it’s fixed now: https://bots.grilledcheese.social/ap/post/17wtczg3emnjg6tap36b

1 Like

Update: https://codeberg.org/fediverse/fep/pulls/839/files

I added two new requirements, including the requirement of forward compatibility.

I also added a note about verification method rotation to “Privacy considerations” section, to address concerns raised by @tesaguri in FEP-ef61 thread: