FEP-c390: Identity Proofs

source: fep/fep-c390.md at main - fep - Codeberg.org


---
authors: @silverpill
status: DRAFT
dateReceived: 2022-11-23
discussionsTo: #34 - [TRACKING] FEP-c390: Identity Proofs - fep - Codeberg.org
---

FEP-c390: Identity Proofs

Summary

This proposal describes a mechanism of linking cryptographic keys to ActivityPub actor profiles.

History

  • Mastodon implemented identity proofs in 2019. Keybase platform was used as an identity provider, but the integration was later removed.
  • Keyoxide can create off-protocol identity proofs for Fediverse profiles using OpenPGP.

Identity proofs

Identity proof is a JSON document that represents a verifiable bi-directional link between a Decentralized Identifier and an ActivityPub actor.

It MUST contain the following properties:

  • type (REQUIRED): the type property MUST contain the string Identity.
  • id (REQUIRED): the decentralized identifier (DID) that represents a cryptographic key belonging to an actor.
  • alsoKnownAs (REQUIRED): the value of this property MUST match the actor ID.
  • proof (REQUIRED): the data integrity proof, as defined by Data Integrity specification.

The document MUST not contain any additional properties.

Identity proofs SHOULD be attached to an actor object, under the attachment property.

Proof generation

The identity proof document MUST contain a data integrity proof, which includes a cryptographic proof and parameters required to verify it. It MUST be created according to the Data Integrity specification, section 4.1 Generate Proof. The value of verificationMethod property of the data integrity proof MUST match the value of id property of the identity proof document.

The resulting data integrity proof MUST be added to identity proof document under the proof key.

Example:

{
    "@context": [
        "https://www.w3.org/ns/activitystreams",
        "https://www.w3.org/ns/did/v1",
        "https://w3id.org/security/data-integrity/v1",
        {
            "fep": "https://codeberg.org/fediverse/fep#",
            "Identity": "fep:Identity"
        }
    ],
    "type": "Person",
    "id": "https://example.com/users/alice",
    "inbox": "https://example.com/users/alice/inbox",
    "attachment": [
        {
            "type": "Identity",
            "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
            "alsoKnownAs": "https://example.com/users/alice",
            "proof": {
                "type": "JcsEd25519Signature2022",
                "created": "2022-11-12T00:00:00Z",
                "verificationMethod": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
                "proofPurpose": "assertionMethod",
                "proofValue": "<proof-value>"
            }
        }
    ]
}

Proof verification

The receiving server MUST check the authenticity of identity proof document by verifying its data integrity proof. If the server can’t verify the proof, or if the value of verificationMethod property of the data integrity proof doesn’t match the value of id property of the identity proof, or if the value of alsoKnownAs property of the identity proof doesn’t match the actor ID, the identity proof MUST be discarded.

Verification process MUST follow the Data Integrity specification, section 4.2 Verify Proof.

The receiving server SHOULD treat identities denoted by id and alsoKnownAs properties of identity proof as belonging to the same entity.

References

Copyright

CC0 1.0 Universal (CC0 1.0) Public Domain Dedication

To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.

1 Like

Hello!

This is a discussion thread for the proposed FEP-c390: Identity Proofs. Please use this thread to discuss the proposed FEP and any potential problems or improvements that can be addressed.

Summary
This proposal describes a mechanism of linking cryptographic keys to ActivityPub actor profiles.

2 Likes

@silverpill you should probably include in the example the DID @context which i assume you are pulling vocabulary from

1 Like

It’s a mixed bag.

The proof property comes from Data Integrity standard, which uses https://w3id.org/security/data-integrity/v1 context in this example: A simple signed JSON-LD data document.

The Identity type was suggested by someone in W3C wiki: Linking Identities. It is also necessary to make a distinction between FEP-c390 identity proofs and Mastodon identity proofs (which have IdentityProof type).

id and alsoKnownAs properties were indeed borrowed from the DID spec, which requires https://www.w3.org/ns/did/v1 context to be present in DID documents, but FEP-c390 attachment is not a DID document, strictly speaking.

What would be the best course of action here?

all of those should be included in the context then, and any new properties should be defined as an extension in your own context

prior art: see any as2 doc generated by mastodon and how it includes the Security context as well (among other things):

https://mastodon.social/users/trwnh.json

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1",
    {
      "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
      "toot": "http://joinmastodon.org/ns#",
      "featured": {
        "@id": "toot:featured",
        "@type": "@id"
      },
      "featuredTags": {
        "@id": "toot:featuredTags",
        "@type": "@id"
      },
      "alsoKnownAs": {
        "@id": "as:alsoKnownAs",
        "@type": "@id"
      },
      "movedTo": {
        "@id": "as:movedTo",
        "@type": "@id"
      },
      "schema": "http://schema.org#",
      "PropertyValue": "schema:PropertyValue",
      "value": "schema:value",
      "discoverable": "toot:discoverable",
      "Device": "toot:Device",
      "Ed25519Signature": "toot:Ed25519Signature",
      "Ed25519Key": "toot:Ed25519Key",
      "Curve25519Key": "toot:Curve25519Key",
      "EncryptedMessage": "toot:EncryptedMessage",
      "publicKeyBase64": "toot:publicKeyBase64",
      "deviceId": "toot:deviceId",
      "claim": {
        "@type": "@id",
        "@id": "toot:claim"
      },
      "fingerprintKey": {
        "@type": "@id",
        "@id": "toot:fingerprintKey"
      },
      "identityKey": {
        "@type": "@id",
        "@id": "toot:identityKey"
      },
      "devices": {
        "@type": "@id",
        "@id": "toot:devices"
      },
      "messageFranking": "toot:messageFranking",
      "messageType": "toot:messageType",
      "cipherText": "toot:cipherText",
      "suspended": "toot:suspended",
      "focalPoint": {
        "@container": "@list",
        "@id": "toot:focalPoint"
      }
    }
  ],
  "id": "https://mastodon.social/users/trwnh",
  // ...
}

note that mastodon currently inlines the context instead of actually hosting a jsonld document at joinmastodon.org/ns (which it should do, ideally)

1 Like

(How) does this relate to GitHub - chatternet/chatternet-client-http ? Should it?

1 Like

Added missing contexts: #35 - FEP-c390: Add missing contexts - fep - Codeberg.org. I don’t know how to create my own context, so I used https://codeberg.org/fediverse/fep namespace for Identity type. The example now passes JSON-LD validation.

They also rely on DID and VC Data Integrity standards. There’s definitely some overlap, I think we can work together to make our implementations interoperable.

1 Like

Thanks to silverpill for bringing this to my attention. Indeed I’ve been working on Chatter Net which shares some ideas with this proposal overlaps. Currently in Chatter Net actors documents look like this:

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://www.w3.org/2018/credentials/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1"
  ],
  "id": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW/actor",
  "type": "Person",
  "inbox": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW/actor/inbox",
  "outbox": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW/actor/outbox",
  "following": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW/actor/following",
  "followers": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW/actor/followers",
  "name": "Garrin",
  "proof": {
    "type": "Ed25519Signature2020",
    "created": "2022-11-26T18:58:47Z",
    "verificationMethod": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW#z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW",
    "proofPurpose": "assertionMethod",
    "proofValue": "z4iLgh38ipTKSVdvNttjJpGWP5kYZ5dv5jHaupwNjZmC6kpifnd4Ep7jV2A5pnYTVKoDvQdi4CYj2hAq5qR4cLMvh"
  }
}

Some notes as they relate to this discussion:

  • A DID should be the ID of a DID document. So in Chatter Net the /actor path is added to DIDs to differentiate the Activity Stream Actor object from the DID doc. You can see in this in action by requesting a DID document versus an Actor object.
  • Indeed wherever the proof key shows up the credential context will be needed, or the proof key could be fully expanded.

One thing to note is that Chatter Net currently isn’t using Activity Stream compliant messages because of the addition of the proof key in the object root, which isn’t part of the Activity Stream standard. These proofs are used not just for a user to assert things about their identity, but also to assert actions they take (i.e. assertion proofs are also added to activities such as Create).

Maybe this proposal could be extended to propose a new object type which isn’t just an Identity proof, but generally an Assertion proof? This way such an object could be added as an attachment to any document to assert something about the document?

It is also probably worth considering whether or not this is best done by instead introducing a assertion property to the base Activity Stream Object? This is would look more like what Chatter Net is currently doing, and be more convenient from the app development point of view because extracting a proof would require only looking up a key instead of traversing an array.

2 Likes

@silverpill, great job on the Identity Proofs FEP! It looks really good.

So this coming Tuesday Nov 29th, the W3C Credentials Community Group (where DIDs and VCs were incubated) is going to host a call on DIDs in Federated Social Web apps. (Here’s the post announcing it to the SocialWeb CG: Join the CCG "DIDs (Decentralized Identifiers) in ActivityPub/Fediverse" conversation from Dmitri Zagidulin on 2022-11-26 (public-swicg@w3.org from November 2022) )

I’ve also sent a link to your Identity Proofs FEP to the CCG mailing list, as suggested pre-call reading.

You should join the call on Tuesday, if you’re able! (The CCG is free to join, anyone can attend the calls).
(This goes for everyone on this thread, who’s interested in the topic).

4 Likes

There’s another proposal that describes the use of data integrity proofs for authenticating activities, perhaps it will be more appropriate for your use case: fep/fep-8b32.md at main - fep - Codeberg.org.

I picked attachment property for identity proofs because attachments are already used in Fediverse to add various metadata to actor profiles (e.g. PropertyValue attachments), so many applications would be traversing this array anyway. This also allows actors to link several identities to their profiles.

2 Likes

Is there a way to shorten this instead of repeating the same (difficult to read) value, e.g.:

  "inbox": "@id:/actor/inbox",
  "outbox": "@id:/actor/outbox",

Neat idea. I’m not familiar with the “@id:” syntax in a json-ld string, would that expand to the full URL? I think this could also be achieved by adding a base URL to the context and then using relative URLs. I might opt for that as I think it will play nicer with the expansion algorithms? Or am I missing something?

I did not test anything, this is just a hunch, but indeed it would make the data more readable and lighter, at the expense, maybe of increased parsing.

Non-normative, sure: JSON-LD 1.1 5.2.1 Shortening IRIs

You can use @base in your @context to add a “base IRI” like so:

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
    "@base": "did:key:z6MkiB1ykfZH2h91GDmY8Nh7iEiVzJX9KP5gvsshtfmpnfwW"
    }    
],
  "@id": "/actor",
  "inbox": "/actor/inbox",
  "outbox": "/actor/outbox",
  // ...
}

This requires support from your JSON-LD (specifically JSON-LD 1.1) parser, so ecosystem support would not be very widespread.

It’s also currently broken in the JSON-LD playground: JSON-LD Playground

{
  "@id": "did:/actor",
  "http://www.w3.org/ns/ldp#inbox": {
    "@id": "did:/actor/inbox"
  },
  "https://www.w3.org/ns/activitystreams#outbox": {
    "@id": "did:/actor/outbox"
  }
}

@trwnh - I think my question is, how often are you reading raw json? I’d personally recommend against doing relative URLs like that. In terms of how much room they take up over the wire, or in storage – don’t forget that everything basically uses gzip over the wire, so all those repeating URLs or whatever will be compressed down anyway.

1 Like