Skip to content
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

Top-Level Spec #173

Open
wants to merge 62 commits into
base: main
Choose a base branch
from
Open

Top-Level Spec #173

wants to merge 62 commits into from

Conversation

expede
Copy link
Member

@expede expede commented Oct 9, 2023

@cla-bot cla-bot bot added the cla-signed label Oct 9, 2023
Copy link
Contributor

@Gozala Gozala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the high level it looks good and improves on some pain points we've experienced. That said, reading this:

  1. I'm no longer sure what the structure of the UCAN looks like any more perhaps even one example somewhere would help.
  2. I think imposing certain structure on commands / abilities would really simplify things yet will not prevent anyone to go with a different approach if they choose so, they'll only loose ability to leverage certain tooling that will become possible.
  3. Given significant changes here, I would be more comfortable calling this 0.20 or something and let it settle before we mark it 1.0 or 1.0-rc
  4. Document often refers to proof chains, yet leaves a lot of ambiguity what they are. I find that confusing, UCANs without proofs feel more like certificates that you can chain into authorization. I feel like it would be good to either call that out explicitly or avoid referring to proof chains


While certificate chains go a long way toward improving security, they do not provide [confinement] on their own. The principle of least authority SHOULD be used when delegating a UCAN: minimizing the amount of time that a UCAN is valid for and reducing authority to the bare minimum required for the delegate to complete their task. This delegate should be trusted as little as is practical since they can further sub-delegate their authority to others without alerting their delegator. UCANs do not offer confinement (as that would require all processes to be online), so it is impossible to guarantee knowledge of all of the sub-delegations that exist. The ability to revoke some or all downstream UCANs exists as a last resort.

## 1.4 Inversion of Control

Unlike many authorization systems where a service controls access to resources in their care, location-independent, offline, and leaderless resources require control to live with the user. Therefore, the same data MAY be used across many applications, data stores, and users.
[Inversion of control] is achieved due to two properties: self-certifying delegation and reference passing. There is no Authorization Server (AS) that sits between requestors and resources. In traditional terms, the owner of a UCAN resource is the resource server (RS) directly.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its a second instance that seems to imply that root issuer is the server. In our use cases root issuer is always the client and server always acts as a replication node that updates local replica state per issued instructions.

I think both approaches make sense and it would be unfortunate if spec emphasized the first one only.

Copy link
Member Author

@expede expede Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add words here to say that the user is the RS... it's mainly for people coming from OAuth.

FWIW Fission used to do it the way you described (for about 3 years), and we've come to reguard that as a mistake 😅 If you talk about something like a space, it misrepresents who the root authority actually is (the server). If it represents a CRDT, then yeah you supply that DID to all replicas that pull from it 💯 But that's not a storage quota or similar.

Comment on lines +154 to +162
``` mermaid
sequenceDiagram
Alice -->> Bob: Delegate
Bob ->> Bob: Validate
Bob -->> Carol: Delegate
Carol ->> Carol: Validate
Carol ->> Alice: Invoke
Alice ->> Alice: Validate
Alice ->> Alice: Execute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nitpicking here, but I think it is misleading to imply that UCAN can be validated at each hop. In practice (an in my experience painfully so) capabilities are open ended and not self describing making it impossible to validate (escalation) at every hop.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to check the UCAN chain at each hop, no?

| Owner | A Subject that controls some external resource |
| Principal | An agent identified by DID (listed in a UCAN's `iss` or `aud` field) |
| Revoker | The Issuer listed in a proof chain that revokes a UCAN |
| Subject | The Principal who's authority is delegated or invoked |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered Authority instead of Subject, which I think is well established term and a standard component of URIs. I'm fine with Subject also but it's bit more general and can be confusing

Suggested change
| Subject | The Principal who's authority is delegated or invoked |
| Authority | The Principal who's authority is delegated or invoked |

Copy link
Member Author

@expede expede Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Subject" is a standard field in many auth tokens. We also use the term "authority" in the ocap sense here: the scope of things the token allows you to do.


`nbf` and `exp` stand for "not before" and "expires at," respectively. These are standard fields from [RFC 7519][JWT] (JWT) (which in turn uses [RFC 3339]), and represent seconds in UTC without time zone or other offset. Taken together, they represent the time bounds for a token. These timestamps MUST be represented as the number of integer seconds since the Unix epoch.
A Subject MUST be referenced by [DID]. This behaves much like a [GUID], with the addition of public key verifiability. This unforgeability prevents malicious namespace collisions which can lead to [confused deputies][confused deputy problem].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ having this here, due to countless times I had to explain why not just use GUID instead. I usually describe them as GUID with provable ownership.


#### Examples
Abilities MAY be organized in a hierarchy. A typical example is a superuser capability ("anything") on a file system. Another is read vs write access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. Organizing abilities this way allows for adding more options over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure where we landed on this, but in our stack (including validator code) it is implied that http/* implies any ability under http/ namespace. I would highly recommend organizing hierarchies through name-spacing and direct reader to doin the same.

While in practice one could still choose to imply that http/put falls under crud/write most third party code will have no way of knowing. That is to say lets have default recommendations that would enable generic validator understand and allow users to opt-out at the cost of becoming opaque to others.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally go as far and say that abilities are directories of commands and are encoded as /* terminated paths containing all of the commands within that path.

I think that would be clear, intuitive and simplify reasoning for both machines and people

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would highly recommend organizing hierarchies through name-spacing and direct reader to doin the same.

We've considered this, but it breaks when you want to be able to update these later. As described, you have to impose these fully up front syntactically. Validation already has to understand the proxying pattern (ucan/*) to connect disparate chains, so validtaion is not fully syntactically driven

README.md Outdated Show resolved Hide resolved
ucan = "ucan:" ["//" resource-owner-did "/"] ucan-selector
ucan-selector = "*" / uri-scheme / ucan-cid
```
Replay attack prevention is REQUIRED. Every UCAN token MUST have a unique CID. Some simple strategies for implementing uniqueness tracking include maintaining a set of previously seen CIDs, or requiring that nonces be monotonically increasing per principal. This MAY be the same structure as a validated UCAN memoization table (if one is implemented).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow this and think more explanation is necessary here. Recommendation seems to be never issue the same UCAN which seems odd, even if almost always true in our stack due to automatic expiry window.

Specifically I do not understand understand what attack can be deployed and I think it would be good to describe it in more detail so that it can be understood by the reader.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommendation seems to be never issue the same UCAN which seems odd

Yeah, you should never use the same Invocation twice. They MUST be unique.

If they're not unique, then a PITM can intercept and replay the token. As a simple example for intuition: if your invocation says "increment counter" they can just run it thousand times even though the one submitting it doens't control the issuer's DID.

Comment on lines +410 to +414
Unlike many authorization systems where a service controls access to resources in their care, location-independent, offline, and leaderless resources require control to live with the user. Therefore, the same data MAY be used across many applications, data stores, and users. Since they don't have a single location, applying UCAN to [RSM]s and [CRDT]s MAY be modelled by lifting the requirement that the Executor be the Subject.

As an example, Alice is a user that would like to sign in to multiple devices, and has a `did:key` (she doesn't want to do complex key management). Her root key (`did:key:aliceRoot`) lives on her desktop. She creates a `ucan:*` delegation to her phone's DID (`did:key:alicePhone`).
Ultimately this comes down to a question of push vs pull. In push, the subject MUST be the specific site being pushed to ("I command you to apply the following updates to your state").

Bob would like to share access to write into a shared directory with Alice. Normally, either Bob would have to be aware of all of Alice's public keys, or the device that Bob delegates to would have to manually redelegate to Alice's other devices. With `ucan:*`, Bob can delegate to `did:key:aliceRoot`, and `did:key:alicePhone` can use the `ucan:*` resource to access Bob's shared directory.
Pull is the broad class of situations where an Invoker doesn't require that a particular replica apply its state. Applying a change to a local CRDT replica and maintaining a UCAN invocation log is a valid update to "the CRDT": a version of the CRDT Subject exists locally even if the Subject's private key is not present. Gossiping these changes among agents allows each to apply changes that it becomes aware of. Thanks to the invocation log (or equivalent integrated directly into the CRDT), provenance of authority is made transparent.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is roughly our model, except our case Subject is the identifier of the state been updated by authorized agent. Invocations are still delivered point to point, it's just our service (end)point updates Subject state per invocation log.

I am not entirely sure that is what is been described here, nor that it necessarily needs to describe that. Yet I feel it is a model that allows bridging conventional web systems with more distributed ones and might make sense to highlight as design pattern.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the difference between a "single system image" (AKA "location") versus a replicated agent. Take a WNFS or an Automerge document for example: there are two things that you may want to express:

  1. I can write into this data staructure in general (even locally)
  2. I'm allowed to update some specific replica (location)

(1) isn't in reference to any particular replica. I can make the update directly on my node, and attach the proofs for anyone to inspect if they pull the CRDT.

In (2), you push to a certain replica. You're consuming storage on a particular node, and works much more like a traditional HTTP PUT or POST request.

README.md Outdated Show resolved Hide resolved
README.md Outdated
Comment on lines 433 to 436
Note over Alice, Carol: Direct P2P Gossip
Carol ->> Bob: invoke(CRDT_ID, merge, {"Carrot"}, proof: [➋,➍])
Alice ->> Carol: invoke(CRDT_ID, merge, {"Apple"}}, proof: [➊])
Bob ->> Alice: invoke(CRDT_ID, merge, {"Banana", "Carrot"}, proof: [➋])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this image it isn't obvious to me who should be the aud of the invocation. In gossip networks it may make sense aud to be CRDT_ID as opposed to receiving actors as sender may want to update all of the actors, however that would prevent them from issuing receipts.

Above is not really a feedback more of a question that perhaps spec could try and provide some guidance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what it's worth in our system aud is receiving actor and not the CRDT_ID which is the subject. Which in our case makes sense as it allows us to issue receipts.

Copy link
Member Author

@expede expede Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gozala Right, I'm trying to show a different use case. You're treating it as a physical location, as opposed to a replicated agent. Expressing this case is one of the tradeoffs of having "closed loop" invocations: they work differently in gossip cases. I've been increasingly describing these as push vs pull.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
expede and others added 14 commits October 18, 2023 10:33
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
@expede expede changed the title DRAFT! High Level Spec Top-Level Spec Nov 2, 2023
expede and others added 4 commits November 2, 2023 15:38
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Irakli Gozalishvili <contact@gozala.io>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
@expede expede marked this pull request as ready for review November 2, 2023 23:05
UCANs (and other forms of PKI) depend on the ambient authority of the owner of each resource. This means that the discharging agent must be able to verify the root ownership at decision time. The rest of the chain in-between is self-certifying.
## Security Considerations

Each UCAN includes an assertions of what it is allowed to do. "Proofs" are positive evidence (elsewhere called "witnesses") of the possession of rights. They are cryptographically verifiable chains showing that the UCAN issuer either claims to directly own a resource, or that it was delegated to them by some claimed owner. In the most common case, the root owner's ID is the only globally unique identity for the resource.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Each UCAN includes an assertions of what it is allowed to do. "Proofs" are positive evidence (elsewhere called "witnesses") of the possession of rights. They are cryptographically verifiable chains showing that the UCAN issuer either claims to directly own a resource, or that it was delegated to them by some claimed owner. In the most common case, the root owner's ID is the only globally unique identity for the resource.
Each UCAN includes assertions of what it is allowed to do. "Proofs" are positive evidence (elsewhere called "witnesses") of the possession of rights. They are cryptographically verifiable chains showing that the UCAN issuer either claims to directly own a resource, or that it was delegated to them by some claimed owner. In the most common case, the root owner's ID is the only globally unique identity for the resource.

"an" looks unneeded here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants