-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat: Add capability spec #6
base: main
Are you sure you want to change the base?
Changes from all commits
054029e
a164ef7
3324e8c
9e92913
1825d82
83996b1
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,98 @@ | ||
# Capability format | ||
|
||
<dl> | ||
<dt>Namespace</dt><dd>`capabilities`</dd> | ||
<dt>Version</dt><dd>1.0</dd> | ||
</dl> | ||
|
||
This is an application format for continuous sharing and minting of capability information | ||
|
||
## Subspace | ||
|
||
### Communal Namespaces | ||
|
||
Capabilities are issued by an entity, and are therefore published under the issuing entities subspace. | ||
|
||
### Owned Namespaces | ||
|
||
The use of an owned Namespace depends on the owner - so they should set the rules on which subspace to use. | ||
In practice - if you only have the ability to write in one subspace, you'd presumably publish the capabilities there. | ||
|
||
## Public capabilities | ||
|
||
The knowledge that you've issued capabilities to the receiver will be public for all. The contents, however, will be encrypted, so the capabilities / subspaces / paths they've been granted access to will not be public. | ||
|
||
`/capabilities/1.0/public/{receiver-pubkey}/{id}` | ||
|
||
## Private Capabilities | ||
|
||
You may want to publish these entries to all (e.g. You'd like help from everyone in the network to distribute, because you don't have a direct peer with the recipient), | ||
while not allowing people to know who you're actually granting these to. | ||
|
||
More formally, given the issuer, a path a capability is published under, and a proposed receiver, it should not be possible for anyone else to know if that is the correct receiver. | ||
|
||
For such cases instead of publishing these under the pubkey of the receiving entity, they are instead published under `encrypt(receiver, concat(receiver-pubkey, nonce))`. The receiver must attempt to decrypt all paths until they find their one. The issuer should save and use the same nonce in future so this only needs to happen once. | ||
|
||
`/capabilities/1.0/private/{encrypt(receiver, concat(receiver-pubkey, nonce))}/{id}` | ||
|
||
## Capability document IDs | ||
|
||
You may want one, or many, depending on the application, if you want to coordinate between multiple client devices etc. These are opaque to the receiver, who should decode all documents under the path prefix. | ||
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. The document exposition needs some clarification on whether we assume peers to reuse keypairs on multiple devices, or whether there should be at most one device per keypair. Do we wish to support both? |
||
|
||
A suggested scheme for this would be using `/1`, `/2` etc. If you're merely extending the lifetime of an existing cap, then you can write to the same path. It doesn't matter if you have two clients that independently do this. If you want to change the contents, you should increment the number, and tombstone older entries. This behaviour doesn't handle the same user making changes to the same receiver's caps concurrently on two devices they own - only one will win. | ||
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. Not quite getting either what exactly you describe here, nor which problem it solves =( 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. Fair, will try and elaborate and reword. This relates to our discussion on Discord on multiple client apps using the same keypair to run this extension process, and what happens if they conflict. I think some more explicit examples would likely help. (Hmm, maybe a diagram? Will give it a go) |
||
|
||
## Payload format | ||
|
||
These should be encrypted under the pubkey of the receiver of the capability. This prevents anyone from knowing which Namespace/Subspace/Path the capability is for | ||
|
||
This should be a list of `McCapability` (TODO whatever the standard encoding of this is) | ||
|
||
## Use cases | ||
|
||
### week-by-week caps | ||
|
||
Imagine you want to grant someone access to your social media posts (e.g. a private Twitter account), but you want to be able to revoke this in future. Granting someone an infinite length of time in a capability prevents you from ever being able to revoke this access. But while giving someone a capability document once is feasible, being able to continually do this while you're both offline is a challenge. | ||
|
||
The solution is to grant two capabilities, first: | ||
|
||
``` | ||
CommunalCapability: | ||
access_mode: read | ||
namespace_key: gardening.xxx... | ||
user_key: UserPublicKey | ||
delegations: | ||
- times: forever | ||
path: /capabilities/1.0/private/{their-bit}... | ||
subspace: you | ||
``` | ||
|
||
This will allow them forever access - but only to capabilities specific to them. | ||
|
||
Then you can put the actual week by week capability in there. Each time you make an entry, or regularly on expiry (whichever makes sense for your app), overwrite the entry at that path with a new capability, extending the time. This prevents there being lots of documents, and the first thing they can do on sync is get the latest capability, and then use that to sync all entries in the past. This could likely be done by the same client you're using to make the entries - if you aren't making you entries, the existing caps are sufficient. If you make an entry outside of the current end time, you'll want to update them all. Any peer will get the updated caps in the same sync as the new entries, whether that's a direct peer or via some pub/relay. | ||
|
||
To stop someone accessing future Entries, stop updating the capability (and optionally tombstone the old one). | ||
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 section needs a discussion of alternatives. Of the top of my head, you could also post a new capability to Also: recommendations for posting overlapping capabilities (chances are you want to issue week-long capabilities every 3.5 days or so). Also: How about immediately merging caps? I.e. not giving one for week 1 first and week 2 second, but one for week 1 first, and one for (week1and wek2) second, and so on. 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.
🤔 I guess I was purely imagining this from a "giving out read-only caps" perspective. So, if you've not written anything new, no need for new caps, there'd be no entries anyway. But, for allowing writing - yeahh, you want overlap. I'll extend this section and cover some of these use-cases. Thanks :)
This is what I was imagining. Will make this more explicit |
||
|
||
### Granting public access | ||
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. Can probably omit the details for this from the design process until everything else has settled. |
||
|
||
For this to work, we generally want to get our capabilities into the hands of our desired recipient, but how do we bootstrap this? People can't request an entity without proving a right to receive it first. For online things, your website could mint the desired capabilitiy entry for their key - but we have the same problem as above for week-by-week caps, needing to update it continually. This means you need to know all your followers. For a generally-public account, this may be undesirable. | ||
|
||
Grant a capability as follows: | ||
|
||
``` | ||
CommunalCapability: | ||
access_mode: read | ||
namespace_key: gardening.xxx... | ||
user_key: btnaix46fptu7nj4hwhkusutly6vhjgbigi6ewnapykza66ucf24a | ||
delegations: | ||
- times: forever | ||
path: /capabilities/1.0/public/btnaix46fptu7nj4hwhkusutly6vhjgbigi6ewnapykza66ucf24a/default | ||
subspace: you | ||
``` | ||
|
||
Publish this on your website, in your bio, etc. | ||
|
||
This public key matches the secret key `bhxh7emac7wtpbwqog4k24kfgmvjwoejexb5523g6mg7tzi7uyiwa` | ||
|
||
This will allow anyone who knows the secret (i.e. everyone. This could well be encoded into client apps by default) to sync the public-access capability. | ||
|
||
You can then put the week-by-week caps into `/capabilities/1.0/public/btnaix46fptu7nj4hwhkusutly6vhjgbigi6ewnapykza66ucf24a/default`, for all the content you want to make public. This will sync to everyone else who's opted-in to syncing the public capability Namespace. |
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.
Counterproposal: publish under
scalarmult(my_sk, their_pk)
. They can computescalarmult(their_sk, mypk)
(which yields the same value) to look up my capabilities for them.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.
Ok, that's super neat, thanks :D
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.
Question - does this pose an issue if any other app tries to use the public/secret combination as a shared secret, e.g. for encryption?
Just been reading a bunch of random stuff like this
Perhaps this should be
HKDF-Expand(scalarmult(my_sk, their_pk), "capability-publication", 32)
, so we never disclose the original shared secret?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.
There are other issues as well: if you and I both write capabilities using the naive implementation, we both use the same curve point, so an observer actually sees that the two of us have exchanged capabilities. So there needs to be some further symmetry breaking. So see this "counterproposal" more as opening up some design space rather than as a serious suggestion.
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.
But a definite "yes" to "Does this pose an issue if any other app tries to use the public/secret combination as a shared secret, e.g. for encryption?"
It might suffice to hash the shared secret (using a hash function that isn't used anywhere else - in practice, the same function you'd use elsewhere, but salted by hashing
concat("CapabilityBuddies!", <shared secret>)
) to work around problems with using the shared secret directly elsewhere? Could also break the symmetry for mutual capability-giving by using different hash functions depending on whether it is the peer with the greater or the lesser publickey issuing the capability.edit: I basically redescribed hkdf, didn't I. I mean, not exactly, but pretty close in intent...
Take all of this with a large I-am-not-a-cryptographer grain of salt.