-
Notifications
You must be signed in to change notification settings - Fork 24
CIP: CapReg - object-capability registry #127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
cc4bb8f
a836464
0d53780
db1779b
1f45659
239fc74
f53fddd
9dd1698
a5723dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,207 @@ | ||||||
| --- | ||||||
| cip: 127 | ||||||
| title: CapReg - object-capability registry | ||||||
| author: Joel Thorstensson (@oed) | ||||||
| discussions-to: https://forum.ceramic.network/t/cip-127-capreg-object-capability-registry | ||||||
| status: Draft | ||||||
| category: RFC | ||||||
| created: 2023-04-28 | ||||||
| updated: 2023-04-28 | ||||||
| --- | ||||||
|
|
||||||
| ## Simple Summary | ||||||
|
|
||||||
| <!--Provide a simplified and layman-accessible explanation of the CIP.--> | ||||||
| The CapReg capability registry enables users to notarize and revoke any object-apability associated with their DID. | ||||||
|
|
||||||
|
|
||||||
| ## Abstract | ||||||
|
|
||||||
| <!--A short (~200 word) description of the technical issue being addressed.--> | ||||||
| Using a simple Ceramic stream, the capability registry enables users to notarize and revoke object-capabilities issued by their DID. The registry is based on hashes of object-capabilities encoded as CACAO. | ||||||
|
|
||||||
|
|
||||||
| ## Motivation | ||||||
|
|
||||||
| <!--Motivation is critical for CIPs that want to change the Ceramic protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the CIP solves. CIP submissions without sufficient motivation may be rejected outright.--> | ||||||
|
|
||||||
| Currently in Ceramic the main accounts types are PKH DID. These are great because they enable existing wallets to be used directly with Ceramic. Once a PKH DID is used to delegate permissions to a session key. That delegation will remain valid until the capability expires. This could be a problem in the case of a stolen session key or a malicious application. By introducing a capability registry these capabilities can be revoked at any time by the main DID (PKH DID in the case above, but this would work for any DID method). A system that uses these object capabilities could refer to the registry to verify that the capability has not been revoked before it was used. | ||||||
|
|
||||||
| ## Specification | ||||||
|
|
||||||
| <!--The technical specification should describe the syntax and semantics of any new feature.--> | ||||||
| This specification describes the data structure of the capability registry, its validation and consensus logic, as well as how wallet UX would look like for someone using the registry. | ||||||
|
|
||||||
|
|
||||||
| ### Capability Registry | ||||||
|
|
||||||
| The capability registry is based on a self-certifying data structure represented as a special type of Ceramic stream. Each DID has uniquely *one* capability registry. Any object capability issued by this DID can be notarized and revoked in the registry. Every update to the registry is recorded as a *Data Event*. | ||||||
|
|
||||||
| ```verilog | ||||||
| type Prinicipal Bytes // multidid | ||||||
| type CACAOHash Bytes // multihash | ||||||
| type Varsig Bytes // varsig | ||||||
|
|
||||||
| type Entry struct { | ||||||
| key CACAOHash | ||||||
| revoked Boolean // true if the capability has been revoked | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it might be nice to make this more than a boolean, but rather a Even if we only support "going forward" revocation in the first version, it would be nice to leave the door open to adding "retroactive" revocation as well in the future. |
||||||
| ocap optional Link // optionally include the CID of the capability | ||||||
| } representation tuple | ||||||
| // The registry should eventually be a HAMT or verkle-tree data structure | ||||||
| type Regsitry { CACAOHash : Entry } | ||||||
| type Snapshot struct { | ||||||
| registry &Regsitry | ||||||
| actions [Entry] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| #### Ceramic Stream | ||||||
|
|
||||||
| ```verilog | ||||||
| type Event InitEvent | DataEvent | TimeEvent | ||||||
|
|
||||||
| type InitEvent &Prinicipal // an inline CID containing raw principal bytes | ||||||
|
|
||||||
| type DataEvent struct { | ||||||
| id &InitEvent | ||||||
| prev [&Event] // optional CID pointer to previous event | ||||||
| prf [&CACAO] // capabilities used to emit this event | ||||||
| data &Snapshot | ||||||
| sig Varsig | ||||||
| } | ||||||
|
|
||||||
| type EthereumTx // <https://ipld.io/specs/codecs/dag-eth/chain/#transaction-ipld> | ||||||
|
|
||||||
| type BlockchainTimestamp struct { // https://chainagnostic.org/CAIPs/caip-168 | ||||||
| root Link | ||||||
| chainID String | ||||||
| txType String | ||||||
| txHash &EthereumTx | ||||||
| } | ||||||
|
|
||||||
| type TimeEvent struct { | ||||||
| id &InitEvent | ||||||
| prev [&DataEvent] // should always be one CID | ||||||
| proof &BlockchainTimestamp | ||||||
| path String | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| #### Streamid | ||||||
|
|
||||||
| Generating the streamid can be done in three steps, | ||||||
|
|
||||||
| 1. Generate the multidid representation of the DID (see [Multidid specification]()). | ||||||
|
|
||||||
| 2. Encode the multidid as an inline CID, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is interesting as it breaks some assumptions in the Ceramic code as it exists today. Currently we always assume the genesis CID can be loaded and used to create an initial StreamState object for the stream |
||||||
|
|
||||||
| ```solidity | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this marked as solidity code? |
||||||
| <genesis-cid> := <varint cidv1><varint 0x55><varint 0x00><varint multidid-length><multidid> | ||||||
| ``` | ||||||
|
|
||||||
| 3. Encode the Streamid (see Appending A for `stream-type`) | ||||||
|
|
||||||
| ```solidity | ||||||
| <streamid> := <varint 0xce><varint stream-type><genesis-cid> | ||||||
| ``` | ||||||
|
|
||||||
| #### Create a `DataEvent` | ||||||
|
|
||||||
| New data events | ||||||
|
|
||||||
| 1. Attain a key with a valid capability chain to the most recent *previous data event(s)* | ||||||
|
|
||||||
| 2. Create one or more `Entry` objects and update the `State` from the | ||||||
|
|
||||||
| previous data event(s): | ||||||
|
|
||||||
| 1. Write the objects to a new array and store them on the `actions` field | ||||||
| 2. Also append them to the `registry` map | ||||||
|
|
||||||
| 3. Create the `DataEvent` struct, | ||||||
|
|
||||||
| 1. Set `id` to the principal (an inline CID containing a multidid encoded 3ID), | ||||||
| 2. Add the capability chain used to the `prf` field | ||||||
| 3. Add the previous event(s) to the `prev` field | ||||||
| 4. Add the updated state to the `data` field | ||||||
| 5. Create a `Varsig` over the `DataEvent` with the key from (1) and add the `sig` field | ||||||
|
|
||||||
| #### Validating a `DataEvent` | ||||||
|
|
||||||
| The certification of a `DataEvent` can be validated using the following algorithm, | ||||||
|
|
||||||
| 1. The varsig validates agains the *aud* `Principal` of the referenced CACAOs | ||||||
|
|
||||||
| 2. The multihash of the CACAO CID (caphash) is one of: | ||||||
|
|
||||||
| 1. CapHash is in the `Registry` and `revoked` is false | ||||||
| 2. CapHash is in the `Principal` and **not** in the `Registry` | ||||||
|
|
||||||
| 3. The CACAO *ability* is crud on the CapReg streamid | ||||||
|
|
||||||
|
|
||||||
| #### Consensus | ||||||
|
|
||||||
| In case of two conflicting events (two events share the same `prev` value) the event with the earliest `TimestampEvent` should be processed first. Note that this might lead to the latter event being invalid due to its delegation chain being revoked. Also, a new event emitted after the conflict must reference both branches in its `prev` and resolve any conflict of the `Registry`. | ||||||
|
|
||||||
| If there is no anchor for either event yet, the `DataEvent` with the lowest binary value of its CID will win. Note that if a `TimeEvent` appears this order might change. | ||||||
|
|
||||||
| #### Verified timestamps | ||||||
|
|
||||||
| For convenience, once a `TimeEvent` has been verified the data used to verify it can be stored as a receipt. This is helpful when resolving the registry at a particular point in time using the `?versionTime=<iso-time>` DID URL parameter. | ||||||
|
|
||||||
| ```verilog | ||||||
| type EthereumHeader // <https://ipld.io/specs/codecs/dag-eth/chain/> | ||||||
|
|
||||||
| type TimestampRecipt struct { | ||||||
| unixtime Integer // same as block.Time | ||||||
| event &TimestampEvent | ||||||
| block &EthereumHeader | ||||||
| path String // ipld path in the block to find event.txHash | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ### Object Capabilities | ||||||
|
|
||||||
| CapReg relies heavily on object-capabilities as they way to add and remove CACAO hashes in the registry. Write access to CapReg can be delegated in the same way as delegting access to any other stream in Ceramic. Example using ReCap: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "tar":{ | ||||||
| "ceramic://<capreg-streamid>": { | ||||||
| "crud/create": [{}], | ||||||
| "crud/delete": [{}] | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| As an extension, it would also be worth exploring the possibility of creating a zero-knowlege proof that proves that a specific CACAO hash is allowed to be added to the registry without revealing what the content of the CACAO itself. Simply the fact that the CACAO is valid should be enough to add it to the registry. | ||||||
|
|
||||||
| ## Rationale | ||||||
|
|
||||||
| <!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.--> | ||||||
|
|
||||||
| CapReg enables any DID to delegate full or partial permission to any other DID without having to worry about the capability getting lost since capabilities can now be revoked. The design requires some special logic for the state transition of the event stream to ensure that the capability is valid when the stream was updated. While Ceramic generally strives towards not including state transtion logic in event streams, for this particular case it seems difficult to avoid. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What does this mean?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't all of our streams have state transition logic? |
||||||
|
|
||||||
| While using capabilities in public is fine for some use cases, the ability to do so privately is quite important for many. Using *capability hashes* enables more privacy since the DIDs that are delegated to don't strictly need to be revealed. It is worth noting that revealing the CACAO object when used is the simplest way to prove a capability chain. However, it is possible to create zero-knowledge proofs that only reveal the hash of the capability used and the session key which was delegated to. | ||||||
|
|
||||||
|
|
||||||
| ## Backwards Compatibility | ||||||
|
|
||||||
| <!--All CIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The CIP must explain how the author proposes to deal with these incompatibilities. CIP submissions without a sufficient backwards compatibility section may be rejected outright.--> | ||||||
| n/a | ||||||
|
|
||||||
| ## Reference Implementations | ||||||
|
|
||||||
| Currently no reference implementation for CapReg currently exists. | ||||||
|
|
||||||
| ## Appendix A: Registrations | ||||||
|
|
||||||
| ### Stream type code | ||||||
|
|
||||||
| **Code:** `5` | ||||||
|
|
||||||
| ## Copyright | ||||||
|
|
||||||
| Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.